錢被扣走了,但是訂單卻未成功!支付掉單異常最全解決方案
編輯導(dǎo)讀:在系統(tǒng)運行中,異常是不可避免的問題。在支付系統(tǒng)中,掉單是最常見的異常,即錢被扣走了,但是訂單卻未成功。為什么會出現(xiàn)這個問題以及如何解決?本文作者基于自身經(jīng)驗,從三個方面展開分析,希望對你有幫助。
今天分享一下支付系統(tǒng)中異常一些處理方式。
其實這些處理方式并不只是局限于支付系統(tǒng),也可以適用于其他系統(tǒng),大家可以借鑒,應(yīng)用到自己系統(tǒng)中,提高自己系統(tǒng)的健壯性。
異常是系統(tǒng)運行不可避免會發(fā)生的問題,如果一切都正常,我們的系統(tǒng)設(shè)計將會相當(dāng)簡單。
但是可惜沒有人能做到這一點,所以為了處理異??赡軐?dǎo)致的問題,我們不得不需要加上很多額外的設(shè)計,用來應(yīng)對這些異常。
可以說系統(tǒng)設(shè)計中,異常處理需要我們著重思考,將會占據(jù)我們大部分的精力。
下面我們先來看下支付系統(tǒng)中最常見的異常:「掉單」。
一、掉單異常
一個最常見的支付平臺架構(gòu)關(guān)系如下所示:
上圖我們是站在第三方支付公司支付角度,如果是自己公司的內(nèi)部支付系統(tǒng),那么外部商戶這一塊其實就是公司內(nèi)部一些系統(tǒng),比如說訂單系統(tǒng),而外部支付渠道其實就是第三方支付公司。
我們以攜程為例,在其上面發(fā)起一筆訂單支付,將會經(jīng)過三個系統(tǒng):
- 攜程創(chuàng)建訂單,向第三方支付公司發(fā)起支付請求
- 第三方支付公司創(chuàng)建訂單,并向工行發(fā)起支付請求
- 工行完成扣款操作,返回第三方支付公司
- 第三方支付完成訂單更新并返回攜程
- 攜程變更訂單狀態(tài)
上面的流程,簡單如下圖所示:
在這個過程就可能會碰到,用戶工行卡已經(jīng)扣款,但是攜程訂單卻還是待支付,我們通常將這種情況稱為「掉單」。
上述掉單的場景,多數(shù)是因為「③、⑤」環(huán)節(jié)信息丟失導(dǎo)致,這種掉單我們將其稱為「外部掉單」。
還有一種極少數(shù)的情況,收到 「③、⑤」環(huán)節(jié)返回信息,但是在「④、⑥」環(huán)節(jié)內(nèi)部系統(tǒng)更新訂單狀態(tài)失敗,從而導(dǎo)致丟失支付成功的信息,這類掉單由于是內(nèi)部問題,我們通常將其稱之為「內(nèi)部掉單」。
二、外部掉單
外部掉單是因為沒有收到對端返回信息,這種情況極有可能是網(wǎng)絡(luò)問題,也有可能對端處理邏輯太慢,導(dǎo)致我方請求超時,直接斷開了網(wǎng)絡(luò)請求。
1. 增加超時時間
對于這種情況,第一個最簡單的解決辦法,「適當(dāng)?shù)脑黾映瑫r時間」。
不過這里需要注意了,在我們增加網(wǎng)絡(luò)超時時間之后,我們可能還需要調(diào)整整個鏈路的超時時間,不然有可能導(dǎo)致整個鏈路內(nèi)部差事從而引起內(nèi)部掉單。
畫外音:對接外部渠道,一定要「設(shè)置網(wǎng)絡(luò)連接超時時間與讀取超時時間」。
2. 接收異步通知
第二個辦法,接收渠道異步回執(zhí)通知信息。
一般來說,現(xiàn)在支付渠道接口我們都可以上送一個異步回調(diào)地址,當(dāng)渠道端處理成功,將會把成功信息通知到這個回調(diào)地址上。
這種情況下,我們只需要接收通知信息,然后解析,再更新內(nèi)部訂單狀態(tài)。
支付系統(tǒng)異常處理-支付異步通知
這種情況下,我們需要注意幾點:
- 對于異步請求信息,一定需要對通知內(nèi)容進(jìn)行簽名驗證,并校驗返回的訂單金額是否與商戶側(cè)的訂單金額一致,防止數(shù)據(jù)泄漏導(dǎo)致出現(xiàn)“假通知”,造成資金損失。
- 異步通知將會發(fā)送多次,所以異步通知處理需要冪等。
3. 掉單查詢
有的渠道可能沒有提供異步通知的功能,只提供了訂單查詢的接口,這種情況下,我們只能使用第三種解決辦法,定時掉單查詢。
我們可以將這類超時未知的訂單的單獨保存到掉單表,然后定時向渠道端查詢訂單的狀態(tài)。
若查詢成功或者明確失?。ū热缬唵尾淮嬖诘龋梢愿掠唵螤顟B(tài),并且刪除掉單表記錄。
若查詢依舊未知,這時我們需要等待下次查詢的結(jié)果。
支付系統(tǒng)異常處理-定時查詢
這里我們需要注意了,有些情況下,有可能無法查詢返回訂單的狀態(tài),所以我們需要設(shè)置訂單查詢的最大次數(shù),防止無限查詢浪費性能。
4. 對賬
最后,極少數(shù)的情況下,訂單查詢與異步通知都無法獲取的支付結(jié)果,這就還剩下最后一種兜底的解決辦法,對賬。
如果第二天渠道端給的對賬文件有這一筆支付結(jié)果,那么我們可以根據(jù)這個記錄更新直接更新我們內(nèi)部支付記錄。
畫外音:穩(wěn)妥一點,可以先發(fā)起查詢,然后根據(jù)查詢結(jié)果更新訂單記錄。
不過有些極端情況,查詢無法獲取結(jié)果,那么直接更新內(nèi)部記錄即可。
那如果第二天也沒有這筆記錄的結(jié)果,這種情況下,我們可以認(rèn)為這筆是失敗的。如果用戶被扣款,渠道端內(nèi)部將會發(fā)起退款,將支付金額返回給用戶。所以這種情況可以無需處理。
三、內(nèi)部掉單異常
1. 支付公司內(nèi)部訂單關(guān)系
接下來我們講下內(nèi)部掉單異常,首先我們來看下為什么會發(fā)生內(nèi)部掉單的異常,這其實跟我們系統(tǒng)架構(gòu)有關(guān)。
如上圖隨所示,第三方支付公司內(nèi)部表通常為支付訂單與渠道訂單這樣一種 1 比 N 的關(guān)系。
支付訂單保存著外部商戶系統(tǒng)的訂單號,代表第三方支付公司內(nèi)部訂單與外部商戶的訂單的關(guān)系。
而渠道訂單代表著第三方支付公司與外部渠道的關(guān)系,其實對于外部渠道系統(tǒng)來講,第三方支付公司就是一個外部商戶。
為什么需要設(shè)計這種關(guān)系那?而不是使用下面這種 1 對 1 關(guān)系的那?
如果我們使用上圖 1 對1 的訂單關(guān)系,如果第一次支付支付失敗,外部商戶可能會再次使用相同訂單號對第三方支付公司發(fā)起支付。
這時如果第三方支付公司也拿相同的內(nèi)部訂單去請求外部渠道系統(tǒng),有可能外部渠道系統(tǒng)并不支持同一訂單號再次請求。
那其實我們也有其他辦法,生成一個新的內(nèi)部單號,更新原有支付訂單上內(nèi)部記錄,然后去請求外部渠道系統(tǒng)。但是這樣的話就會丟失上次支付失敗記錄,這就不利于我們做一些事后統(tǒng)計了。
那其實第三方支付公司也可以不支持相同的訂單號再次發(fā)起請求,但是這樣的話,就需要外部商戶重新生成的新的訂單號。
這樣的話,第三方支付公司是系統(tǒng)是簡單了,全部復(fù)雜度都交給了外部商戶。
但是現(xiàn)實的情況,很多外部商戶并不是那么容易更換生成新的訂單號,所以一般第三方支付公司都需要支持同一外部商戶訂單號在未成功的情況下,支持重復(fù)支付。
在這種情況下,就需要我們上面的 1:N 的訂單關(guān)系圖了。
2. 內(nèi)部掉單異常的原因
當(dāng)我們收到外部渠道系統(tǒng)的成功的返回信息,成功更新了渠道訂單表的記錄。但是由于渠道訂單表與支付訂單表可能不是同一個數(shù)據(jù)庫,也有可能兩者并不在同一個應(yīng)用中,這就有可能導(dǎo)致更新支付訂單表的更新失敗。
由于支付訂單是表保存著外部商戶訂單與內(nèi)部訂單關(guān)系,支付訂單未成功,所以外部商戶也無法查詢得到成功的支付結(jié)果。
此時渠道訂單表已經(jīng)成功,所以上面外部掉單的方法并不適用內(nèi)部掉單。
3. 內(nèi)部掉單異常解決辦法
「第一種解決辦法,分布式事務(wù)?!?/strong>
內(nèi)部掉單異常,說白就是因為支付訂單表與渠道訂單表無法使用數(shù)據(jù)庫事務(wù)保證兩者同時更新成功或失敗。
那么這種情況下,我們其實就需要使用分布式事務(wù)了。
不過我們沒有采用這種分布式事務(wù),一是因為之前開發(fā)的時候市面上并沒有開源成熟分布式事務(wù)框架,第二自己自己開發(fā)難度又很大。
所以對于分布式事務(wù)這一塊,并沒有什么使用經(jīng)驗。如果有使用分布式事務(wù)解決這類的問題同學(xué),留言去可以評論一下。
「第二種解決辦法,異步補償更新?!?/strong>
當(dāng)發(fā)生內(nèi)部掉單的情況,即更新支付訂單失敗等情況,可以將這里支付訂單保存到一張內(nèi)部掉單表。
但是這里可能會有一個問題,我們無法保證保存到內(nèi)部掉單表這一步驟也一定成功。
所以說,我們還需要定時查詢,查詢一段時間內(nèi)支付訂單未成功,而渠道訂單表已成功的支付訂單記錄,然后也將其插入到內(nèi)部掉單表。
另一個系統(tǒng)應(yīng)用,只需要定時掃描內(nèi)部掉單表,將支付訂單成功,然后再刪除內(nèi)部掉單記錄即可。
這里需要注意了,當(dāng)支付訂單表數(shù)據(jù)量很大之后,定時查詢可能會慢,為了防止影響主庫,所以這類查詢可以在備庫進(jìn)行。
四、總結(jié)
今天主要介紹了支付系統(tǒng)中的掉單異常,這類異常往往會導(dǎo)致用戶實際已經(jīng)被扣錢,但是商戶訂單還是等待支付的情況。
這個異常如果沒有很好處理,將會導(dǎo)致客戶用戶體驗很不好,還有可能收到客戶的投訴。
掉單的異常,通??梢酝獠肯到y(tǒng)與內(nèi)部系統(tǒng)。而大部分的掉單都是因為外部系統(tǒng)導(dǎo)致,我們可以增加超時時間,掉單查詢,以及接受異步通知解決 99% 的問題,剩下 1% 的掉單只能通過次日的對賬來兜底。
內(nèi)部系統(tǒng)導(dǎo)致掉單異常是典型的分布式環(huán)境數(shù)據(jù)一致性的問題,這類問題我們可以不需要追求強一致性,只要保證最終一致性即可。我們可以使用分布式事務(wù)解決這類問題,也可以定時掃描狀態(tài)不一致的訂單,然后在做批量更新。
最后,這次只是介紹支付系統(tǒng)中一類掉單異常,下一篇文章中,再給大家介紹一下支付系統(tǒng)的其他異常,敬請期待!
參考資料:
作者:樓下小黑哥;微信公號@程序通事,支付行業(yè),后端技術(shù)
本文由 @樓下小黑哥 原創(chuàng)發(fā)布于人人都是產(chǎn)品經(jīng)理。未經(jīng)許可,禁止轉(zhuǎn)載
題圖來自Pexels,基于CC0協(xié)議
很清晰,我一個沒接觸過支付系統(tǒng)的人都看懂了,感謝。
寫的很清晰!