Table of Contents
這篇文章整理自 s09g 頻道的《[系統設計 Mock] POS payment service》系統設計 mock interview。整支影片只有十分鐘,但資訊密度很高——它講的不是「POS 是什麼」這種白話文,而是面試官會一直追問的設計細節:三階段時序、跨系統一致性、Idempotency Key 的物理意義。如果你是後端工程師要準備 system design interview,這幾個點一定要搞懂。
一、POS 不是「一個 API 收錢」——先選變體
影片一開頭就把題目切成三個變體:
- 全部在線變體(online-only):使用者從下單到付款到出貨,所有事都在一條 HTTP 鏈路上跑完。這是電商網站最常見的情境。
- 刷卡變體(card-present):今天的主角——實體店面、用戶拿信用卡刷 POS 終端。這個變體必須拆成線上跟線下兩段。
- P2P 變體:用戶對用戶的轉帳(像 PayPal 朋友轉帳、Venmo)。今天不展開。
為什麼要先選變體?因為變體決定架構。Online-only 變體你可以用同步 transaction 處理完;刷卡變體就沒辦法,因為你不知道用戶什麼時候會把卡拿開、什麼時候會按取消、銀行的清算又是另一個時段。
面試的時候,如果你沒先講「我們今天討論的是刷卡變體」,面試官會直接假設你是 online-only——這題就解掉了,沒什麼好設計的。
二、三階段時序:Authorize → Processing → Complete
這是整個設計的脊椎。三個階段不是「123 三步」,而是發生在三個完全不同的時段:
| 階段 | 時機 | 發生什麼 | 延遲特性 |
|---|---|---|---|
| Authorize | 用戶刷卡瞬間 | 終端直接 call 銀行,銀行 200ms 內回應卡是否有效 | 同步 |
| Processing | 當天避典後(通常是凌晨) | 系統把當天所有 authorize 打包成 batch file,丟給銀行做清算 | 批次、非同步 |
| Complete | 銀行清算完後 | 銀行回 statement file,商家做最終對帳 | 非同步,可能隔天 |
關鍵點:Authorize 在白天即時發生,但商家帳戶真正收到錢是 Complete 之後的事。影片裡面試官特別點出——很多面試者寫成「123 三步驟」就直接被挑戰,因為這三件事不是連續的,是跨時段的。
這也是為什麼「authorize 成功」不等於「款項到帳」——你早上看到刷卡成功,只是銀行說「這筆錢我先幫你鎖住」,真正的撥款要等晚上批次清算完。
實務上這個時序對商家很重要:你不能假設用戶刷完卡你就可以出貨——你要等到 Complete 階段確認款項真的進來。但對消費者體驗來說,Authorize 成功就會印收據、開發票,所以內部系統的狀態機至少要追蹤這三個狀態。
三、Idempotency Key:這是整個設計的核心
這是影片裡講得最細、也是最容易踩坑的一段。
內部 payment ID 絕對不能曝露
你的內部系統會有 payment_id,但這個 ID 絕對不能丟到 idempotency key 裡面,原因是:
- Idempotency Key 會跨系統流通(商家 → Bank processor → PSP → 銀行)
- 它是給外部系統去重用的(同一筆交易別 retry 兩次扣款)
- 如果你把內部 ID 放進去,等於把內部資料表結構曝露給外部
正確做法:用 hash 處理過再放進 idempotency key。例如 sha256(payment_id + internal_secret)。或者更乾脆,idempotency key 就是另一套獨立的命名空間。
Idempotency Key 對應的物理意義
這是面試官會追問的第二層:
「Idempotency Key 到底對應什麼?一次 payment?一次 order?一次刷卡?」
影片的答案是:對應用戶的一次支付意圖(payment intent)。
為什麼這很重要?因為現實世界裡:
- 一個 order 可以有多個 idempotency key:你和朋友去吃飯 split the bill,你刷一張卡、朋友刷一張卡。同一個 order 兩次刷卡 → 兩個 idempotency key。
- 同一個 intent 可以有多個 idempotency key:你刷卡後取消,然後再刷一次——同一個支付意圖(我要付這筆錢),但產生了兩次 idempotency key(第一次失敗、第二次成功)。
所以 idempotency key 不是 outer-level(整個 order 一次),也不是刷卡一次 level(每次刷卡機械動作一次)——它是支付意圖這個抽象層。
為什麼不要把兩個 ID 混用
常見錯誤:
idempotency_key = "{order_id}-{payment_id}" # ❌
這會爆露內部 ID,而且如果使用者退貨 + 重付,order_id 相同但 payment_id 不同,idempotency key 重複,系統會以為是同一筆——然後第二次扣款失敗。
四、跨系統一致性:為什麼一定塞 Message Queue
影片裡面試官點出了一個關鍵:不同系統之間的 TPS(throughput per second)是完全不同量級的。
- 你的 API 服務可以撐幾萬 TPS(單機上千,QPS 過萬)
- 銀行的清算 batch file 可能一天處理一次
- 銀行的 statement 回傳可能延遲數小時
如果你用同步 request 串起來,你的 API 會被銀行的 batch 拖死——使用者刷卡 200ms 內 authorize 成功,但你的內部處理流程被卡在「等銀行回 statement」上。
所以架構必須是:
[POS 終端]
↓ (同步,~200ms)
[Authorize API]
↓
[Transaction DB] ← 寫一筆 payment record
↓
[Message Queue] ← 非同步,塞進去就 return
↓
[Batch Worker] ← 慢慢消化,打包 batch file
↓
[Bank / PSP] ← 夜間清算
↓
[Statement Worker] ← 處理回傳
這個 Message Queue 不能省。如果你試圖用同步呼叫串起來,一定會在某個環節塞住。
訊息佇列選型考量
實務上會選:
- Kafka:高吞吐、持久化、可重放——如果你要處理 statement 回放、對帳,選 Kafka 沒錯
- RabbitMQ:傳統銀行整合常用,有 ack 機制、支援 transaction
- AWS SQS / GCP Pub/Sub:雲端服務,不用自己維運
POS 場景我個人偏好 Kafka——因為你可能要 replay 當天所有 transaction 來對帳,Kafka 的 retention model 適合。
五、跟 Stripe 級別的差別:不要為了潮而做 Event Sourcing
影片最後講了一個容易被忽略的點:大部分系統不是 Stripe,不要直接套 Stripe 的架構。
Stripe 的特殊地位
Stripe 同時是 Bank Provider + Payment Service Provider(PSP)——它直接連到銀行網絡,中間不需要再插一層 PSP。所以 Stripe 才可以:
- 同步完成 authorize + capture
- 即時告訴商家「款項已到」
- 提供完整的 webhook 事件流
但你不是 Stripe。你是 Stripe 的客戶(商家)。所以你的架構是:
[你的系統] → [Stripe/Adyen/Braintree] → [銀行網絡]
你拿到的 webhook 是 Stripe 已經幫你做完 capture 的事件,你沒辦法自己 capture。
Event Sourcing / CQRS 評價
有人會說「payment 應該用 Event Sourcing 啊,所有事件都記下來,可以 replay」——影片的面試官直接打槍:
「大部分系統不是 Stripe 級別,你做成 event sourcing 沒有太大收益。Event sourcing 適合已經建成 audit log 系統的公司——它本來就要處理任意時間點 replay。」
對一般 POS 系統來說:
- Saga 模式 + 補償交易就夠了:每個本地交易 + 失敗時的補償動作
- 不要把 payment 設計成 event sourcing 主架構:投資報酬率不對
- Audit log 該做但獨立做:寫一份完整的事件記錄,但查詢走傳統 SQL
這是一個務實的工程判斷——很多面試者會為了「看起來很專業」硬上 event sourcing,但面試官會追問「你為什麼這樣選?」,這時就要講得出 trade-off。
六、面試追問 checklist
整理一下影片中被追問的點,如果你要面 POS 系統設計,這些一定要準備:
- Authorize / Processing / Complete 三階段時序:有沒有講清楚發生在不同時段?不是「三步驟」是「三個時段」。
- Idempotency Key 的 scope:是 outer-level 還是刷卡一次 level?答:支付意圖層。
- 一個 order 多個 intent:split the bill、退款重付——有沒有想過這兩個 case?
- 內部 ID 不要曝露:你的 idempotency key 怎麼產生?hash 過嗎?
- Message Queue 為什麼必要:同步串起來會發生什麼?
- Credit Card vs Debit Card 要不要分:影片最後被追問這個——實務上會分,因為清算規則不同。
- 跟 Stripe 的差別:你是 Stripe 的客戶,不是 Stripe——架構不一樣。
結語
POS 系統設計的精髓不是 API 介面設計,而是時序和跨系統一致性。
記住三件事大概就能 cover 80% 的面試場景:
- 三階段時序:Authorize 即時、Processing 批次、Complete 非同步
- Idempotency Key:對應支付意圖、跨系統流通、絕不曝露內部 ID
- Message Queue:跨 TPS 系統之間必塞
剩下的是 trade-off——Saga 還是 Event Sourcing?Kafka 還是 RabbitMQ?Credit Card / Debit Card 分不分?這些沒有標準答案,但你要講得出為什麼這樣選。
如果你正要準備 system design interview,推薦把這支影片找出來看——s09g 講話速度不快,而且面試官的追問邏輯很清楚。十分鐘的影片,抵你自己讀半小時的設計文章。