當前位置:
首頁 > 知識 > 我對支付平台架構設計的一些思考

我對支付平台架構設計的一些思考

我在前一家公司的第一個任務是開發統一支付平台,由於公司的業務需求,需要接入多個第三方支付,之前公司的支付都是散落在各個項目中,及其不利於支付的管理,於是聚合三方支付,統一支付平台的任務就落在我手上,可以說是完全從 0 開始設計,經過一翻實戰總結,我得出了一些架構設計上的思考,之前就一直很想把自己的架構設計思路寫出來,但一直沒動手,前幾天在技術群里有人問到相關問題,我覺得有必要把它寫出來,以幫助到更多需要開發支付平台的開發人員。

組件模式

由於公司業務在很多地區都有,需要提供多種支付途徑,以滿足業務的發展,所以設計的支付平台需要接入多種第三方支付渠道,如:微信支付、支付寶支付、PayPal、IPayLinks 等等,我們都知道,每個第三方支付,都有自己一套對外 API,官方都有一套 SDK 來實現這些 API,我們應該如何組織這些 API 呢?

由於第三方支付渠道會隨著業務的發展變動,所以組織這些 SDK 就需要在不影響支付平台整體架構的前提下可靈活插拔,這裡我使用了組件的思想,將支付 API 拆分成各種組件支付組件、退款組件、訂單組件、賬單組件等等,那麼這樣就可以當引入一個第三方支付 SDK 時,可靈活在組件上面添加需要的 API,架構設計如下:

我對支付平台架構設計的一些思考

通過 Builder 模式根據請求參數構建對應的組件對象,將組件與外部分離,隱藏組件構建的實現。組件模式 + Builder 模式使得支付平台具備了高擴展性。

多賬戶體系

在接入各種第三方支付平台,我們當時又遇到一個賬戶的問題,原因是公司當時的小程序與 APP 使用的是不同的微信賬號,因此會出現微信支付會對應到多個賬戶的問題,而我當時設計支付平台時,沒有考慮到這個問題,當時第三方支付只對應了一個賬戶,而且不同的第三方支付的賬戶之間相互獨立且不統一。

於是我引入了多賬戶體系,多賬戶體系最重要的一個核心概念是以賬戶為粒度,接入多個第三方支付,統一賬戶的參數,構建了統一的支付賬戶體系,支付平台無需關心不同支付之間的賬戶差異以及第三方支付是否有多少個賬戶。

此時我在支付平台架構圖加上賬戶層:

我對支付平台架構設計的一些思考

前端只需要傳遞 accountId,支付平台就可以根據 accountId 查詢出對應的支付賬戶,然後通過 Builder 模式構建支付賬戶對應的組件對象,完全屏蔽不同支付之間的差異,在多賬戶體系裡面,可以支持無限多個支付賬戶,完全滿足了公司業務的發展需求。

統一回調與非同步分發處理

做過支付開發的同學都知道,目前的第三方支付都有一個特點,就是支付/退款成功後,會有一個支付/退款回調的功能,目的是為了讓商戶平台自行校驗該筆訂單是否合法,比如:防止在支付時,客戶端惡意篡改金額等參數,那麼此時支付成功後,訂單會處於支付中狀態,需要等待第三方支付的回調,如果此時收到了回調,在校驗時發現訂單的金額與支付的金額不對,然後將訂單改成支付失敗,以防止資金損失。回調的思想是只要保證最終的一致性,所以我們調起支付時,並不需要在此時校驗參數的正確性,只需要在回調時校驗即可。

講完了回調的目的,那麼我們如何來設計支付平台的回調呢?

由於支付平台接入了多個第三方支付,如果此時每個第三方支付設置一個回調地址,那麼將會出現多個回調地址,由於回調的 API 必須是暴露出去才能接受第三方的回調請求,所以就會有安全問題,我們必須在 API 外層設置安全過濾,不然很容易出現一些非法暴力訪問,所以我們需要統一回調 API,統一做安全校驗,之後再進行一層分發。

