Checkout Session 是 Hosted Checkout 的核心資源。在後端建立 Session 後,將用戶導向回傳的 url,即可在 Recur 託管的結帳頁面完成付款。
欄位命名:回應一律使用 snake_case(如 external_customer_id)。請求本文接受 snake_case(建議)與 legacy camelCase 兩種寫法。
認證
建立與查詢端點都支援 Secret Key(Authorization: Bearer sk_xxx)與 Publishable Key(X-Recur-Publishable-Key: pk_xxx)。差異:
- Publishable Key 不可設定
metadata(server-only 欄位) - 建議在後端以 Secret Key 建立 Session
作廢端點只接受 Secret Key。
建立 Checkout Session
POST https://api.recur.tw/v1/checkout/sessions
請求參數
| 參數 | 必填 | 說明 |
|---|---|---|
product_id | ✅* | 商品 ID(與 product_slug 二擇一) |
product_slug | ✅* | 商品 Slug(與 product_id 二擇一) |
success_url | ✅ | 成功後的重新導向 URL(HTTPS,支援 {CHECKOUT_SESSION_ID} 佔位符) |
cancel_url | ✅ | 取消時的重新導向 URL(HTTPS) |
mode | ❌ | PAYMENT、SUBSCRIPTION、SETUP;未指定時依商品類型自動判斷 |
customer_email | ❌ | 預填客戶 Email(未提供時由結帳頁向付款者收集) |
customer_name | ❌ | 預填客戶姓名 |
external_customer_id | ❌ | 您系統中的用戶 ID。無論 email 是在建立時提供或結帳頁才收集,都會套用到該 Session 建立/連結的客戶,詳見客戶身份綁定 |
promotion_code | ❌ | 預先套用的優惠碼(建立時即驗證與計算折扣) |
collect_payment_method | ❌ | always(預設)或 if_required(零元結帳免綁卡) |
metadata | ❌ | 自訂 metadata(僅 Secret Key 可用) |
einvoice | ❌ | 買家電子發票選擇(Publishable Key 也可用),用於預填結帳頁的發票資訊區塊。格式錯誤(如統編檢查碼不符)回 422,詳見電子發票 |
範例
curl -X POST https://api.recur.tw/v1/checkout/sessions \
-H "Authorization: Bearer sk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"product_id": "prod_abc123",
"mode": "SUBSCRIPTION",
"success_url": "https://example.com/success?session_id={CHECKOUT_SESSION_ID}",
"cancel_url": "https://example.com/pricing",
"external_customer_id": "user_123"
}'
回應(201):
{
"id": "cs_abc123xyz",
"url": "https://checkout.recur.tw/cs_abc123xyz",
"expires_at": "2025-01-01T00:30:00Z",
"amount": 29900,
"requires_payment_method": true,
"livemode": true
}
| 欄位 | 說明 |
|---|---|
id | Session ID |
url | 導向用戶的結帳頁 URL |
expires_at | Session 到期時間(建立後 30 分鐘) |
amount | 折扣後實際金額 |
requires_payment_method | 是否需要收集信用卡(零元結帳時可能為 false) |
client_secret | (選)PaymentIntent client secret,供前端 SDK 輪詢狀態 |
discount | (選)套用優惠碼時的折扣明細 |
einvoice_enabled | 組織是否已啟用電子發票自動開立(可據此決定是否收集發票資訊) |
livemode | 是否為正式環境 |
查詢 Checkout Session
GET https://api.recur.tw/v1/checkout/sessions/{id}
用於付款完成重新導向後確認 Session 狀態,或稽核 Session 上儲存的值。
回應(200):
{
"id": "cs_abc123xyz",
"status": "COMPLETE",
"mode": "SUBSCRIPTION",
"customer_email": "user@example.com",
"external_customer_id": "user_123",
"order_id": "ord_xyz789",
"subscription_id": "sub_def456",
"payment_intent_id": "pi_ghi012",
"setup_intent_id": null,
"amount_total": 29900,
"currency": "TWD",
"product": { "id": "prod_monthly", "name": "Monthly Plan" },
"expires_at": "2025-01-01T00:30:00Z",
"created_at": "2025-01-01T00:00:00Z",
"completed_at": "2025-01-01T00:05:00Z",
"livemode": true
}
| 欄位 | 說明 |
|---|---|
status | OPEN(等待付款)、COMPLETE(付款完成)、EXPIRED(已過期) |
customer_email | 建立時預填或結帳頁收集到的 Email |
external_customer_id | 建立 Session 時帶入的外部客戶 ID(可用於稽核綁定值) |
subscription_id | 訂閱建立後回填(SUBSCRIPTION mode) |
付款完成後,建議以 checkout.completed webhook 為準進行開通;此端點適合重新導向頁的即時確認與除錯。
作廢 Checkout Session
POST https://api.recur.tw/v1/checkout/sessions/{id}/expire
讓尚未付款的 Session 立即失效,顧客無法再透過它付款。適用情境:顧客改選了另一個方案、你為他建立了新的 Session,要避免舊的那個也被付款。
- 不需要請求本文,只接受 Secret Key。
- 成功時回傳與查詢相同格式的 Session,
status為EXPIRED,expires_at為作廢的時間。 - Session 的付款(Payment Intent)與待付款的訂單會一併取消。
- 對已經是
EXPIRED的 Session(包含超過 30 分鐘自然過期的)再呼叫一次,同樣回傳 200。
curl -X POST https://api.recur.tw/v1/checkout/sessions/cs_abc123xyz/expire \
-H "Authorization: Bearer sk_test_xxx"
付款已開始時無法作廢:Session 已完成付款(COMPLETE),或顧客的付款正在處理中、正在進行 3D 驗證時,回傳 409 conflict,Session 維持原狀。這時顧客仍可能付款成功,請以 checkout.completed webhook 為準。
作廢不會發送 webhook,回傳的 Session 即為結果。
錯誤
| HTTP | code | 情境 |
|---|---|---|
| 400 | bad_request | JSON 格式錯誤、Publishable Key 帶 metadata |
| 401 | unauthorized | API Key 無效 |
| 404 | not_found | 商品不存在或未上架(含存取其他組織的商品) |
| 401 | unauthorized | 作廢時使用 Publishable Key |
| 404 | not_found | 查詢或作廢的 Session 不存在,或屬於其他組織。作廢時另含金鑰環境不符(如以測試金鑰作廢正式環境的 Session) |
| 409 | conflict | 建立時:客戶已有該商品的有效訂閱,詳見錯誤代碼。作廢時:Session 已完成,或付款已開始 |
| 422 | validation_error | 請求本文驗證失敗(含 einvoice 格式錯誤,如統編檢查碼不符、手機條碼格式錯誤) |
完整錯誤格式請見錯誤代碼。