千萬級支付對賬系統怎麼玩(上篇)?

上篇文章聊到了對賬系統業務邏輯以及千萬數據集對賬系統存在的難點,這篇文章就來聊下千萬級數據集下對賬系統實現方案。

首先我們先來看下對賬整體時序圖,先有個印象:

下面整篇文章將會圍繞上面時序圖開始講解,由於文章篇幅過長,所以文章將會拆分成上下兩部分。

數據平台

上次文章中提到,千萬級數據需要使用 Hive,Spark等相關大數據技術,這就離不開大數據平台的技術支持。

簡單聊下我們這邊大數據平台DP (Data Platform),它提供用戶大數據離線任務開發所需要的環境、工具以及數據,具有入口統一性、一站式、簡化hadoop本身的複雜性、數據安全等特點。

DP 平台提供功能如下:

  • 數據雙向離線同步,MySQL 與 Hive 互相同步
  • 大數據離線計算,支持SQL(SparkSQL/HiveSQL/Presto)形式處理各類的數據清洗、轉化、聚合操作,也支持使用MapReduce、Spark等形式,處理比較複雜的計算場景
  • 即時的SQL查詢,允許用戶即時的執行SQL、查看執行的日誌和結果數以及進行結果數據的可視化分析
  • 數據報表

那本篇文章不會涉及具體的大數據技術相關的實現細節,相關原理(主要是咱也不會~),主要聊下對賬系統如何聯合 DP 平台實現完整數據對賬方案。

對賬系統概覽

開頭的時序圖,我們可以看到整個對賬過程設計好幾個業務流程,那在這裡對賬系統內部將會維護一個流程狀態機,當前一個流程處理結束之後,下一個流程才能被觸發。

由於當前對賬系統實現方案,涉及對賬系統與 DP 平台,對賬系統目前沒辦法調用 DP 平台觸發任務,但是 DP 平台可以通過通過 HTTP 接口調用對賬系統。

所以當前流程觸發的方式使用的是定時任務的方案,每個流程有一個單獨的定時任務。

對賬系統內的定時任務觸發的時候,將會判斷當前流程是否已經到達執行條件,即判斷一下當前任務的狀態。

每個定時任務觸發時間人為設置的時候,岔開一兩分鐘,防止同時運行。

DP 平台使用自帶調度任務,對賬系統無法控制 DP 任務的運行。

DP 平台定時任務可以通過運行 Scala 腳本代碼,調用對賬系統提供 HTTP 查詢接口,通過這種方式判斷當前流程是否已經到達執行條件。

下面詳細解釋一下每個流程。

初始化對賬任務

對賬系統依靠對賬任務記錄推動流轉,目前每天凌晨將會初始化生成對賬任務記錄,後續任務流轉就可以從這裡開始。

對賬系統維護一張對賬核對規則表:

對賬核對規則表關鍵字段含義如下:

  • channel_code 渠道編碼,每個支付渠道將會分配一個唯一渠道編碼,例如微信,支付寶
  • biz_type 業務類型,例如支付,退款,提現等
  • status 是否生效

每次對接新的支付渠道,對賬配置規則需要新增核對規則。

初始化對賬定時任務將會查找核對規則表中所有的生效的配置規則,依次生成當天的對賬任務記錄:

對賬任務記錄部分字段與核對規則表含義一樣,不再贅述,其他字段含義如下:

  • bill_date 賬期,一般 D 日對賬任務核對 D-1 數據,所以賬期為 D-1 日
  • batch_no 對賬批次,生成規則如下:賬期+渠道編碼+ 001
  • phase,當前對賬任務處於階段,根據上面對賬流程可以分為
    • 初始化
    • 數據收集
    • 存疑處理
    • 數據核對
    • 二次存疑處理
    • 數據匯總
    • 差錯數據推送
  • error_reason 錯誤原因

初始化對賬任務結束之後,對賬任務流程推動到第二階段,數據收集。

數據收集

數據收集階段,收集兩端待核對的數據,為後面的數據核對任務提供核對數據。

數據收集階段分為兩部分:

  • 本端數據收集,即自己方產生的支付數據
  • 對端數據收集,即三方渠道產生支付數據

本端數據收集

本端數據,是自己業務產生的支付數據,這些數據原本存在各個業務的數據庫中。

對賬系統獲取這些支付數據,一般有兩種方式:

  • 查詢,對賬系統主動拉取
  • 推送,對賬系統監聽獲取數據

查詢數據方式上篇文章也聊到過,數據量小的情況下,沒什麼問題。一旦數據量變大,查詢效率就會變低。

所以這裡我們採用推送的方式,對賬系統監聽各個業務數據表 binlog,每當業務數據發生變動,對賬系統就可以接受到 binlog 消息。

對賬系統接受到 binlog 消息,將會判斷當前消息是否需要過濾,是否已經支付成功等等,滿足條件之後,binlog 消息將會插入本端數據表中,表結構如下:

本端記錄表關鍵字段含義如下:

  • channel_code 渠道編碼,每個支付渠道將會分配一個唯一渠道編碼,例如微信,支付寶
  • biz_order_no 本端支付流水號
  • bill_date 賬期
  • status 狀態
  • is_check 對賬狀態,0-未核對,1-已核對
  • trade_amount 支付金額
  • channel_order_no 三方渠道支付單號
  • merchant_no 商戶號
  • sub_merchant_no 子商戶號

上面展示的支付記錄表結構,根據業務類型不同,本端其實還有退款記錄表,提現記錄表等。

這裡設計的時候,實際上也可以將所有業務數據放在一張表中,然後根據業務類型字段區分。

對端數據收集

對端數據,就是第三方支付渠道產生支付數據,一般 D 日產生交易之後,D+1 日第三方渠道將會生成一個對賬文件。