分發的機制我這裡建議用 RocketMQ 來處理,可能有人會問,如果用 RocketMQ 來做分發處理,此時怎麼實時返回校驗結果到第三方支付呢?這個問題也是我當時一直頭疼的問題,以下是我對回調設計的一些思考:

  1. 公司的系統是基於 SpringCloud 微服務架構,微服務之間通過 HTTP 通信,當時有很多個微服務接入了我的支付平台,如果用 HTTP 作分發,可以保證消息返回的實時性,但也會出現一個問題,由於網路不穩定,就會出現請求失敗或超時的問題,介面的穩定性得不到保障。
  2. 由於第三方支付如果收到 false 響應,就在接下來一段時間內再次發起回調請求,這麼做的目的是為了保證回調的成功率,對於第三方支付來說,這沒毛病,但對於商戶支付平台來說,也許就是一個比較坑爹的設計,你想一下,假設有一筆訂單在支付時惡意篡改了金額,回調校驗失敗,返回 false 到第三方支付,此時第三方支付會再重複發送回調,無論發送多少次回調,都會校驗失敗,這就額外增加了不必要的交互,當然這裡也可以用冪等作處理,以下是微信支付回調的應用場景說明:

我對支付平台架構設計的一些思考

基於以上兩點思考,我認為返回 false 到第三方支付是沒必要的,為了系統的健壯性,我採用了消息隊列來做非同步分發,支付平台收到回調請求後直接返回 true,這時你可能會提出一個疑問,如果此時校驗失敗了,但此時返回 true,會不會出現問題?首先,校驗失敗情況,訂單必定是處於支付失敗的狀態,此時返回 true 目的是為了減少與第三方支付不必要的遠程交互。

因為 RocketMQ 的消息是持久化到磁碟的,所以用消息隊列來做非同步分發最大的好處,就是可以複查消息隊列裡面的消息來排查問題,而且消息隊列可以在業務的高峰期進行流量削峰。

以下是統一回調與分發處理的架構設計圖:

我對支付平台架構設計的一些思考

聚合支付

支付平台聚合了多種第三方支付,因此在請求層需要做很多的適配工作,以滿足多種支付的需求,可能你會想,直接在適配那裡加幾行 if else 不就得了嗎,這麼做也沒問題,也可以滿足多種支付的需求,但你有沒有想過,假設此時再加一個第三方支付,你會怎麼做?你只能原有方法上加多個 else 條件,這樣就會導致請求層代碼不斷地隨著業務發展改變,使得代碼及其不優雅,而且也不好維護,這時我們就得用上策略模式,將這些 if else 代碼消除,當我們增加一個第三方支付時,我們只需要新建一個 Strategy 類就可以了,策略模式究竟怎麼使用可以看看大話設計模式。

因此我在 Builder 模式前加多了一層支付策略層:

我對支付平台架構設計的一些思考

請求處理

由於支付平台涉及到資金,支付的各種請求與返回,以及異常記錄在一個支付平台中異常重要,因此我們需要記錄每一次的支付請求記錄,以便後續排查問題。

基於這點需求,我在開始請求第三方支付之前,設計了一層 Handler 層,所有的請求都必須經過 Handler 層進行處理,Handler 核心方法如下:

public K handle(T t) {
K k;
try {
before(t);
k = execute(t);
after(k);
} catch (Exception e) {
exception(t, e);
}
return k;
}
protected abstract void before(T t);
protected abstract void after(K k);
protected abstract void exception(T t, Exception exception);

原則上來說,我設計的 Handler 層,利用了模版模式,不僅僅可以實現日誌的記錄,還可以實現多種處理方式,比如請求監控,消息推送等等,實現了 Handler 層的高擴展性。

以下是 Handler 層的架構設計圖:

我對支付平台架構設計的一些思考

寫在最後

以上就是我的支付平台架構設計思路,總結來說,支付平台需要具備可擴展性、穩定性、高可用性,因此我在設計支付平台時使用了很多設計模式以及引入消息隊列處理回調分發的問題,使得支付平台具備這幾點特性,希望能夠給你一些啟發與幫助

作者: 後端進階

原文:https://my.oschina.net/objcoding/blog/3058575

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 程序員小新人學習 的精彩文章:

TAG:程序員小新人學習 |