Recur
開發者指南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結帳(redirectToCheckoutcheckout)、產品查詢

UI 元件

元件用途說明
PromoCodeInput優惠碼輸入框搭配 usePromoCode,含 input + 按鈕 + 狀態顯示。支援 classNamestyleunstyled 自訂樣式

大多數情境下你只需要 useRecurredirectToCheckout)+ useProductsuseSubscribe 封裝了 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

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.publishableKeystring必填 — Publishable Key
config.baseUrlstringAPI base URL(預設 https://api.recur.tw
config.checkoutModestring'embedded'(預設)或 'modal'(僅 embedded checkout 使用)
customerobject客戶識別(用於 useCustomer),見 權限檢查

典型整合範例

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

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>
  );
}

Last updated on

On this page