---
title: useSubscribe
description: 發起訂閱結帳、管理付款狀態的 React Hook
---

# useSubscribe

`useSubscribe` 是發起結帳的主要 Hook，封裝了 `useRecur().checkout()` 並提供自動狀態管理。

  需要在 `RecurProvider` 內使用。

## 基本用法

```tsx
import { useSubscribe } from 'recur-tw';

function SubscribeButton() {
  const { subscribe, isLoading, error } = useSubscribe({
    onPaymentComplete: (subscription) => {
      console.log('付款成功！', subscription.id);
      window.location.href = '/welcome';
    },
  });

  return (
    <div>
      <button
        onClick={() => subscribe({
          productId: 'prod_xxx',
          customerEmail: 'user@example.com',
        })}
        disabled={isLoading}
      >
        {isLoading ? '處理中...' : '立即訂閱'}
      </button>
      {error && <p className="text-red-500">{error.message}</p>}
    </div>
  );
}
```

## Options（初始化參數）

傳入 `useSubscribe()` 的 callback 設定：

| 參數 | 類型 | 說明 |
|------|------|------|
| `onSuccess` | `(result) => void` | Checkout session 建立成功（卡片表單已準備好） |
| `onPaymentComplete` | `(subscription) => void` | 付款完成、訂閱已啟用 |
| `onError` | `(error) => void` | 發生錯誤 |
| `onPaymentFailed` | `(error) => PaymentFailedAction \| void` | 付款失敗（卡號錯誤等），可回傳自訂行為 |
| `onPaymentCancel` | `() => void` | 用戶取消付款 |

## subscribe() 參數

呼叫 `subscribe()` 時傳入的結帳選項：

| 參數 | 類型 | 必填 | 說明 |
|------|------|------|------|
| `productId` | string | 是* | 產品 ID |
| `productSlug` | string | 是* | 或用產品 slug（二擇一） |
| `customerEmail` | string | 是 | 顧客 email |
| `customerName` | string | 否 | 顧客名稱 |
| `externalCustomerId` | string | 否 | 您系統中的客戶 ID |
| `promoCode` | string | 否 | 優惠碼（搭配 `usePromoCode` 使用） |
| `trialDays` | number | 否 | 覆蓋預設試用天數 |
| `quantity` | number | 否 | 數量（預設 1） |
| `metadata` | object | 否 | 自訂資料（最多 10 key） |
| `collectPaymentMethod` | string | 否 | `'always'` 或 `'if_required'` |

## Return 值

| 屬性 | 類型 | 說明 |
|------|------|------|
| `subscribe` | `(options) => Promise<void>` | 發起結帳 |
| `mutate` | `(options) => Promise<void>` | `subscribe` 的別名 |
| `isLoading` | boolean | 結帳流程進行中 |
| `error` | `CheckoutError \| null` | 錯誤物件 |
| `reset` | `() => void` | 清除錯誤狀態 |

## useSubscribe vs useRecur

| | `useSubscribe` | `useRecur` |
|--|----------------|------------|
| 狀態管理 | 內建（isLoading, error, reset） | 需自己管理 |
| Callbacks | 透過 options 設定 | 在 checkout() 內設定 |
| 推薦場景 | 大多數情境 | 需要完整控制流程時 |

## 搭配優惠碼

```tsx
import { useSubscribe, usePromoCode } from 'recur-tw';

function Checkout({ userId, userEmail }) {
  const promo = usePromoCode({ customerId: userId });
  const { subscribe, isLoading } = useSubscribe();

  return (
    <div>
      <input onBlur={(e) => promo.apply(e.target.value)} />
      <button onClick={() => subscribe({
        productId: 'prod_xxx',
        customerEmail: userEmail,
        promoCode: promo.code,  // null = 不帶優惠碼
      })}>
        訂閱
      </button>
    </div>
  );
}
```

## 錯誤處理

```tsx
const { subscribe, error, reset } = useSubscribe({
  onError: (err) => {
    if (err.code === 'DUPLICATE_SUBSCRIPTION') {
      alert('您已經訂閱了此方案');
    }
  },
  onPaymentFailed: (err) => {
    // 自訂付款失敗行為
    if (err.details?.failureCode === 'INSUFFICIENT_FUNDS') {
      return { action: 'show_message', message: '餘額不足，請更換付款方式' };
    }
    // 回傳 undefined 使用預設處理
  },
});
```

詳細的錯誤碼說明請參考 [錯誤處理指南](/guides/checkout/error-handling)。
