在支付系統中,支付網關和支付渠道的對接是最核心的功能。其中支付網關是對外提供服務的接口,所有需要渠道支持的資金操作都需要通過網關分發到對應的渠道模塊上。一旦定型,后續就很少,也很難調整。而支付渠道模塊是接收網關的請求,調用渠道接口執行真正的資金操作。每個渠道的接口,傳輸方式都不盡相同,所以在這里,支付網關相對於支付渠道模塊的作用,類似設計模式中的 wrapper,封裝各個渠道的差異,對網關呈現統一的接口。而網關的功能是為業務提供通用接口,一些和渠道交互的公共操作,也會放置到網關中。
支付網關在支付系統參考架構圖中的位置如下圖所示:
1 功能概述
支付系統對其他系統,特別是交易系統,提供的支付服務包括簽約、支付、退款、充值、轉帳和解約等。有些地方還會額外提供簽約並支付的接口,用於支持在支付過程中綁卡。每個服務實現的流程也是基本類似,包括下單、取消訂單、退單、查單等操作。每個操作實現,都包括參數校驗、支付路由、生成訂單、風險評估、調用渠道服務、更新訂單和發送消息這 7 步,對於一些比較復雜的渠道服務,還會涉及到異步同通知處理的步驟。
一般來說,支付主流程會涉及到如下模塊:
- 商戶側應用發起支付請求。注意,這個請求一般是從服務器端發起的,比如用戶在手機端提交“立即支付”按鈕后,商戶的服務器端會先生成訂單,然后請求支付網關執行支付。
- 支付請求被發送到支付(API)網關上。網關對這個請求進行一些通用的處理,比如 QPS 控制、驗簽等,然后根據支付請求的場景(網銀、快捷、外卡等),調用對應的支付產品。
- 支付產品對用戶請求進行預處理,包括執行參數校驗、根據支付路由尋找合適的支付通道、評估交易風險、生成訂單、調用通道落地執行支付、響應通道的結果並將交易結果通知到商戶側。
- 支付產品調用支付通道執行支付。這個請求並不是直接落地到通道上,而是通過支付通道前置來封裝,由支付通道前置來完成和通道的交付。支付產品是按照可以提供的支付服務來設計的。
- 支付通道前置(以下在不引起混淆的情況下,都簡稱支付通道),負責和支付通道之間的通訊,調用支付通道接口完成最終的支付操作。
不同類型的支付產品,其對外提供的接口也會有區別。后續分類別介紹各種支付產品的設計。這里重點介紹支付(API)網關設計、支付產品的整體流程實現。而軟件架構的設計,是基於微服務架構來描述的。
2 支付(API)網關
支付網關是直接對接業務系統的接口,它本身並不執行任何支付相關的業務邏輯。它將支付產品接口中和業務無關的功能提取出來,在這里統一實現。這樣在具體產品接口中,就無需考慮這些和業務無關的邏輯。支付網關設計還和對外的接口參數有關。我們看一下業內幾個主流的支付平台的接口設計。
2.1 支付寶
對外接口采用統一參數的方式,參考「App支付請求參數說明」。接口參數分為三層: 公共參數、業務參數、還有業務擴展參數,其中公共參數是各個請求接口中公用的。
業務相關的參數,通過特定的規則拼接再biz_content
上,最后將參數生成簽名,放到sign
字段中。
支付寶的接口混合
json
格式和query string
格式,在參數命名上,既有下划線方式的,也有駝峰的。英文單詞的使用也不太規范。期待后續版本能做的更好。
2.2 微信支付
和支付寶不一樣,微信支付是采用 XML 格式來作為報文傳輸。在其「接口文檔說明」中, 對 XML 報文格式有詳細的描述。當然,也使用簽名字符串來保證接口的安全,簽名結果放在sign
標簽下。
在接口設計上,和支付寶還有一些差距。有些參數命名不一致,比如商戶號,有些接口中叫
mch_id
,有些接口是partnerid
.
2.3 PayPal
PayPal 是標准的 Restful 設計,將支付中涉及到的對象,如 Payment、Order、Credit Card 等,以資源的形式,支持通過 Restful API 來操作。
PayPal 的定位以及設計目標和國內第三方支付平台不同,它以支持國際營收為主。對國內應用來說,其易用性和支付寶、微信支付相比還稍遜一些,不過 Paypal 一直是支付 API 設計的典范。
對電商支付平台來說,其定位更接近於一個聚合支付。聚合多種支付方式,為公司各個業務提供支持。 在這里,支付網關和支付產品的設計尤為關鍵。合理的接口設計能夠大大降低支付渠道對接的開發工作量。一般支付產品不會超過 10 個,而根據公司的規模,對接的支付渠道超過 100 個都有可能。
3 設計原則
如上所述,支付網關、支付產品和支付渠道的職責分工為:
- 按照支付能力來划分支付產品。
- 同一支付能力的公共支付流程,在支付產品中實現。支付產品提供的是和渠道無關的、和支付能力流程相關的功能。
- 在各支付產品中,其和支付能力無關的公共功能,在支付網關上實現。
按照這個分工,在支付網關上實現的主要功能:
- API 路由:在聚合支付場景下,當有多個支付產品可以提供支持時,使用支付網關可以讓接入方對接時無需考慮支付產品的部署問題。
- 接口安全:熔斷、限流與隔離。這對支付服務來說尤為重要。這是微服務架構的基本功能,本文不做描述。
如下功能,是在支付產品中提供:
- 風控攔截:風控是和支付產品有關,不同產品的風控措施、處理對策也是不同的,所以風控是在產品層實現。
- 支付路由:路由也是和產品有關,不同產品路由策略也不同。
- 參數校驗:這也是和支付產品相關的,不同的產品接口其參數也不同。
- 支付流程:生成交易記錄、落地渠道執行支付、同步和異步通知等操作。
如下功能,可以在產品層或者網關層實現:
- 身份驗證:確認付款方、收款方、渠道是否有執行當前操作的權限。在那一層實現取決於這些信息是否有提煉為公共行為。
- 驗簽:對接口參數進行簽名並驗證其簽名。這是為了避免接口被盜刷和篡改的必要手段。如果對各個接口采用統一的簽名規則,則可以在網關層實現。
4 簽名和驗簽
API 路由、接口安全這兩塊內容是微服務的基本模式,本文不再介紹。有興趣同學可以參考相關資料。這里重點說下支付所必須的簽名和驗簽。
對接口進行簽名是防止接口被盜刷的重要手段。大部分第三方支付和銀行的接口簽名規則類似。query string
格式參數可以參考支付寶的簽名過程,XML 格式的可以參考微信支付的簽名過程。其實兩者都是類似的,他們的簽名和驗簽過程可以為支付系統服務器端和商戶側交互提供參考。
主流的加密算法有 RSA、MD5 和 DES。支付寶使用 RSA, 微信支付使用 MD5.
- 使用 RSA 來簽名,需要商戶側提供 RSA 的公鑰給支付系統,將私鑰自己保存。商戶側使用私鑰來加密請求字符串,支付系統使用公鑰來解密。
- 使用 MD5 來簽名,需要商戶側和支付系統都保留 MD5 的 Key,商戶側和支付系統都使用這個 Key 來加密請求字符串,驗證結果是否一致。
加密的一個通用過程是:
第 1 步:將各個參數拼接成一個有序的字符串。參數是key=value
的格式, 按照 key 的字符順序排序,以&
或者其他符號來拼接。
-
appid:wxd930ea5d5a258f4f
-
mch_id: 10000100
-
device_info: 1000
-
body:test
-
nonce_str:ibuaiVcKdpRxkhJA
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
這種請求,將被拼接為:
appid=wxd930ea5d5a258f4f&body=test&device_info=1000&mch_id=10000100&nonce_str=ibuaiVcKdpRxkhJA
- 1
- 1
第 2 步:使用 RSA 對字符串進行簽名,生成簽名字符串。
cYmuUnKi5QdBsoZEAbMXVMmRWjsuUj%2By48A2DvWAVVBuYkiBj13CFDHu2vZQvmOfkjE0YqCUQE04kqm9Xg3tIX8tPeIGIFtsIyp%2FM45w1ZsDOiduBbduGtRo1XRsvAyVAv2hCrBLLrDI5Vi7uZZ66Lo5J0PpUUWwyQGt0M4cj8g%3D
- 1
- 1
第 3 步:將簽名字符串拼接到原請求中,生成最終的字符串。
appid=wxd930ea5d5a258f4f&body=test&device_info=1000&mch_id=10000100&nonce_str=ibuaiVcKdpRxkhJA&sign=cYmuUnKi5QdBsoZEAbMXVMmRWjsuUj%2By48A2DvWAVVBuYkiBj13CFDHu2vZQvmOfkjE0YqCUQE04kqm9Xg3tIX8tPeIGIFtsIyp%2FM45w1ZsDOiduBbduGtRo1XRsvAyVAv2hCrBLLrDI5Vi7uZZ66Lo5J0PpUUWwyQGt0M4cj8g%3D
- 1
- 1
服務器端在接收到這個請求后,使用 RSA 的公鑰來解密sign
字段,如果解密成功,則對比解密結果和原始請求是否一致。如果是使用 MD5,則在商戶側和支付系統都使用這個過程來加密,檢查最終的結果是否一致。