支付系統(tǒng)返回碼設(shè)計(jì)及映射避坑指南
在支付系統(tǒng)的構(gòu)建與管理中,返回碼的設(shè)計(jì)和映射是確保交易順暢進(jìn)行的關(guān)鍵環(huán)節(jié)。本文將深入探討支付系統(tǒng)返回碼的重要性、常見(jiàn)問(wèn)題及其解決方案,旨在幫助開(kāi)發(fā)者和支付系統(tǒng)管理者避免潛在的陷阱,提升用戶體驗(yàn),并確保交易的準(zhǔn)確性和安全性。
我在支付行業(yè)呆了十來(lái)年,和返回碼映射導(dǎo)致的線上生產(chǎn)問(wèn)題交手無(wú)數(shù)次,有很多是因?yàn)橛绊懹脩趔w驗(yàn),也有一些直接導(dǎo)致了線上資損,所以有必要開(kāi)一篇小文聊一下。
一、返回碼還是錯(cuò)誤碼
有些人喜歡用“返回碼”,有些人喜歡用“錯(cuò)誤碼”。兩者本質(zhì)相同,都是用于標(biāo)識(shí)通信或交易的狀態(tài)。但我更傾向于使用“返回碼”,因?yàn)樗粌H涵蓋錯(cuò)誤狀態(tài),還包括成功狀態(tài),而成功并非錯(cuò)誤,所以使用“返回碼”更為合適。
二、返回碼的本質(zhì)
我很喜歡探尋事物的本質(zhì),那么返回碼的本質(zhì)是什么?我覺(jué)得是解決2個(gè)問(wèn)題:
- 標(biāo)識(shí)單一系統(tǒng)內(nèi)的業(yè)務(wù)處理結(jié)果:在一個(gè)單一系統(tǒng)內(nèi),如何表達(dá)業(yè)務(wù)處理的結(jié)果,比如參數(shù)不對(duì),余額不足,還是成功等。
- 完成異構(gòu)系統(tǒng)或應(yīng)用之間的處理結(jié)果同步:在多個(gè)系統(tǒng)之間如何同步業(yè)務(wù)處理的結(jié)果,比如渠道的結(jié)果同步給支付平臺(tái)的網(wǎng)關(guān)系統(tǒng),網(wǎng)關(guān)系統(tǒng)同步給支付引擎等。
三、返回碼最核心的關(guān)注點(diǎn)
返回碼最核心的關(guān)注點(diǎn)也只有2個(gè):
- 同一系統(tǒng)內(nèi)定義是否足夠清晰明確。減少歧義,減少誤解。
- 異構(gòu)系統(tǒng)或應(yīng)用之間的映射是否足夠準(zhǔn)確。映射不好,輕則影響用戶體驗(yàn),重則有資損。
四、曾經(jīng)碰到過(guò)的坑
踩過(guò)的坑很多,大致可以歸為以下幾類:
- 對(duì)客映射不準(zhǔn)確,導(dǎo)致用戶持續(xù)重試失敗,影響用戶體驗(yàn)。比如“余額不足”或“風(fēng)控不過(guò)”,返回給用戶“系統(tǒng)異常,請(qǐng)重試”,有些用戶就瘋狂地重試。
- 外部渠道沒(méi)有明確成功或失敗,內(nèi)部映射成明確成功或失敗,造成資損。比如:
支付同步請(qǐng)求渠道響應(yīng)還沒(méi)有回來(lái),發(fā)起了查詢,查詢返回“訂單不存在”,直接推進(jìn)失敗,但最后銀行扣款成功。
退款同步請(qǐng)求渠道響應(yīng)返回“系統(tǒng)異常”,直接推進(jìn)到失敗,但最后銀行退款成功。
3. 外部渠道有雙層返回碼,沒(méi)有做完整判斷。比如第1層只表示接口是否成功(通信層面),第2層才是表示業(yè)務(wù)是否成功,但是只判斷了接口層面,就推進(jìn)了內(nèi)部訂單的業(yè)務(wù)狀態(tài)。
4. 返回碼制定過(guò)于籠統(tǒng)或太細(xì)。
五、最佳實(shí)踐
5.1. 基本原則
- 制定統(tǒng)一返回碼規(guī)范:在團(tuán)隊(duì)或公司層面制定統(tǒng)一的返回碼規(guī)范,明確各個(gè)返回碼的含義,確保各模塊一致性。
- 嚴(yán)格遵守返回碼定義:研發(fā)人員在編碼時(shí),應(yīng)嚴(yán)格按照規(guī)范返回對(duì)應(yīng)的返回碼,確保返回碼與實(shí)際狀態(tài)匹配。明確成功才推進(jìn)成功,明確失敗才推進(jìn)失敗,其它全部按“未知”處理。
- 區(qū)分接口/通信成功與業(yè)務(wù)成功。
- 流入到平臺(tái)的(支付、充值等),謹(jǐn)慎映射到成功。從平臺(tái)流出的(提現(xiàn),代發(fā)等),謹(jǐn)慎映射到失敗。
5.2. 三級(jí)返回碼體系
外部商戶對(duì)接支付平臺(tái),支付平臺(tái)內(nèi)部有自己的業(yè)務(wù)處理,同時(shí)還對(duì)接了外部的很多渠道,所以需要管理三套返回碼:
- 提供給商戶OpenAPI使用的返回碼:這塊可以直接參考微信支付、支付寶等機(jī)構(gòu)的門戶網(wǎng)站。
- 內(nèi)部各應(yīng)用使用的標(biāo)準(zhǔn)返回碼:用于內(nèi)部業(yè)務(wù)的處理。
- 渠道返回碼:外部渠道提供的返回碼,每個(gè)渠道都不一樣,需要映射到內(nèi)部標(biāo)準(zhǔn)返回碼。
為什么需要三層?主要有3個(gè)原因:
- 內(nèi)部應(yīng)用使用的標(biāo)準(zhǔn)返回碼需要精確,便于內(nèi)部系統(tǒng)運(yùn)行的監(jiān)控。
- 給商戶OpenAPI的返回碼需要業(yè)務(wù)語(yǔ)義明確,但不能過(guò)于精確。比如內(nèi)部出現(xiàn)“卡的有效期不正確”,對(duì)外則是“卡號(hào)或持卡人或有效期不正確”,避免輪詢攻擊。
- 外部渠道返回碼不能全部一對(duì)一映射到內(nèi)部,因?yàn)橥獠壳捞?,容易膨脹?/li>
5.3. 商戶OpenAPI返回碼設(shè)計(jì)
這部分建議直接參考微信支付、支付寶或者ISO20022標(biāo)準(zhǔn),這幾家代表了行業(yè)的最高水準(zhǔn)。
一般來(lái)說(shuō)最少有兩個(gè)字段:resultCode和message,一個(gè)表示碼,一個(gè)表示碼的描述。
也可以增加一個(gè)參數(shù)result,使用S,F(xiàn),U表示業(yè)務(wù)狀態(tài)的成功、失敗、未知。
如果是查詢類接口,一定要明確說(shuō)明是接口成功,還是業(yè)務(wù)成功。
5.4. 內(nèi)部標(biāo)準(zhǔn)返回碼設(shè)計(jì)
支付平臺(tái)內(nèi)部也分了不同域,建議使用一個(gè)共同的規(guī)范,比如:RS+子系統(tǒng)編號(hào)+錯(cuò)誤級(jí)別+具體返回碼。具體如下圖所示:
說(shuō)明:
- 1-2位:固定值RS,Result縮寫(xiě)。
- 3-5位:子系統(tǒng)編號(hào)。比如001:收銀支付,002:會(huì)員等??煞奖愣ㄎ荒膫€(gè)系統(tǒng)出的問(wèn)題。
- 6位:錯(cuò)誤類或等級(jí)。比如:0:正常,1:業(yè)務(wù)級(jí)異常,2:系統(tǒng)級(jí)異常。
- 7-9位:各業(yè)務(wù)線自己定。比如:1xx:參數(shù)相關(guān),2xx:數(shù)據(jù)庫(kù)相關(guān),3xx:賬戶狀態(tài)/Token狀態(tài)相關(guān)等。
- 這樣的好處在于,每個(gè)子域或子系統(tǒng)既有全局的規(guī)范,又有自己的靈活性,減少溝通成本。
- 注意:上面只是寫(xiě)了resultCode,還需要有message,用于描述這個(gè)碼代表什么語(yǔ)義。
- 核心代碼(注:使用chatGPT o1生成,請(qǐng)自行增刪):
public interface IResultCode {
String getResultCode();
String getMessage();
}
public enum PaymentResultCode implements IResultCode {
SUCCESS( "0000", "success"),
FAIL("2998", "fail"),
SYSTEM_ERROR("2999","system error"),
// 額度相關(guān) 11XX
INSUFFICIENT_FUND("1101", "insufficient fund"),
// 風(fēng)控相關(guān) 12XX
RISK_REJECTED("1201", "risk rejected"),
// DB相關(guān) 21XX ;
private static final String PREFIX = "RS";
private static final String SYSTEM_CODE = "101";
private String codeNumber;
private String message;
@Override
public String getResultCode() {
return PREFIX + SYSTEM_CODE + codeNumber;
}
@Override
public String getMessage() {
return message;
}
PaymentResultCode(String codeNumber, String message) {
this.codeNumber = codeNumber;
this.message = message;
}
}
5.5. 渠道返回碼映射
每個(gè)渠道的返回碼都是不一樣的,所以需要設(shè)計(jì)外部渠道返回碼映射到內(nèi)部標(biāo)準(zhǔn)返回碼。需要遵守幾個(gè)原則:
- 只有明確成功,才能映射到成功。
- 只有明確失敗,才能映射到失敗。比如渠道返回:訂單不存在,或者系統(tǒng)異常,不能直接映射到失敗,因?yàn)橛锌赡軙?huì)成功。
- 涉及個(gè)人敏感信息或內(nèi)部系統(tǒng)敏感信息的,需要轉(zhuǎn)成模糊返回碼和描述出去,不能給最終用戶展示精確信息。比如內(nèi)部一個(gè)系統(tǒng)宕機(jī),不能直接把異常拋出去。有效期錯(cuò)誤也需要映射成“卡號(hào)或姓名或有效期不正確”。
- 與敏感信息無(wú)關(guān)的,越準(zhǔn)確越好,避免用戶無(wú)謂的重試。比如余額不足,就不要映射成“系統(tǒng)異常,請(qǐng)重試”。
- 查詢類接口,務(wù)必要區(qū)分是接口成功,還是業(yè)務(wù)成功。
具體的技術(shù)實(shí)現(xiàn),通過(guò)使用映射表就足夠,加到緩存中,增加運(yùn)算速度。如果找不到映射關(guān)系,就全部轉(zhuǎn)到一個(gè)默認(rèn)的返回碼上面,同時(shí)對(duì)這個(gè)默認(rèn)返回碼做監(jiān)控,定期把這些沒(méi)有做映射的返回碼映射到正確的返回碼上面去。避免應(yīng)該把類似“余額不足”映射成了“系統(tǒng)異常請(qǐng)重試”的場(chǎng)景。
5.6. 返回碼監(jiān)控與告警
大部分團(tuán)隊(duì)都會(huì)監(jiān)控成功率,只有少數(shù)團(tuán)隊(duì)會(huì)監(jiān)控返回碼或定期分析返回碼。然而當(dāng)交易量足夠大時(shí),成功率的波動(dòng)可能只有0.5%,很難看出異常,而如果去分析返回碼,則可以快速看出并定位問(wèn)題。
一般來(lái)說(shuō),有幾個(gè)建議:
- 實(shí)時(shí)監(jiān)控返回碼的突變異常。比如:最近10分鐘,某個(gè)返回碼突然增加50%,或者比明天突然增加50%等。都需要介入看看。
- 定期觀察返回碼曲線表。如果某個(gè)返回碼連續(xù)多天持續(xù)在上升,一般都是有問(wèn)題的。
- 建立返回碼全鏈路映射大盤(pán)。比如渠道返回“余額不足”或“風(fēng)控不通過(guò)”,映射到用戶展示“系統(tǒng)異常,請(qǐng)重試”,那就有問(wèn)題。而且類似這種情況還非常常見(jiàn)。
- 定期分析用戶支付行為。以前在分析用戶行為時(shí),發(fā)現(xiàn)同一用戶重試了20多次,最后排查發(fā)現(xiàn),就是返回碼映射不準(zhǔn)確,導(dǎo)致用戶無(wú)謂的重試。
六、結(jié)束語(yǔ)
卷用戶體驗(yàn)和成功率時(shí),往往需要于細(xì)微處見(jiàn)真章,而返回碼的設(shè)計(jì)和映射就是如此。做得不好,輕則影響用戶體驗(yàn),重則資損。
希望對(duì)大家在設(shè)計(jì)標(biāo)準(zhǔn)返回碼及映射時(shí)有所啟發(fā),也歡迎點(diǎn)贊轉(zhuǎn)發(fā)。
這是《圖解支付系統(tǒng)設(shè)計(jì)與實(shí)現(xiàn)》專欄系列文章中的第(48)篇。歡迎和我一起深入解碼支付系統(tǒng)的方方面面。
作者:隱墨星辰,公眾號(hào):隱墨星辰
本文由 @隱墨星辰 原創(chuàng)發(fā)布于人人都是產(chǎn)品經(jīng)理。未經(jīng)作者許可,禁止轉(zhuǎn)載
題圖來(lái)自Unsplash,基于CC0協(xié)議
該文觀點(diǎn)僅代表作者本人,人人都是產(chǎn)品經(jīng)理平臺(tái)僅提供信息存儲(chǔ)空間服務(wù)
- 目前還沒(méi)評(píng)論,等你發(fā)揮!