API 參考
錯誤碼參考
Recur API 錯誤碼完整說明與處理建議
錯誤碼參考
Recur API 使用標準的 HTTP 狀態碼和統一的錯誤格式。本頁說明所有可能的錯誤碼及處理建議。
錯誤回應格式
所有 API 錯誤都使用以下格式:
{
"error": {
"code": "error_code",
"message": "人類可讀的錯誤訊息",
"doc_url": "https://docs.recur.tw/api/error-codes#error_code",
"details": [
{
"key": "額外資訊"
}
]
}
}| 欄位 | 類型 | 說明 |
|---|---|---|
code | string | 錯誤碼(如 conflict、not_found) |
message | string | 人類可讀的錯誤訊息 |
doc_url | string | 相關文件連結(選填) |
details | array | 錯誤相關的額外資訊(選填) |
錯誤碼列表
bad_request (400)
請求參數無效或缺少必要參數。
{
"error": {
"code": "bad_request",
"message": "Missing required fields: sessionId, productId, email"
}
}處理建議:檢查請求參數是否正確,參考 API 文件確認必要欄位。
unauthorized (401)
API Key 無效、缺失或已過期。
{
"error": {
"code": "unauthorized",
"message": "Invalid API key"
}
}處理建議:
- 確認 API Key 是否正確
- 確認使用正確的環境(Sandbox/Production)
- 確認 API Key 未被撤銷
payment_required (402)
付款失敗。
{
"error": {
"code": "payment_required",
"message": "Payment failed: card declined"
}
}處理建議:引導用戶更換付款方式或聯繫銀行。
forbidden (403)
無權存取此資源。
{
"error": {
"code": "forbidden",
"message": "Access denied to this resource"
}
}處理建議:確認 API Key 有足夠權限。Secret Key 才能存取某些端點。
not_found (404)
找不到請求的資源。
{
"error": {
"code": "not_found",
"message": "Product not found or inactive"
}
}處理建議:確認資源 ID 是否正確,或資源是否已被刪除。
conflict (409)
資源衝突。最常見的情況是客戶已有該商品的有效訂閱。
{
"error": {
"code": "conflict",
"message": "Customer already has an active subscription to this product",
"details": [
{
"existing_subscription_id": "sub_xxxxx",
"status": "ACTIVE"
}
]
}
}重複訂閱 (Duplicate Subscription)
當客戶嘗試建立已有有效訂閱(ACTIVE、TRIAL 或 PAST_DUE)的商品訂閱時會觸發此錯誤。
details 欄位說明:
| 欄位 | 類型 | 說明 |
|---|---|---|
existing_subscription_id | string | 現有訂閱的 ID |
status | string | 現有訂閱的狀態(ACTIVE、TRIAL、PAST_DUE) |
處理建議:
// 引導用戶到 Customer Portal 管理現有訂閱
if (error.code === 'conflict') {
const portalUrl = await recur.portal.sessions.create({
customer: customerId,
returnUrl: window.location.href,
});
window.location.href = portalUrl.url;
}// 使用 details 中的 ID 查詢現有訂閱詳情
if (error.code === 'conflict') {
const existingSubId = error.details?.[0]?.existing_subscription_id;
const response = await fetch(
`https://api.recur.tw/v1/subscriptions?customer_id=${customerId}`,
{ headers: { 'Authorization': `Bearer ${secretKey}` } }
);
const { subscriptions } = await response.json();
const subscription = subscriptions.find(s => s.id === existingSubId);
console.log('現有訂閱:', subscription);
}// 如需切換方案,使用訂閱方案切換 API
if (error.code === 'conflict') {
const existingSubId = error.details?.[0]?.existing_subscription_id;
const response = await fetch(
`https://api.recur.tw/v1/subscriptions/${existingSubId}/switch`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${secretKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
target_product_id: newProductId,
}),
}
);
}validation_error (422)
請求參數驗證失敗。
{
"error": {
"code": "validation_error",
"message": "Invalid request parameters",
"details": [
{
"path": "email",
"message": "Invalid email format",
"code": "invalid_type"
}
]
}
}處理建議:根據 details 中的資訊修正參數格式。
rate_limit_exceeded (429)
請求過於頻繁,已超過速率限制。
{
"error": {
"code": "rate_limit_exceeded",
"message": "Too many requests"
}
}處理建議:
- 實作指數退避重試(exponential backoff)
- 減少請求頻率
- 考慮使用 Webhook 取代輪詢
internal_server_error (500)
伺服器發生錯誤。
{
"error": {
"code": "internal_server_error",
"message": "An unexpected error occurred"
}
}處理建議:
- 稍後重試請求
- 如果問題持續,請聯繫 Recur 支援團隊
SDK 錯誤處理
TypeScript / JavaScript
SDK 提供型別定義方便處理錯誤:
import type { ApiErrorCode, CheckoutError } from 'recur-tw';
function handleError(error: CheckoutError) {
switch (error.code as ApiErrorCode) {
case 'conflict':
// 重複訂閱
const details = error.details?.[0];
console.log('現有訂閱:', details?.existing_subscription_id);
break;
case 'payment_required':
// 付款失敗
console.log('請更換付款方式');
break;
case 'not_found':
// 找不到資源
console.log('商品不存在');
break;
default:
console.log('發生錯誤:', error.message);
}
}Server SDK
import { Recur, RecurAPIError } from 'recur-tw/server';
const recur = new Recur(process.env.RECUR_SECRET_KEY!);
try {
const session = await recur.portal.sessions.create({
email: 'user@example.com',
});
} catch (error) {
if (error instanceof RecurAPIError) {
console.log('錯誤碼:', error.code);
console.log('狀態碼:', error.statusCode);
console.log('詳情:', error.details);
}
}最佳實踐
1. 針對特定錯誤碼處理
不要只檢查 HTTP 狀態碼,應該檢查 error.code:
// ✅ 好的做法
if (error.code === 'conflict') {
// 處理重複訂閱
}
// ❌ 不好的做法
if (response.status === 409) {
// 409 可能有多種含義
}2. 使用 details 獲取更多資訊
details 陣列包含錯誤的額外資訊:
if (error.code === 'conflict' && error.details?.[0]) {
const { existing_subscription_id, status } = error.details[0];
// 使用這些資訊提供更好的用戶體驗
}3. 記錄錯誤用於除錯
onError: (error) => {
// 記錄完整錯誤資訊
console.error('[Recur Error]', {
code: error.code,
message: error.message,
details: error.details,
docUrl: error.docUrl,
});
// 發送到錯誤追蹤服務
Sentry.captureException(error);
}下一步
- 錯誤處理指南 - SDK 錯誤處理最佳實踐
- Webhook 整合 - 接收即時事件通知
- API 認證 - API Key 管理