支付系統的對賬處理:對賬,軋賬,平賬,交易記錄,退款記錄
關鍵詞:對賬,軋賬,平賬,交易記錄,退款記錄
對賬是支付系統最頭疼的事情。每一筆交易,都要做到各參與者的記錄能夠吻合,沒有偏差。 對賬系統的工作,是發現有差異的記錄,即軋帳;然後通過人工或者自動的方式,解決這些差異,即平帳;對電商系統來說,每一筆交易,在所有相關主體側都要能對得上:
- 交易主體,如果發起人是個人,必須能夠從個人交易歷史記錄中找到這筆交易。但大部分人不會保留電子記錄,所以一般是提供可以下載的賬單或交易記錄,讓使用者自己對去。
- 交易對手,一般是商戶。商戶側對賬處理同用戶側,也僅僅提供對賬單。
- 交易渠道側,這是對賬的重點,一是核實交易流水,二是核實交易佣金,畢竟是租用人家通道做結算的。
那有哪些記錄需要對賬? 目前主要是兩個:一個是交易記錄;一個是退款記錄。
對賬處理流程
一般來說,對賬流程涉及到如下步驟: 渠道對賬單下載、本地交易記錄準備、軋賬、平賬。
渠道對賬單下載
銀行,第三方支付,銀聯等,基本都會提供對賬單下載的功能。不過也有少數工作做不到位或者太到位的銀行,只提供賬單查詢後臺,不提供對賬單下載功能。 對開發人員來說,這裡有幾個坑:
對賬單格式不一。文字,XML,csv的都有。為了後續能夠統一處理,在賬單下載完成後,需要進行標準化處理。
下載方式不一,HTTP,HTTPS,FTP的,都有。下載程式需要按照渠道的協議來處理。
下載時間不一,一般是凌晨1點後,到中午12才能用的也有。如果在預定的時間取不到資料,需要注意重試讀取。
穩定性差。FTP伺服器出問題那是常有的事。渠道側解決方案往往就是重啟。所以重試機制是必要的。
看一下第三方支付的對賬單情況:
渠道 | 對賬週期 | 賬單提供方式 | 賬單檔案格式 |
---|---|---|---|
支付寶 | 每天 2:10 | HTTPS | XML |
支付寶退款 | 每天3:10 | HTTPS | XML |
百付寶 | 每天7:00 | FTP | TXT |
百付寶退款 | 每天7:00 | FTP | TXT |
微信支付 | 每天10:30 | HTTPS | TXT |
微信退款 | 每天10:30 | HTTPS | TXT |
技術選型上,HTTP(S)用apache httpclient即可實現連結池和斷點續傳, FTP也可以使用Apache Commons Net API。 但不管是哪一個,都需要設定重試次數和連結超時間。重試次數和間隔的設定需要小心,重試太頻繁,容易把伺服器打死.;時間間隔太大,又會阻塞後續處理步驟。5~10分鐘是一個合適的重試間隔區間。
連結超時指在伺服器出現問題時,連線在指定時間內獲取不到資料即自動斷開。這個很容易被忽略。我們有一次系統出問題,是渠道側的FTP假死後重啟,導致我們的客戶端掛住,一直在等待重新連結。
渠道對賬單標準化
找個例子大家看看, 比如微信的對賬單,他是csv格式的,包括如下資訊:
交易時間:這是在微信側的支付完成的時間。 這個時間會成為一個陷阱。
公眾賬號ID,商戶號,子商戶號,裝置號: 這些資訊需要做驗證,確保是自己的單子,不要讓微信把老王家的單子也給發過來了;
微信訂單號,商戶訂單號: 這兩個是對單的核心。前者是微信側產生的訂單號,在微信支付介面返回值中有。但是萬一收不到這個返回值,那在本地記錄中可能就空了。 後者是我們傳送給微信的訂單號,一般用這個來做對單依據。兩邊的資料中都會有這個值。
使用者標識,交易型別,交易狀態,付款銀行,貨幣種類,總金額,企業紅包金額: 這幾個就是對單的核心欄位,必須確保雙方是一致的。
商品名稱,商戶資料包,手續費,費率:這些是可選驗證。
而某寶的對賬單,是文字格式的,用空格隔開。他們家的就簡單很多,只有商戶訂單號,交易流水號,交易時間,支付時間,付款方,交易金額,交易型別,交易狀態這些欄位。
由於每個渠道的賬單格式都不盡相同, 在得到賬單後,下一步是對賬單做標準化處理,這樣軋帳以及後續工作就可以統一處理了。 標準化後的賬單資料可以放在檔案系統或者資料庫中。這取決於交易資料量。每天百萬以上的量,還是使用檔案系統,比較合適。資料庫操作相對比較慢,也浪費資源。 基於檔案系統的標準化涉及如下內容:
檔案格式標準化統一使用csv或者json或者xml格式。如果是使用hadoop或者spark來對賬,使用csv是個不錯的選擇。
檔案儲存統一化檔案目錄,檔名都需要遵循統一命名規範。
為了加快處理速度,我們使用hdfs作為檔案系統,有利於後續的對賬的處理。
本地交易記錄準備
本地交易記錄的準備,總的來說有如下方法: - 啥都不做,直接用原始資料。鑑於大部分系統使用的是mysql,這也意味著在MySQL上做對賬。對賬時需要大量的資料查詢工作,必然會影響線上業務。在資料規模較大,比如超過100萬時,就不太合適了。
當然,還有一個選擇是使用備庫來執行對賬,這樣既簡單,也不影響線上業務。這是典型的空間換時間的做法。
如果業務大到需要分表分庫才能處理,那對賬資料準備也不一樣。使用分庫也不現實,因為分庫一般是按照主體id,而不是渠道id,來分庫,這樣對賬就需要在多個庫上進行,效率反而降低了。而對分表分庫建立從庫也非常耗費資源。這種情況下,需要同步一份資料到(hdfs)檔案系統中,或者NOSQL資料庫上。
由於交易記錄是支付系統核心資料,有大量的應用,如信用、風控等,都需要交易記錄資料。這些應用對交易記錄的需求還不完全一致,為了提升效能, 交易記錄會使用非同步的方式來將資料投遞給使用方。 交易記錄在入庫時,投遞訊息到訊息系統中。使用方監聽這個訊息,一旦收到新訊息,則從交易記錄庫中查詢資料,獲取資料並更新到庫中。關於此類資料同步的文章不少,這裡就不詳細介紹。
軋帳
軋帳是按照客戶訂單號來比較本地交易記錄和渠道交易記錄是否一致。從演算法角度,是計算兩個陣列的差異。在單機執行時,可以採用的演算法不少,這裡不詳細介紹。 我們推薦採用mapreduce來軋帳,這有個優勢,可以按照訂單號將渠道提供的記錄和本地記錄shuffle到同一個reduce處理上,這樣就可以很容易進行資料比對。 軋帳中最大的坑,莫過於切分點的問題。比如以整0點為切分點,那存在一個問題,本地23:59發起的交易,到了渠道側,可能會在00:01處理,這一筆交易變成第二天的帳了。實際處理中,一筆交易在渠道側處理,花上幾分鐘都有可能。 對於切分點附近無法確認的帳,做一個時間窗,在時間窗內的資料,留待第二天對賬時繼續處理。
平帳
發現兩邊不一致的資料,那應該如何處理?資料量不大時,記錄起來,人工甄別就行。但如果資料量很大,每天上千條,人工處理就成本太高了。這個沒有統一的處理方法,需要根據有問題的資料,做個分析,然後做自動處理。 針對交易記錄的對賬的處理,主要有如下情況:
本地未支付,支付渠道已支付。這主要是本地未正確接收到渠道下發的非同步通知導致。 一般處理是將本地狀態修改為已支付,並做響應的後續處理,比如通知業務方等。
本地已支付,支付渠道已支付,但是金額不同,這個需要人工核查。
本地已支付,但是支付渠道中無記錄;或者本地無記錄,支付渠道有記錄。在排除跨日因素外,這種情況非常少見,需要了解具體原因後做處理。
針對退款的對賬處理,主要有如下情況:
本地未退款,支付渠道已退款,則以支付渠道為準,修改本地為已退款狀態,並出發後續處理。
本地已退款、支付渠道已退款,但是金額不同,需要人工核查;
本地已退款,但是支付渠道無記錄;或者支付渠道有記錄,但是本地沒有。 在排除跨日因素外, 這種情況非常少見,需要了解具體原因後做處理。
總之,對賬工作,即複雜也不復雜。需要細心,對業務要有深入的瞭解,並選擇合適的架構。