recur-checkout

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Recur Checkout Integration

Recur结账流程集成

You are helping implement Recur checkout flows. Recur supports multiple checkout modes for different use cases.
你将协助实现Recur结账流程。Recur支持多种结账模式以适配不同使用场景。

Checkout Modes

结账模式

ModeBest ForUser Experience
embedded
SPA appsForm renders inline in your page
modal
Quick purchasesForm appears in a dialog overlay
redirect
Simple integrationFull page redirect to Recur
模式适用场景用户体验
embedded
单页应用(SPA)表单直接在页面内渲染
modal
快速购买表单以弹窗浮层形式展示
redirect
简单集成整页重定向到Recur

Basic Implementation

基础实现

Using useRecur Hook

使用useRecur Hook

tsx
import { useRecur } from 'recur-tw'

function CheckoutButton({ productId }: { productId: string }) {
  const { checkout, isLoading } = useRecur()

  const handleClick = async () => {
    await checkout({
      productId,
      // Or use productSlug: 'pro-plan'

      // Optional: Pre-fill customer info
      customerEmail: 'user@example.com',
      customerName: 'John Doe',

      // Optional: Link to your user system
      externalCustomerId: 'user_123',

      // Callbacks
      onPaymentComplete: (result) => {
        console.log('Success!', result)
        // result.id - Subscription/Order ID
        // result.status - 'ACTIVE', 'TRIALING', etc.
      },
      onPaymentFailed: (error) => {
        console.error('Failed:', error)
        return { action: 'retry' } // or 'close' or 'custom'
      },
      onPaymentCancel: () => {
        console.log('User cancelled')
      },
    })
  }

  return (
    <button onClick={handleClick} disabled={isLoading}>
      {isLoading ? 'Processing...' : 'Subscribe'}
    </button>
  )
}
tsx
import { useRecur } from 'recur-tw'

function CheckoutButton({ productId }: { productId: string }) {
  const { checkout, isLoading } = useRecur()

  const handleClick = async () => {
    await checkout({
      productId,
      // 也可以使用 productSlug: 'pro-plan'

      // 可选:预填充客户信息
      customerEmail: 'user@example.com',
      customerName: 'John Doe',

      // 可选:关联到你的用户系统
      externalCustomerId: 'user_123',

      // 回调函数
      onPaymentComplete: (result) => {
        console.log('成功!', result)
        // result.id - 订阅/订单ID
        // result.status - 'ACTIVE'、'TRIALING'等
      },
      onPaymentFailed: (error) => {
        console.error('失败:', error)
        return { action: 'retry' } // 或 'close' 或 'custom'
      },
      onPaymentCancel: () => {
        console.log('用户已取消')
      },
    })
  }

  return (
    <button onClick={handleClick} disabled={isLoading}>
      {isLoading ? '处理中...' : '订阅'}
    </button>
  )
}

Using useSubscribe Hook (with state management)

使用useSubscribe Hook(带状态管理)

tsx
import { useSubscribe } from 'recur-tw'

function SubscribeButton({ productId }: { productId: string }) {
  const { subscribe, isLoading, error, subscription } = useSubscribe()

  const handleClick = () => {
    subscribe({
      productId,
      onPaymentComplete: (sub) => {
        // Subscription created successfully
        router.push('/dashboard')
      },
    })
  }

  if (subscription) {
    return <p>Subscribed! ID: {subscription.id}</p>
  }

  return (
    <>
      <button onClick={handleClick} disabled={isLoading}>
        Subscribe
      </button>
      {error && <p className="error">{error.message}</p>}
    </>
  )
}
tsx
import { useSubscribe } from 'recur-tw'

function SubscribeButton({ productId }: { productId: string }) {
  const { subscribe, isLoading, error, subscription } = useSubscribe()

  const handleClick = () => {
    subscribe({
      productId,
      onPaymentComplete: (sub) => {
        // 订阅创建成功
        router.push('/dashboard')
      },
    })
  }

  if (subscription) {
    return <p>已订阅!ID: {subscription.id}</p>
  }

  return (
    <>
      <button onClick={handleClick} disabled={isLoading}>
        订阅
      </button>
      {error && <p className="error">{error.message}</p>}
    </>
  )
}

