聚合支付系統設計(二)
阿新 • • 發佈:2018-11-05
支付閘道器與非同步通知設計
支付閘道器
使用者下單成功後,要經過收銀臺發起支付流程,支付閘道器就是使用者發起支付流程的入口地址。支付閘道器需要接收訂單的部分資料(訂單號、待支付金額、商品描述資訊等)和交易資料(支付方式、交易起止時間、回撥地址等)以及簽名,支付閘道器接收到收銀臺的支付請求後,驗證並處理支付請求資料,再根據支付方式獲取支付例項(比如WechatAPPPayment物件),發起支付(執行doPay)。 支付交易流水錶,以下重要欄位:
Name | Field | remark |
系統訂單號 | order_id | 商戶訂單系統的真實訂單號 |
商戶支付單號 | trade_no | 傳給第三方平臺的訂單號 |
支付流水號 | out_trade_no | 第三方平臺返回的交易流水號 |
支付金額 | total_fee | 訂單支付金額 |
支付狀態 | pay_status | enum(wait、success、failed) |
同步狀態 | sync_status | enum(wait、success、failed) |
支付時間 | pay_time | |
非同步通知時間 | sync_time |
支付閘道器設計,需要注意以下幾點:
- 支付閘道器用來接收來自訂單系統的支付請求,由於考慮到系統做活動要支撐比平日多出幾倍甚至幾十倍的QPS,在支付閘道器,就要考慮用訊息佇列中介軟體來快取請求的支付資料,再有後端消費程序資料寫入db,比如使用Redis List做佇列服務,Redis Hash表做快取。不建議單條訂單資料直接做為key儲存至Redis,Redis例項如果keys總量太大,會導致查詢效能驟降。因為無法直接對Redis Hash表中field設定過期時間,我們需要寫指令碼按規則去清理老資料以騰空間。
- 如果系統使用php來程式設計,某些商業銀行直連支付,建行app支付、招行一網通支付的驗籤流程需要通過JavaBridge來呼叫銀行提供的jar包,完成簽名,php例項化java類庫效能較差。因此,要避免每次閘道器支付請求的初始化過程來引入這些jar包, 需要載入的時候再例項化。
- 商戶支付系統在向第三方支付平臺發起支付請求時,商戶訂單號欄位不能直接使用商戶訂單系統的真實訂單號orderid,要重新生成一個支付單號tradeno,為什麼要這樣做,有以下幾個原因:
- 微信支付,不是直接call支付閘道器,而是先請求統一下單介面,獲取預支付交易會話標識,然後有這個會話標識發起支付請求。那麼問題來了,如果使用者在app下單選擇微信app支付,獲取到會話標識後,沒有完成支付,然後在商戶的公眾號平臺發現這個訂單,再次支付,系統會切換到微信公眾號支付,這時候統一下單介面會報錯,因為該訂單已經申請過會話標識了。因此,需要重新生成一個新的支付單號,再來呼叫統一下單介面;
- 招商銀行閘道器支付,對商戶訂單號的格式要求為6位或10位數字,因此,所生成的支付單號比較特殊,要按其要求生成;
- 對於擁有賬戶餘額模組的商戶平臺,收銀臺一般都支援第三方支付和餘額抵扣組合使用,已完成訂單支付。當用戶在多次選擇或取消餘額部分抵扣,導致訂單支付金額變動時,已經通過介面申請到支付憑證的,訂單無法再次申請支付憑證,需要重新生成一個新的支付單號使用。
支付非同步通知
支付通知,是用來接收來自銀行或者第三方支付平臺的訂單支付結果通知,分為兩種,一種是同步通知(又稱前臺通知),一種是非同步通知(又稱後臺通知),簡單的說,商戶支付系統收到支付同步通知並且支付狀態為已支付,我們需要將訂單支付狀態修改為支付確認中,商戶支付系統收到支付非同步通知並且支付狀態為支付成功,我們需要將訂單支付狀態修改為已支付。再次強調下,商戶支付系統要以非同步通知的結果為準。
非同步通知設計,需要注意以下幾點:
- 商戶支付系統對已接入的第三方支付平臺提供的通知地址不要重複,商戶支付系統通過判斷請求的URI就清楚的知道這是來自哪個平臺哪一種支付方式的支付通知,不要嘗試對已接入的第三方支付平臺只提供一個公用的URL,然後通過解析報文格式來判斷是來自哪個平臺哪一種支付方式,這樣做是混亂且不可控的;
- 支付系統拿到支付通知報文之後,首先解析成統一的格式,比如array,然後要驗證簽名是否正確、還要驗證支付金額是否一致,最後再去判斷訂單支付狀態;
- 支付系統對所有金額(float型別)的判斷要先使用BC數學函式轉換為int型別,再做判斷,避免因php浮點數精度問題導致金額校驗失敗;
- 對已通過驗證的訂單資料,可以將其push到支付成功佇列,有獨立的常駐程序(佇列消費程序)去通知訂單中心,為什麼這樣做,有以下三個原因:
- 來自第三方支付平臺的非同步通知,是有第三方支付平臺的服務端主動發起,目的只有一個,商戶支付系統拿到支付通知報文並驗證通過後,要立刻對其返回正確的回執,因此我們儘量不引入除接收支付報文之外的其他邏輯。我們商戶伺服器在接受到通知請求後,處理好資料並將其push到佇列,是相對效能開銷最小且穩定的做法,可以最快速度給第三方支付平臺伺服器以正確回執,且第三方支付平臺不受商戶自身業務處理流程的影響;
- 如果在非同步通知處理流程中,直接發起對商戶訂單系統的通知,如遇到極端情況,商戶訂單系統出現異常或者響應過慢,勢必會影響到商戶支付系統對第三方支付平臺的回執,雖然部分第三方支付平臺有重發機制,但基於效能以及訂單到賬效率考慮,我們商戶方儘可能做到一次就響應成功;
- 通過訊息佇列這一中介軟體,消費程序在對商戶訂單系統通知支付結果的過程中,如遇異常,沒有收到商戶訂單系統的成功應答,那麼我們可以將資料push到異常處理佇列中,再有異常處理佇列的消費程序定時去消費這部分資料,直到消費程序收到商戶訂單系統的成功應答為止。
原文地址:https://blog.csdn.net/think2017/article/details/79821281