{
  "openapi": "3.1.0",
  "info": {
    "title": "Recur API",
    "version": "1.0.0",
    "description": "Recur 提供完整的訂閱計費 REST API，讓您能夠輕鬆地將訂閱功能整合到您的應用程式中。\n\n## 認證\n\nRecur API 使用 API Key 認證。有兩種類型：\n\n- **Secret Key** (`sk_xxx`): 後端使用，擁有完整權限\n- **Publishable Key** (`pk_xxx`): 前端使用，限制權限\n\n### 環境\n\n| 環境 | Key 前綴 | 用途 |\n|------|---------|------|\n| Sandbox | `sk_test_` / `pk_test_` | 測試環境 |\n| Production | `sk_live_` / `pk_live_` | 正式環境 |\n\n### 認證方式\n\n在請求標頭中加入 API Key：\n\n```\nAuthorization: Bearer sk_test_xxx\n```\n\n或使用專用標頭：\n\n```\nX-Recur-Secret-Key: sk_test_xxx\nX-Recur-Publishable-Key: pk_test_xxx\n```\n",
    "contact": {
      "name": "Recur Support",
      "email": "support@recur.tw",
      "url": "https://recur.tw"
    },
    "license": {
      "name": "Elastic License 2.0",
      "url": "https://www.elastic.co/licensing/elastic-license"
    }
  },
  "servers": [
    {
      "url": "https://api.recur.tw/v1",
      "description": "Production"
    },
    {
      "url": "http://localhost:3000/api/v1",
      "description": "Development"
    }
  ],
  "security": [
    {
      "SecretKey": []
    },
    {
      "PublishableKey": []
    },
    {
      "OAuth2": [
        "openid",
        "products:read",
        "orders:read",
        "subscriptions:read",
        "customers:read",
        "analytics:read"
      ]
    }
  ],
  "tags": [
    {
      "name": "Checkout Sessions",
      "description": "Hosted Checkout Sessions API - 建立與管理託管結帳會話。\n提供類似 Stripe Checkout 的體驗，將用戶導向 Recur 託管的結帳頁面。\n"
    },
    {
      "name": "Checkouts",
      "description": "Embedded Checkouts API - 嵌入式結帳表單。\n使用 PAYUNi SDK 在您的網站上嵌入支付表單。\n"
    },
    {
      "name": "Subscriptions",
      "description": "訂閱生命週期管理 - 建立、查詢和管理訂閱"
    },
    {
      "name": "Subscription Switching",
      "description": "訂閱方案切換 API - 升級、降級、週期變更和跨等級切換。\n\n## 切換類型\n\n| 類型 | 說明 | 執行方式 |\n|------|------|---------|\n| `UPGRADE` | 升級至更高價格方案 | 立即執行 + 按比例計費 |\n| `DOWNGRADE` | 降級至更低價格方案 | 排程至帳期結束執行 |\n| `PERIOD_CHANGE` | 變更計費週期（月→年或年→月） | 視價格方向決定 |\n| `CROSSGRADE` | 切換至同價位不同方案 | 立即執行，無按比例計費 |\n\n## 按比例計費 (Proration)\n\n升級時系統會自動計算：\n- **Credit**: 現有方案未使用天數的退款\n- **Charge**: 新方案的完整週期費用\n- **Net Amount**: 客戶需支付的淨額\n"
    },
    {
      "name": "Subscription Schedules",
      "description": "排程切換 API - 管理待執行的訂閱變更。\n\n降級和部分週期變更會被排程到帳期結束時執行。\n每個訂閱同時只能有一個待執行的排程。\n"
    },
    {
      "name": "Products",
      "description": "產品查詢 - 取得可用的產品（訂閱方案、一次性付款等）"
    },
    {
      "name": "Webhooks",
      "description": "Webhook 事件 - 接收支付和訂閱事件通知"
    }
  ],
  "paths": {
    "/checkout/sessions": {
      "post": {
        "summary": "建立 Checkout Session",
        "description": "建立新的 Checkout Session，將用戶導向託管結帳頁面。\n\n自動建立 Order 和 PaymentIntent（或 SETUP 模式的 SetupIntent）。\n返回結帳頁面 URL。\n",
        "operationId": "createCheckoutSession",
        "tags": [
          "Checkout Sessions"
        ],
        "security": [
          {
            "SecretKey": []
          },
          {
            "PublishableKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCheckoutSessionRequest"
              },
              "examples": {
                "payment": {
                  "summary": "一次性付款",
                  "value": {
                    "productId": "prod_abc123",
                    "mode": "PAYMENT",
                    "successUrl": "https://example.com/success?session_id={CHECKOUT_SESSION_ID}",
                    "cancelUrl": "https://example.com/cancel"
                  }
                },
                "subscription": {
                  "summary": "訂閱結帳",
                  "value": {
                    "productId": "prod_monthly",
                    "mode": "SUBSCRIPTION",
                    "successUrl": "https://example.com/welcome",
                    "cancelUrl": "https://example.com/pricing",
                    "customerEmail": "user@example.com"
                  }
                },
                "setup": {
                  "summary": "僅儲存卡片",
                  "value": {
                    "productId": "prod_abc123",
                    "mode": "SETUP",
                    "successUrl": "https://example.com/card-saved",
                    "cancelUrl": "https://example.com/settings"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Checkout Session 建立成功",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateCheckoutSessionResponse"
                },
                "example": {
                  "id": "cs_abc123xyz",
                  "url": "https://checkout.recur.tw/cs_abc123xyz",
                  "expiresAt": "2025-01-01T00:30:00Z"
                }
              }
            }
          },
          "400": {
            "description": "請求無效",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "未授權 - API Key 無效或缺失",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "商品不存在",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/checkout/sessions/{id}": {
      "get": {
        "summary": "取得 Checkout Session",
        "description": "取得 Checkout Session 的詳細資訊。\n用於在重新導向後檢查 Session 狀態。\n",
        "operationId": "getCheckoutSession",
        "tags": [
          "Checkout Sessions"
        ],
        "security": [
          {
            "SecretKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Checkout Session ID",
            "schema": {
              "type": "string",
              "example": "cs_abc123xyz"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Checkout Session 詳情",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckoutSession"
                }
              }
            }
          },
          "404": {
            "description": "Session 不存在",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/products": {
      "get": {
        "summary": "列出所有產品",
        "description": "取得組織下所有可用的產品。\n可透過 `type` 參數篩選產品類型。\n",
        "operationId": "listProducts",
        "tags": [
          "Products"
        ],
        "security": [
          {
            "PublishableKey": []
          }
        ],
        "parameters": [
          {
            "name": "type",
            "in": "query",
            "required": false,
            "description": "篩選產品類型",
            "schema": {
              "type": "string",
              "enum": [
                "SUBSCRIPTION",
                "ONE_TIME",
                "CREDITS",
                "DONATION"
              ],
              "example": "SUBSCRIPTION"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "產品列表",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "object",
                    "data",
                    "livemode"
                  ],
                  "properties": {
                    "object": {
                      "type": "string",
                      "enum": [
                        "list"
                      ],
                      "description": "固定為 `\"list\"`"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "allOf": [
                          {
                            "type": "object",
                            "properties": {
                              "object": {
                                "type": "string",
                                "enum": [
                                  "product"
                                ],
                                "description": "固定為 `\"product\"`"
                              }
                            }
                          },
                          {
                            "$ref": "#/components/schemas/Product"
                          }
                        ]
                      }
                    },
                    "livemode": {
                      "type": "boolean",
                      "description": "是否為正式環境"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/products/by-slug/{slug}": {
      "get": {
        "summary": "按 slug 取得產品",
        "description": "使用 URL slug 取得特定產品",
        "operationId": "getProductBySlug",
        "tags": [
          "Products"
        ],
        "security": [
          {
            "PublishableKey": []
          }
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "產品的 URL slug",
            "schema": {
              "type": "string",
              "example": "monthly-plan"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "產品詳情",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Product"
                }
              }
            }
          },
          "404": {
            "description": "產品不存在",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/subscriptions/{subscriptionId}/switch-preview": {
      "get": {
        "summary": "預覽方案切換",
        "description": "計算並返回切換到不同方案的預覽結果。不會執行任何變更。\n\n用於在執行切換前向客戶顯示：\n- 切換類型（升級/降級/週期變更/跨等級）\n- 執行方式（立即/排程）\n- 按比例計費詳情（若適用）\n- 新的帳期結束日期\n",
        "operationId": "getSwitchPreview",
        "tags": [
          "Subscription Switching"
        ],
        "security": [
          {
            "SecretKey": []
          }
        ],
        "parameters": [
          {
            "name": "subscriptionId",
            "in": "path",
            "required": true,
            "description": "訂閱 ID",
            "schema": {
              "type": "string",
              "example": "sub_abc123"
            }
          },
          {
            "name": "target_product_id",
            "in": "query",
            "required": true,
            "description": "目標產品 ID",
            "schema": {
              "type": "string",
              "example": "prod_premium"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "切換預覽結果",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SwitchPreviewResponse"
                },
                "examples": {
                  "upgrade": {
                    "summary": "升級預覽",
                    "value": {
                      "object": "switch_preview",
                      "subscription_id": "sub_abc123",
                      "switch_type": "UPGRADE",
                      "execution_mode": "immediate",
                      "current_plan": {
                        "product_id": "prod_basic",
                        "product_name": "基本版",
                        "amount": 29900,
                        "currency": "TWD",
                        "interval": "month",
                        "interval_count": 1,
                        "monthly_equivalent": 29900
                      },
                      "new_plan": {
                        "product_id": "prod_premium",
                        "product_name": "專業版",
                        "amount": 59900,
                        "currency": "TWD",
                        "interval": "month",
                        "interval_count": 1,
                        "monthly_equivalent": 59900
                      },
                      "proration": {
                        "credit_amount": 14950,
                        "charge_amount": 59900,
                        "net_amount": 44950,
                        "unused_days": 15,
                        "total_days_in_period": 30,
                        "credit_description": "15 unused days on 基本版"
                      },
                      "effective_date": "2025-01-15T10:30:00Z",
                      "next_billing_date": "2025-02-15T10:30:00Z",
                      "proration_behavior": "create_prorations",
                      "requires_payment": true,
                      "can_proceed": true,
                      "blocking_reason": null,
                      "is_in_trial": false,
                      "trial_end": null
                    }
                  },
                  "downgrade": {
                    "summary": "降級預覽（排程）",
                    "value": {
                      "object": "switch_preview",
                      "subscription_id": "sub_abc123",
                      "switch_type": "DOWNGRADE",
                      "execution_mode": "scheduled",
                      "current_plan": {
                        "product_id": "prod_premium",
                        "product_name": "專業版",
                        "amount": 59900,
                        "currency": "TWD",
                        "interval": "month",
                        "interval_count": 1,
                        "monthly_equivalent": 59900
                      },
                      "new_plan": {
                        "product_id": "prod_basic",
                        "product_name": "基本版",
                        "amount": 29900,
                        "currency": "TWD",
                        "interval": "month",
                        "interval_count": 1,
                        "monthly_equivalent": 29900
                      },
                      "proration": null,
                      "effective_date": "2025-02-01T00:00:00Z",
                      "next_billing_date": "2025-03-01T00:00:00Z",
                      "proration_behavior": "none",
                      "requires_payment": false,
                      "can_proceed": true,
                      "blocking_reason": null,
                      "is_in_trial": false,
                      "trial_end": null
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "請求無效（如：切換至同一產品）",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SwitchErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "未授權",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "訂閱或產品不存在",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SwitchErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/subscriptions/{subscriptionId}/switch": {
      "post": {
        "summary": "執行方案切換",
        "description": "執行訂閱方案切換。\n\n根據切換類型自動決定執行方式：\n- **升級/跨等級**: 立即執行，按比例計費\n- **降級/年→月週期變更**: 排程至帳期結束執行\n\n對於立即執行的切換，若需支付差額，會建立發票。\n",
        "operationId": "executeSwitch",
        "tags": [
          "Subscription Switching"
        ],
        "security": [
          {
            "SecretKey": []
          }
        ],
        "parameters": [
          {
            "name": "subscriptionId",
            "in": "path",
            "required": true,
            "description": "訂閱 ID",
            "schema": {
              "type": "string",
              "example": "sub_abc123"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ExecuteSwitchRequest"
              },
              "examples": {
                "basic": {
                  "summary": "基本切換",
                  "value": {
                    "target_product_id": "prod_premium"
                  }
                },
                "with_proration": {
                  "summary": "指定按比例計費行為",
                  "value": {
                    "target_product_id": "prod_premium",
                    "proration_behavior": "create_prorations"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "切換執行成功",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExecuteSwitchResponse"
                },
                "examples": {
                  "immediate": {
                    "summary": "立即執行（升級）",
                    "value": {
                      "object": "switch_result",
                      "execution_mode": "immediate",
                      "subscription": {
                        "id": "sub_abc123",
                        "product_id": "prod_premium",
                        "product_name": "專業版",
                        "status": "ACTIVE",
                        "amount": 59900,
                        "currency": "TWD",
                        "interval": "month",
                        "interval_count": 1,
                        "current_period_start": "2025-01-15T10:30:00Z",
                        "current_period_end": "2025-02-15T10:30:00Z",
                        "next_billing_date": "2025-02-15T10:30:00Z",
                        "previous_product_id": "prod_basic",
                        "switched_at": "2025-01-15T10:30:00Z",
                        "switch_type": "UPGRADE"
                      },
                      "invoice": {
                        "id": "inv_xyz789",
                        "invoice_number": "INV-2025-001",
                        "amount": 44950,
                        "currency": "TWD",
                        "status": "PENDING",
                        "billing_reason": "SUBSCRIPTION_UPDATE",
                        "billing_entries": [
                          {
                            "id": "entry_1",
                            "type": "PRORATION_CREDIT",
                            "direction": "CREDIT",
                            "amount": 14950,
                            "description": "15 unused days on 基本版"
                          },
                          {
                            "id": "entry_2",
                            "type": "PRORATION_DEBIT",
                            "direction": "DEBIT",
                            "amount": 59900,
                            "description": "專業版 - Full billing period"
                          }
                        ]
                      },
                      "schedule": null,
                      "switch_type": "UPGRADE",
                      "proration": {
                        "credit_amount": 14950,
                        "charge_amount": 59900,
                        "net_amount": 44950,
                        "unused_days": 15,
                        "total_days_in_period": 30,
                        "credit_description": "15 unused days on 基本版"
                      },
                      "effective_date": "2025-01-15T10:30:00Z"
                    }
                  },
                  "scheduled": {
                    "summary": "排程執行（降級）",
                    "value": {
                      "object": "switch_result",
                      "execution_mode": "scheduled",
                      "subscription": {
                        "id": "sub_abc123"
                      },
                      "invoice": null,
                      "schedule": {
                        "id": "sched_abc123",
                        "target_product_id": "prod_basic",
                        "target_product_name": "基本版",
                        "switch_type": "DOWNGRADE",
                        "effective_at": "2025-02-01T00:00:00Z",
                        "status": "PENDING",
                        "created_at": "2025-01-15T10:30:00Z"
                      },
                      "switch_type": "DOWNGRADE",
                      "proration": null,
                      "effective_date": "2025-02-01T00:00:00Z"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "請求無效",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SwitchErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "未授權",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "402": {
            "description": "需要付款或付款失敗",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SwitchErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "訂閱或產品不存在",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SwitchErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/subscriptions/{subscriptionId}/schedule": {
      "get": {
        "summary": "取得待執行排程",
        "description": "取得訂閱的待執行排程（若存在）。\n\n每個訂閱同時只能有一個待執行的排程。\n",
        "operationId": "getSchedule",
        "tags": [
          "Subscription Schedules"
        ],
        "security": [
          {
            "SecretKey": []
          }
        ],
        "parameters": [
          {
            "name": "subscriptionId",
            "in": "path",
            "required": true,
            "description": "訂閱 ID",
            "schema": {
              "type": "string",
              "example": "sub_abc123"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "排程資訊",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetScheduleResponse"
                },
                "examples": {
                  "has_schedule": {
                    "summary": "有待執行排程",
                    "value": {
                      "object": "schedule",
                      "has_pending_schedule": true,
                      "schedule": {
                        "id": "sched_abc123",
                        "subscription_id": "sub_abc123",
                        "target_product_id": "prod_basic",
                        "target_product_name": "基本版",
                        "switch_type": "DOWNGRADE",
                        "effective_at": "2025-02-01T00:00:00Z",
                        "status": "PENDING",
                        "created_at": "2025-01-15T10:30:00Z"
                      }
                    }
                  },
                  "no_schedule": {
                    "summary": "無待執行排程",
                    "value": {
                      "object": "schedule",
                      "has_pending_schedule": false,
                      "schedule": null
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "未授權",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "訂閱不存在",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SwitchErrorResponse"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "取消待執行排程",
        "description": "取消訂閱的待執行排程。\n\n只能取消狀態為 `PENDING` 的排程。\n已執行或已取消的排程無法再次取消。\n",
        "operationId": "cancelSchedule",
        "tags": [
          "Subscription Schedules"
        ],
        "security": [
          {
            "SecretKey": []
          }
        ],
        "parameters": [
          {
            "name": "subscriptionId",
            "in": "path",
            "required": true,
            "description": "訂閱 ID",
            "schema": {
              "type": "string",
              "example": "sub_abc123"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "排程已取消",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CancelScheduleResponse"
                },
                "example": {
                  "object": "schedule_cancellation",
                  "cancelled": true,
                  "subscription_id": "sub_abc123"
                }
              }
            }
          },
          "401": {
            "description": "未授權",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "訂閱不存在或無待執行排程",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SwitchErrorResponse"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "SecretKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "Secret API Key (`sk_xxx`) - 後端使用，擁有完整權限。\n支援 metadata 設定。\n"
      },
      "PublishableKey": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Recur-Publishable-Key",
        "description": "Publishable API Key (`pk_xxx`) - 前端使用，限制權限。\n不支援 metadata 設定。\n"
      },
      "OAuth2": {
        "type": "oauth2",
        "description": "OAuth 2.1 (Authorization Code + PKCE) for AI agents and MCP clients.\nUsed by the Recur MCP server (https://mcp.recur.tw) so agents can request\nleast-privilege, scoped access on behalf of a user. Human-facing API\nintegrations should use `SecretKey`/`PublishableKey` instead.\n\nDiscovery: https://app.recur.tw/.well-known/openid-configuration\n",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://app.recur.tw/api/auth/oauth2/authorize",
            "tokenUrl": "https://app.recur.tw/api/auth/oauth2/token",
            "refreshUrl": "https://app.recur.tw/api/auth/oauth2/token",
            "scopes": {
              "openid": "OpenID Connect authentication.",
              "profile": "Read the user's basic profile.",
              "email": "Read the user's email address.",
              "org:read": "Read organization details.",
              "api-keys:read": "List and read API keys.",
              "api-keys:write": "Create and revoke API keys.",
              "products:read": "Read products and pricing.",
              "products:write": "Create and update products.",
              "integration:verify": "Verify integration and connection status.",
              "orders:read": "Read orders and payment history.",
              "subscriptions:read": "Read subscriptions and their schedules.",
              "customers:read": "Read customer records.",
              "analytics:read": "Read revenue analytics (MRR, ARR, churn, LTV)."
            }
          }
        }
      }
    },
    "schemas": {
      "CreateCheckoutSessionRequest": {
        "type": "object",
        "required": [
          "productId",
          "successUrl",
          "cancelUrl"
        ],
        "properties": {
          "productId": {
            "type": "string",
            "description": "商品 ID",
            "example": "prod_abc123"
          },
          "mode": {
            "type": "string",
            "enum": [
              "PAYMENT",
              "SUBSCRIPTION",
              "SETUP"
            ],
            "default": "PAYMENT",
            "description": "結帳模式：\n- `PAYMENT`: 一次性付款\n- `SUBSCRIPTION`: 訂閱（綁定卡片）\n- `SETUP`: 僅儲存卡片（不扣款）\n"
          },
          "successUrl": {
            "type": "string",
            "format": "uri",
            "description": "成功後的重新導向 URL。必須是 HTTPS。\n可使用 `{CHECKOUT_SESSION_ID}` 佔位符。\n",
            "example": "https://example.com/success?session_id={CHECKOUT_SESSION_ID}"
          },
          "cancelUrl": {
            "type": "string",
            "format": "uri",
            "description": "取消時的重新導向 URL。必須是 HTTPS。",
            "example": "https://example.com/cancel"
          },
          "customerEmail": {
            "type": "string",
            "format": "email",
            "description": "預填客戶 Email（選填）。\n如果未提供，客戶會在結帳頁面輸入。\n注意：Email 在結帳時為必填（用於寄送收據）。\n",
            "example": "user@example.com"
          },
          "customerName": {
            "type": "string",
            "description": "預填客戶姓名（選填）。\n如果未提供，客戶可在結帳頁面輸入。\n",
            "example": "王小明"
          },
          "promotionCode": {
            "type": "string",
            "description": "預先套用的優惠碼（選填）。\n建立時即計算折扣並記錄於 Session 和 Order。\n",
            "example": "SUMMER20"
          },
          "collectPaymentMethod": {
            "type": "string",
            "enum": [
              "always",
              "if_required"
            ],
            "default": "always",
            "description": "信用卡收集策略：\n- `always`（預設）：即使金額為 $0 仍需輸入信用卡（驗證扣款 NT$2）\n- `if_required`：金額為 $0 時跳過信用卡，直接完成訂閱\n"
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true,
            "description": "自訂 metadata（僅 Secret Key 可用）"
          }
        }
      },
      "CreateCheckoutSessionResponse": {
        "type": "object",
        "required": [
          "id",
          "url",
          "expiresAt",
          "amount",
          "requiresPaymentMethod"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Checkout Session ID",
            "example": "cs_abc123xyz"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "託管結帳頁面 URL",
            "example": "https://checkout.recur.tw/cs_abc123xyz"
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "description": "Session 過期時間（建立後 30 分鐘）",
            "example": "2025-01-01T00:30:00Z"
          },
          "amount": {
            "type": "integer",
            "description": "折扣後的實際金額（TWD 整數）",
            "example": 0
          },
          "requiresPaymentMethod": {
            "type": "boolean",
            "description": "是否需要信用卡。由 `collectPaymentMethod` 和實際金額共同決定：\n- `always` → 永遠為 `true`\n- `if_required` + 金額 > 0 → `true`\n- `if_required` + 金額 = 0 → `false`\n",
            "example": true
          },
          "clientSecret": {
            "type": "string",
            "description": "PaymentIntent client secret（用於前端 SDK 安全輪詢）",
            "example": "pi_secret_abc123"
          },
          "discount": {
            "type": "object",
            "description": "折扣資訊（僅在套用優惠碼時返回）",
            "properties": {
              "code": {
                "type": "string",
                "description": "優惠碼"
              },
              "couponId": {
                "type": "string",
                "description": "優惠券 ID"
              },
              "couponName": {
                "type": "string",
                "description": "優惠券名稱"
              },
              "discountType": {
                "type": "string",
                "enum": [
                  "PERCENTAGE",
                  "FIXED_AMOUNT",
                  "FIRST_PERIOD_PRICE"
                ]
              },
              "discountAmount": {
                "type": "integer",
                "description": "折扣金額（TWD 整數）"
              },
              "finalAmount": {
                "type": "integer",
                "description": "折扣後金額"
              },
              "originalAmount": {
                "type": "integer",
                "description": "原價"
              }
            }
          }
        }
      },
      "CheckoutSession": {
        "type": "object",
        "required": [
          "id",
          "status",
          "mode",
          "amountTotal",
          "currency",
          "product",
          "createdAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "example": "cs_abc123xyz"
          },
          "status": {
            "type": "string",
            "enum": [
              "OPEN",
              "COMPLETE",
              "EXPIRED"
            ],
            "description": "Session 狀態：\n- `OPEN`: 等待付款\n- `COMPLETE`: 付款完成\n- `EXPIRED`: 已過期\n"
          },
          "mode": {
            "type": "string",
            "enum": [
              "PAYMENT",
              "SUBSCRIPTION",
              "SETUP"
            ]
          },
          "customerEmail": {
            "type": "string",
            "format": "email",
            "nullable": true,
            "description": "客戶 Email"
          },
          "customerName": {
            "type": "string",
            "nullable": true,
            "description": "客戶姓名"
          },
          "orderId": {
            "type": "string",
            "nullable": true,
            "description": "建立的訂單 ID"
          },
          "subscriptionId": {
            "type": "string",
            "nullable": true,
            "description": "建立的訂閱 ID（僅 SUBSCRIPTION 模式）"
          },
          "paymentIntentId": {
            "type": "string",
            "nullable": true,
            "description": "PaymentIntent ID"
          },
          "amountTotal": {
            "type": "integer",
            "description": "總金額（最小單位）",
            "example": 29900
          },
          "currency": {
            "type": "string",
            "example": "TWD"
          },
          "product": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              }
            }
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "completedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "Product": {
        "type": "object",
        "required": [
          "id",
          "name",
          "type",
          "price",
          "currency"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "產品 ID",
            "example": "prod_abc123"
          },
          "name": {
            "type": "string",
            "description": "產品名稱",
            "example": "專業版月訂閱"
          },
          "slug": {
            "type": "string",
            "description": "URL slug",
            "example": "pro-monthly"
          },
          "description": {
            "type": "string",
            "nullable": true,
            "description": "產品描述"
          },
          "type": {
            "type": "string",
            "enum": [
              "SUBSCRIPTION",
              "ONE_TIME",
              "CREDITS",
              "DONATION"
            ],
            "description": "產品類型：\n- `SUBSCRIPTION`: 訂閱型產品（定期扣款）\n- `ONE_TIME`: 一次性付款產品\n- `CREDITS`: 點數/代幣包\n- `DONATION`: 捐款產品\n",
            "example": "SUBSCRIPTION"
          },
          "billingPeriod": {
            "type": "string",
            "enum": [
              "WEEKLY",
              "MONTHLY",
              "YEARLY",
              "ONE_TIME",
              "CUSTOM"
            ],
            "nullable": true,
            "description": "計費週期（僅訂閱型產品有效）",
            "example": "MONTHLY"
          },
          "price": {
            "type": "integer",
            "description": "價格（最小單位）",
            "example": 29900
          },
          "currency": {
            "type": "string",
            "default": "TWD",
            "example": "TWD"
          },
          "trialDays": {
            "type": "integer",
            "nullable": true,
            "description": "試用天數（僅訂閱型產品有效）",
            "example": 14
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true,
            "nullable": true,
            "description": "產品 metadata"
          },
          "productFamily": {
            "type": "string",
            "nullable": true,
            "description": "產品家族（用於分組相關產品）",
            "example": "pro"
          },
          "displayOrder": {
            "type": "integer",
            "description": "顯示順序",
            "default": 0
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "invalid_request",
                  "resource_not_found",
                  "unauthorized",
                  "forbidden",
                  "internal_error"
                ],
                "description": "錯誤代碼"
              },
              "message": {
                "type": "string",
                "description": "錯誤訊息"
              },
              "details": {
                "type": "object",
                "additionalProperties": true,
                "description": "額外錯誤詳情"
              }
            }
          }
        }
      },
      "SwitchType": {
        "type": "string",
        "enum": [
          "UPGRADE",
          "DOWNGRADE",
          "PERIOD_CHANGE",
          "CROSSGRADE"
        ],
        "description": "切換類型：\n- `UPGRADE`: 升級至更高價格方案\n- `DOWNGRADE`: 降級至更低價格方案\n- `PERIOD_CHANGE`: 變更計費週期（如月→年）\n- `CROSSGRADE`: 切換至同價位不同方案\n"
      },
      "ExecutionMode": {
        "type": "string",
        "enum": [
          "immediate",
          "scheduled"
        ],
        "description": "執行方式：\n- `immediate`: 立即執行\n- `scheduled`: 排程至帳期結束執行\n"
      },
      "ProrationBehavior": {
        "type": "string",
        "enum": [
          "create_prorations",
          "none",
          "always_invoice"
        ],
        "description": "按比例計費行為：\n- `create_prorations`: 建立按比例計費項目\n- `none`: 不計算按比例計費\n- `always_invoice`: 總是建立發票\n"
      },
      "ScheduleStatus": {
        "type": "string",
        "enum": [
          "PENDING",
          "EXECUTED",
          "CANCELLED"
        ],
        "description": "排程狀態：\n- `PENDING`: 待執行\n- `EXECUTED`: 已執行\n- `CANCELLED`: 已取消\n"
      },
      "PlanSummary": {
        "type": "object",
        "required": [
          "product_id",
          "product_name",
          "amount",
          "currency",
          "interval",
          "interval_count",
          "monthly_equivalent"
        ],
        "properties": {
          "product_id": {
            "type": "string",
            "description": "產品 ID"
          },
          "product_name": {
            "type": "string",
            "description": "產品名稱"
          },
          "amount": {
            "type": "integer",
            "description": "價格（最小單位）"
          },
          "currency": {
            "type": "string",
            "description": "幣別"
          },
          "interval": {
            "type": "string",
            "enum": [
              "day",
              "week",
              "month",
              "year"
            ],
            "description": "計費間隔"
          },
          "interval_count": {
            "type": "integer",
            "description": "間隔數量"
          },
          "monthly_equivalent": {
            "type": "integer",
            "description": "月費等價（用於比較）"
          }
        }
      },
      "ProrationDetails": {
        "type": "object",
        "required": [
          "credit_amount",
          "charge_amount",
          "net_amount",
          "unused_days",
          "total_days_in_period",
          "credit_description"
        ],
        "properties": {
          "credit_amount": {
            "type": "integer",
            "description": "退款金額（現有方案未使用天數）"
          },
          "charge_amount": {
            "type": "integer",
            "description": "收款金額（新方案完整週期）"
          },
          "net_amount": {
            "type": "integer",
            "description": "淨額（客戶需支付）"
          },
          "unused_days": {
            "type": "integer",
            "description": "現有方案剩餘天數"
          },
          "total_days_in_period": {
            "type": "integer",
            "description": "帳期總天數"
          },
          "credit_description": {
            "type": "string",
            "description": "退款描述"
          }
        }
      },
      "SwitchPreviewResponse": {
        "type": "object",
        "required": [
          "object",
          "subscription_id",
          "switch_type",
          "execution_mode",
          "current_plan",
          "new_plan",
          "effective_date",
          "next_billing_date",
          "proration_behavior",
          "requires_payment",
          "can_proceed"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "switch_preview"
            ]
          },
          "subscription_id": {
            "type": "string"
          },
          "switch_type": {
            "$ref": "#/components/schemas/SwitchType"
          },
          "execution_mode": {
            "$ref": "#/components/schemas/ExecutionMode"
          },
          "current_plan": {
            "$ref": "#/components/schemas/PlanSummary"
          },
          "new_plan": {
            "$ref": "#/components/schemas/PlanSummary"
          },
          "proration": {
            "$ref": "#/components/schemas/ProrationDetails",
            "nullable": true
          },
          "effective_date": {
            "type": "string",
            "format": "date-time",
            "description": "生效日期"
          },
          "next_billing_date": {
            "type": "string",
            "format": "date-time",
            "description": "下次帳單日期"
          },
          "proration_behavior": {
            "$ref": "#/components/schemas/ProrationBehavior"
          },
          "requires_payment": {
            "type": "boolean",
            "description": "是否需要支付"
          },
          "can_proceed": {
            "type": "boolean",
            "description": "是否可以執行切換"
          },
          "blocking_reason": {
            "type": "string",
            "nullable": true,
            "description": "阻擋原因（若無法切換）"
          },
          "is_in_trial": {
            "type": "boolean",
            "description": "是否在試用期"
          },
          "trial_end": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "試用期結束時間"
          }
        }
      },
      "ExecuteSwitchRequest": {
        "type": "object",
        "required": [
          "target_product_id"
        ],
        "properties": {
          "target_product_id": {
            "type": "string",
            "description": "目標產品 ID"
          },
          "proration_behavior": {
            "$ref": "#/components/schemas/ProrationBehavior",
            "description": "按比例計費行為（可選，使用商戶設定預設值）"
          }
        }
      },
      "SubscriptionSummary": {
        "type": "object",
        "required": [
          "id",
          "product_id",
          "status",
          "amount",
          "currency",
          "interval",
          "interval_count",
          "current_period_start",
          "current_period_end"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "product_id": {
            "type": "string"
          },
          "product_name": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "ACTIVE",
              "TRIAL",
              "PAST_DUE",
              "CANCELLED",
              "EXPIRED"
            ]
          },
          "amount": {
            "type": "integer"
          },
          "currency": {
            "type": "string"
          },
          "interval": {
            "type": "string"
          },
          "interval_count": {
            "type": "integer"
          },
          "current_period_start": {
            "type": "string",
            "format": "date-time"
          },
          "current_period_end": {
            "type": "string",
            "format": "date-time"
          },
          "next_billing_date": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "previous_product_id": {
            "type": "string",
            "nullable": true
          },
          "switched_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "switch_type": {
            "$ref": "#/components/schemas/SwitchType",
            "nullable": true
          }
        }
      },
      "BillingEntry": {
        "type": "object",
        "required": [
          "id",
          "type",
          "direction",
          "amount"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "CYCLE",
              "PRORATION_CREDIT",
              "PRORATION_DEBIT",
              "ADJUSTMENT"
            ]
          },
          "direction": {
            "type": "string",
            "enum": [
              "DEBIT",
              "CREDIT"
            ]
          },
          "amount": {
            "type": "integer"
          },
          "description": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "SwitchInvoice": {
        "type": "object",
        "required": [
          "id",
          "invoice_number",
          "amount",
          "currency",
          "status",
          "billing_reason"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "invoice_number": {
            "type": "string"
          },
          "amount": {
            "type": "integer"
          },
          "currency": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "PENDING",
              "PAID",
              "FAILED",
              "VOIDED"
            ]
          },
          "billing_reason": {
            "type": "string",
            "enum": [
              "SUBSCRIPTION_UPDATE"
            ]
          },
          "billing_entries": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BillingEntry"
            }
          }
        }
      },
      "ScheduleSummary": {
        "type": "object",
        "required": [
          "id",
          "target_product_id",
          "switch_type",
          "effective_at",
          "status",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "target_product_id": {
            "type": "string"
          },
          "target_product_name": {
            "type": "string",
            "nullable": true
          },
          "switch_type": {
            "$ref": "#/components/schemas/SwitchType"
          },
          "effective_at": {
            "type": "string",
            "format": "date-time"
          },
          "status": {
            "$ref": "#/components/schemas/ScheduleStatus"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ExecuteSwitchResponse": {
        "type": "object",
        "required": [
          "object",
          "execution_mode",
          "subscription",
          "switch_type",
          "effective_date"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "switch_result"
            ]
          },
          "execution_mode": {
            "$ref": "#/components/schemas/ExecutionMode"
          },
          "subscription": {
            "$ref": "#/components/schemas/SubscriptionSummary"
          },
          "invoice": {
            "$ref": "#/components/schemas/SwitchInvoice",
            "nullable": true
          },
          "schedule": {
            "$ref": "#/components/schemas/ScheduleSummary",
            "nullable": true
          },
          "switch_type": {
            "$ref": "#/components/schemas/SwitchType"
          },
          "proration": {
            "$ref": "#/components/schemas/ProrationDetails",
            "nullable": true
          },
          "effective_date": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "GetScheduleResponse": {
        "type": "object",
        "required": [
          "object",
          "has_pending_schedule"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "schedule"
            ]
          },
          "has_pending_schedule": {
            "type": "boolean"
          },
          "schedule": {
            "type": "object",
            "nullable": true,
            "properties": {
              "id": {
                "type": "string"
              },
              "subscription_id": {
                "type": "string"
              },
              "target_product_id": {
                "type": "string"
              },
              "target_product_name": {
                "type": "string",
                "nullable": true
              },
              "switch_type": {
                "$ref": "#/components/schemas/SwitchType"
              },
              "effective_at": {
                "type": "string",
                "format": "date-time"
              },
              "status": {
                "$ref": "#/components/schemas/ScheduleStatus"
              },
              "created_at": {
                "type": "string",
                "format": "date-time"
              }
            }
          }
        }
      },
      "CancelScheduleResponse": {
        "type": "object",
        "required": [
          "object",
          "cancelled",
          "subscription_id"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "schedule_cancellation"
            ]
          },
          "cancelled": {
            "type": "boolean"
          },
          "subscription_id": {
            "type": "string"
          }
        }
      },
      "SwitchErrorResponse": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "subscription_not_found",
                  "product_not_found",
                  "product_not_available",
                  "same_product",
                  "subscription_not_active",
                  "past_due_blocks_switch",
                  "trial_period_active",
                  "payment_failed",
                  "payment_required",
                  "schedule_already_exists",
                  "schedule_not_found",
                  "schedule_already_executed",
                  "schedule_already_cancelled",
                  "insufficient_permissions",
                  "no_pending_schedule",
                  "validation_error"
                ],
                "description": "切換錯誤代碼"
              },
              "message": {
                "type": "string",
                "description": "錯誤訊息"
              },
              "details": {
                "type": "object",
                "additionalProperties": true,
                "description": "額外錯誤詳情"
              }
            }
          }
        }
      }
    }
  }
}
