coder,你會設計交易系統嗎(概念篇)?

文中咱們從嚴謹的角度一步步聊到支付如何演變成獨立的系統。內容包括:系統演進過程、接口設計、數據庫設計以及代碼如何組織的示例。如有不足之處,歡迎討論共同窗習。git

從模塊到服務

我記得最開始工做的時候,全部的功能:加購物車/下單/支付 等邏輯都是放在一個項目裏。若是一個新的項目須要某個功能,就把這個部分的功能包拷貝到新的項目。數據庫也原封不動的拷貝過來,稍微根據需求改改。github

這就是所謂的 單體應用 時代,隨着公司產品線開始多元,每條產品線都須要用到支付服務。若是支付模塊調整了代碼,那麼就會到處改動、到處測試。另外一方面公司的交易數據割裂在不一樣的系統中,沒法有效彙總統一分析、管理。數據庫

這時就到了系統演進的時候,咱們把每一個產品線的支付模塊抽離成統一的服務。對本身公司內部提供統一的API使用,能夠對這些API進一步包裝成對應的SDK,供內部業務線快速接入。這裏服務使用HTTP或者是RPC協議均可以根據公司實際狀況決定。不過若是考慮到將來給第三方使用,建議使用HTTP協議,json

系統的演變過程:markdown

image-20190309104541749

總結下,將支付單獨抽離成服務後,帶來好處以下:數據結構

  1. 避免重複開發,數據隔離的現象出現;
  2. 支付系統周邊功能演進更容易,整個系統更完善豐滿。如:對帳系統、實時交易數據展現;
  3. 隨時可對外開發,對外輸出Paas能力,成爲有收入的項目;
  4. 專門的團隊進行維護,系統更有機會演進成頂級系統;
  5. 公司重要帳號信息保存一處,風險更小。

系統能力

若是咱們接手該需求,須要爲公司從零搭建支付系統。咱們該從哪些方面入手?這樣的系統到底須要具有什麼樣的能力呢?app

首先支付系統咱們能夠理解成是一個適配器。他須要把不少第三方的接口進行統一的整合封裝後,對內部提供統一的接口,減小內部接入的成本。作爲一個最基本的支付系統。須要對內提供以下接口出來:異步

  1. 發起支付,咱們取名:/gopay
  2. 發起退款,咱們取名:/refund
  3. 接口異步通知,咱們取名:/notify/支付渠道/商戶交易號
  4. 接口同步通知,咱們取名:/return/支付渠道/商戶交易號
  5. 交易查詢,咱們取名:/query/trade
  6. 退款查詢,咱們取名:/query/refund
  7. 帳單獲取,咱們取名:/query/bill
  8. 結算明細,咱們取名:/query/settle

一個基礎的支付系統,上面8個接口是確定須要提供的(這裏忽略某些支付中的轉帳、綁卡等接口)。如今咱們來基於這些接口看看都有哪些系統會用到。數據庫設計

image-20190309111001880

下面按照系統維度,介紹下這些接口如何使用,以及內部的一些邏輯。學習

應用系統

通常支付網關會提供兩種方式讓應用系統接入:

  1. 網關模式,也就是應用系統本身須要開發一個收銀臺;(適合提供給第三方)
  2. 收銀臺模式,應用系統直接打開支付網關的統一收銀臺。(內部業務)

下面爲了講清楚設計思路,咱們按照 網關模式 進行講解。

對於應用系統它須要可以請求支付,也就是調用 gopay 接口。這個接口會處理商戶的數據,完成後會調用第三方網關接口,並將返回結果統一處理後返回給應用方。

這裏須要注意,第三方針對支付接口根據個人經驗大體有如下狀況:

  1. 支付時,不須要調用第三方,按照規則生成數據便可;
  2. 支付時,須要調用第三方多個接口完成邏輯(這可能比較慢,大型活動時須要考慮限流/降配);
  3. 返回的數據是一個url,可直接跳轉到第三方完成支付(wap/pc站);
  4. 返回的數據是xml/json結構,須要拼裝或做爲參數傳給她的sdk(app)。

這裏因爲第三方返回結構的不統一,咱們須要統一處理成統一格式,返回給商戶端。我推薦使用json格式。

{
    "errno":0,
    "msg":"ok",
    "data":{

    }
}
複製代碼

咱們把全部的變化封裝在 data 結構中。舉個例子,若是返回的一個url。只須要應用程序發起 GET 請求。咱們能夠這樣返回:

{
    "errno":0,
    "msg":"ok",
    "data":{
        "url":"xxxxx",
        "method":"GET"
    }
}
複製代碼

若是是返回的結構,須要應用程序直接發起 POST 請求。咱們能夠這樣返回:

{
    "errno":1,
    "msg":"ok",
    "data":{
        "from":"<form action="xxx" method="POST">xxxxx</form>",
        "method":"POST"
    }
}
複製代碼

