# React Hooks 總覽 (/guides/react-hooks)





React Hooks [#react-hooks]

Recur React SDK 提供一組 Hooks，讓你在 React 應用中輕鬆整合訂閱付款功能。

安裝 [#安裝]

```bash
npm install recur-tw
```

快速對照 [#快速對照]

Hooks [#hooks]

| Hook                                                 | 用途                                       | 需要 Provider |
| ---------------------------------------------------- | ---------------------------------------- | :---------: |
| [`useSubscribe`](/guides/react-hooks/use-subscribe)  | 發起結帳、管理付款流程                              |      是      |
| [`useProducts`](/guides/react-hooks/use-products)    | 取得產品列表與價格                                |      是      |
| [`usePromoCode`](/guides/react-hooks/use-promo-code) | 驗證優惠碼、取得折扣資訊                             |    **選填**   |
| [`useCustomer`](/guides/entitlements)                | 檢查客戶權限、paywall 控制                        |      是      |
| `useRecur`                                           | 結帳（`redirectToCheckout`、`checkout`）、產品查詢 |      是      |

UI 元件 [#ui-元件]

| 元件                                                                       | 用途     | 說明                                                                           |
| ------------------------------------------------------------------------ | ------ | ---------------------------------------------------------------------------- |
| [`PromoCodeInput`](/guides/react-hooks/use-promo-code#promocodeinput-元件) | 優惠碼輸入框 | 搭配 `usePromoCode`，含 input + 按鈕 + 狀態顯示。支援 `className`、`style`、`unstyled` 自訂樣式 |

<Callout type="info">
  大多數情境下你只需要 `useRecur`（`redirectToCheckout`）+ `useProducts`。`useSubscribe` 封裝了 embedded checkout 的狀態管理。
</Callout>

useRecur 方法 [#userecur-方法]

`useRecur()` 提供以下方法：

| 方法                               | 說明                                           |
| -------------------------------- | -------------------------------------------- |
| `redirectToCheckout(options)`    | **推薦** — 跳轉到 Hosted Checkout（支援 localhost）   |
| `createCheckoutSession(options)` | 建立 checkout session 但不自動跳轉                   |
| `checkout(options)`              | Embedded/Modal checkout（需註冊網域，不支援 localhost） |
| `fetchProducts(options?)`        | 取得產品列表                                       |
| `getCheckoutStatus(id, secret)`  | 查詢結帳狀態                                       |

```tsx
'use client';
import { useRecur } from 'recur-tw';

function SubscribeButton({ productId }: { productId: string }) {
  const { redirectToCheckout, isCheckingOut } = useRecur();

  return (
    <button
      disabled={isCheckingOut}
      onClick={() => redirectToCheckout({
        productId,
        successUrl: `${window.location.origin}/success`,
        cancelUrl: `${window.location.origin}/pricing`,
      })}
    >
      {isCheckingOut ? '處理中...' : '訂閱'}
    </button>
  );
}
```

RecurProvider 設置 [#recurprovider-設置]

除了 `usePromoCode` 以外，所有 hooks 都需要在外層包裹 `RecurProvider`：

```tsx title="app/layout.tsx"
'use client';
import { RecurProvider } from 'recur-tw';

export default function Layout({ children }) {
  return (
    <RecurProvider config={{ publishableKey: 'pk_test_xxx' }}>
      {children}
    </RecurProvider>
  );
}
```

常用設定 [#常用設定]

| 參數                      | 類型     | 說明                                                    |
| ----------------------- | ------ | ----------------------------------------------------- |
| `config.publishableKey` | string | **必填** — Publishable Key                              |
| `config.baseUrl`        | string | API base URL（預設 `https://api.recur.tw`）               |
| `config.checkoutMode`   | string | `'embedded'`（預設）或 `'modal'`（僅 embedded checkout 使用）   |
| `customer`              | object | 客戶識別（用於 `useCustomer`），見 [權限檢查](/guides/entitlements) |

典型整合範例 [#典型整合範例]

一個完整的 pricing page 通常會這樣組合使用：

```tsx title="components/pricing.tsx"
'use client';
import { useProducts, useSubscribe, usePromoCode, PromoCodeInput } from 'recur-tw';

function PricingPage({ userEmail, userId }) {
  const { data: products, isLoading } = useProducts({ type: 'SUBSCRIPTION' });
  const { subscribe, isLoading: isCheckingOut } = useSubscribe({
    onPaymentComplete: () => window.location.href = '/welcome',
  });
  const promo = usePromoCode({ customerId: userId });

  if (isLoading) return <div>載入中...</div>;

  return (
    <div>
      {/* 方式 A：用元件（推薦） */}
      <PromoCodeInput promo={promo} />

      {/* 方式 B：自己組 UI */}
      {/* <input onBlur={(e) => promo.apply(e.target.value)} />
      {promo.isValid && <span>{promo.discount?.label}</span>}
      {promo.error && <span>{promo.error}</span>} */}

      {/* 產品列表 */}
      {products?.map((product) => (
        <div key={product.id}>
          <h3>{product.name}</h3>
          <p>NT${product.price}/月</p>
          <button
            disabled={isCheckingOut}
            onClick={() => subscribe({
              productId: product.id,
              customerEmail: userEmail,
              promoCode: promo.code, // null if no valid promo
            })}
          >
            {isCheckingOut ? '處理中...' : '訂閱'}
          </button>
        </div>
      ))}
    </div>
  );
}
```

<Cards>
  <Card title="useSubscribe" description="結帳與付款流程" href="/guides/react-hooks/use-subscribe" />

  <Card title="useProducts" description="取得產品列表與價格" href="/guides/react-hooks/use-products" />

  <Card title="usePromoCode" description="驗證優惠碼與折扣" href="/guides/react-hooks/use-promo-code" />

  <Card title="useCustomer" description="權限檢查與 paywall" href="/guides/entitlements" />
</Cards>
