錢被扣走了,可是訂單卻未成功!支付掉單異常最全解決方案

前言

好了,迴歸到今天的主題,今天分享一下支付系統中異常一些處理方式。數據庫

其實這些處理方式並不僅是侷限於支付系統,也能夠適用於其餘系統,你們能夠借鑑,應用到本身系統中,提升本身系統的健壯性。網絡

異常是系統運行不可避免會發生的問題,若是一切都正常,咱們的系統設計將會至關簡單。架構

可是惋惜沒有人能作到這一點,因此爲了處理異常可能致使的問題,咱們不得不須要加上不少額外的設計,用來應對這些異常。框架

能夠說系統設計中,異常處理須要咱們着重思考,將會佔據咱們大部分的精力。異步

下面咱們先來看下支付系統中最多見的異常:掉單分佈式

歡迎關注個人公衆號:程序通事,得到平常乾貨推送。若是您對個人專題內容感興趣,也能夠關注個人博客:studyidea.cnide

掉單異常

一個最多見的支付平臺架構關係以下所示:性能

上圖咱們是站在第三方支付公司支付角度,若是是本身公司的內部支付系統,那麼外部商戶這一塊其實就是公司內部一些系統,好比說訂單系統,而外部支付渠道其實就是第三方支付公司idea

咱們以攜程爲例,在其上面發起一筆訂單支付,將會通過三個系統:設計

  1. 攜程建立訂單,向第三方支付公司發起支付請求
  2. 第三方支付公司建立訂單,並向工行發起支付請求
  3. 工行完成扣款操做,返回第三方支付公司
  4. 第三方支付完成訂單更新並返回攜程
  5. 攜程變動訂單狀態

上面的流程,簡單以下圖所示:

在這個過程就可能會碰到,用戶工行卡已經扣款,可是攜程訂單卻仍是待支付,咱們一般將這種狀況稱爲掉單

上述掉單的場景,多數是由於③、⑤環節信息丟失致使,這種掉單咱們將其稱爲外部掉單

還有一種極少數的狀況,收到 ③、⑤環節返回信息,可是在④、⑥環節內部系統更新訂單狀態失敗,從而致使丟失支付成功的信息,這類掉單因爲是內部問題,咱們一般將其稱之爲內部掉單

外部掉單

外部掉單是由於沒有收到對端返回信息,這種狀況極有多是網絡問題,也有可能對端處理邏輯太慢,致使我方請求超時,直接斷開了網絡請求。

增長超時時間

對於這種狀況,第一個最簡單的解決辦法,適當的增長超時時間

不過這裏須要注意了,在咱們增長網絡超時時間以後,咱們可能還須要調整整個鏈路的超時時間,否則有可能致使整個鏈路內部差事從而引發內部掉單。

畫外音:對接外部渠道,必定要設置網絡鏈接超時時間與讀取超時時間

接收異步通知

第二個辦法,接收渠道異步回執通知信息。

通常來講,如今支付渠道接口咱們均可以上送一個異步回調地址,當渠道端處理成功,將會把成功信息通知到這個回調地址上。

這種狀況下,咱們只須要接收通知信息,而後解析,再更新內部訂單狀態。

支付系統異常處理-支付異步通知

這種狀況下,咱們須要注意幾點:

  1. 對於異步請求信息,必定須要對通知內容進行簽名驗證,並校驗返回的訂單金額是否與商戶側的訂單金額一致,防止數據泄漏致使出現「假通知」,形成資金損失。
  2. 異步通知將會發送屢次,因此異步通知處理須要冪等。

掉單查詢

有的渠道可能沒有提供異步通知的功能,只提供了訂單查詢的接口,這種狀況下,咱們只能使用第三種解決辦法,定時掉單查詢。

咱們能夠將這類超時未知的訂單的單獨保存到掉單表,而後定時向渠道端查詢訂單的狀態。

若查詢成功或者明確失敗(好比訂單不存在等),能夠更新訂單狀態,而且刪除掉單表記錄。

若查詢依舊未知,這時咱們須要等待下次查詢的結果。

支付系統異常處理-定時查詢

這裏咱們須要注意了,有些狀況下,有可能沒法查詢返回訂單的狀態,因此咱們須要設置訂單查詢的最大次數,防止無限查詢浪費性能。

對帳

最後,極少數的狀況下,訂單查詢與異步通知都沒法獲取的支付結果,這就還剩下最後一種兜底的解決辦法,對帳。

若是次日渠道端給的對帳文件有這一筆支付結果,那麼咱們能夠根據這個記錄更新直接更新咱們內部支付記錄。

以前小黑哥寫過一篇對帳文章,感興趣的能夠再看一下:聊聊對帳系統的設計方案

畫外音:穩妥一點,能夠先發起查詢,而後根據查詢結果更新訂單記錄。

不過有些極端狀況,查詢沒法獲取結果,那麼直接更新內部記錄便可。

那若是次日也沒有這筆記錄的結果,這種狀況下,咱們能夠認爲這筆是失敗的。若是用戶被扣款,渠道端內部將會發起退款,將支付金額返回給用戶。因此這種狀況能夠無需處理。

