POST /subscriptions/:subscriptionHid/change-card-checkout
- 正式環境:
https://payment-api.oen.tw/subscriptions/:subscriptionHid/change-card-checkout - 測試環境:
https://payment-api.testing.oen.tw/subscriptions/:subscriptionHid/change-card-checkout
為一筆 Payment API 建立的定期定額建立換卡頁,回傳 checkoutUrl。把消費者導到這個網址輸入新卡並完成 3D 驗證。換卡頁 15 分鐘內有效。換卡成功後送出 subscription_card_changed 通知;扣款失敗(retryScheduled、error)的定期定額換卡成功後,會用新卡補扣失敗的那一期,結果以 charge 通知送出。目前只支援信用卡由 CyberSource 收單的網域。這支 API 不支援 Idempotency-Key,改用 requestId 防止重複建立。
Header 帶 Authorization: Bearer <token> 與 Content-Type: application/json 。token 的取得方式見驗證與 token。
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
subscriptionHid | string | 必填 | S 開頭的定期定額編號(17 字元)。只接受 Payment API 建立的定期定額。 |
Body 欄位
Section titled “Body 欄位”| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
merchantId | string | 必填 | 你的網域名稱。例如應援頁是 https://ming.oen.tw,就填 ming。必須與 token 所屬的網域相同,不同會回 400 V0001。範例: ming |
requestId | string | 必填 | 你自訂的換卡請求編號,用來防止重複建立。同一個 requestId 再送一次會回 409 C0010,只帶原本換卡頁的 checkoutSessionId 與 expiresAt,不會再給 checkoutUrl,請妥善保存第一次的回應。限制:1 到 128 字元 |
successUrl | string (uri) | 必填 | 換卡成功後導回的網址,不帶任何參數。 限制:必須是 https://,不可含帳號密碼 |
failureUrl | string (uri) | 必填 | 換卡失敗後導回的網址,會加上 error 參數(例如 CARD_CHANGE_FAILED)。參數名稱是 error,不是 payment_error。限制:必須是 https://,不可含帳號密碼 |
範例一律打測試環境。把 OEN_API_TOKEN 設成測試環境 CRM 產生的 token。
curl -X POST "https://payment-api.testing.oen.tw/subscriptions/S20260928EF56GH78/change-card-checkout" \ -H "Authorization: Bearer $OEN_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "merchantId": "ming", "requestId": "SUB20260928001-card-1", "successUrl": "https://shop.example.com/account/card-updated", "failureUrl": "https://shop.example.com/account/card-failed"}'const res = await fetch("https://payment-api.testing.oen.tw/subscriptions/S20260928EF56GH78/change-card-checkout", { method: "POST", headers: { Authorization: `Bearer ${process.env.OEN_API_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ "merchantId": "ming", "requestId": "SUB20260928001-card-1", "successUrl": "https://shop.example.com/account/card-updated", "failureUrl": "https://shop.example.com/account/card-failed" }),});
const result = await res.json();if (result.code !== "S0000") { throw new Error(`${result.code} ${result.message}`);}<?php$ch = curl_init('https://payment-api.testing.oen.tw/subscriptions/S20260928EF56GH78/change-card-checkout');curl_setopt_array($ch, [ CURLOPT_CUSTOMREQUEST => 'POST', CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . getenv('OEN_API_TOKEN'), 'Content-Type: application/json', ], CURLOPT_POSTFIELDS => json_encode([ 'merchantId' => 'ming', 'requestId' => 'SUB20260928001-card-1', 'successUrl' => 'https://shop.example.com/account/card-updated', 'failureUrl' => 'https://shop.example.com/account/card-failed', ]),]);
$result = json_decode(curl_exec($ch), true);if ($result['code'] !== 'S0000') { throw new Exception($result['code'] . ' ' . $result['message']);}import osimport requests
res = requests.request( "POST", "https://payment-api.testing.oen.tw/subscriptions/S20260928EF56GH78/change-card-checkout", headers={"Authorization": f"Bearer {os.environ['OEN_API_TOKEN']}"}, json={ "merchantId": "ming", "requestId": "SUB20260928001-card-1", "successUrl": "https://shop.example.com/account/card-updated", "failureUrl": "https://shop.example.com/account/card-failed", }, timeout=30,)
result = res.json()if result["code"] != "S0000": raise RuntimeError(f"{result['code']} {result['message']}")| HTTP | 說明 |
|---|---|
200 | 建立成功 |
400 | 參數錯誤、定期定額不存在或目前不能換卡,見下方錯誤表。多帶規格外的欄位也會回 V0001 |
401 | token 錯誤(A0001)或已過期(A0002) |
409 | 同一個 requestId 已用過(C0010),或已有進行中的換卡頁(C0004) |
| 欄位 | 型別 | 出現 | 說明 |
|---|---|---|---|
checkoutSessionId | string | 一定有 | 換卡頁的 id |
expiresAt | string | 一定有 | 換卡頁到期時間(UTC,建立後 15 分鐘) |
checkoutUrl | string | 一定有 | 換卡頁網址,含一次性的 secret。只有這次回應會給,請安全保存到換卡完成;不要寫進一般日誌或分享 |
{ "code": "S0000", "data": { "checkoutSessionId": "2Hhp3cXq9Lm0RzK7aVwT1bNsYdE", "expiresAt": "2026-10-16T02:15:00.000Z", "checkoutUrl": "https://ming.oen.tw/checkout/subscription-card-change/2Hhp3cXq9Lm0RzK7aVwT1bNsYdE?secret=…" }, "message": ""}| 錯誤碼 | HTTP | 什麼時候會發生 |
|---|---|---|
V0001 | 400 | 欄位不符規則,或帶了規格外的欄位 |
V0002 | 400 | 網域的金流服務尚未開通 |
C0001 | 400 | 找不到定期定額、不屬於你的網域,或不是 Payment API 建立的定期定額 |
C0002 | 400 | 目前狀態不能換卡。可以換卡的狀態是 scheduled、ongoing、retryScheduled、error |
C0003 | 400 | 這一期正在扣款,稍後再試 |
C0008 | 400 | 網域不接受這種卡 |
C0011 | 400 | 網域目前不能收信用卡 |
C0012 | 400 | 風控拒絕 |
C0013 | 400 | successUrl 或 failureUrl 不是合法的 https:// 網址 |
C0000 | 400 | 其他原因,包含網域的信用卡不是由 CyberSource 收單(目前不支援換卡) |
C0004 | 409 | 這筆定期定額已有進行中的換卡頁。detail.expiresAt 是它的到期時間 |
C0010 | 409 | 同一個 requestId 已建立過換卡頁。detail 只帶原本的 checkoutSessionId、expiresAt,不會再給 checkoutUrl |
A0001 | 401 | token 錯誤 |
A0002 | 401 | token 已過期,或重新產生後超過保留期限的舊 token |
完整清單與處理方式見錯誤碼一覽。
導覽助手暫停服務中。請使用右上角的搜尋,或聯絡客服。
我可以幫你選擇收款方式、回答 API 串接與 CRM 操作問題,並附上文件出處。
