具代表性的面試主題

後端面試:如何設計基於 HTTP 402/x402 的按請求付費 API?

後端困難
Offer.cc 編輯團隊發佈 更新

題幹

如何為資料 API 設計基於 HTTP 402/x402 的按請求付費協定,並處理付款證明、資源綁定、重播防護、冪等重試、退款與對帳?

題目

你要為資料 API 增加按請求付費能力。未付款請求應回傳 HTTP 402,客戶端完成付款後重試原請求。請說明 402 在 RFC 9110 中的地位,並設計一個類似 x402 的端到端協定,涵蓋付款要求、證明驗證、資源綁定、重播防護、冪等性、退款與帳務對帳。

面試官考察點

  • 能否區分 402 的標準語意與具體付款方案:RFC 9110 保留此狀態碼,但沒有規定付款網路、貨幣或回應格式。
  • 能否把付款證明綁定到資源、金額、收款方、網路與有效期,避免一筆付款被挪到另一個請求。
  • 能否處理客戶端重試、逾時、重複扣款、區塊鏈確認延遲與服務端帳務一致性。
  • 能否說清楚付款服務、資源服務、結算方與稽核日誌之間的信任邊界。

參考答案

402 是 HTTP 狀態碼註冊表中的 “Payment Required”。RFC 9110 保留了它,但沒有定義通用付款協定。協定需要把 402 當成可解析的挑戰回應,而不是把「收到 402」當成已經付款。

資源服務可在 402 回應中返回唯一的 payment requirement,包含資源識別、請求方法與路徑、金額、資產、網路、收款地址、過期時間與隨機數。客戶端只針對這些欄位簽名或付款。服務端或受信任的 facilitator 驗證證明,確認金額、收款方、網路與資源都相符,再把一次性的 receipt 交給資源服務。

資源服務應先記錄 request id 與 payment id 的冪等關係,再執行昂貴操作。同一 request id 重試時,服務端返回相同結果或明確的處理中狀態。付款成功不等於資源操作成功,因此要把付款、授權、業務執行與退款拆成可追蹤狀態,並以對帳任務找出鏈上確認、服務端記錄與實際交付之間的差異。

實作範例

以下偽程式碼展示挑戰與重試的核心邊界;真實系統還需要接入付款驗證器、冪等儲存與稽核日誌。

text
handle(request):
  id = request.idempotencyKey
  if receiptStore.has(id):
    return receiptStore.result(id)

  requirement = makeRequirement(
    resource = canonicalResource(request),
    amount = quote(request),
    network = "base",
    expiresAt = now + 60s,
    nonce = randomBytes(16)
  )

  proof = request.headers["Payment-Proof"]
  if proof is missing:
    return 402, { "payment-required": requirement }

  payment = verifyProof(proof, requirement)
  if payment.invalid or payment.expired or payment.replayed:
    return 402, { "payment-required": requirement, "reason": "invalid-proof" }

  result = executeOnce(id, request, payment)
  receiptStore.put(id, payment.id, result)
  return 200, result

關鍵是 canonicalResourceverifyProof 的輸入必須由同一套正規化規則產生;否則同一資源可能有多個字串表示,造成簽名驗證與授權判斷不一致。executeOnce 需要使用唯一約束、交易或持久化狀態,確保重試不會重複交付副作用。

常見誤區

  • 認為 402 自帶付款流程。它只表達「需要付款」,付款欄位與驗證規則必須由協定定義。
  • 只驗證金額,不驗證資源、網路、收款方、資產與過期時間,導致跨資源替換或跨網路重播。
  • 付款確認後直接執行副作用,卻沒有 request id 冪等記錄,逾時重試會重複扣款或重複建立資源。
  • 把鏈上交易已送出當成最終結算;確認延遲、分叉、facilitator 故障與退款都要進入狀態機。
  • 把付款證明放進日誌或 URL,造成憑證洩露與可重播風險。

實戰取捨

小額、低風險的讀取 API 可以採用短期報價、一次性 nonce 與非同步最終對帳;高價值寫入操作應先取得可驗證的結算狀態,再在冪等交易中交付。若客戶端沒有錢包或鏈上能力,可以讓 facilitator 代付,但要明確界定 facilitator 的信任範圍、費率、限額與故障回退。

協定還要定義價格變化、過期挑戰、部分付款、付款成功但資源失敗、退款與服務降級。快取層不能把含付款證明的回應分享給其他主體,快取鍵必須包含授權結果,或只快取公開的 402 challenge。

參考資料

  • RFC 9110 HTTP Semantics:402 的註冊語意與 HTTP 狀態碼約束。
  • x402 Introduction:以 402 challenge 驅動無帳戶按請求付費的協定概念。
  • Coinbase HTTP 402 Core Concepts:付款要求、驗證與資源存取的實作邊界。

追問

如何防止同一付款證明被用於兩個不同資源?

把正規化後的方法、路徑、查詢參數摘要或資源 ID 寫入 payment requirement,讓證明覆蓋這些欄位;服務端用同一正規化演算法重算摘要,並為 nonce 或 payment id 建立一次性消費記錄。

402 challenge 應該放在回應標頭還是回應本文?

先定義版本化的機器可讀格式。標頭適合輕量提示,回應本文適合攜帶多欄位付款要求;無論位置如何,都要限制大小、宣告內容類型,並避免把敏感憑證放入可快取標頭。

如何處理付款成功但業務執行失敗?

把 payment、authorization、execution 與 refund 分成獨立狀態,使用 request id 關聯。若業務不可重試,進入退款或人工對帳佇列;若可重試,回傳處理中狀態並確保後續查詢得到相同結果。

x402 是否要求區塊鏈?

現有 x402 資料以鏈上或 facilitator 付款為例,但 HTTP 402 本身不規定結算網路。面試時應把「狀態碼語意」與「付款軌道」分開,說明替換為其他付款網路時必須重新定義證明、最終性與退款語意。

怎樣驗證系統沒有重複扣款?

對 payment id、request id 與業務操作建立唯一約束,記錄每次驗證與執行結果,並用故障注入覆蓋客戶端逾時、服務重啟、驗證器重複回呼與對帳延遲。

公開來源

同類題目