http://www.cnblogs.com/zhengyun_ustc/archive/2012/12/14/topic7.html
一. 通用規(guī)則 1.1. 歷史記錄不得直接篡改 電商核心服務基本都是分布式應用,分布式事務如處理不妥善,容易產(chǎn)生數(shù)據(jù)不一致。一旦出現(xiàn)數(shù)據(jù)不一致,一定要有旁證來修正。 所以數(shù)據(jù)庫中以下關鍵資源的記錄,鄭昀提醒您注意,原則上不允許
直接修改歷史數(shù)據(jù):
- 下單;
- 支付購買;
- 生碼/驗碼/物流信息記錄;
- 退款(含部分退款);
- 結算;
- 用戶注冊;
- 與第三方數(shù)據(jù)同步;
這里的“直接修改”特指,沒有把變更行為記錄到日志表里,而是直接在原始記錄上 update 甚至 delete ,這種“篡改”和“毀尸滅跡”是明文禁止的,即使留下了文件類型日志也是不允許的。 第一,要修改這些記錄的關鍵字段時,必須在相關日志表里保留變更日志,并記錄操作人和發(fā)起人,
一定要確保歷史可回溯。 第二,嚴禁對記錄做物理刪除,只能是軟刪除。 實例: 對于××團收到第三方支付的通知,我們有第三方交易流水記錄表; 對于××團發(fā)起的到第三方支付的交易請求。我們有 jxxxe_pay_log 記錄; 訂單操作變更記錄,我們有 jxxxe_order_action日志表記錄。
Q:什么叫歷史可回溯? A:系統(tǒng)可能對關鍵記錄做了一系列修改,甚至有程序在某個時間段內誤寫引入了臟數(shù)據(jù),但鄭昀提示您,我們依然要能從各種操作日志表中隨時倒推回歷史某一個時刻的快照,一是確保隨時能安全地把數(shù)據(jù)還原回去,二是管理平臺可以清晰地展示出由誰引發(fā)、怎么變化的歷史,三是便于排查問題。 譬如,對于記錄了訂單信息的 order_info 表,會員如果點擊使用賬戶余額支付了訂單的應付金額,那么該訂單操作日志表就會做如下記錄,原訂單記錄的重要字段(what)在什么時候(when)從什么變?yōu)榱耸裁?#xff08;how),都會詳細記錄。
1.2. 對關鍵資源的操作,當接口保證不了冪等性時,必須能防并發(fā) 如果你的接口不具備冪等性,那么請保證整個(分布式)系統(tǒng)內對一個重要事物(訂單,賬戶的資金變動等)的有效操作線程,同一時間內有且只有一個。 比如交易中心有N臺服務器負載均衡,訂單中心則有M臺服務器,如何保證一個訂單的同一筆支付處理,一個賬戶的同一筆資金變動操作是原子性的。 原因也很簡單:
- 第三方支付平臺可能同一時刻給你的 pay.5xxxxn.com 交易中心集群服務推送過來兩個一模一樣的支付成功通知。
- 用戶瀏覽器可能安裝了某種插件(如早期的迅雷插件),插件本身為了探測 一個URL 是否是BT資源,會同時模擬發(fā)起一個 HTTP GET 請求。
- 上游服務不可控,不可預知地向發(fā)起下游服務發(fā)起并發(fā)請求。
此時可以基于 memcache 實現(xiàn)一個分布式鎖,更多詳情請閱讀鄭昀撰寫的《電商課題:分布式鎖》。
1.3. 支付子系統(tǒng)的獨立性 電商業(yè)務容易出現(xiàn)以下問題:
- 受到DDoS攻擊,帶寬被打滿;
- 某一個業(yè)務突然響應變慢,如從20ms激增為1s,業(yè)務請求被大量阻塞;
所以不同業(yè)務之間必須嚴格隔離,防止一個業(yè)務超負荷或宕機連累其他業(yè)務。 因此,我們至少要做到:
- 支付子系統(tǒng)是一個獨立工程,獨立部署,有單獨的二級域名。
最好能做到:
1.4. 支付子系統(tǒng)接收第三方支付通知的可靠性 電商的支付子系統(tǒng)提供一個 Web Service,來接收各家第三方支付的各種異步通知。 接收對方通知之后,你可能會遭遇各種異常,如:
- 解析時發(fā)生異常;
- 調用支付中心時發(fā)生異常,如網(wǎng)絡故障,如支付中心宕機,如調用超時;
- 寫日志時發(fā)生異常;
即使如此,你也不應該丟棄該通知,并且沒有返回“success”字符給第三方支付, 因為這樣的話,相當于你的業(yè)務完全依賴于第三方支付下一次重試了。 所以,鄭昀提醒您,你應該主動地、積極地先把對方的(支付成功、退款處理等)通知存入一個存儲介質,如消息隊列; 一旦同步處理失敗,那么能以某種策略重播這個消息,直到業(yè)務系統(tǒng)恢復正常、處理完畢為止。
1.5. 補錄數(shù)據(jù)的時間準則 電商結算和對賬,由于帳期定義(如日清日結,如T+2結算,如銷售傭金計算,如CPS聯(lián)盟結算),非常依賴于數(shù)據(jù)記錄的時間。 所以,當由于以下原因補錄或同步數(shù)據(jù)時,請慎重考慮數(shù)據(jù)的時間字段到底如何采信:
- 掉單后主動處理(注:主動查詢第三方支付網(wǎng)關,獲得訂單支付狀態(tài));
- 不同系統(tǒng)之間同步失敗后手動觸發(fā)重新同步;
- 不同公司的平臺之間交換退款等數(shù)據(jù)。
下面舉兩個小例子: 例一: ××商城對供應商的結算標準是,僅僅以訂單進入他們的ERP系統(tǒng)的時間為準,而不是以該訂單的下單時間、支付時間等數(shù)據(jù)自身時間記錄為準。 即,一個12月1日23:58支付成功的訂單,數(shù)據(jù)一層一層傳遞到ERP時,同步時間是12月2日00:01,那么此訂單就被判定在12月2日的應結算明細中,而不是12月1日的。 咋聽上去好像不合理,仔細想想,供應商有很多,IT系統(tǒng)也就很多,彼此之間的服務器時間肯定不同步,更別提會有很多種類型的臟數(shù)據(jù),所以××商城只有選擇用ERP系統(tǒng)自身的時間作為唯一結算憑據(jù),而不采信第三方系統(tǒng)的 add_time、update_time、pay_time 五花八門的時間,這樣才不會重復結算或漏結算。 例二: 支付系統(tǒng)宕機,一段時間后才恢復,此時客服主動處理顧客投訴掉單的訂單,從第三方支付查到已支付后,將訂單置為已付款。那么,該訂單的支付時間怎么記呢? 一是,采信第三方支付系統(tǒng)傳遞的真實支付時間。二是,記錄為手動重置的當前時間。 鄭昀的答案是,后者更安全。 因為,有可能補錄數(shù)據(jù)已跨日或跨月,前一日的結算清單可能已計算完畢,如果按前者的邏輯,突然又補錄一條記錄,結果前一日(上一個月)也不結算它,后一日(下一個月)也不結算,那這個訂單就漏結算了。 當然,真實支付時間也還是要記錄到日志表的。
二. 易被忽略的邏輯處理 2.1. 交易關閉通知的處理 支付寶是這么定義“
交易關閉”的:
| 枚舉名稱 | 枚舉說明 |
| TRADE_CLOSED | - 在指定時間段內未支付時關閉的交易;
- 在交易完成全額退款成功時關閉的交易。
|
交易關閉通知默認是不發(fā)送的,如下表格所示:
| 觸發(fā)條件名 | 觸發(fā)條件描述 | 觸發(fā)條件默認值 |
| TRADE_CLOSED | 交易關閉 | false(不觸發(fā)通知) |
但如果商戶(也就是你的網(wǎng)站)向支付寶申請打開了該配置,那么請注意接收 TRADE_CLOSED 通知,它會對你的核心購買邏輯產(chǎn)生影響。
如何主動指定交易關閉時間呢? 即時到帳交易接口中有這么一個參數(shù):
| 參數(shù) | 參數(shù)名稱 | 類型 | 參數(shù)說明 | 是否可為空 | 樣例 |
| it_b_pay | 超時時間 | String(3) | 設置未付款交易的超時時間,一旦超時,該筆交易就會自動被關閉。 取值范圍:1m~15d。 m:分鐘、h:小時、d:天、1c:當天(無論交易何時創(chuàng)建,都在0點關閉)。 該功能需要聯(lián)系技術支持來配置關閉時間。 | 可空 | 1h |
支付寶收到這個參數(shù)后,界面會有如下展示:
? 此時提示幾點: 1)如果交易已經(jīng)關閉,但商戶的網(wǎng)站上仍保留了訂單的“付款”按鈕,那么點擊跳轉到支付寶后,會看到如下警告信息:
或 2)當交易狀態(tài)為交易關閉時,就算用戶能通過第三方網(wǎng)銀對支付寶賬單進行付款(用戶可能已經(jīng)跳轉至銀行交易頁面,并且未關閉頁面),第三方網(wǎng)銀能將支付成功信息通知支付寶,支付寶也不會通知商戶,而是會自動退還至支付寶余額中。 3)網(wǎng)銀直連的訂單是關閉不了的,因為它沒有跟支付寶賬戶綁定。 4)商戶如發(fā)現(xiàn)交易已關閉的訂單被用戶支付后,那么必須進入異常支付流程(能原路退返就退,如無法退返則返還至賬戶余額)。
2.2. 退款通知的處理 支付寶對此的定義是:
(1)?交易成功之后,商戶(高級即時到賬或機票平臺商)可調用批量退款接口,系統(tǒng)會發(fā)送退款通知給商戶。 (2) 當商戶使用站內退款時,系統(tǒng)會發(fā)送包含 refund_status(退款狀態(tài))和 gmt_refund(退款時間)字段的通知給商戶。 其中退款狀態(tài)有兩種:
| 枚舉名稱 | 枚舉說明 |
| REFUND_SUCCESS | 退款成功: - 全額退款情況:trade_status=?TRADE_CLOSED,而refund_status=REFUND_SUCCESS
- 非全額退款情況:trade_status=?TRADE_SUCCESS,而refund_status=REFUND_SUCCESS
|
| REFUND_CLOSED | 退款關閉 |
第三方支付在退款處理完畢后,會發(fā)送異步通知給商戶,如下表格所示:
| 觸發(fā)條件名 | 觸發(fā)條件默認值 |
| 退款處理結束 | true(觸發(fā)通知) |
這個所謂退款處理結束通知,實際上仍是一個“trade_status_sync(交易狀態(tài)同步)”通知,特殊性在于攜帶的 refund_stauts =REFUND_SUCCESS 參數(shù),實際例子如下所示:
注意幾個要點:
當交易狀態(tài)為 TRADE_FINISHED(交易完成) ,那么不可退款! - 此處有一個“退款期限”概念,交易關閉(TRADE_CLOSED)后3個月(或6個月)內可以退款,超過此期限后,該筆交易成功且結束,從此不可退款!對于業(yè)務邏輯,意味著此時只能退還金額到賬戶余額,無法原路退返。支付寶、快錢、財付通等均有此設定。
- 手機支付退款如返回 D23190 錯誤碼,含義是退款日期超過最大有效期(有效期是半年)。
退款處理結束的通知到達時,支付寶會先發(fā)送一個支付成功通知,防止你的系統(tǒng)不知道有此交易。請正確處理這個支付成功通知,不要誤認為這是“重復支付”(因為對應的訂單可能已確認+已支付),以至于誤判給原路退返了。
三. 異常處理類 3.1. 掉單的被動處理 支付寶的文檔說的很清楚:
服務器異步通知頁面(由參數(shù) notify_url 指定頁面文件)獲取支付寶返回的結果數(shù)據(jù) ,(商戶的)程序執(zhí)行完后必須打印輸出“success”(不包含引號)。 如果商戶反饋給支付寶的字符不是 success 這7個字符,支付寶服務器會不斷重發(fā)通知,直到超過24小時22分鐘。 一般情況下,25小時以內完成8次通知(通知的間隔頻率一般是:2m,10m,10m,1h,2h,6h,15h); 所以,如果你用來接收支付寶異步通知的服務阻塞了(hang/stuck)或掛了(shutdown/crash),就無法給支付寶返回 success 響應,所以它會不斷地發(fā)起重試,直到25小時內你恢復服務返回 success 。 所以如果
掉單(用戶已付款/已扣款,但你的數(shù)據(jù)庫里這個訂單還是未付款狀態(tài)),你還有機會補救。
3.2. 掉單的主動處理 掉單后,等待支付寶補發(fā)通知給你,商戶可能來不及應對洶涌而來的顧客投訴。 此時,服務器端應該主動提交此訂單對應的(一個或多個)
唯一訂單號(out_trade_no,支付寶合作商戶網(wǎng)站唯一訂單號),調用支付寶的單筆交易查詢接口?single_trade_query,從而獲得交易狀態(tài)、支付寶交易號、付款時間、交易總金額等明細。 (具體細節(jié)請看支付寶的《單筆交易查詢接口(single_trade_query).pdf》) 一旦查詢到了支付成功的細節(jié),而且付款金額也等于商戶記錄的應付金額,那么就可以給操作人員展示一個畫面,使得他能手工置這個訂單為已確認+已付款。
3.3. 來自于多個支付渠道的重復支付 什么情況下會產(chǎn)生重復支付呢? 看一個真實的顧客投訴案例: 『
顧客購買××團的商品后,第一次付款時,由于付款故障(如系統(tǒng)掉單),使得顧客認為付款未成功,所以,顧客換用了其他支付渠道(如選擇支付寶的網(wǎng)銀直連,或者選擇網(wǎng)銀在線)進行了第二次付款,于是××團帳號收到兩筆付款,且兩筆付款均付款成功。』 這不是偶然現(xiàn)象。 處理辦法還是:按時間順序,稍晚一些的付款被認為是重復支付,進入異常支付流程(能原路退返就退,如無法退返則返還至賬戶余額)。
3.4. 支付成功時系統(tǒng)發(fā)現(xiàn)商品已不可售賣 商品不可售賣有兩個原因:1)庫存不足;2)商品已下線。 如果是庫存不足: 團購商戶為了避免
超賣,應該
- 將訂單關閉,
- 將交易關閉,
- 將實付金額原路退返(如無法退返,退至賬戶余額),
- 記錄支付失敗日志,
- 記錄資金變動日志,標記退返原因是“庫存不足”,
- 顧客可以在前臺賬戶余額變更歷史中看到有過付款以及被退款的明細。
如果是實物類電商商戶,因為可以事后干預,補足庫存,所以可以接受這次付款行為。 如果是商品已下線,處理方式同上,只不過要標記退返原因是“商品已下線”。
3.5. 訂單名稱中不能包含敏感詞 支付寶對商品標題核查得非常嚴格,所以鄭昀鄭重提醒您,為了避免顧客發(fā)起支付時看到支付寶如下警告:
請?zhí)崆罢{用?
支付寶交易信息敏感詞分析接口(fast_text_trade)?,在錄入信息時就阻止保存。 注意,此敏感詞分析接口需要聯(lián)系支付寶開通權限。
四. 正常支付流程的兩個要點 4.1.正常支付的處理流程 對于一個團購商品來說,顧客的正常支付成功通知到達服務器端時,業(yè)務規(guī)則簡述為: 商品活動結束后,所有未付款訂單,一律不允許支付。必須符合庫存管理規(guī)則。本次支付金額應該小于等于該訂單的待支付金額。訂單狀態(tài)正常(不能處于“已取消”、“已付款”等狀態(tài))。 一旦發(fā)現(xiàn)違背業(yè)務規(guī)則的支付成功通知到達,則:
并不修改訂單狀態(tài);將此筆支付款項自動返還到該會員的賬戶余額里;支付中心記錄支付失敗日志;并記錄資金變動日志;會員在前臺“個人中心”下的“賬戶余額”里能看到這個余額變更歷史以及對應的說明。 支付流程圖如下圖4.1所示:
?
圖4.1?團購支付中心判斷的簡單流程
? 4.2. 交易流水號變化規(guī)則 商戶發(fā)給第三方支付的?out-trade-no?標識了一次交易的
商戶唯一訂單號。 該參數(shù)的定義為:
| 參數(shù) | 參數(shù)名稱 | 類型 | 參數(shù)說明 | 是否可為空 | 樣例 |
| out_trade_no | 商戶網(wǎng)站唯一訂單號 | String(64) | 支付寶合作商戶網(wǎng)站唯一訂單號,并非支付寶交易流水號 (確保在合作伙伴系統(tǒng)中唯一)。 | 不可空 | 58942120-tuan-001 |
商戶完全可以自定義這個 out_trade_no 的字符串組成規(guī)則。 即使對應同一個訂單,也可以構造出不同的 out_trade_no 。 只要當支付寶的交易通知把這個參數(shù)原樣返回時,你的程序能知道這是哪一個訂單的哪一筆交易,它的應付金額是多少,這個應付金額被支付后訂單產(chǎn)生什么變化,這樣就行。 下面舉幾個例子。
例一:修改訂單,訂單應付金額或支付方式發(fā)生變化 背景:訂單在沒有支付成功之前,顧客都是可以修改的。做了以下修改后,可能會引起訂單應付金額或支付方式發(fā)生變化:
- 余額支付的金額變化
- 購買份數(shù)的調整
- 優(yōu)惠券/代金券的使用
而支付寶等第三方支付,對于一個用
商戶唯一訂單號標識的交易,禁止變更 total_fee(交易總金額)字段! 所以,我們的同一個訂單,發(fā)起不同應付金額的支付請求時,必須更換 out_trade_no ,流程如下圖4.2所示: 圖4.2 訂單應付金額變化,out_trade_no 必須變化
例二:修改訂單,訂單支付方式發(fā)生變化 背景:訂單未成功支付前,用戶也是可以調整支付方式的:
- 支付方式的調整(不僅僅指從支付寶變?yōu)榭戾X這種第三方支付之間的變更,而且包括從支付寶之網(wǎng)銀直連變?yōu)橹Ц秾氝@種第三方支付內部變更)
此時,建議更換 out_trade_no 。
例三:訂單已付款,但追加一部分商品,需要補支付 背景:選擇了菜品1、2、3、4的訂單已支付成功,顧客追加菜品5,不需要創(chuàng)建新訂單,可以在原訂單基礎上補充支付。 做法:如下圖4.3所示 圖4.3 訂單補支付
4.3. 對賬拉單 無論系統(tǒng)是否可靠,商戶終歸還是要對賬的。 對的就是數(shù)據(jù)庫里記錄的當天應收帳款,與第三方支付商戶帳號里收到的錢是否吻合。 如果你數(shù)據(jù)庫記錄的顧客用支付寶支付的款項是10001元,而你的支付寶帳號里只收到了10000元,那一定有問題,必須要深究下去。 核對的辦法就是, 每天零點,從數(shù)據(jù)庫里查詢出前一日用支付寶支付的所有交易,得到支付寶交易流水號和支付金額的集合; 遍歷這個集合,拿交易流水號去支付寶的單筆交易查詢接口(single_trade_query),這樣查出交易金額,對比一下,看你數(shù)據(jù)庫里記的支付金額和實際收到的交易金額是否一致。
轉載于:https://www.cnblogs.com/bluejoe/p/5115992.html
總結
以上是生活随笔為你收集整理的支付交易一般性准则的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內容還不錯,歡迎將生活随笔推薦給好友。