Recur
API 參考

Subscriptions API

訂閱狀態查詢與管理 API

Subscriptions API

訂閱相關的 API 端點,用於查詢和管理訂閱狀態。

查詢訂閱狀態

查詢訂閱狀態,支援列出所有訂閱或按客戶篩選。此端點需要 Secret Key 認證,適合後端使用。

端點

GET /subscriptions

認證

此端點需要 Secret Key 認證,僅限後端使用。

支援以下認證方式:

# 方式 1: Authorization Header(推薦)
Authorization: Bearer sk_test_xxx

# 方式 2: 專用 Header
X-Recur-Secret-Key: sk_test_xxx

請求參數

客戶篩選(選填)

參數類型說明
emailstring客戶 Email
external_idstring您系統中的客戶 ID(外部 ID)
customer_idstringRecur 內部客戶 ID

若不提供任何客戶識別參數,將返回該組織下所有訂閱。每筆訂閱會包含對應的客戶資訊。

商品篩選(選填)

參數類型說明
product_idstring商品 ID
product_slugstring商品 Slug

product_idproduct_slug 為選填。若不提供,將返回該客戶的所有訂閱。

狀態篩選(選填)

參數類型說明
activeboolean設為 true 僅返回有效訂閱(ACTIVE、TRIAL、PAST_DUE)
statusstring篩選特定狀態(如 ACTIVECANCELED

分頁參數(選填)

參數類型說明
limitnumber每頁返回數量(預設 10,最大 100)
starting_afterstring分頁游標,傳入上一頁最後一筆訂閱的 ID

回應格式

按客戶篩選(有訂閱)

{
  "object": "list",
  "has_active_subscription": true,
  "subscriptions": [
    {
      "id": "sub_xxxxx",
      "status": "ACTIVE",
      "product_id": "prod_xxxxx",
      "product_slug": "pro-monthly",
      "product_name": "Pro Plan",
      "amount": 299,
      "interval": "month",
      "interval_count": 1,
      "current_period_start": "2025-01-01T00:00:00.000Z",
      "current_period_end": "2025-02-01T00:00:00.000Z",
      "canceled_at": null,
      "started_at": "2024-12-01T00:00:00.000Z",
      "coupon": null,
      "coupon_remaining_cycles": null,
      "discount_amount": 0,
      "promotion_code": null
    }
  ],
  "customer": {
    "id": "cus_xxxxx",
    "email": "user@example.com",
    "name": "王小明",
    "external_id": "user_123"
  },
  "has_more": false,
  "next_cursor": null,
  "livemode": false
}

列出所有訂閱(無客戶篩選)

當不提供任何客戶識別參數時,每筆訂閱會包含對應的客戶資訊:

{
  "object": "list",
  "has_active_subscription": true,
  "subscriptions": [
    {
      "id": "sub_xxxxx",
      "status": "ACTIVE",
      "product_id": "prod_xxxxx",
      "product_slug": "pro-monthly",
      "product_name": "Pro Plan",
      "amount": 299,
      "interval": "month",
      "interval_count": 1,
      "current_period_start": "2025-01-01T00:00:00.000Z",
      "current_period_end": "2025-02-01T00:00:00.000Z",
      "canceled_at": null,
      "started_at": "2024-12-01T00:00:00.000Z",
      "customer": {
        "id": "cus_xxxxx",
        "email": "user@example.com",
        "name": "王小明",
        "external_id": "user_123"
      }
    },
    {
      "id": "sub_yyyyy",
      "status": "ACTIVE",
      "product_id": "prod_xxxxx",
      "product_slug": "pro-monthly",
      "product_name": "Pro Plan",
      "amount": 299,
      "interval": "month",
      "interval_count": 1,
      "current_period_start": "2025-01-05T00:00:00.000Z",
      "current_period_end": "2025-02-05T00:00:00.000Z",
      "canceled_at": null,
      "started_at": "2024-12-05T00:00:00.000Z",
      "customer": {
        "id": "cus_yyyyy",
        "email": "another@example.com",
        "name": "李小華",
        "external_id": "user_456"
      }
    }
  ],
  "customer": null,
  "has_more": true,
  "next_cursor": "sub_yyyyy",
  "livemode": false
}

含優惠的訂閱範例:

{
  "object": "list",
  "has_active_subscription": true,
  "subscriptions": [
    {
      "id": "sub_xxxxx",
      "status": "ACTIVE",
      "product_id": "prod_xxxxx",
      "product_slug": "pro-monthly",
      "product_name": "Pro Plan",
      "amount": 299,
      "interval": "month",
      "interval_count": 1,
      "current_period_start": "2025-01-01T00:00:00.000Z",
      "current_period_end": "2025-02-01T00:00:00.000Z",
      "canceled_at": null,
      "started_at": "2024-12-01T00:00:00.000Z",
      "coupon": {
        "id": "cpn_xxxxx",
        "name": "新年優惠 8 折",
        "discount_type": "PERCENTAGE",
        "discount_amount": 2000,
        "duration": "REPEATING"
      },
      "coupon_remaining_cycles": 2,
      "discount_amount": 60,
      "promotion_code": "NEWYEAR2025"
    }
  ],
  "customer": {
    "id": "cus_xxxxx",
    "email": "user@example.com",
    "name": "王小明",
    "external_id": "user_123"
  },
  "has_more": false,
  "next_cursor": null,
  "livemode": false
}

無有效訂閱的客戶

{
  "object": "list",
  "has_active_subscription": false,
  "subscriptions": [
    {
      "id": "sub_xxxxx",
      "status": "CANCELED",
      "product_id": "prod_xxxxx",
      "product_slug": "pro-monthly",
      "product_name": "Pro Plan",
      "amount": 299,
      "interval": "month",
      "interval_count": 1,
      "current_period_start": "2024-12-01T00:00:00.000Z",
      "current_period_end": "2025-01-01T00:00:00.000Z",
      "canceled_at": "2025-01-01T00:00:00.000Z",
      "started_at": "2024-12-01T00:00:00.000Z",
      "coupon": null,
      "coupon_remaining_cycles": null,
      "discount_amount": 0,
      "promotion_code": null
    }
  ],
  "customer": {
    "id": "cus_xxxxx",
    "email": "user@example.com",
    "name": "王小明",
    "external_id": null
  },
  "has_more": false,
  "next_cursor": null,
  "livemode": false
}

找不到客戶

當提供客戶篩選參數但找不到對應客戶時:

{
  "object": "list",
  "has_active_subscription": false,
  "subscriptions": [],
  "customer": null,
  "has_more": false,
  "next_cursor": null,
  "livemode": false
}

回應欄位說明

欄位類型說明
objectstring固定為 "list"
has_active_subscriptionboolean是否有有效訂閱(ACTIVE、TRIAL、PAST_DUE)
subscriptionsarray所有符合條件的訂閱(按建立時間降冪排序)
customerobject | null客戶資訊(僅在按客戶篩選時有值)
has_moreboolean是否有更多結果(用於分頁)
next_cursorstring | null下一頁的游標(傳入 starting_after 參數)
livemodeboolean是否為正式環境

訂閱物件欄位

欄位類型說明
idstring訂閱 ID
statusstring訂閱狀態
product_idstring商品 ID
product_slugstring商品 Slug
product_namestring商品名稱
amountnumber金額
intervalstring週期單位(month、year)
interval_countnumber週期數量
current_period_startstring當期開始時間
current_period_endstring當期結束時間
canceled_atstring | null取消時間
started_atstring開始時間
customerobject客戶資訊(僅在無客戶篩選時出現)
couponobject | null套用的優惠資訊(見下方說明)
coupon_remaining_cyclesnumber | null週期性折扣剩餘期數(僅 REPEATING 類型)
discount_amountnumber本期折扣金額
promotion_codestring | null使用的推廣代碼

優惠物件結構(coupon)

欄位類型說明
idstring優惠 ID
namestring優惠名稱
discount_typestring折扣類型:FIXED_AMOUNTPERCENTAGEFIRST_PERIOD_PRICE
discount_amountnumber折扣數值(固定金額為分、百分比為 basis points)
durationstring折扣期間:ONCEREPEATINGFOREVER

訂閱狀態說明

以下狀態視為「有效訂閱」(hasActiveSubscription: true):

狀態說明
ACTIVE正常訂閱中
TRIAL試用期中
PAST_DUE逾期但尚未取消(仍有效)

以下狀態視為「無效訂閱」:

狀態說明
CANCELED已取消
EXPIRED已過期
PAUSED已暫停

程式碼範例

// 使用 email 查詢所有訂閱
const response = await fetch(
  'https://api.recur.tw/v1/subscriptions?' +
  new URLSearchParams({
    email: 'user@example.com',
  }),
  {
    headers: {
      'Authorization': `Bearer ${process.env.RECUR_SECRET_KEY}`,
    },
  }
);

const data = await response.json();

if (data.has_active_subscription) {
  console.log('用戶有有效訂閱');
  console.log('訂閱數:', data.subscriptions.length);
  console.log('第一筆訂閱狀態:', data.subscriptions[0].status);
} else {
  console.log('用戶沒有有效訂閱');
}

// 使用 external_id 查詢特定方案
const response2 = await fetch(
  'https://api.recur.tw/v1/subscriptions?' +
  new URLSearchParams({
    external_id: 'user_123',
    product_slug: 'pro-monthly',
  }),
  {
    headers: {
      'Authorization': `Bearer ${process.env.RECUR_SECRET_KEY}`,
    },
  }
);
import requests
import os

# 使用 email 查詢所有訂閱
response = requests.get(
    'https://api.recur.tw/v1/subscriptions',
    headers={
        'Authorization': f'Bearer {os.environ["RECUR_SECRET_KEY"]}',
    },
    params={
        'email': 'user@example.com',
    }
)

data = response.json()

if data['has_active_subscription']:
    print('用戶有有效訂閱')
    print(f'訂閱數: {len(data["subscriptions"])}')
    print(f'第一筆訂閱狀態: {data["subscriptions"][0]["status"]}')
else:
    print('用戶沒有有效訂閱')

# 使用 external_id 查詢
response2 = requests.get(
    'https://api.recur.tw/v1/subscriptions',
    headers={
        'Authorization': f'Bearer {os.environ["RECUR_SECRET_KEY"]}',
    },
    params={
        'external_id': 'user_123',
        'active': 'true',
    }
)
# 列出所有有效訂閱(不需指定客戶)
curl -X GET "https://api.recur.tw/v1/subscriptions?active=true&limit=50" \
  -H "Authorization: Bearer sk_test_xxx"

# 分頁:取得下一頁
curl -X GET "https://api.recur.tw/v1/subscriptions?active=true&limit=50&starting_after=sub_xxxxx" \
  -H "Authorization: Bearer sk_test_xxx"

# 使用 email 查詢特定客戶的訂閱
curl -X GET "https://api.recur.tw/v1/subscriptions?email=user@example.com" \
  -H "Authorization: Bearer sk_test_xxx"

# 使用 external_id 查詢特定方案
curl -X GET "https://api.recur.tw/v1/subscriptions?external_id=user_123&product_slug=pro-monthly" \
  -H "X-Recur-Secret-Key: sk_test_xxx"

# 使用 customer_id 查詢僅有效訂閱
curl -X GET "https://api.recur.tw/v1/subscriptions?customer_id=cus_xxxxx&active=true" \
  -H "Authorization: Bearer sk_test_xxx"

# 篩選特定狀態
curl -X GET "https://api.recur.tw/v1/subscriptions?email=user@example.com&status=CANCELED" \
  -H "Authorization: Bearer sk_test_xxx"

使用案例

1. 權限控制(使用 external_id)

在您的後端驗證用戶是否有權限存取付費功能:

// middleware/checkSubscription.ts
import { NextRequest, NextResponse } from 'next/server';

export async function checkSubscription(req: NextRequest, userId: string) {
  // 使用您系統的 userId 作為 external_id 查詢
  const response = await fetch(
    `${process.env.RECUR_API_URL}/v1/subscriptions?` +
    new URLSearchParams({
      external_id: userId,
      active: 'true',  // 只查詢有效訂閱
    }),
    {
      headers: {
        'Authorization': `Bearer ${process.env.RECUR_SECRET_KEY}`,
      },
    }
  );

  const { has_active_subscription } = await response.json();

  if (!has_active_subscription) {
    return NextResponse.json(
      { error: '此功能需要付費訂閱' },
      { status: 403 }
    );
  }

  return null; // 允許通過
}

2. 查詢用戶所有訂閱

不指定方案,取得用戶的所有訂閱歷史:

// app/api/user/subscriptions/route.ts
export async function GET(req: NextRequest) {
  const session = await getSession();

  const response = await fetch(
    `${process.env.RECUR_API_URL}/v1/subscriptions?` +
    new URLSearchParams({
      email: session.user.email,
      // 不指定 product_slug,返回所有訂閱
    }),
    {
      headers: {
        'Authorization': `Bearer ${process.env.RECUR_SECRET_KEY}`,
      },
    }
  );

  const data = await response.json();

  // Find active subscription
  const activeSubscription = data.subscriptions.find(
    (sub: { status: string }) => ['ACTIVE', 'TRIAL', 'PAST_DUE'].includes(sub.status)
  );

  return NextResponse.json({
    hasActiveSubscription: data.has_active_subscription,
    currentProduct: activeSubscription?.product_name,
    allSubscriptions: data.subscriptions.map((sub: { product_name: string; status: string; started_at: string; current_period_end: string }) => ({
      product: sub.product_name,
      status: sub.status,
      startedAt: sub.started_at,
      expiresAt: sub.current_period_end,
    })),
  });
}

3. 顯示訂閱資訊

在用戶設定頁面顯示當前訂閱狀態:

// app/api/user/subscription/route.ts
export async function GET(req: NextRequest) {
  const session = await getSession();

  const response = await fetch(
    `${process.env.RECUR_API_URL}/v1/subscriptions?` +
    new URLSearchParams({
      email: session.user.email,
      product_slug: 'pro',
    }),
    {
      headers: {
        'Authorization': `Bearer ${process.env.RECUR_SECRET_KEY}`,
      },
    }
  );

  const data = await response.json();

  // Get the first subscription (sorted by createdAt desc)
  const subscription = data.subscriptions[0];

  return NextResponse.json({
    isSubscribed: data.has_active_subscription,
    product: subscription?.product_name,
    renewsAt: subscription?.current_period_end,
    status: subscription?.status,
  });
}

錯誤處理

HTTP 狀態錯誤訊息說明
401This endpoint requires a Secret Key需要 Secret Key 認證
401Invalid API keyAPI Key 無效
404Product not found: xxx找不到指定的商品(僅在提供 product_id/product_slug 時)

注意事項

安全提醒:此端點使用 Secret Key 認證,請勿在前端調用。所有查詢應透過您的後端進行。

快取建議:為了效能考量,建議在您的後端實作適當的快取機制,避免頻繁查詢相同用戶的訂閱狀態。


訂閱方案切換

允許您代替客戶執行訂閱方案的升級、降級或週期變更。

所有訂閱切換 API 都需要 Secret Key 認證,僅限後端使用。

切換類型說明

類型說明執行方式
UPGRADE升級到更高價方案立即執行,按比例計算差額
DOWNGRADE降級到較低價方案排程至當期結束時執行
PERIOD_CHANGE變更計費週期(月→年或年→月)月→年立即執行;年→月排程執行
CROSSGRADE切換到同價方案立即執行,無需補差額

預覽切換

在執行切換前,預覽切換結果和費用計算。

端點

GET /subscriptions/{subscription_id}/switch-preview

請求參數

參數類型必填說明
target_product_idstring目標方案 ID

回應範例

{
  "object": "switch_preview",
  "subscription_id": "sub_xxxxx",
  "switch_type": "UPGRADE",
  "execution_mode": "immediate",
  "current_plan": {
    "product_id": "prod_basic",
    "product_name": "Basic Plan",
    "amount": 299,
    "currency": "TWD",
    "interval": "month",
    "interval_count": 1,
    "monthly_equivalent": 299
  },
  "new_plan": {
    "product_id": "prod_pro",
    "product_name": "Pro Plan",
    "amount": 599,
    "currency": "TWD",
    "interval": "month",
    "interval_count": 1,
    "monthly_equivalent": 599
  },
  "proration": {
    "credit_amount": 150,
    "charge_amount": 599,
    "net_amount": 449,
    "unused_days": 15,
    "total_days_in_period": 30,
    "credit_description": "15 天未使用的 Basic Plan"
  },
  "effective_date": "2025-01-15T10:00:00.000Z",
  "next_billing_date": "2025-02-15T00:00:00.000Z",
  "requires_payment": true,
  "can_proceed": true,
  "is_in_trial": false
}

回應欄位說明

欄位類型說明
switch_typestring切換類型(UPGRADE、DOWNGRADE、PERIOD_CHANGE、CROSSGRADE)
execution_modestring執行方式(immediate 立即執行、scheduled 排程執行)
prorationobject | null按比例計算結果(降級時為 null)
proration.net_amountnumber客戶需支付的淨額(charge - credit)
requires_paymentboolean是否需要付款
can_proceedboolean是否可執行切換
blocking_reasonstring | null無法切換的原因(如有)

程式碼範例

const response = await fetch(
  `https://api.recur.tw/v1/subscriptions/${subscriptionId}/switch-preview?` +
  new URLSearchParams({
    target_product_id: 'prod_pro',
  }),
  {
    headers: {
      'Authorization': `Bearer ${process.env.RECUR_SECRET_KEY}`,
    },
  }
);

const preview = await response.json();

if (preview.can_proceed) {
  console.log(`切換類型: ${preview.switch_type}`);
  console.log(`執行方式: ${preview.execution_mode}`);
  if (preview.proration) {
    console.log(`需付金額: ${preview.proration.net_amount}`);
  }
}
curl -X GET "https://api.recur.tw/v1/subscriptions/sub_xxxxx/switch-preview?target_product_id=prod_pro" \
  -H "Authorization: Bearer sk_test_xxx"

執行切換

執行訂閱方案切換。根據切換類型,會立即執行或排程至當期結束。

端點

POST /subscriptions/{subscription_id}/switch

請求參數

參數類型必填說明
target_product_idstring目標方案 ID
proration_behaviorstring按比例計算行為:create_prorations(預設)、none

回應範例(立即執行)

升級或月轉年會立即執行:

{
  "object": "switch_result",
  "execution_mode": "immediate",
  "subscription": {
    "id": "sub_xxxxx",
    "product_id": "prod_pro",
    "status": "ACTIVE",
    "amount": 599,
    "currency": "TWD",
    "interval": "month",
    "interval_count": 1,
    "current_period_start": "2025-01-15T10:00:00.000Z",
    "current_period_end": "2025-02-15T00:00:00.000Z",
    "next_billing_date": "2025-02-15T00:00:00.000Z",
    "previous_product_id": "prod_basic",
    "switched_at": "2025-01-15T10:00:00.000Z",
    "switch_type": "UPGRADE"
  },
  "invoice": {
    "id": "inv_xxxxx",
    "invoice_number": "INV-20250115-ABC123",
    "amount": 449,
    "currency": "TWD",
    "status": "PAID",
    "billing_reason": "SUBSCRIPTION_UPDATE",
    "billing_entries": [
      {
        "id": "entry_1",
        "type": "PRORATION_CREDIT",
        "direction": "CREDIT",
        "amount": 150,
        "description": "15 天未使用的 Basic Plan"
      },
      {
        "id": "entry_2",
        "type": "SUBSCRIPTION",
        "direction": "CHARGE",
        "amount": 599,
        "description": "Pro Plan (月繳)"
      }
    ]
  },
  "schedule": null,
  "switch_type": "UPGRADE",
  "proration": {
    "credit_amount": 150,
    "charge_amount": 599,
    "net_amount": 449,
    "unused_days": 15,
    "total_days_in_period": 30,
    "credit_description": "15 天未使用的 Basic Plan"
  },
  "effective_date": "2025-01-15T10:00:00.000Z"
}

回應範例(排程執行)

降級或年轉月會排程至當期結束:

{
  "object": "switch_result",
  "execution_mode": "scheduled",
  "subscription": {
    "id": "sub_xxxxx"
  },
  "invoice": null,
  "schedule": {
    "id": "sched_xxxxx",
    "target_product_id": "prod_basic",
    "switch_type": "DOWNGRADE",
    "effective_at": "2025-02-15T00:00:00.000Z",
    "status": "PENDING",
    "created_at": "2025-01-15T10:00:00.000Z"
  },
  "switch_type": "DOWNGRADE",
  "proration": null,
  "effective_date": "2025-02-15T00:00:00.000Z"
}

程式碼範例

// 執行升級
const response = await fetch(
  `https://api.recur.tw/v1/subscriptions/${subscriptionId}/switch`,
  {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.RECUR_SECRET_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      target_product_id: 'prod_pro',
    }),
  }
);

