iT邦幫忙

2026 iThome 鐵人賽

DAY 12
0
Software Development

Re:從零開始做直播代購電商平台系列 第 12 篇

Day 12|第三方金流(下):沒有坑的地形

  • 分享至 

  • xImage
  •  

昨天留了個懸念:教科書的三個坑(重複、亂序、失蹤),當年一個都沒踩。今天把原因攤開。

沒有坑的地形

當年的系統,這三個坑的體感是:「好像沒遇到什麼問題」。把昨天的架構代入,原因一目瞭然:

同樣的三個威脅,兩種地形:一邊要蓋三座碉堡,一邊根本沒有可攻擊的東西。

  • 重複通知? 回調只是把「銀行 A 說這筆轉帳入帳了」這個事實寫下來——同一筆事實寫幾次,聚合出來的答案都一樣。
  • 亂序? 五筆部分付款誰先到誰後到,根本不重要——事實各自落地,SUM 是讀的時候算的。
  • 沒來? polling 排程會把漏掉的事實補上——而且因為冪等,polling 和 webhook 重疊也無害,雙通道互為備援。

三套防禦的成本歸零,因為沒有可以被打壞的狀態。這是整個系列「事實 append、狀態派生」主旋律的最終回收:同一個原則,在留言層教過你事實要接上路徑才算數(raw 躺在庫裡,重放從沒發動)、在庫存層給了你對帳、在金流層直接讓最兇的一類 bug 絕種。

對帳也順著這個結構分了級:某家銀行的付款記錄會帶 orders payment id,自動對回;現金靠營運標記——又是一個「自動收大宗、人工收殘量」的漏斗,跟身分章的綁定漏斗同構。

插曲:那顆大家一直想加的狀態欄位

這個「狀態讀時派生」的設計,不是全隊拍手通過的——它是被擋出來的。當年 CTO 和幾位同仁不只一次想加一顆訂單狀態欄位:一個直接可查、可篩、放在列表頁很方便的 status。我一意孤行地反對,理由每次都一樣:這個狀態本質上就是各種欄位與事實的聚合——做這顆欄位,就是做一份 cache。 cache 一旦存在,就要回答所有 cache 都要回答的問題:誰負責更新它、哪些寫入路徑要記得同步它、它歪掉的時候以誰為準。而我們當時根本沒有遇到效能問題。在真的遇到效能問題之前,不要做 cache。

這個立場不是教條地反固化——庫存章的賣出計數就是固化的派生值,因為它躺在下單的熱路徑上(每則留言都要檢查不變量),而且配了每小時重算的自我修復。兩個決定用的是同一把尺:派生值要不要固化,唯一正當的理由是量測到的讀取成本;固化了,就要同時承認它是 cache、配上重算與對帳。「查起來方便」不在正當理由清單上——方便的代價,是從此多一個會說謊的欄位。

退款:系統退到觀察者的位置

退款的故事是狀態機教訓的姊妹篇。當年認真做了退款 API——串好銀行、包好流程——結果營運人員不愛用。他們習慣直接開銀行後台按退款,快、熟、看得到餘額。

掙扎過後的決定:放手。營運去銀行按他們的,按完把訂單狀態標成「退款中」——然後排程接手,sync 退款的實際進度。系統從「執行者」退到「觀察者」:人做動作,系統對帳。

這個退位其實跟整章的架構一脈相承:退款進度也只是另一組事實,誰觸發的不重要,系統的職責是把事實追回來、把狀態派生對。硬要營運走你的 API,本質上是把自尊建在「別人要不要用你的介面」上;讓系統有能力追上現實,才是把工程花在對的地方。

反思

錢的系統,謙遜比聰明重要

這章有兩次「退讓」:狀態不做轉移限制(跟著事實走)、退款不走自家 API(讓營運去銀行按)。兩次都是系統對現實低頭——而回頭看,兩次都對。錢的流動牽著銀行、營運、客人三方,你的系統永遠只是其中一個參與者;把自己定位成「所有金錢事實的忠實帳本」,而不是「金錢流程的唯一入口」,反而讓正確性更容易守住。帳本不需要控制世界,只需要如實記下世界。

「沒遇到什麼問題」是最高級的戰果

金流照理是整個系統最容易出事的地方——外部依賴最多、不可靠網路、錢的違約成本最高——但它是這個系列寫到現在最平靜的一章。連著兩章「無聊」(Day 9 的結帳也是),而且無聊的原因相同:前面把事實與派生的關係擺對了,後面的複雜度就長不出來。這也是工程裡最不公平的一件事:好架構的回報,以「沒有故事」的形式發放——出事救火的人人看得見,讓火燒不起來的人沒有戰功。我後來當 EM,很大一部分工作就是學會看見、並獎勵這種「沒有故事」的人。

明天講錢收完之後的事:出貨——賣的是承諾,出的是現實。


本文改寫自我的部落格系列《Re:從零開始做直播代購電商平台》,本篇完整版:https://blog.aidan.tw/blog/rezero-payment/


上一篇
Day 11|第三方金流(上):教科書的三個坑
下一篇
Day 13|出貨前處理:賣的是承諾,出的是現實
系列文
Re:從零開始做直播代購電商平台 共 15 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言