Embedded Mode Setup

嵌入式模式设置

For embedded mode, you need a container element:
tsx
// In RecurProvider config
<RecurProvider
  config={{
    publishableKey: process.env.NEXT_PUBLIC_RECUR_PUBLISHABLE_KEY,
    checkoutMode: 'embedded',
    containerElementId: 'recur-checkout-container',
  }}
>
  {children}
</RecurProvider>

// In your checkout page
function CheckoutPage() {
  return (
    <div>
      <h1>Complete Your Purchase</h1>
      {/* Recur will render the payment form here */}
      <div id="recur-checkout-container" />
    </div>
  )
}
对于嵌入式模式,你需要一个容器元素:
tsx
// 在RecurProvider配置中
<RecurProvider
  config={{
    publishableKey: process.env.NEXT_PUBLIC_RECUR_PUBLISHABLE_KEY,
    checkoutMode: 'embedded',
    containerElementId: 'recur-checkout-container',
  }}
>
  {children}
</RecurProvider>

// 在你的结账页面中
function CheckoutPage() {
  return (
    <div>
      <h1>完成你的购买</h1>
      {/* Recur会在此处渲染付款表单 */}
      <div id="recur-checkout-container" />
    </div>
  )
}

Handling 3D Verification

处理3D验证

Recur handles 3D Secure automatically. For mobile apps or specific flows:
tsx
await checkout({
  productId,
  // These URLs are used when 3D verification requires redirect
  successUrl: 'https://yourapp.com/checkout/success',
  cancelUrl: 'https://yourapp.com/checkout/cancel',
})
Recur会自动处理3D安全验证。对于移动应用或特定流程:
tsx
await checkout({
  productId,
  // 当3D验证需要重定向时会使用这些URL
  successUrl: 'https://yourapp.com/checkout/success',
  cancelUrl: 'https://yourapp.com/checkout/cancel',
})

Product Types

产品类型

Recur supports different product types:
tsx
// Subscription (recurring)
checkout({ productId: 'prod_subscription_xxx' })

// One-time purchase
checkout({ productId: 'prod_onetime_xxx' })

// Credits (prepaid wallet)
checkout({ productId: 'prod_credits_xxx' })

// Donation (variable amount)
checkout({ productId: 'prod_donation_xxx' })
Recur支持多种产品类型:
tsx
// 订阅(定期扣费)
checkout({ productId: 'prod_subscription_xxx' })

// 一次性购买
checkout({ productId: 'prod_onetime_xxx' })

// 点数(预付费钱包)
checkout({ productId: 'prod_credits_xxx' })

// 捐赠(金额可变)
checkout({ productId: 'prod_donation_xxx' })

Listing Products

产品列表展示

tsx
import { useProducts } from 'recur-tw'

function PricingPage() {
  const { products, isLoading } = useProducts({
    type: 'SUBSCRIPTION', // Filter by type
  })

  if (isLoading) return <div>Loading...</div>

  return (
    <div className="pricing-grid">
      {products.map(product => (
        <PricingCard key={product.id} product={product} />
      ))}
    </div>
  )
}
tsx
import { useProducts } from 'recur-tw'

function PricingPage() {
  const { products, isLoading } = useProducts({
    type: 'SUBSCRIPTION', // 按类型筛选
  })

  if (isLoading) return <div>加载中...</div>

  return (
    <div className="pricing-grid">
      {products.map(product => (
        <PricingCard key={product.id} product={product} />
      ))}
    </div>
  )
}

Payment Failed Handling

付款失败处理