const result = await response.json();

if (result.execution_mode === 'immediate') {
  console.log('切換已立即生效');
  console.log(`新方案: ${result.subscription.product_id}`);
  if (result.invoice) {
    console.log(`帳單金額: ${result.invoice.amount}`);
  }
} else {
  console.log('切換已排程');
  console.log(`生效日期: ${result.schedule.effective_at}`);
}
curl -X POST "https://api.recur.tw/v1/subscriptions/sub_xxxxx/switch" \
  -H "Authorization: Bearer sk_test_xxx" \
  -H "Content-Type: application/json" \
  -d '{"target_product_id": "prod_pro"}'

查詢排程切換

查詢訂閱是否有待執行的排程切換。

端點

GET /subscriptions/{subscription_id}/schedule

回應範例(有排程)

{
  "object": "schedule",
  "has_pending_schedule": true,
  "schedule": {
    "id": "sched_xxxxx",
    "subscription_id": "sub_xxxxx",
    "target_product_id": "prod_basic",
    "target_product_name": "Basic Plan",
    "switch_type": "DOWNGRADE",
    "effective_at": "2025-02-15T00:00:00.000Z",
    "status": "PENDING",
    "created_at": "2025-01-15T10:00:00.000Z"
  }
}

回應範例(無排程)

{
  "object": "schedule",
  "has_pending_schedule": false,
  "schedule": null
}

