你以為防線夠了,其實少了最後一道

做金流串接,該做的你都做了:callback 有驗簽(確認通知真的來自金流商)、有冪等(同一筆通知重複送不會入帳兩次)、失敗有重試。看起來滴水不漏。

但這些防的全是「單筆流程走歪」。還有一整類問題它們碰不到:

  • 金流商那邊付款成功了,callback 卻一次都沒送到(網路斷、對方系統故障)——你的系統根本不知道有這筆錢
  • 你收到通知標了已付款,金流商那邊事後取消或調帳了——你記的是舊帳
  • 金額被中途改掉、或你自己的 bug 記錯數字——兩邊各記各的,誰也不知道

共同點:每一邊系統各自看自己都「沒有錯」,錯的是兩本帳對不起來。單筆防線抓不到「帳本級」的分歧——這就是對帳(reconciliation)存在的理由:定期把兩邊帳本攤開來逐筆比對

對帳在比什麼:一張表講完

我自己的電商後端有一個獨立的對帳模組,做法是每天一批:把金流商的交易明細抓下來,跟自己資料庫的付款紀錄逐筆對。比對結果只會有四種差異,值得背起來:

差異類型意思典型原因
金額不符兩邊都有這筆,但錢不一樣手續費沒算、部分退款沒同步、bug
狀態不符兩邊都有,但一邊說已付、一邊說沒付callback 掉了、事後調帳
內部遺漏金流商有、我沒有callback 全沒送到(最危險:錢收了你不知道)
外部遺漏我有、金流商沒有通常是我這邊的假資料或測試單混進來

抓資料的管道通常有兩種,我的系統兩種都有:逐筆查詢 API(拿單號去問金流商「這筆現在什麼狀態」)和對帳檔(金流商每天產一份 CSV,裡面還會有你系統裡沒有的資訊——實收手續費、實際撥款金額)。對帳檔那兩個欄位很重要:訂單收 1000 不等於你拿到 1000,手續費扣完的撥款金額只有對帳檔會告訴你。

三個設計細節(從實作裡學到的)

  1. 每天一批、一批唯一。對帳批次用「日期+金流商」當唯一鍵,跑過就不能重複建——不然重跑一次就多一堆重複差異單。對帳系統自己也要冪等,很諷刺但很真實。
  2. 差異不是拿來自動修的,是拿來開單的。抓到不一致,系統該做的是產生一筆「待人工處理」的差異紀錄,而不是自動改帳——因為你不知道錯的是哪一邊。自動「修正」等於用猜的蓋掉證據。
  3. 對帳要有自己的排程。我的系統目前只有手動觸發按鈕,沒有每日自動跑——這是個誠實的洞:對帳做一半,等於「有保險但要記得自己去申請理賠」。(已列待補。)

把視野拉開:對帳是一個通用 pattern

雖然例子是金流,但「兩個系統各記各的帳,定期核對」這個 pattern 到處適用:

  • 你的庫存數字 vs 倉庫系統(WMS)的庫存數字
  • 你的訂閱狀態 vs Apple/Google 的訂閱狀態
  • 主資料庫 vs 搬去搜尋引擎/快取的副本(資料漂移檢測)
  • 微服務拆開後,A 服務的訂單數 vs B 服務的出貨數

判斷要不要做對帳的準則:只要有兩個系統各自持有「同一件事」的紀錄、中間靠非同步訊息同步,那些訊息就有掉的一天——對不起來只是時間問題。 事件驅動架構講「最終一致性」,對帳就是那個「最終」的執行者:平常靠事件同步,對帳兜底把漏網的抓回來。

相關