對賬系統需要從對端提供的對賬文件獲取對端數據。

渠道的對賬文件,下載方式,文件類型存在很大的差異,每次接入新的支付渠道,這裡需要經過新的開發。

對端數據這裡維護了一張渠道下載配置表,對端數據收集的時候將會獲取所有可用配置:

渠道下載配置表關鍵字段含義如下:

  • mch_id 三方渠道分配的商戶號
  • type 下載類型
    • FTP
    • SFTP
    • HTTP
  • download_param 下載的配置參數,比如 ftp 的地址,登錄密碼,下載地址等。

對賬文件下載成功之後,需要根據文件類型進行解析,最後轉化自己的需要的對賬數據入庫。

對端數據表結構如下:

上面關鍵字段與本端記錄表類似,額外新增字段:

  • channel_fee 渠道手續費,用於統計渠道收的手續費

同樣渠道記錄表根據根據業務類型也分為退款渠道記錄表,提現渠道記錄表等,同樣也可以合併成一張表,根據業務類型區分。

對端數據收集階段,由於拉取三方渠道的對賬文件,那有時候渠道端存在異常,將會導致對賬文件下載延遲,從而導致其他任務也出現的相應的延遲。

這一點是整個對賬流程中,相對不可控的問題。我們需要在對賬流程設計中考慮這一點。

對賬文件下載解析成功入庫之後,對賬流程將會流轉到下一個流程存疑數據處理。

存疑數據處理

講解這個流程之前,先給大家解釋一下什麼是存疑數據?

正常支付過程中,會存在一個兩邊賬期不一致的問題,比如說本端數據支付時間是 2021 年 12 月 28 日 23 點 59 分 59 秒,那麼本端認為這筆支付交易賬期是 2021 年 12 月 28 日。

然而這筆支付發送給三方渠道之後,三方渠道支付成功的時間已經是 2021 年 12 月 29 日 0 點 0 分 2 秒,三方渠道支付賬期記為2021 年 12 月 29 日。

這種情況下我們這邊記錄賬期是2021 年 12 月 28 日,但是第三方渠道這筆記錄是 2021 年 12 月 29 日,所以 2021 年 12 月 28 日對賬單上沒有這筆支付記錄,這就導致一筆差異數據(一端有/一端無)的情況。

上面這種情況就是典型因為日切問題導致差異。

但是我們知道 2021 年 12 月 29 日對賬單上肯定會包含這筆,所以我們可以先把這筆差異數據掛起,當做存疑數據,等到2021 年 12 月 29 日賬期對賬的時候,對方賬單包含這筆,當天就能核對成功,這就解決這筆差異數據。

所以說存疑數據,就跟其字面意思一樣,當這筆數據當前處理不了的時候,那就現放着,不做定論,過一天我再嘗試處理一下。

除了上面日切問題導致的差異數據以外,還有一些情況:

  • 網絡問題,導致兩邊訂單狀態不一致。
  • 測試環境與生產環境共用一個三方渠道商戶號,測試環境產生的交易出現在對賬單里

存疑數據分為三種類型:

  • 本端有,渠道無,即本端存在訂單信息,渠道賬單記錄沒有訂單信息,可能是日切導致的問題
  • 渠道有,本端無,即本端不存在訂單信息,渠道端賬單記錄卻有訂單信息,可能是測試環境與生產環境共用渠道參數
  • 金額不平,即雙方都存在訂單信息,但是雙方訂單金額不一致

了解完存疑數據的定義,我們再來看下存疑數據處理的流程。

存疑數據將會由下面的流程中產生,這裡先來看下存疑表結構:

關鍵字段如下:

  • batch_no 批次號
  • biz_id 業務單號
  • biz_amount 金額
  • status 0-未處理,1-已處理
  • biz_date 賬期
  • biz_type 業務類型
  • channel_code 渠道類型
  • delayed_times 延遲天數
  • merchant_no 商戶號
  • sub_merchant_no 子商戶號
  • buffer_type 存疑類型,0-本端存疑,1-渠道存疑

存疑處理過程將會撈起所有存疑表中還未處理的存疑數據,根據存疑類型反向查找對賬數據表。例如:

  • 渠道存疑(第一天對賬,本端有,渠道無),查找對端數據
  • 本端存疑(第一天對賬,本端無,渠道有),查找本端數據

查找對端/本端數據,都是根據支付流水號加業務類型查找定位。

如果在本端/對端數據中找到,這裡還需要再對比一下金額

  • 如果金額不相等,代表單號相同,但是金額不等,將這筆移動到支付差異表
  • 如果金額相等,代表這兩筆核平,存疑表將這筆數據更新為核對成功,本端/對端數據更新為對賬成功

上面這一步比較重要,因為下面對賬核對過程主要核對要素是支付流水號+支付金額,通過這種方式收集單片賬是無法知道是因為單號不存在,還是因為金額不存在原因,具體流程可以看下下面核對流程。

如果在本端/對端數據還是找不到,那就根據渠道配置的存疑規則,如果當前已經存疑的天數大於配置渠道存疑天數,則將數據直接移動到差錯表。

如果存疑天數小於當前渠道配置天數,那就不要管,繼續保存在存疑表,等待下一天存疑數據處理。

一般來說,日切導致的數據,存疑一天,就可以解決。但是有些渠道可能是 T+1 在對賬,這種情況需要配置的存疑天數就要長一點了。

本地存疑數據處理結束之後,下面就要開始 DP 數據處理。

總結

上篇文章主要聊了對賬流程前半部分,這幾個流程主要是為了後續 DP 平台核對收集業務數據。

這裡存疑流程處理比較關鍵,不熟悉對賬流程的同學,這裡需要重點關注下。

下篇文章主要講下 DP 平台對賬數據如何核對,敬請期待。