工作階段
CheckoutSession 是買家互動的對象 — 一個帶 TTL、單次使用的物件, 擁有託管結帳 URL。它是三個核心實體中最薄的一層;更重的工作落在 訂單與 Payment Intent(見下方)上。
三實體資料模型
CheckoutSession ← 1:1 → Order ← 1:N → PaymentIntent
(買家) (目錄) (每次付款嘗試)| 實體 | 用途 | 生命週期 |
|---|---|---|
| CheckoutSession | 買家面向 — 有 session_key、checkout_url、TTL | 分鐘級(預設 30 分鐘) |
| Order | 你的目錄狀態 — 商品項目、總額、退款 | 永久紀錄 |
| PaymentIntent | 在某條鏈 / 資產上的一次付款嘗試 | 小時級;結算或過期 |
你會一起建立 session 與 order(透過 POST /b2b/v1/checkout-sessions/quick)。
每次買家在結帳頁選擇資產,系統會在對應的鏈上開啟新的 PaymentIntent。
若買家在結帳中途切換資產,先前的 intent 會進入 EXPIRED,新的會
開始。
識別碼
Session 以其 session_key 識別:
cst_G-SO92J7HNWkwMHEHjD4oO1ZURL-safe,在前綴 cst_ 之後約 24 字元(18 個隨機位元組以
base64url 編碼,無 padding)。託管結帳頁是
https://checkout.infraio.xyz/<session_key> — 請把 session key
當作該一筆結帳的 bearer 憑證對待。
生命週期 — CheckoutSession
| 狀態 | 意義 |
|---|---|
ACTIVE | Session 已建立並開啟。結帳 URL 可用。 |
COMPLETED | 此 session 上的某個 PaymentIntent 結算。Order 此時為 PAID(或若短付則為 PARTIAL_PAID)。 |
EXPIRED | expires_at 在未結算下過去。Cleanup worker 翻了狀態並取消任何開啟的 Order。 |
CANCELED | 明確取消 — 買家按下「取消」或你呼叫了 cancel endpoint。 |
三個終態互斥且最終。如果想要重試(例如短付後),可以對同一個 Order 建立新的 CheckoutSession。
TTL
- 預設: 30 分鐘(可透過 create 時的
expires_in欄位設定, 單位為秒)。 - 範圍: 伺服端不強制任何最小/最大上下限。請使用合理值 — 低於 60 秒可能讓合法買家逾時;超過 7 天會在幾乎肯定被放棄的 token 上 保留容量。請選擇一個符合買家預期決策視窗的數字。
- 每商家預設: 可在儀表板設定,但只有舊版的兩步驟路徑
POST /b2b/v1/checkout-sessions會遵循它。POST /b2b/v1/checkout-sessions/quick路徑在expires_in省略時永遠回退到 30 分鐘,無論每商家 設定為何。如果你需要在 quick 路徑用不同預設,請在每次呼叫都明確 帶expires_in。 - 強制執行: Lazy on read + 週期 cleanup worker。一個
expires_at已過的 session 即使狀態欄位還沒寫入,也會被視為EXPIRED,所以 別在恰好過期的時刻倚賴 API 讀到的狀態。
短付
若買家送出少於 session 金額,PaymentIntent 仍會以該部分金額結算,
Order 轉為 PARTIAL_PAID。CheckoutSession 移到 COMPLETED
(一個 PaymentIntent 已結算),不再可重複使用。
要接受短缺作為全額付款,訂單可被 resolve 為 PAID — 見
概念 → 訂單(目前無公開的 resolve API;
自助流程請改為收集差額)。要收集差額,請對同一個 Order 建立新的
CheckoutSession,以剩餘金額計算。
超付
若買家送出超過 session 金額(罕見,但手動轉帳時會發生),鏈上 scanner 會在 24 小時內擷取超付並發出商家警報。沒有自動退款 — 請透過退款 API 或儀表板手動發起。
錯誤資產付款
存款位址是依 (session, chain, asset) 三元組產生的。如果買家把
錯誤資產送到該位址,鏈上 matcher 不會辨識,PaymentIntent 會保持
開啟直到 TTL 過期。我們可以救回資金,但是 support 流程,不會自動 —
請指示買家送出結帳頁顯示的確切資產。
建立時的冪等性
POST /b2b/v1/checkout-sessions/quick 在請求 body 中接受
idempotency_key 欄位(注意:body 欄位,不是 HTTP header)。若你的
client 沒有自然 key,請為它自動產生 UUID — SDK 預設就是這樣做。
這個 key 去重的是 Order,而不是 CheckoutSession。以相同 key 重試時,
/quick 會回傳 原始 order(order_id 穩定),但每次都鑄造一個 全新的
CheckoutSession — 每次都是新的 session_key 與 checkout_url。這是
刻意設計:一個 Order 可以背負多次結帳嘗試(見
訂單),所以重試的 /quick 會在不重複 order
的前提下給買家一個乾淨的 session。
有兩點行為與 Stripe 風格的冪等層不同 — 別被坑到:
- body 不會被雜湊或比較。 以 不同 的 body 重用一個 key 不會
回傳
409— 伺服器會靜默回傳該 key 名下已存的 order,並忽略新的 body。所以請把idempotency_key當作單一邏輯 order 的一次性 token; 絕不要跨不同購物車回收使用。 - 只有 Order 被去重,session 不會。 若你需要拿回 同一個 結帳 URL,
請把第一次回應裡的
session_key/checkout_url持久化下來 — 再次 呼叫/quick不會回傳舊的。要列舉某個 order 名下鑄造過的每一個 session, 請用GET /b2b/v1/checkout-sessions/by-order/:order_id。
API endpoints
| Method | Path | 備註 |
|---|---|---|
POST | /b2b/v1/checkout-sessions/quick | 一次呼叫建立 order + session |
POST | /b2b/v1/checkout-sessions | 對既有訂單建立 session |
GET | /b2b/v1/checkout-sessions/by-order/:order_id | 列出某訂單的所有 session(重試歷史) |
GET | /checkout/:session_key | 公開 — 買家瀏覽器接觸的對象 |
完整 create 請求 body 與簽章請見快速入門。
下一步
- 概念 → 訂單 — Order 實體(你應視為履約 真相之源的)。
- 概念 → 支援的鏈與資產 — 支援的網路與 各鏈的 finality 假設。
- Webhooks → 總覽 — 每個 session 狀態 轉換時觸發哪些事件。