Skip to Content
View as Markdown

錯誤

所有 4xx/5xx 回應都帶有相同的 JSON 信封:

{ "code": 400, "message": "invalid_input", "details": [ { "field": "items[0].unit_price", "message": "must be a positive decimal string" } ] }
  • code — 數值型的 HTTP status(400、401、404 …)。用於通用的 HTTP 層處理很方便,但需要分支邏輯時請改用 message — code 無法區分如 invalid_input 與 payment_method_not_supported(兩者都是 400)這類情境。
  • message — lower-snake-case 的 sentinel 名稱,即 sentinel 名稱的 lower snake case 形式(例如 INVALID_INPUT 變成 "invalid_input")。跨版本穩定 — 以此分支。
  • details — 僅在驗證錯誤時出現。{ field, message } 物件陣列,讓 client 能將錯誤對應到輸入欄位。其他情境下會省略。

錯誤 body 不包含 trace ID 或 timestamp。回報問題時,請向 support 提供回應的 Date 標頭、X-RateLimit-* 標頭、你的 merchant ID、endpoint,以及大約的請求時間。

驗證失敗使用不同的形態。 上面的信封是 API 對大多數錯誤所回傳的格式。在處理之前就被拒絕的請求 —— /b2b/v1/* 呼叫上缺失 / 非法的 X-Signature、未知的 X-Client-ID,或過期的 X-Timestamp —— 會以 { "error": "...", "message": "..." } 的形態回傳,其中 error 是粗粒度 slug(unauthorized / bad_request / service_unavailable),message 攜帶具體資訊。沒有數值 code,也沒有 details。請先依 HTTP 狀態分支,再讀 message。範例(401):

{ "error": "unauthorized", "message": "invalid signature" }

HTTP 狀態 → 常見 code

HTTP常見 code 值意義
400INVALID_INPUT、MISSING_REQUIRED、INVALID_FORMAT、INVALID_LENGTH、INVALID_VALUE、PAYMENT_METHOD_NOT_SUPPORTED、AMOUNT_BELOW_MINIMUM請求有誤 — 請看 details
401INVALID_CREDENTIALS、INVALID_TOKEN、TOKEN_EXPIRED、INVALID_SIGNATURE(B2B);SESSION_EXPIRED(僅儀表板)驗證失敗 — 金鑰錯誤、timestamp 過期、簽章不符。儀表板登入相關的 code 與 B2B 整合無關。
402INSUFFICIENT_CREDIT商家預付餘額用盡 — 請先儲值再重試
403FORBIDDEN、IP_BLOCKED金鑰有效但缺少呼叫此 API 所需的 scope / IP 許可
404NOT_FOUND、RECORD_NOT_FOUND資源不存在(或對此商家不存在)
409ALREADY_EXISTS不同 body 的冪等 replay,或狀態機拒絕該轉移
429TOO_MANY_REQUESTS、TOO_MANY_ATTEMPTS觸發速率限制;請退避後重試
500INTERNAL_SERVER_ERROR、EXTERNAL_SERVICE_ERROR我們的問題;可帶退避安全重試。
503SERVICE_UNAVAILABLE下游依賴出問題。請退避後重試

完整 code 參考

你可能看到的 code 值:

身分驗證(401)

  • INVALID_CREDENTIALS — API 金鑰組合被拒絕
  • INVALID_TOKEN — token 無法解析或被竄改
  • TOKEN_EXPIRED — token 已過期
  • SESSION_EXPIRED — 儀表板 session 已過期
  • INVALID_SIGNATURE — B2B / webhook 呼叫的 HMAC 簽章不符

授權(403)

  • FORBIDDEN — 已驗證,但你無權執行此動作
  • IP_BLOCKED — 來自此 IP 位址的請求已被封鎖

找不到(404)

  • NOT_FOUND — 通用
  • RECORD_NOT_FOUND — 找不到該 ID 對應的列

衝突(409)

  • ALREADY_EXISTS — 通用

驗證(400)

  • INVALID_INPUT — 通用;請查 details
  • MISSING_REQUIRED — 缺少必要欄位
  • INVALID_FORMAT — 值不符合預期格式(例如 UUID、URL、email)
  • INVALID_LENGTH — 值過短或過長
  • INVALID_VALUE — 值超出允許的 enum / 範圍
  • PAYMENT_METHOD_NOT_SUPPORTED — 該 provider / 資產組合未對此商家啟用
  • AMOUNT_BELOW_MINIMUM — 訂單金額低於該網路或環境的下限。回應的 details.floor_usd 攜帶目前設定的下限(USD),可直接取用顯示;message 文字也會說明它。
  • INSUFFICIENT_BALANCE — 買家的錢包持有的支付資產不足以支付此次轉帳。
  • INSUFFICIENT_GAS — 買家的錢包缺少用於支付網路手續費(gas)的原生代幣,無法廣播此次轉帳。

付款 / 計費(402)

  • INSUFFICIENT_CREDIT — 商家預付餘額不足以支付網路手續費(gas)/ 平台手續費。請從儀表板儲值後再重試

速率限制(429)

  • TOO_MANY_REQUESTS — 超過 IP 速率限制
  • TOO_MANY_ATTEMPTS — 對同一資源(例如 OTP)反覆失敗導致觸發 throttle

伺服器錯誤(500)

  • EXTERNAL_SERVICE_ERROR — 第三方 provider 失敗
  • INTERNAL_SERVER_ERROR — 非預期錯誤;請聯繫 support,並提供回應的 Date 標頭與 X-RateLimit-* 標頭

可用性(503)

  • SERVICE_UNAVAILABLE — 服務暫時無法使用;請退避後重試

驗證錯誤(400)

當問題是請求 body 不正確時,details 是陣列,讓你可以把錯誤對應 回欄位:

{ "code": 400, "message": "invalid_input", "details": [ { "field": "items[0].unit_price", "message": "must be a positive decimal string" }, { "field": "success_url", "message": "must be a valid https URL" } ] }

身分驗證錯誤(401)

INVALID_SIGNATURE 有三個常見原因:

  • Secret key 錯誤(再次確認環境變數)
  • Timestamp 漂移 > 5 分鐘(同步 NTP)
  • Canonical string 錯誤(最常見:忘記 \n 分隔符,或簽了一份 經過 parse-then-re-stringify 與實際送出不同的 body)

確切的簽章演算法請見身分驗證。

速率限制(429)

範圍限制
所有 API 路由(含 /b2b/v1/*)以 IP 位址計算 — 500 req/min,共享 bucket。並非以商家為單位。
公開結帳(/checkout/*)更嚴格的子 bucket — 每 IP 20 req/min

X-RateLimit-Limit 與 X-RateLimit-Remaining 會包含在每一個 受速率限制的回應中,不只是成功的回應。請把它們視為 你 IP 的即時預算。

被速率限制的回應會包含 Retry-After 標頭(距離視窗重置的秒數)— 請遵循它。作為備援,可以 jitter 退避 — 1s 基底 + 指數成長到 30s。

速率限制可能會調整。如果你的合法流量達到上限(例如對帳大量歷史 資料),請聯繫 support。

信用額度不足(402)

INSUFFICIENT_CREDIT(HTTP 402 Payment Required)代表你的預付 餘額無法支付你嘗試的操作所需的網路手續費(gas)與平台手續費,典型情境是結算 加密支付,或網路手續費由平台代付的鏈上動作。請從商家儀表板 (Billing → Add credit)儲值後再重試該操作; 已在處理中的操作會在儲值後自動接續。

伺服器錯誤(5xx)

500 代表我們無法處理該請求。請以退避方式重試 — 你的 idempotency_key 確保即使原請求部分成功,也不會被重複收費。

若 1 分鐘內重試仍未恢復,請對買家顯示通用的「付款暫時無法使用」 訊息,並向 support 回報失敗的 endpoint、你的 merchant ID、回應的 Date 標頭與 X-RateLimit-* 的值,以及大約的請求時間 — 這些資訊 足以讓我們找到該請求。

Webhook 投遞錯誤

Webhook 投遞是獨立的失敗通道 — 它們不會以 API 錯誤的形式出現, 因為呼叫的不是你的伺服器。當投遞回非 2xx(或逾時)時,會以指數 退避重試:0s、1min、5min、15min、1h、6h(共 6 次嘗試,與 Webhooks → 總覽相同排程)。完整的每次 投遞歷史在儀表板的 Developers → Webhooks → [endpoint] → Delivery log 中。第 6 次嘗試失敗後,儀表板會將該投遞標示為 「Failed」,你的伺服器恢復後可手動 replay。