開發者指南React Hooks
React Hooks 總覽
Recur React SDK 提供的所有 Hooks — 結帳、產品、優惠碼、權限檢查
React Hooks
Recur React SDK 提供一組 Hooks,讓你在 React 應用中輕鬆整合訂閱付款功能。
安裝
npm install recur-tw快速對照
Hooks
| Hook | 用途 | 需要 Provider |
|---|---|---|
useSubscribe | 發起結帳、管理付款流程 | 是 |
useProducts | 取得產品列表與價格 | 是 |
usePromoCode | 驗證優惠碼、取得折扣資訊 | 選填 |
useCustomer | 檢查客戶權限、paywall 控制 | 是 |
useRecur | 結帳(redirectToCheckout、checkout)、產品查詢 | 是 |
UI 元件
| 元件 | 用途 | 說明 |
|---|---|---|
PromoCodeInput | 優惠碼輸入框 | 搭配 usePromoCode,含 input + 按鈕 + 狀態顯示。支援 className、style、unstyled 自訂樣式 |
大多數情境下你只需要 useRecur(redirectToCheckout)+ useProducts。useSubscribe 封裝了 embedded checkout 的狀態管理。
useRecur 方法
useRecur() 提供以下方法:
| 方法 | 說明 |
|---|---|
redirectToCheckout(options) | 推薦 — 跳轉到 Hosted Checkout(支援 localhost) |
createCheckoutSession(options) | 建立 checkout session 但不自動跳轉 |
checkout(options) | Embedded/Modal checkout(需註冊網域,不支援 localhost) |
fetchProducts(options?) | 取得產品列表 |
getCheckoutStatus(id, secret) | 查詢結帳狀態 |
'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 設置
除了 usePromoCode 以外,所有 hooks 都需要在外層包裹 RecurProvider:
'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),見 權限檢查 |
典型整合範例
一個完整的 pricing page 通常會這樣組合使用:
'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>
);
}Last updated on