在電商網站結帳,輸入信用卡號,按下確認,畫面顯示「付款成功」,整個過程大概三秒;但是在這三秒裡,請求穿越了至少五個不同機構的系統,觸發了一連串訊息交換、風控判斷、資料寫入,最後才換來那個綠色勾勾。
而那筆錢,則要再等一到兩個工作天,才會真的從客戶的帳戶移動到商家的帳戶,這個「三秒體驗、兩天資金流」的落差,是支付生命週期的核心,理解它有助於設計出正確的訂單狀態機、對帳邏輯、退款流程。
本文將以信用卡交易(以四方模型為例),完整拆解從授權到結算的每個步驟,並說明 Void、Refund、Chargeback 三種特殊情況的處理方式。
三個階段,五個角色
信用卡交易分三個截然不同的階段,每個階段的時間跨度和參與者都不一樣。
- 授權(Authorization):幾秒內完成,目的是確認卡片有效、額度足夠,此時資金並未移動,只是凍結對應額度。
- 清算(Clearing):以當日批次處理,商家正式請款、各方確認金額,資金仍未實際流動。
- 結算(Settlement):在 T+1 或 T+2 工作天完成,這才是資金在銀行間真正移動的時刻。
參與者共有五個:持卡人(Cardholder)、商家(Merchant)、收單行(Acquirer)、卡組織(Card Network)、發卡行(Issuer)。
階段一:授權(Authorization)
目標:確認這張卡可不可以用、有沒有足夠額度。
完整流程
持卡人在結帳頁面輸入卡號、有效期、CVV,按下確認後:
Step 1:商家側加密並送出
POS 機或支付閘道(Payment Gateway)將卡片資訊加密,組成授權請求,送給收單行(Acquirer);線上交易的格式通常是 HTTPS + JSON;實體刷卡走 ISO 8583 訊息格式。
Step 2:Acquirer 路由
收單行接收請求,解析卡號前六到八碼(BIN / IIN),判斷這張卡屬於哪個卡組織(Visa / Mastercard),再將授權請求轉送到對應的 Card Network。
Step 3:Card Network 轉發
Visa 或 Mastercard 依 BIN 找到發卡行,將授權請求轉發給 Issuer。
Step 4:Issuer 審核
這是整個流程中判斷最複雜的一步,發卡行要在短時間內完成:
- 額度檢查:可用額度是否足夠?
- 風控判斷:地區是否異常?金額是否超過消費習慣?近期是否有可疑交易?
- 身份驗證:是否需要觸發 3DS2(線上交易的持卡人驗證)?
- 卡片狀態:是否已掛失、過期、凍結?
Step 5:回傳結果
Issuer 回傳核准碼(Approval Code)或拒絕原因碼(Response Code),沿著原路反向傳回給商家,整個來回通常要在極短時間內完成。
授權成功後的狀態
訂單狀態此時應為 AUTHORIZED,系統需儲存:
authorization_code // Issuer 核准碼,後續 Capture 必用
authorized_amount // 授權金額(可能與實際收費不同)
authorized_at // 授權時間戳記
card_bin // 卡號前 6~8 碼(用於費率計算)
acquirer_reference // 收單行參考號
授權有效期通常是 7 天(部分產業如飯店可達 30 天),系統需要一支排程任務,掃描超過有效期卻未 Capture 的授權,主動 Void 並更新狀態,避免持卡人額度被無限期凍結。
常見拒絕原因碼
常見的拒絕原因碼及建議對使用者顯示的訊息如下:
51代表額度不足,顯示「您的卡片可用額度不足,請換張卡或調高額度」。54代表卡片過期,顯示「卡片已過期,請更新卡片資訊」。57代表不允許此交易類型,顯示「此卡不支援此類交易」。62代表風控限制,顯示「交易因安全原因被拒絕,請聯繫發卡銀行」。05是通用拒絕碼,顯示「交易失敗,請聯繫發卡銀行」。
05 是最常見的「什麼都不說」拒絕碼,Issuer 刻意不透露原因以防詐騙者測試;系統不應嘗試解讀它,直接引導用戶聯繫銀行。
階段二:清算(Clearing / Capture)
目標:商家正式請款,各方確認交易金額
授權只是「確認可以收這筆錢」,清算才是「正式說我要收了」。
Capture:請款觸發點
商家在確認可以請款後(商品出貨、服務完成),發送 Capture 請求:
1POST /v1/payments/{payment_id}/capture
2{
3 "amount": 3200, // 實際金額,可小於授權金額
4 "currency": "TWD",
5 "authorization_code": "A12345"
6}
Capture 的金額規則
- 可以小於授權金額:飯店先授權 NT$5,000 押金,退房後實際收 NT$3,200。
- 可以等於授權金額:大多數電商場景。
- 不可超過授權金額:需重新授權。
- 部分場景支援多次 Capture:分批出貨時分次請款。
批次清算
收單行在日終將當天所有 Capture 請求彙整成批次檔(Batch File),送給 Card Network;Card Network 計算各方的應收應付金額,包含:
- 交易本金。
- Interchange fee(Issuer 應收)。
- Network fee(Card Network 應收)。
- Acquirer Markup(Acquirer 應收)。
清算完成後,訂單狀態即更新為 CAPTURED。
Void 與 Refund 的分水嶺:Capture 之前可以 Void(直接取消,額度立即解凍,無資金移動),Capture 之後只能 Refund(退款,需要反向資金流),這兩條路在狀態機裡需明確區分。
階段三:結算(Settlement)
目標:資金在銀行間實際移動,商家入帳
清算確認金額後,T+1 或 T+2 工作天,資金開始流動:
Issuer → 將交易金額(扣除 Interchange fee)透過 RTGS 或 ACH 撥給 Card Network
Card Network → 扣除 Network fee 後,撥給 Acquirer
Acquirer → 扣除 Acquirer Markup 後,淨額撥入商家銀行帳戶
以刷卡 NT$1,000、MDR 1.5% 為例:
商家實際入帳:NT$985
其中 NT$10 → Issuer(Interchange fee)
NT$2 → Visa/Mastercard(Network fee)
NT$3 → Acquirer(Markup)
結算完成後,訂單狀態更新為 SETTLED。
對帳的三本帳
工程師設計對帳系統時,要核對三個數字來源:
- 系統帳:訂單表記錄的交易金額。
- Acquirer 結算報表:實際撥款金額(已扣除 MDR)。
- 銀行帳戶:實際入帳金額。
三者不一致時,產生差異單(Exception),需要人工核查,常見的差異原因有:退款未對齊、Chargeback 扣回、費率計算誤差;對帳系統的完整設計是另一個獨立的主題,本文不展開,但在設計訂單資料表時,就應預留 settled_date 欄位,以結算日而非訂單建立日作為對帳的基準。
特殊情況:Void、Refund、Chargeback
Void(授權取消)
發生在 Capture 之前。
觸發:商家主動取消,或授權超時
資金:無移動,持卡人額度立即解凍
狀態:AUTHORIZED → VOIDED
API:DELETE /v1/payments/{payment_id}/authorization
Refund(退款)
發生在 Capture 之後。
觸發:商家主動退款,或消費者申請
資金:反向移動,通常 3–5 個工作天入帳至持卡人
狀態:原訂單維持 SETTLED,另開一筆退款記錄
退款不應覆蓋原訂單,而是在獨立的 refunds 表建立記錄,支援部分退款:
1CREATE TABLE refunds (
2 id UUID PRIMARY KEY,
3 order_id UUID NOT NULL REFERENCES orders(id),
4 amount DECIMAL(12,2) NOT NULL,
5 reason VARCHAR(255),
6 status VARCHAR(20) NOT NULL, -- PENDING, SUCCEEDED, FAILED
7 gateway_refund_id VARCHAR(100), -- 支付閘道回傳的退款 ID
8 created_at TIMESTAMPTZ NOT NULL,
9 settled_at TIMESTAMPTZ
10);
Chargeback(爭議款)
由持卡人向 Issuer 申訴發起。
流程:持卡人申訴 → Issuer 審查 → Card Network 通知 Acquirer
→ Acquirer 以 Webhook 通知商家 → 商家舉證(7–30 天限期)
→ Card Network 裁定 → 資金退回持卡人或維持商家
收到 Chargeback Webhook 時,系統要立即能拉出:訂單記錄、付款憑證、物流軌跡、客服記錄,這要求 audit log 在設計初期就要做好。
訂單狀態機全圖
┌─────────┐
│INITIATED│
└────┬────┘
│ 送出授權請求
┌──────────┴──────────┐
│ 授權成功 │ 授權失敗
┌────▼────┐ ┌─────▼─────┐
│AUTHORIZED│ │ FAILED │
└────┬────┘ └───────────┘
│
┌─────────┴──────────┐
│ Capture 之前 │ Capture
│ │
┌───▼───┐ ┌──────▼──────┐
│VOIDED │ │ CAPTURED │
└───────┘ └──────┬──────┘
│ 結算完成
┌──────▼──────┐
│ SETTLED │
└──────┬──────┘
│
┌───────────┴───────────┐
│ 退款 │ 爭議
┌────▼────┐ ┌──────▼─────┐
│REFUNDED │ │ DISPUTED │
└─────────┘ └────────────┘
Webhook 的可靠性設計
信用卡閘道在授權、結算、退款、Chargeback 等關鍵節點都會以 Webhook 通知商家系統;正確處理 Webhook 是支付後端最基礎的工程要求。
立即回 200,非同步處理
1[HttpPost("webhook/payment")]
2public async Task<IActionResult> ReceiveWebhook([FromBody] WebhookPayload payload)
3{
4 // Step 1:驗證簽名
5 if (!VerifySignature(payload, Request.Headers["X-Signature"]))
6 return Unauthorized();
7
8 // Step 2:立即將事件寫入佇列,馬上回 200
9 await _queue.EnqueueAsync(payload);
10 return Ok(); // ← 在這裡回應,不要等處理完
11}
12
13// 背景 Worker 非同步處理
14public async Task ProcessWebhookAsync(WebhookPayload payload)
15{
16 // 冪等性檢查
17 // 業務邏輯處理
18 // 更新訂單狀態
19}
原因:支付平台通常設定短暫的回應超時(3–10 秒);如果在 Controller 裡同步處理,一旦下游有延遲,平台會誤以為通知失敗,觸發重試。
冪等性(Idempotence)處理
交易閘道在未收到 200 回應時會重試 Webhook,因此系統要能安全地處理重複事件:
1public async Task ProcessWebhookAsync(WebhookPayload payload)
2{
3 // 以 event_id 作為冪等鍵,防止重複處理
4 var alreadyProcessed = await _db.WebhookEvents
5 .AnyAsync(e => e.EventId == payload.EventId);
6
7 if (alreadyProcessed)
8 {
9 _logger.LogInformation("Duplicate webhook {EventId}, skipping", payload.EventId);
10 return; // 靜默忽略,不拋錯
11 }
12
13 // 在同一個 DB transaction 內寫入事件記錄並處理業務邏輯
14 using var tx = await _db.BeginTransactionAsync();
15 await _db.WebhookEvents.AddAsync(new WebhookEvent { EventId = payload.EventId });
16 await UpdateOrderStatusAsync(payload);
17 await tx.CommitAsync();
18}
驗證簽名
每個支付平台都有自己的簽名機制,通常是 HMAC-SHA256:
1private bool VerifySignature(WebhookPayload payload, string receivedSignature)
2{
3 var secret = _config["PaymentGateway:WebhookSecret"];
4 var rawBody = JsonSerializer.Serialize(payload);
5
6 using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
7 var expectedSignature = Convert.ToHexString(
8 hmac.ComputeHash(Encoding.UTF8.GetBytes(rawBody))
9 ).ToLower();
10
11 // 使用 constant-time 比較防止 timing attack
12 return CryptographicOperations.FixedTimeEquals(
13 Encoding.UTF8.GetBytes(expectedSignature),
14 Encoding.UTF8.GetBytes(receivedSignature)
15 );
16}
信用卡交易系統的核心思維
- 授權不等於扣款:信用卡授權只是凍結額度,Capture 之後才是真正請款,Settlement 之後才有實際資金移動,訂單狀態機必須清楚反映這些時間點的差異。
- Capture 是 Void 和 Refund 的分水嶺:Capture 之前用 Void,不涉及任何資金流動;Capture 之後只能用 Refund,走反向資金流程;兩者在 API 呼叫、資料寫入、對帳邏輯上完全不同。
- 退款是新建記錄,不是修改訂單:獨立的 refunds 表讓部分退款、多次退款都能被正確追蹤,也讓對帳系統有清楚的數字可以核對。
- Chargeback 要有備料:持卡人可以在交易後數週才發起爭議,舉證需要訂單的完整 audit log。這個需求從第一天就要設計進系統,不是出了問題才補救。
- Webhook 冪等性不是選項:閘道重試是設計上的預期行為,以
event_id做去重複,並在同一個 Transaction 內寫入事件記錄和更新訂單狀態,是可靠的防護方法。
理解信用卡交易的完整生命週期,是設計支付系統的基礎;下一步將深入研究電子支付的生命週期,其在授權即扣款的架構下,狀態機的設計和信用卡有根本上的差異。
本文為個人學習筆記,持續更新中。