我現所在公司是開發統一支付平臺,因爲公司的業務需求,須要接入多個第三方支付,爲了後續對支付平臺更深刻的思考,整理出來。安全
因爲公司業務在不少地區都有,須要提供多種支付途徑,以知足業務的發展,因此設計的支付平臺須要接入多種第三方支付渠道,如:微信支付、支付寶支付、卡聯支付、迅聯 等等,咱們都知道,每一個第三方支付,都有本身一套對外 API,官方都有一套 SDK 來實現這些 API,咱們應該如何組織這些 API 呢?微信
因爲第三方支付渠道會隨着業務的發展變更,因此組織這些 SDK 就須要在不影響支付平臺總體架構的前提下可靈活插拔,這裏我使用了組件的思想,將支付 API 拆分紅各類組件支付組件、退款組件、訂單組件、帳單組件、訂單異常處理組件等等,那麼這樣就能夠當引入一個第三方支付 SDK 時,可靈活在組件上面添加須要的 API,架構設計以下:網絡
經過Builder模式根據請求參數,構建對應的組件對象,將組件與外部分離,隱藏組件構建的實現。 組件模式+builder模式使得支付平臺具有了高擴展性。對接第三方支付都有一個特色,就是支付、退款成功後,會有一個支付、退款的回調功能,目的是:一、讓商戶平臺自行校驗訂單的合法性,好比:防止支付時,客戶端惡意篡改金額等參數,那麼此時支付成功後訂單處於支付中狀態,須要等待第三方回調,若是收到回調校驗發現支付金額和訂單金額不對,咱們能夠把訂單狀態改成失敗,防止資金損失。2:經過回調咱們能夠處理咱們本身系統的業務邏輯,商品的分發等等。回調的思想只要保證數據的最終一致性,因此咱們發起支付時不須要校驗參數的正確性,只需在回調時校驗。那咱們應該如何來設置支付平臺的回調。架構
因爲支付平臺須要接入多個第三方支付,若是此時每一個第三方支付設置一個回調地址,那麼將會出現多個回調地址,因爲回調的API必須是暴露出去才能接受第三的回調,因此就會有安全問題,咱們必須在API外層設置安全過濾,因此咱們須要統一回調API,統一作安全校驗,以後再進行一層分發。異步
分發機制咱們採用的是RabbitMq,這裏可能會有疑問,若是用mq來作分發處理,此時怎麼實時返回校驗結果給第三方?如下是對回調的一些思考:微服務
1. 系統是基於Spring boot微服務架構,服務之間經過Http通訊,若是用Http分發,能夠保證消息返回的實時性,但因爲網絡不穩定,會出現請求失敗或超時的問題,接口的穩定性得不到保障。 2. 因爲第三方支付若是收到false響應,就會在接下來的一段時間內再次發起回調請求,這麼作的目的是爲了保障回調的成功率,對於第三方支付來講,這沒毛病,但對於商戶支付平臺來講,也許就是一個比較坑爹的設計,你想一下,假設有一筆訂單在支付時惡意篡改了金額,回調校驗失敗,返回 false 到第三方支付,此時第三方支付會再重複發送回調,不管發送多少次回調,都會校驗失敗,這就額外增長了沒必要要的交互,固然這裏也能夠用冪等做處理,如下是微信支付回調的應用場景說明:微信支付
基於以上兩點思考,我認爲返回 false 到第三方支付是不必的,爲了系統的健壯性,我採用了消息隊列來作異步分發,支付平臺收到回調請求後直接返回 true,這時你可能會提出一個疑問,若是此時校驗失敗了,但此時返回 true,會不會出現問題?首先,校驗失敗狀況,訂單一定是處於支付失敗的狀態,此時返回 true 目的是爲了減小與第三方支付沒必要要的遠程交互。ui
由於Mq是把消息持久化到磁盤的,因此消息隊列來作分發的最大好處,就是複查消息隊列裏面的消息來排查問題,並且消息隊列能夠在業務高峯期進行流量削峯。架構設計
如下是統一回調和分發的架構圖:設計
支付平臺聚合了多種第三方支付,所以在請求層須要作不少的適配工做,以知足多種支付的需求,可能你會想,直接在適配那邊加幾行if else 不就得了嗎,這麼作也沒問題,也能夠知足多種支付的需求,但若是再加一個第三方支付呢,你只能在原有方法上加多個else條件,這麼會致使請求層代碼不斷隨着業務發展改變,使得代碼極其不優雅,並且也不方便維護,這時咱們能夠用策略模式,將這些if else代碼消除,當咱們增長一個第三方支付時,咱們只須要新建一個strategy類就能夠了,架構以下:
因爲支付平臺涉及到資金,支付的各類請求和返回,以及異常記錄在一個支付平臺中異常重要,所以咱們須要記錄每一次的支付請求記錄,以便後續排查問題。
基於這一點,咱們在開始請求第三方支付以前,設計一層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層的高擴展性