mcpField
S
subscriptions-best-practices

subscriptions-best-practices

github.com/getpaykit开发155 次安装29 周前更新
代码仓库

关于 subscriptions-best-practices

PayKit 订阅最佳实践技能,指导开发者使用统一的 subscribe() 处理订阅状态转换、结账和账单管理。内容涵盖客户端与服务端用法、状态读取、Stripe Webhook 和客户门户。

主要功能

  1. 01按当前状态处理订阅变更
  2. 02支持升级、降级、取消与恢复
  3. 03提供客户端和服务端调用示例
  4. 04说明读取订阅状态与结账跳转
  5. 05涵盖 Webhook 和客户门户

适用场景

  1. 01为用户开通或变更订阅计划
  2. 02将订阅状态同步到应用
  3. 03引导用户完成付款结账
  4. 04允许客户管理付款方式与发票

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/getpaykit/skills --skill subscriptions-best-practices

安装后在 Claude Code 中输入/subscriptions-best-practices确认已加载。

详细介绍

订阅

PayKit 使用一个 subscribe() 方法处理所有订阅变更。

subscribe()

const result = await paykit.subscribe({
  customerId: "user_123",
  planId: "pro",
  successUrl: "https://example.com/billing?success=true",
  cancelUrl: "https://example.com/billing",
})
参数
参数必填说明
customerId是你应用的用户 ID
planId是目标套餐 ID(类型安全)
successUrl是结账成功后重定向到的地址
cancelUrl否用户取消结账时重定向到的地址
forceCheckout否始终重定向到结账页面,即使已有支付方式
返回值
interface SubscribeResult {
  paymentUrl: string | null
  invoice?: {
    providerInvoiceId: string
    hostedUrl: string | null
    currency: string
    totalAmount: number
    status: string | null
  }
  requiredAction?: {
    type: string
    paymentIntentId?: string
    clientSecret?: string
  } | null
}

变更语义

subscribe() 会根据客户的当前状态确定正确的操作:

当前状态目标行为
无订阅免费套餐立即启用
无订阅付费套餐,无支付方式返回 Stripe Checkout 的 paymentUrl
无订阅付费套餐,已有支付方式直接创建订阅
使用免费套餐付费套餐通过结账或直接创建订阅
使用付费套餐更高价套餐(同一组)升级:立即切换
使用付费套餐更低价套餐(同一组)降级:安排在周期结束时生效
使用付费套餐免费套餐(同一组)取消:安排在周期结束时生效
待取消同一套餐恢复:清除取消操作
待降级当前套餐恢复:取消预定的变更
使用当前套餐同一套餐,无待处理变更无操作

服务端用法

// New subscription (may require checkout)
const { paymentUrl } = await paykit.subscribe({
  customerId: "user_123",
  planId: "pro",
  successUrl: "/billing?success=true",
})

if (paymentUrl) {
  // Redirect user to Stripe Checkout
  redirect(paymentUrl)
}

// Upgrade (immediate, no checkout needed)
await paykit.subscribe({
  customerId: "user_123",
  planId: "enterprise",
  successUrl: "/billing",
})

// Downgrade (scheduled for period end)
await paykit.subscribe({
  customerId: "user_123",
  planId: "free",
  successUrl: "/billing",
})

// Resume a pending cancellation
await paykit.subscribe({
  customerId: "user_123",
  planId: "pro", // same plan they're on
  successUrl: "/billing",
})

客户端用法

客户端 SDK 不包含 customerId,因为它会通过 identify 解析。

import { paykitClient } from "@/lib/paykit-client"

// Subscribe to a plan
await paykitClient.subscribe({
  planId: "pro",
  successUrl: `${window.location.origin}/billing?success=true`,
  cancelUrl: `${window.location.origin}/billing`,
})

// Open Stripe Customer Portal (manage payment methods, invoices)
await paykitClient.customerPortal({
  returnUrl: window.location.href,
})

读取订阅状态

const customer = await paykit.getCustomer({ id: "user_123" })

for (const sub of customer.subscriptions) {
  console.log(sub.planId)              // "pro"
  console.log(sub.status)             // "active", "canceled", etc.
  console.log(sub.cancelAtPeriodEnd)  // true if pending cancellation
  console.log(sub.currentPeriodEnd)   // when the current period ends
}

处理结账重定向

当 subscribe() 返回 paymentUrl 时,将用户重定向到该地址以完成支付:

服务端组件 / API 路由:

import { redirect } from "next/navigation"

const { paymentUrl } = await paykit.subscribe({
  customerId: userId,
  planId: "pro",
  successUrl: `${process.env.NEXT_PUBLIC_APP_URL}/billing?success=true`,
})

if (paymentUrl) {
  redirect(paymentUrl)
}

客户端:

const result = await paykitClient.subscribe({
  planId: "pro",
  successUrl: `${window.location.origin}/billing?success=true`,
})

返回 paymentUrl 时,客户端 SDK 会自动处理重定向。


Webhook

Stripe 会针对订阅生命周期事件发送 Webhook。PayKit 会通过路由处理程序自动处理这些事件。

确保在 Stripe 中将 Webhook 端点配置为 {your-domain}/paykit/api/webhook。

如需在应用中响应订阅变更:

export const paykit = createPayKit({
  // ...
  on: {
    "customer.updated": ({ payload }) => {
      // Sync subscription state to your app
      await updateUserRole(payload.customerId, payload.subscriptions)
    },
  },
})

Stripe 客户门户

让客户管理其账单(更新支付方式、查看发票、取消订阅):

// Server-side
const { url } = await paykit.customerPortal({
  customerId: "user_123",
  returnUrl: "https://example.com/billing",
})

// Client-side
await paykitClient.customerPortal({
  returnUrl: window.location.href,
})

评论

支持 Markdown