退款與 Idempotency

退款遵循 preview → confirm → execute once 的流程;所有會動到錢的操作都需要一把 Idempotency-Key,避免網路重試造成重複扣款/退款。

檢視 Markdown

為什麼需要 Idempotency-Key

網路請求可能會逾時、斷線,但你不知道伺服器端是否已經處理完成。如果你的程式碼在沒收到回應時自動重試,沒有 Idempotency-Key 保護的話,可能會造成同一筆退款被送出兩次。

做法:呼叫財務類端點時帶上 Idempotency-Key header(一個你自己產生的唯一字串)。用同一把 key + 同樣的內容重試,會直接拿回原本那次的結果,不會真的執行第二次;用同一把 key 但內容不同,會收到 409 IDEMPOTENCY_KEY_CONFLICT。

以下端點一定要帶 Idempotency-Key,缺少會直接被拒絕(IDEMPOTENCY_KEY_REQUIRED):POST /api/Subscription、POST /api/Subscription/{id}/Cancel、POST /api/Checkout、POST /api/PaymentLink/{id}/Complete、POST /api/Payment/{id}/Attempt、POST /api/Payment/{id}/Reconcile、POST /api/Refund。

退款流程:preview → confirm → execute once

  1. 1商家後台的退款按鈕會先顯示「退款預覽」——可退金額、是否可退。
  2. 2商家確認後才真正呼叫 POST /api/Refund(帶著 Idempotency-Key)。
  3. 3退款一旦送出無法復原;重複點擊「確認退款」會因為同一把 key 而被安全地擋掉,不會退兩次。
POST/api/Refund— 對一筆付款申請全額或部分退款
curl -sS -X POST "$BASE/api/Refund" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Idempotency-Key: refund-order-42" \
  -H "Content-Type: application/json" \
  -d '{"paymentId":"pay_...","amount":200,"reason":"customer_request"}'

付款狀態變成 unknown 時怎麼辦

Sandbox 的 timeout 情境會讓付款狀態變成 unknown——這代表「還不確定成功或失敗」,絕對不要因為看起來像失敗就直接重試扣款(可能造成顧客被扣兩次)。正確做法是呼叫對帳端點取得最終結果。

POST/api/Payment/{id}/Reconcile— 確認一筆 unknown 付款的最終結果