文中咱們從嚴謹的角度一步步聊到支付如何演變成獨立的系統。內容包括:系統演進過程、接口設計、數據庫設計以及代碼如何組織的示例。如有不足之處,歡迎討論共同窗習。git
我記得最開始工做的時候,全部的功能:加購物車/下單/支付 等邏輯都是放在一個項目裏。若是一個新的項目須要某個功能,就把這個部分的功能包拷貝到新的項目。數據庫也原封不動的拷貝過來,稍微根據需求改改。github
這就是所謂的 單體應用 時代,隨着公司產品線開始多元,每條產品線都須要用到支付服務。若是支付模塊調整了代碼,那麼就會到處改動、到處測試。另外一方面公司的交易數據割裂在不一樣的系統中,沒法有效彙總統一分析、管理。數據庫
這時就到了系統演進的時候,咱們把每一個產品線的支付模塊抽離成統一的服務。對本身公司內部提供統一的API使用,能夠對這些API進一步包裝成對應的SDK,供內部業務線快速接入。這裏服務使用HTTP或者是RPC協議均可以根據公司實際狀況決定。不過若是考慮到將來給第三方使用,建議使用HTTP協議,json
系統的演變過程:數據結構
總結下,將支付單獨抽離成服務後,帶來好處以下:app
若是咱們接手該需求,須要爲公司從零搭建支付系統。咱們該從哪些方面入手?這樣的系統到底須要具有什麼樣的能力呢?異步
首先支付系統咱們能夠理解成是一個適配器。他須要把不少第三方的接口進行統一的整合封裝後,對內部提供統一的接口,減小內部接入的成本。作爲一個最基本的支付系統。須要對內提供以下接口出來:數據庫設計
/gopay
/refund
/notify/支付渠道/商戶交易號
/return/支付渠道/商戶交易號
/query/trade
/query/refund
/query/bill
/query/settle
一個基礎的支付系統,上面8個接口是確定須要提供的(這裏忽略某些支付中的轉帳、綁卡等接口)。如今咱們來基於這些接口看看都有哪些系統會用到。學習
下面按照系統維度,介紹下這些接口如何使用,以及內部的一些邏輯。測試
通常支付網關會提供兩種方式讓應用系統接入:
下面爲了講清楚設計思路,咱們按照 網關模式 進行講解。
對於應用系統它須要可以請求支付,也就是調用 gopay
接口。這個接口會處理商戶的數據,完成後會調用第三方網關接口,並將返回結果統一處理後返回給應用方。
這裏須要注意,第三方針對支付接口根據個人經驗大體有如下狀況:
這裏因爲第三方返回結構的不統一,咱們須要統一處理成統一格式,返回給商戶端。我推薦使用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
接口,拿到在第三方那邊完成的交易數據。而後跟我方的交易成功數據進行比較。檢查是否存在偏差。
這個邏輯很是簡單,可是有幾點須要你們注意:
針對這些狀況都須要有對應的處理手段進行處理。在個人經驗中上面的狀況都有過遇到。
金額不對:主要是因爲第三方的問題,多是系統升級故障、多是帳單接口金額錯誤;
第三方無交易數據: 多是拉去的帳單時間維度問題(好比存在時差),這種時區問題須要本身跟第三方確認找到對應的時間差。也多是被攻擊,有人冒充第三方異步通知(說明系統校驗機制又問題或者密鑰泄漏了)。
本身系統無交易數據: 這種緣由多是第三方通知未發出或者未正確處理致使的。
上面這些問題的處理絕大部份均可以依賴 query/trade
query/refund
來完成自動化處理。
那麼有了上面的 交易對帳 爲何還須要 結算對帳 呢?這個系統又是幹嗎的?先來看下結算的含義。
結算,就是第三方網關在固定時間點,將T+x或其它約定時間的金額,匯款到公司帳號。
下面咱們假設結算週期是: T+1。結算對帳主要使用到的接口是 /query/settle
,這個接口獲取的主要內容是:每一筆結算的款項都是由哪些筆交易組成(交易成功與退款數據)。以及本次結算扣除多少手續費用。
它的邏輯其實也很簡單。咱們先從本身的系統按照 T+1 的結算週期,計算出對方應該匯款給咱們多少金額。而後與剛剛接口獲取到的數據金額比較:
銀行收款金額 + 手續費 = 我方系統計算的金額
這一步檢查經過後,說明金額沒有問題。接下來須要檢查本次結算下的每一筆訂單是否一致。
結算系統是 強依賴 對帳系統的。若是對帳發現異常,那麼結算金額確定會出現異常。另外結算須要注意的一些問題是:
針對上面的問題,你們根據本身的業務需求須要作一些方案來進行自動化處理。
財務系統有不少內部業務,我這裏只聊與支付系統相關的。(固然上面的對帳系統也能夠算是財務範疇)。
財務系統與支付主要的一個關係點在於校驗交易、以及退款。這裏校驗交易可使用 query/trade
query/refund
這兩個接口來完成。這個邏輯過程就不須要說了。下面重點說下退款。
我看到不少的系統退款是直接放在了應用裏邊,用戶申請退款直接就調用退款接口進行退款。這樣的風險很是高。支付系統的關於資金流向的接口必定要慎重,不能過多的直接暴露給外部,帶來風險。
退款的功能應該是放到財務系統來作。這樣能夠走內部的審批流程(是否須要根據業務來),而且在財務系統中能夠進行更多檢查來以爲是否當即進行退款,或者進入等待、拒絕等流程。
針對第三方主要使用到的其實就是異步通知與同步通知兩個接口。這一部分的邏輯其實很是簡單。就是根據第三方的通知完成交易狀態的變動。以及通知到本身對應的應用系統。
這部分比較複雜的是,第三方的通知數據結構不統1、通知的類型不統一。好比:有的退款是同步返回結果、有的是異步返回結果。這裏如何設計會在後面的 系統設計 中給出答案。
第一部份的內容就到此結束了。若是有什麼疑問歡迎到咱們GitHub主頁留言。
GitHub: https://github.com/skr-shop