這裏的 form 字段,生成了一個form表單,應用程序拿到後可直接顯示而後自動提交。固然封裝成 from表單這一步也能夠放在商戶端進行。

上面的數據格式僅僅是一個參考。你們可根據本身的需求進行調整。

通常應用系統除了會調用發起支付的接口外,可能還須要調用 支付結果查詢接口。固然大多數狀況下不須要調用,應用系統對交易的狀態只應該依賴本身的系統狀態。

對帳系統

對於對帳,通常分爲兩個類型:交易對帳結算對帳

交易對帳

交易對帳的核心點是:檢查每一筆交易是否正確。它主要目的是看咱們系統中的每一筆交易與第三方的每一筆交易是否一致。

這個檢查邏輯很簡單,對兩份帳單數據進行比較。它主要是使用 /query/bill 接口,拿到在第三方那邊完成的交易數據。而後跟我方的交易成功數據進行比較。檢查是否存在偏差。

這個邏輯很是簡單,可是有幾點須要你們注意:

  1. 我方的數據須要正常支付數據+重複支付數據的總和;
  2. 對帳檢查不成功主要包括:金額不對第三方沒有找到對應的交易數據我方不存在對應的交易數據

針對這些狀況都須要有對應的處理手段進行處理。在個人經驗中上面的狀況都有過遇到。

金額不對:主要是因爲第三方的問題,多是系統升級故障、多是帳單接口金額錯誤;

第三方無交易數據: 多是拉去的帳單時間維度問題(好比存在時差),這種時區問題須要本身跟第三方確認找到對應的時間差。也多是被攻擊,有人冒充第三方異步通知(說明系統校驗機制又問題或者密鑰泄漏了)。

本身系統無交易數據: 這種緣由多是第三方通知未發出或者未正確處理致使的。

上面這些問題的處理絕大部份均可以依賴 query/trade query/refund 來完成自動化處理。

結算對帳

那麼有了上面的 交易對帳 爲何還須要 結算對帳 呢?這個系統又是幹嗎的?先來看下結算的含義。

結算,就是第三方網關在固定時間點,將T+x或其它約定時間的金額,匯款到公司帳號。

下面咱們假設結算週期是: T+1。結算對帳主要使用到的接口是 /query/settle,這個接口獲取的主要內容是:每一筆結算的款項都是由哪些筆交易組成(交易成功與退款數據)。以及本次結算扣除多少手續費用。

它的邏輯其實也很簡單。咱們先從本身的系統按照 T+1 的結算週期,計算出對方應該匯款給咱們多少金額。而後與剛剛接口獲取到的數據金額比較:

銀行收款金額 + 手續費 = 我方系統計算的金額

這一步檢查經過後,說明金額沒有問題。接下來須要檢查本次結算下的每一筆訂單是否一致。

結算系統是 強依賴 對帳系統的。若是對帳發現異常,那麼結算金額確定會出現異常。另外結算須要注意的一些問題是:

  • 銀行可能會自行退款給用戶,由於用戶可直接向本身髮卡行申請退款;
  • 結算也存在時區差問題;
  • 結算接口中的明細交易狀態與我方並不徹底一致。好比:銀行結算時發現某筆退款完成,但我方系統在進行比較時按照未退款完成的邏輯在處理。

針對上面的問題,你們根據本身的業務需求須要作一些方案來進行自動化處理。

財務系統

財務系統有不少內部業務,我這裏只聊與支付系統相關的。(固然上面的對帳系統也能夠算是財務範疇)。

財務系統與支付主要的一個關係點在於校驗交易、以及退款。這裏校驗交易可使用 query/trade query/refund這兩個接口來完成。這個邏輯過程就不須要說了。下面重點說下退款。

我看到不少的系統退款是直接放在了應用裏邊,用戶申請退款直接就調用退款接口進行退款。這樣的風險很是高。支付系統的關於資金流向的接口必定要慎重,不能過多的直接暴露給外部,帶來風險。

退款的功能應該是放到財務系統來作。這樣能夠走內部的審批流程(是否須要根據業務來),而且在財務系統中能夠進行更多檢查來以爲是否當即進行退款,或者進入等待、拒絕等流程。

第三方網關

針對第三方主要使用到的其實就是異步通知與同步通知兩個接口。這一部分的邏輯其實很是簡單。就是根據第三方的通知完成交易狀態的變動。以及通知到本身對應的應用系統。

這部分比較複雜的是,第三方的通知數據結構不統1、通知的類型不統一。好比:有的退款是同步返回結果、有的是異步返回結果。這裏如何設計會在後面的 系統設計 中給出答案。

第一部份的內容就到此結束了。若是有什麼疑問歡迎到咱們GitHub主頁留言。

GitHub: https://github.com/skr-shop

相關文章
相關標籤/搜索