取消排程切換

取消待執行的排程切換,訂閱將維持當前方案。

端點

DELETE /subscriptions/{subscription_id}/schedule

回應範例

{
  "object": "schedule_cancellation",
  "cancelled": true,
  "subscription_id": "sub_xxxxx"
}

程式碼範例

// 取消排程的降級
const response = await fetch(
  `https://api.recur.tw/v1/subscriptions/${subscriptionId}/schedule`,
  {
    method: 'DELETE',
    headers: {
      'Authorization': `Bearer ${process.env.RECUR_SECRET_KEY}`,
    },
  }
);

const result = await response.json();
if (result.cancelled) {
  console.log('排程切換已取消,訂閱將維持當前方案');
}
curl -X DELETE "https://api.recur.tw/v1/subscriptions/sub_xxxxx/schedule" \
  -H "Authorization: Bearer sk_test_xxx"

使用案例

1. 客服協助升級

當客服需要幫客戶升級方案時:

async function upgradeSubscription(subscriptionId: string, newProductId: string) {
  // 1. 先預覽切換
  const previewRes = await fetch(
    `${RECUR_API_URL}/v1/subscriptions/${subscriptionId}/switch-preview?` +
    new URLSearchParams({ target_product_id: newProductId }),
    {
      headers: { 'Authorization': `Bearer ${RECUR_SECRET_KEY}` },
    }
  );
  const preview = await previewRes.json();

  if (!preview.can_proceed) {
    throw new Error(`無法切換: ${preview.blocking_reason}`);
  }

  // 2. 確認後執行切換
  const switchRes = await fetch(
    `${RECUR_API_URL}/v1/subscriptions/${subscriptionId}/switch`,
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${RECUR_SECRET_KEY}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ target_product_id: newProductId }),
    }
  );

  return switchRes.json();
}

2. 顯示待執行的降級

在用戶設定頁面顯示排程中的降級:

async function getPendingDowngrade(subscriptionId: string) {
  const res = await fetch(
    `${RECUR_API_URL}/v1/subscriptions/${subscriptionId}/schedule`,
    {
      headers: { 'Authorization': `Bearer ${RECUR_SECRET_KEY}` },
    }
  );
  const data = await res.json();

  if (data.has_pending_schedule) {
    return {
      hasPendingChange: true,
      newPlan: data.schedule.target_product_name,
      effectiveDate: data.schedule.effective_at,
      canCancel: true,
    };
  }

  return { hasPendingChange: false };
}

錯誤處理

HTTP 狀態錯誤碼說明
401unauthorized需要 Secret Key 認證
404subscription_not_found找不到訂閱
404product_not_found找不到目標方案
400same_product目標方案與當前相同
400subscription_not_active訂閱非有效狀態
400past_due_blocks_switch訂閱逾期中,無法切換
402payment_required需要付款但付款失敗

下一步

On this page