Recur Docs

Customer Portal 讓您的客戶可以自助管理訂閱,包括查看訂閱詳情、更新付款方式、取消訂閱等。

建立 Portal Session

建立一個短期有效的 Portal Session,用於將客戶導向 Customer Portal。

端點

POST /portal/sessions

認證

此端點需要 Secret Key 認證,僅限後端使用。Portal Session 包含敏感的客戶資訊,不應在前端建立。

# Authorization Header(推薦)
Authorization: Bearer sk_test_xxx

# 或專用 Header
X-Recur-Secret-Key: sk_test_xxx

請求參數

客戶識別(至少需要其中一個):

參數必填類型說明
customer_idstring客戶的內部 ID(優先順序最高)
external_idstring客戶的外部 ID(來自您的系統)
emailstring客戶的 email 地址(優先順序最低)

客戶識別優先順序customer_id > external_id > email

您必須提供至少一個識別參數。如果同時提供多個,系統會按照優先順序選擇。

其他參數:

參數必填類型說明
return_urlstring客戶離開 Portal 後的返回 URL
configuration_idstring指定 Portal 設定 ID
localestring語言偏好(zh-TWen

如果未提供 return_url,將使用 Portal 設定中的預設返回 URL。若都未設定,會返回錯誤。

回應格式

{
  "id": "portal_session_xxxxx",
  "object": "portal.session",
  "url": "https://portal.recur.tw/s/ps_xxxxx",
  "return_url": "https://your-site.com/account",
  "customer_id": "cus_xxxxx",
  "status": "active",
  "expires_at": "2025-01-15T12:00:00.000Z",
  "accessed_at": null,
  "created_at": "2025-01-15T11:00:00.000Z"
}

回應欄位說明

欄位類型說明
idstringPortal Session ID
objectstring固定為 portal.session
urlstring導向客戶的 Portal URL
return_urlstring離開 Portal 後的返回 URL
customer_idstring客戶 ID
statusstringSession 狀態(activeexpiredrevoked
expires_atstringSession 過期時間(ISO 8601)
accessed_atstring | null最後存取時間
created_atstring建立時間

程式碼範例

// 推薦:使用 Recur Server SDK
import { Recur } from 'recur-tw/server';

const recur = new Recur(process.env.RECUR_SECRET_KEY!);

// 方式一:使用客戶 ID
const session = await recur.portal.sessions.create({
  customer: 'cus_xxxxx',
  returnUrl: 'https://your-site.com/account',
});

// 方式二:使用 email
const session = await recur.portal.sessions.create({
  email: 'customer@example.com',
  returnUrl: 'https://your-site.com/account',
});

// 方式三:使用外部 ID(您系統中的用戶 ID)
const session = await recur.portal.sessions.create({
  externalId: 'user_123',
  returnUrl: 'https://your-site.com/account',
});

// 導向客戶
redirect(session.url);

Session 生命週期

有效期限

  • Portal Session 有效期為 1 小時
  • 過期後 Session 狀態變為 expired
  • 每個客戶最多同時擁有 5 個 有效 Session

狀態說明

狀態說明
activeSession 有效,可以存取 Portal
expiredSession 已過期
revokedSession 已被撤銷

錯誤處理

HTTP 狀態錯誤代碼說明
400invalid_param請求參數無效(需要至少一個客戶識別參數)
400missing_return_url未提供 return_url 且無預設值
401invalid_api_key需要 Secret Key 認證
404customer_not_found找不到指定的客戶

錯誤回應範例

// 找不到客戶(使用 email)
{
  "error": {
    "type": "invalid_request_error",
    "code": "customer_not_found",
    "message": "Customer not found (email: customer@example.com)"
  }
}

// 找不到客戶(使用 external_id)
{
  "error": {
    "type": "invalid_request_error",
    "code": "customer_not_found",
    "message": "Customer not found (external_id: user_123)"
  }
}

// 缺少客戶識別參數
{
  "error": {
    "type": "invalid_request_error",
    "code": "invalid_param",
    "message": "At least one of customer_id, email, or external_id is required"
  }
}

使用案例

1. 帳戶設定頁面的「管理訂閱」按鈕

// app/account/page.tsx
'use client';

import { useState } from 'react';

export function ManageSubscriptionButton({ customerId }: { customerId: string }) {
  const [loading, setLoading] = useState(false);

  const handleClick = async () => {
    setLoading(true);

    try {
      const response = await fetch('/api/portal/create', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ customer_id: customerId }),
      });

      const { url, error } = await response.json();

      if (error) {
        alert('發生錯誤:' + error.message);
        return;
      }

      window.location.href = url;
    } finally {
      setLoading(false);
    }
  };

  return (
    <button onClick={handleClick} disabled={loading}>
      {loading ? '載入中...' : '管理訂閱'}
    </button>
  );
}

2. 使用 Recur SDK Web Component

<!-- 方式一:直接提供 Portal URL(server-rendered) -->
<recur-portal portal-url="https://portal.recur.tw/s/ps_xxxxx">
  管理訂閱
</recur-portal>

<!-- 方式二:透過 API 動態建立 Session -->
<recur-portal
  api-endpoint="/api/portal/create"
  customer-id="cus_xxxxx">
  管理訂閱
</recur-portal>

安全性考量

重要安全提醒

  • Portal Session URL 包含敏感存取權杖,請勿記錄或暴露
  • 務必驗證用戶身份後再建立對應客戶的 Portal Session
  • 使用 HTTPS 確保傳輸安全

下一步

Last updated on