tsx
onPaymentFailed: (error) => {
  // error.code tells you what went wrong
  switch (error.code) {
    case 'CARD_DECLINED':
      return { action: 'retry' }
    case 'INSUFFICIENT_FUNDS':
      return {
        action: 'custom',
        customTitle: '餘額不足',
        customMessage: '請使用其他付款方式',
      }
    default:
      return { action: 'close' }
  }
}
tsx
onPaymentFailed: (error) => {
  // error.code会告诉你失败原因
  switch (error.code) {
    case 'CARD_DECLINED':
      return { action: 'retry' }
    case 'INSUFFICIENT_FUNDS':
      return {
        action: 'custom',
        customTitle: '餘額不足',
        customMessage: '請使用其他付款方式',
      }
    default:
      return { action: 'close' }
  }
}

Server-Side Checkout (API)

服务端结账(API方式)

For server-rendered apps or custom flows:
typescript
// Create checkout session
const response = await fetch('https://api.recur.tw/v1/checkouts', {
  method: 'POST',
  headers: {
    'X-Recur-Secret-Key': process.env.RECUR_SECRET_KEY,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    productId: 'prod_xxx',
    customerEmail: 'user@example.com',
    successUrl: 'https://yourapp.com/success',
    cancelUrl: 'https://yourapp.com/cancel',
  }),
})

const { checkoutUrl } = await response.json()
// Redirect user to checkoutUrl
适用于服务端渲染应用或自定义流程:
typescript
// 创建结账会话
const response = await fetch('https://api.recur.tw/v1/checkouts', {
  method: 'POST',
  headers: {
    'X-Recur-Secret-Key': process.env.RECUR_SECRET_KEY,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    productId: 'prod_xxx',
    customerEmail: 'user@example.com',
    successUrl: 'https://yourapp.com/success',
    cancelUrl: 'https://yourapp.com/cancel',
  }),
})

const { checkoutUrl } = await response.json()
// 重定向用户到checkoutUrl

Checkout Result Structure

结账结果结构

typescript
interface CheckoutResult {
  id: string              // Subscription or Order ID
  status: string          // 'ACTIVE', 'TRIALING', 'PENDING'
  productId: string
  amount: number          // In cents (e.g., 29900 = NT$299)
  billingPeriod?: string  // 'MONTHLY', 'YEARLY' for subscriptions
  currentPeriodEnd?: string  // ISO date
  trialEndsAt?: string    // ISO date if trial
}
typescript
interface CheckoutResult {
  id: string              // 订阅或订单ID
  status: string          // 'ACTIVE'、'TRIALING'、'PENDING'
  productId: string
  amount: number          // 单位为分(例如:29900 = 新台币299元)
  billingPeriod?: string  // 订阅的计费周期:'MONTHLY'(月度)、'YEARLY'(年度)
  currentPeriodEnd?: string  // ISO格式日期
  trialEndsAt?: string    // 试用结束日期(ISO格式,若有试用)
}

Best Practices

最佳实践

  1. Always handle all callbacks - onPaymentComplete, onPaymentFailed, onPaymentCancel
  2. Show loading states - Use isLoading to disable buttons during checkout
  3. Pre-fill customer info - Reduces friction if you already have user data
  4. Use externalCustomerId - Links Recur customers to your user system
  5. Test in sandbox first - Use
    pk_test_
    keys during development
  1. 务必处理所有回调函数 - onPaymentComplete、onPaymentFailed、onPaymentCancel
  2. 显示加载状态 - 使用isLoading在结账过程中禁用按钮
  3. 预填充客户信息 - 若已拥有用户数据,可减少操作步骤
  4. 使用externalCustomerId - 将Recur客户与你的用户系统关联
  5. 先在沙箱环境测试 - 开发阶段使用
    pk_test_
    开头的密钥

Related Skills

相关技能

  • /recur-quickstart
    - Initial SDK setup
  • /recur-webhooks
    - Receive payment notifications
  • /recur-entitlements
    - Check subscription access
  • /recur-quickstart
    - 初始SDK设置
  • /recur-webhooks
    - 接收付款通知
  • /recur-entitlements
    - 检查订阅权限