內部掉單異常

支付公司內部訂單關係

接下來咱們講下內部掉單異常,首先咱們來看下爲何會發生內部掉單的異常,這其實跟咱們系統架構有關。

如上圖隨所示,第三方支付公司內部表一般爲支付訂單與渠道訂單這樣一種 1 比 N 的關係。

支付訂單保存着外部商戶系統的訂單號,表明第三方支付公司內部訂單與外部商戶的訂單的關係。

而渠道訂單表明着第三方支付公司與外部渠道的關係,其實對於外部渠道系統來說,第三方支付公司就是一個外部商戶。

爲何須要設計這種關係那?而不是使用下面這種 1 對 1 關係的那?

若是咱們使用上圖 1 對1 的訂單關係,若是第一次支付支付失敗,外部商戶可能會再次使用相同訂單號對第三方支付公司發起支付。

這時若是第三方支付公司也拿相同的內部訂單去請求外部渠道系統,有可能外部渠道系統並不支持同一訂單號再次請求。

那其實咱們也有其餘辦法,生成一個新的內部單號,更新原有支付訂單上內部記錄,而後去請求外部渠道系統。可是這樣的話就會丟失上次支付失敗記錄,這就不利於咱們作一些過後統計了。

那其實第三方支付公司也能夠不支持相同的訂單號再次發起請求,可是這樣的話,就須要外部商戶從新生成的新的訂單號。

這樣的話,第三方支付公司是系統是簡單了,所有複雜度都交給了外部商戶。

可是現實的狀況,不少外部商戶並非那麼容易更換生成新的訂單號,因此通常第三方支付公司都須要支持同一外部商戶訂單號在未成功的狀況下,支持重複支付。

在這種狀況下,就須要咱們上面的 1:N 的訂單關係圖了。

內部掉單異常的緣由

當咱們收到外部渠道系統的成功的返回信息,成功更新了渠道訂單表的記錄。可是因爲渠道訂單表與支付訂單表可能不是同一個數據庫,也有可能二者並不在同一個應用中,這就有可能致使更新支付訂單表的更新失敗。

因爲支付訂單是表保存着外部商戶訂單與內部訂單關係,支付訂單未成功,因此外部商戶也沒法查詢獲得成功的支付結果。

此時渠道訂單表已經成功,因此上面外部掉單的方法並不適用內部掉單。

內部掉單異常解決辦法

第一種解決辦法,分佈式事務。

內部掉單異常,說白就是由於支付訂單表與渠道訂單表沒法使用數據庫事務保證二者同時更新成功或失敗。

那麼這種狀況下,咱們其實就須要使用分佈式事務了。

不過咱們沒有采用這種分佈式事務,一是由於以前開發的時候市面上並無開源成熟分佈式事務框架,第二本身本身開發難度又很大。

因此對於分佈式事務這一塊,並無什麼使用經驗。若是有使用分佈式事務解決這類的問題同窗,留言去能夠評論一下。

第二種解決辦法,異步補償更新。

當發生內部掉單的狀況,即更新支付訂單失敗等狀況,能夠將這裏支付訂單保存到一張內部掉單表。

可是這裏可能會有一個問題,咱們沒法保證保存到內部掉單表這一步驟也必定成功。

因此說,咱們還須要定時查詢,查詢一段時間內支付訂單未成功,而渠道訂單表已成功的支付訂單記錄,而後也將其插入到內部掉單表。

另外一個系統應用,只須要定時掃描內部掉單表,將支付訂單成功,而後再刪除內部掉單記錄便可。

這裏須要注意了,當支付訂單表數據量很大以後,定時查詢可能會慢,爲了防止影響主庫,因此這類查詢能夠在備庫進行。

總結

今天主要介紹了支付系統中的掉單異常,這類異常每每會致使用戶實際已經被扣錢,可是商戶訂單仍是等待支付的狀況。

這個異常若是沒有很好處理,將會致使客戶用戶體驗很很差,還有可能收到客戶的投訴。

掉單的異常,一般能夠外部系統與內部系統。而大部分的掉單都是由於外部系統致使,咱們能夠增長超時時間,掉單查詢,以及接受異步通知解決 99% 的問題,剩下 1% 的掉單隻能經過第二天的對帳來兜底。

內部系統致使掉單異常是典型的分佈式環境數據一致性的問題,這類問題咱們能夠不須要追求強一致性,只要保證最終一致性便可。咱們可使用分佈式事務解決這類問題,也能夠定時掃描狀態不一致的訂單,而後在作批量更新。

最後,此次只是介紹支付系統中一類掉單異常,下一篇文章中,再給你們介紹一下支付系統的其餘異常,敬請期待!

參考資料

  1. 知乎@天順 談談異常(一)

歡迎關注個人公衆號:程序通事,得到平常乾貨推送。若是您對個人專題內容感興趣,也能夠關注個人博客:studyidea.cn

相關文章
相關標籤/搜索