Stripe
Stripe 插件将 Stripe 的支付和订阅功能与 Better Auth 集成。由于支付和身份验证通常密切相关,该插件简化了 Stripe 在应用中的集成,处理客户创建、订阅管理和 webhook 处理。
【The Stripe plugin integrates Stripe's payment and subscription functionality with Better Auth. Since payment and authentication are often tightly coupled, this plugin simplifies the integration of Stripe into your application, handling customer creation, subscription management, and webhook processing.】
特性
【Features】
- 用户注册时自动创建 Stripe 客户
- 管理订阅计划和定价
- 处理订阅生命周期事件(创建、更新、取消)
- 通过签名验证安全处理 Stripe 网关回调
- 将订阅数据公开给你的应用
- 支持试用期和订阅升级
- 自动试用滥用防护 - 用户每个账户在所有套餐中只能获得一次试用
- 灵活的参考系统,用于将订阅与用户或组织关联
- 团队订阅支持及席位管理
安装
【Installation】
安装插件
首先,安装插件:
npm install @better-auth/stripeIf you're using a separate client and server setup, make sure to install the plugin in both parts of your project.
Add the plugin to your auth config
import { betterAuth } from "better-auth"
import { stripe } from "@better-auth/stripe"
import Stripe from "stripe"
const stripeClient = new Stripe(process.env.STRIPE_SECRET_KEY!, {
apiVersion: "2025-11-17.clover", // Latest API version as of Stripe SDK v20.0.0
})
export const auth = betterAuth({
// ... your existing config
plugins: [
stripe({
stripeClient,
stripeWebhookSecret: process.env.STRIPE_WEBHOOK_SECRET!,
createCustomerOnSignUp: true,
})
]
})从 Stripe v18 升级? 版本 19 使用异步 webhook 签名验证(constructEventAsync),由插件内部处理。你无需更改任何代码!
添加客户端插件
import { createAuthClient } from "better-auth/client"
import { stripeClient } from "@better-auth/stripe/client"
export const authClient = createAuthClient({
// ... your existing config
plugins: [
stripeClient({
subscription: true //if you want to enable subscription management
})
]
})Migrate the database
运行迁移或生成架构以将必要的表添加到数据库中。
npx @better-auth/cli migratenpx @better-auth/cli generateSee the Schema section to add the tables manually.
Set up Stripe webhooks
在你的 Stripe 控制面板中创建一个指向以下地址的 webhook 端点:
https://your-domain.com/api/auth/stripe/webhook/api/auth is the default path for the auth server.
确保至少选择以下事件:
checkout.session.completedcustomer.subscription.createdcustomer.subscription.updatedcustomer.subscription.deleted
保存 Stripe 提供的 webhook 签名密钥,并将其添加到你的环境变量中,命名为 STRIPE_WEBHOOK_SECRET。
用法
【Usage】
客户管理
【Customer Management】
你可以仅使用此插件进行客户管理,而无需启用订阅。如果你只是想将 Stripe 客户与你的用户关联起来,这会很有用。
【You can use this plugin solely for customer management without enabling subscriptions. This is useful if you just want to link Stripe customers to your users.】
默认情况下,当用户注册时,如果你设置了 createCustomerOnSignUp: true,会自动创建一个 Stripe 客户。这个客户会与你数据库中的用户关联。
你可以自定义客户的创建过程:
【By default, when a user signs up, a Stripe customer is automatically created if you set createCustomerOnSignUp: true. This customer is linked to the user in your database.
You can customize the customer creation process:】
stripe({
// ... other options
createCustomerOnSignUp: true,
onCustomerCreate: async ({ stripeCustomer, user }, ctx) => {
// Do something with the newly created customer
console.log(`Customer ${stripeCustomer.id} created for user ${user.id}`);
},
getCustomerCreateParams: async (user, ctx) => {
// Customize the Stripe customer creation parameters
return {
metadata: {
referralSource: user.metadata?.referralSource
}
};
}
})订阅管理
【Subscription Management】
定义计划
【Defining Plans】
你可以静态或动态地定义你的订阅计划:
【You can define your subscription plans either statically or dynamically:】
// Static plans
subscription: {
enabled: true,
plans: [
{
name: "basic", // the name of the plan, it'll be automatically lower cased when stored in the database
priceId: "price_1234567890", // the price ID from stripe
annualDiscountPriceId: "price_1234567890", // (optional) the price ID for annual billing with a discount
limits: {
projects: 5,
storage: 10
}
},
{
name: "pro",
priceId: "price_0987654321",
limits: {
projects: 20,
storage: 50
},
freeTrial: {
days: 14,
}
}
]
}
// Dynamic plans (fetched from database or API)
subscription: {
enabled: true,
plans: async () => {
const plans = await db.query("SELECT * FROM plans");
return plans.map(plan => ({
name: plan.name,
priceId: plan.stripe_price_id,
limits: JSON.parse(plan.limits)
}));
}
}请参见计划配置了解更多信息。
【see plan configuration for more.】
创建订阅
【Creating a Subscription】
要创建订阅,请使用 subscription.upgrade 方法:
【To create a subscription, use the subscription.upgrade method:】
const { data, error } = await authClient.subscription.upgrade({ plan: "pro", // required annual: true, referenceId: "123", subscriptionId: "sub_123", metadata, customerType, seats: 1, locale, successUrl, // required cancelUrl, // required returnUrl, disableRedirect: false, // required});| Prop | Description | Type |
|---|---|---|
plan | The name of the plan to upgrade to. | string |
annual? | Whether to upgrade to an annual plan. | boolean |
referenceId? | Reference id of the subscription. Defaults based on customerType. | string |
subscriptionId? | The id of the subscription to upgrade. | string |
metadata? | Additional metadata to store with the subscription. | Record<string, any> |
customerType? | The type of customer for billing. (Default: "user") | "user" | "organization" |
seats? | Number of seats to upgrade to (if applicable). | number |
locale? | The IETF language tag of the locale Checkout is displayed in. If not provided or set to auto, the browser's locale is used. | string |
successUrl | The URL to which Stripe should send customers when payment or setup is complete. | string |
cancelUrl | If set, checkout shows a back button and customers will be directed here if they cancel payment. | string |
returnUrl? | The URL to return to from the Billing Portal (used when upgrading existing subscriptions) | string |
disableRedirect | Disable redirect after successful subscription. | boolean |
简单示例:
await authClient.subscription.upgrade({
plan: "pro",
successUrl: "/dashboard",
cancelUrl: "/pricing",
annual: true, // Optional: upgrade to an annual plan
referenceId: "org_123", // Optional: defaults based on customerType
seats: 5, // Optional: for team plans
locale: "en" // Optional: display checkout in English
});这将创建一个结账会话,并将用户重定向到 Stripe 结账页面。
【This will create a Checkout Session and redirect the user to the Stripe Checkout page.】
如果用户已经有一个有效的订阅,你必须提供 subscriptionId 参数。否则,用户将会订阅(并支付)两个计划。
重要提示:
successUrl参数将会在内部被修改,以处理结账完成与网页回调处理之间的竞争条件。该插件会创建一个中间重定向,确保在跳转到你的成功页面之前订阅状态已被正确更新。
const { error } = await authClient.subscription.upgrade({
plan: "pro",
successUrl: "/dashboard",
cancelUrl: "/pricing",
});
if(error) {
alert(error.message);
}对于每个参考 ID(用户或组织),一次只能支持一个有效或试用订阅。该插件目前不支持同一参考 ID 的多个并发有效订阅。
更改计划
【Switching Plans】
要将订阅切换到不同的计划,请使用 subscription.upgrade 方法:
【To switch a subscription to a different plan, use the subscription.upgrade method:】
await authClient.subscription.upgrade({
plan: "pro",
successUrl: "/dashboard",
cancelUrl: "/pricing",
subscriptionId: "sub_123", // the Stripe subscription ID of the user's current plan
});This ensures that the user only pays for the new plan, and not both.
列出活跃订阅
【Listing Active Subscriptions】
获取用户的活跃订阅:
【To get the user's active subscriptions:】
const { data: subscriptions, error } = await authClient.subscription.list({ query: { referenceId: '123', customerType, },});// get the active subscriptionconst activeSubscription = subscriptions.find( sub => sub.status === "active" || sub.status === "trialing");// Check subscription limitsconst projectLimit = subscriptions?.limits?.projects || 0;| Prop | Description | Type |
|---|---|---|
referenceId? | Reference id of the subscription to list. | string |
customerType? | The type of customer for billing. (Default: "user") | "user" | "organization" |
请确保在插件配置中提供 authorizeReference 以授权参考 ID
【Make sure to provide authorizeReference in your plugin config to authorize the reference ID】
stripe({
// ... other options
subscription: {
// ... other subscription options
authorizeReference: async ({ user, session, referenceId, action }) => {
if(action === "list-subscription") {
const org = await db.member.findFirst({
where: {
organizationId: referenceId,
userId: user.id
}
});
return org?.role === "owner"
}
// Check if the user has permission to list subscriptions for this reference
return true;
}
}
})取消订阅
【Canceling a Subscription】
要取消订阅:
【To cancel a subscription:】
const { data, error } = await authClient.subscription.cancel({ referenceId: 'org_123', customerType, subscriptionId: 'sub_123', returnUrl: '/account', // required});| Prop | Description | Type |
|---|---|---|
referenceId? | Reference id of the subscription to cancel. Defaults based on customerType. | string |
customerType? | The type of customer for billing. (Default: "user") | "user" | "organization" |
subscriptionId? | The id of the subscription to cancel. | string |
returnUrl | URL to take customers to when they click on the billing portal's link to return to your website. | string |
这会将用户重定向到 Stripe 账单门户,在那里他们可以取消订阅。
【This will redirect the user to the Stripe Billing Portal where they can cancel their subscription.】
理解取消状态
Stripe 支持不同类型的取消,插件会跟踪所有这些类型:
【Stripe supports different types of cancellation, and the plugin tracks all of them:】
| Field | Description |
|---|---|
cancelAtPeriodEnd | Whether this subscription will (if status=active) or did (if status=canceled) cancel at the end of the current billing period. |
cancelAt | If the subscription is scheduled to be canceled, this is the time at which the cancellation will take effect. |
canceledAt | If the subscription has been canceled, this is the time when it was canceled. |
endedAt | If the subscription has ended, the date the subscription ended. |
status | Changes to "canceled" only after the subscription has actually ended. |
恢复已取消的订阅
【Restoring a Canceled Subscription】
注意: 这只适用于仍在激活但计划取消的订阅。它无法恢复已经结束的订阅('status: “canceled”,且设置为“endedAt”)。
如果用户在取消订阅后(但在订阅期结束前)改变主意,你可以恢复订阅:
【If a user changes their mind after canceling a subscription (but before the subscription period ends), you can restore the subscription:】
const { data, error } = await authClient.subscription.restore({ referenceId: '123', customerType, subscriptionId: 'sub_123',});| Prop | Description | Type |
|---|---|---|
referenceId? | Reference id of the subscription to restore. Defaults based on customerType. | string |
customerType? | The type of customer for billing. (Default: "user") | "user" | "organization" |
subscriptionId? | The id of the subscription to restore. | string |
这将重新激活之前计划取消的订阅。订阅将继续自动续订。
【This will reactivate a subscription that was previously scheduled to cancel. The subscription will continue to renew automatically.】
当订阅恢复时:
cancelAtPeriodEnd设置为falsecancelAt清空为nullcanceledAt清空为null
创建计费门户会话
【Creating Billing Portal Sessions】
要创建一个 Stripe 计费门户会话,让客户可以管理他们的订阅、更新支付方式并查看账单历史记录:
【To create a Stripe billing portal session where customers can manage their subscriptions, update payment methods, and view billing history:】
const { data, error } = await authClient.subscription.billingPortal({ locale, referenceId: "123", customerType, returnUrl, disableRedirect: false,});| Prop | Description | Type |
|---|---|---|
locale? | The IETF language tag of the locale Customer Portal is displayed in. If not provided or set to auto, the browser's locale is used. | string |
referenceId? | Reference id of the subscription. | string |
customerType? | The type of customer for billing. (Default: "user") | "user" | "organization" |
returnUrl? | Return URL to redirect back after exiting the billing portal. | string |
disableRedirect? | Disable the automatic redirect to the billing page. @default false | boolean |
For supported locales, see the IETF language tag documentation.
此端点会创建一个 Stripe 计费门户会话,并在响应中以 data.url 返回一个 URL。你可以将用户重定向到此 URL,以便他们管理自己的订阅、支付方式和账单历史记录。
【This endpoint creates a Stripe billing portal session and returns a URL in the response as data.url. You can redirect users to this URL to allow them to manage their subscription, payment methods, and billing history.】
参考系统
【Reference System】
默认情况下,订阅与用户 ID 关联。不过,你可以使用自定义参考 ID 将订阅与其他实体关联,例如组织:
【By default, subscriptions are associated with the user ID. However, you can use a custom reference ID to associate subscriptions with other entities, such as organizations:】
// Create a subscription for an organization
await authClient.subscription.upgrade({
plan: "pro",
referenceId: "org_123456",
successUrl: "/dashboard",
cancelUrl: "/pricing",
seats: 5 // Number of seats for team plans
});
// List subscriptions for an organization
const { data: subscriptions } = await authClient.subscription.list({
query: {
referenceId: "org_123456"
}
});带席位的团队订阅
【Team Subscriptions with Seats】
对于团队或组织计划,你可以指定座位数量:
【For team or organization plans, you can specify the number of seats:】
await authClient.subscription.upgrade({
plan: "team",
referenceId: "org_123456",
seats: 10, // 10 team members
successUrl: "/org/billing/success",
cancelUrl: "/org/billing"
});seats 参数作为订阅项目的数量传递给 Stripe。你可以在应用逻辑中使用此值来限制团队或组织中的成员数量。
【The seats parameter is passed to Stripe as the quantity for the subscription item. You can use this value in your application logic to limit the number of members in a team or organization.】
要授权参考 ID,请实现 authorizeReference 函数:
【To authorize reference IDs, implement the authorizeReference function:】
subscription: {
// ... other options
authorizeReference: async ({ user, session, referenceId, action }) => {
// Check if the user has permission to manage subscriptions for this reference
if (action === "upgrade-subscription" || action === "cancel-subscription" || action === "restore-subscription") {
const org = await db.member.findFirst({
where: {
organizationId: referenceId,
userId: user.id
}
});
return org?.role === "owner"
}
return true;
}
}Webhook 处理
【Webhook Handling】
该插件会自动处理常见的 Webhook 事件:
【The plugin automatically handles common webhook events:】
checkout.session.completed:在结账后更新订阅状态customer.subscription.created:在结账流程之外创建订阅时触发customer.subscription.updated:当订阅详情更改时更新订阅信息customer.subscription.deleted:标记订阅为已取消
你也可以处理自定义事件:
【You can also handle custom events:】
stripe({
// ... other options
onEvent: async (event) => {
// Handle any Stripe event
switch (event.type) {
case "invoice.paid":
// Handle paid invoice
break;
case "payment_intent.succeeded":
// Handle successful payment
break;
}
}
})订阅生命周期钩子
【Subscription Lifecycle Hooks】
你可以连接到各种订阅生命周期事件:
【You can hook into various subscription lifecycle events:】
subscription: {
// ... other options
onSubscriptionComplete: async ({ event, subscription, stripeSubscription, plan }) => {
// Called when a subscription is successfully created via checkout
await sendWelcomeEmail(subscription.referenceId, plan.name);
},
onSubscriptionCreated: async ({ event, subscription, stripeSubscription, plan }) => {
// Called when a subscription is created outside the checkout flow (e.g. Stripe dashboard)
await sendSubscriptionCreatedEmail(subscription.referenceId, plan.name);
},
onSubscriptionUpdate: async ({ event, subscription }) => {
// Called when a subscription is updated
console.log(`Subscription ${subscription.id} updated`);
},
onSubscriptionCancel: async ({ event, subscription, stripeSubscription, cancellationDetails }) => {
// Called when a subscription is canceled
await sendCancellationEmail(subscription.referenceId);
},
onSubscriptionDeleted: async ({ event, subscription, stripeSubscription }) => {
// Called when a subscription is deleted
console.log(`Subscription ${subscription.id} deleted`);
}
}试用期
【Trial Periods】
你可以为你的计划配置试用期:
【You can configure trial periods for your plans:】
{
name: "pro",
priceId: "price_0987654321",
freeTrial: {
days: 14,
onTrialStart: async (subscription) => {
// Called when a trial starts
await sendTrialStartEmail(subscription.referenceId);
},
onTrialEnd: async ({ subscription }, ctx) => {
// Called when a trial ends
await sendTrialEndEmail(subscription.referenceId);
},
onTrialExpired: async (subscription, ctx) => {
// Called when a trial expires without conversion
await sendTrialExpiredEmail(subscription.referenceId);
}
}
}架构
【Schema】
Stripe 插件会向你的数据库添加以下表格:
【The Stripe plugin adds the following tables to your database:】
用户
【User】
表名:user
【Table Name: user】
| Field Name | Type | Key | Description |
|---|---|---|---|
| stripeCustomerId | string | The Stripe customer ID |
组织
【Organization】
表名:organization (仅当 organization.enabled 为 true 时)
| Field Name | Type | Key | Description |
|---|---|---|---|
| stripeCustomerId | string | The Stripe customer ID for the organization |
订阅
【Subscription】
表名:subscription
【Table Name: subscription】
| Field Name | Type | Key | Description |
|---|---|---|---|
| id | string | Unique identifier for each subscription | |
| plan | string | - | The name of the subscription plan |
| referenceId | string | - | The ID this subscription is associated with (user ID by default). This should NOT be a unique field in your database, as it must allow users to resubscribe after a cancellation. |
| stripeCustomerId | string | The Stripe customer ID | |
| stripeSubscriptionId | string | The Stripe subscription ID | |
| status | string | - | The status of the subscription (active, canceled, etc.) |
| periodStart | Date | Start date of the current billing period | |
| periodEnd | Date | End date of the current billing period | |
| cancelAtPeriodEnd | boolean | Whether the subscription will be canceled at the end of the period | |
| cancelAt | Date | If the subscription is scheduled to be canceled, this is the time at which the cancellation will take effect | |
| canceledAt | Date | If the subscription has been canceled, this is the time when the cancellation was requested. Note: If the subscription was canceled with cancelAtPeriodEnd, this reflects the cancellation request time, not when the subscription actually ends | |
| endedAt | Date | If the subscription has ended, this is the date the subscription ended | |
| seats | number | Number of seats for team plans | |
| trialStart | Date | Start date of the trial period | |
| trialEnd | Date | End date of the trial period |
自定义模式
【Customizing the Schema】
要更改模式表名或字段,你可以向 Stripe 插件传递 schema 选项:
【To change the schema table names or fields, you can pass a schema option to the Stripe plugin:】
stripe({
// ... other options
schema: {
subscription: {
modelName: "stripeSubscriptions", // map the subscription table to stripeSubscriptions
fields: {
plan: "planName" // map the plan field to planName
}
}
}
})选项
【Options】
| Option | Type | Description |
|---|---|---|
stripeClient | Stripe | The Stripe client instance. Required. |
stripeWebhookSecret | string | The webhook signing secret from Stripe. Required. |
createCustomerOnSignUp | boolean | Whether to automatically create a Stripe customer when a user signs up. Default: false. |
onCustomerCreate | function | Callback called after a customer is created. Receives { stripeCustomer, user } and context. |
getCustomerCreateParams | function | Customize Stripe customer creation parameters. Receives user and context. |
onEvent | function | Callback called for any Stripe webhook event. Receives Stripe.Event. |
subscription | object | Subscription configuration. See below. |
organization | object | Enable Organization Customer support. See below. |
schema | object | Customize the database schema for the Stripe plugin. |
订阅选项
【Subscription Options】
| Option | Type | Description |
|---|---|---|
enabled | boolean | Whether to enable subscription functionality. Required. |
plans | StripePlan[] or function | An array of subscription plans or an async function that returns plans. Required if enabled. |
requireEmailVerification | boolean | Whether to require email verification before allowing subscription upgrades. Default: false. |
authorizeReference | function | Authorize reference IDs. Receives { user, session, referenceId, action } and context. |
getCheckoutSessionParams | function | Customize Stripe Checkout session parameters. Receives { user, session, plan, subscription }, request, and context. |
onSubscriptionComplete | function | Called when a subscription is created via checkout. Receives { event, stripeSubscription, subscription, plan } and context. |
onSubscriptionCreated | function | Called when a subscription is created outside checkout. Receives { event, stripeSubscription, subscription, plan }. |
onSubscriptionUpdate | function | Called when a subscription is updated. Receives { event, subscription }. |
onSubscriptionCancel | function | Called when a subscription is canceled. Receives { event, subscription, stripeSubscription, cancellationDetails }. |
onSubscriptionDeleted | function | Called when a subscription is deleted. Receives { event, stripeSubscription, subscription }. |
计划配置
【Plan Configuration】
| 选项 | 类型 | 描述 |
|---|---|---|
name | string | 计划的名称。必填。 |
priceId | string | Stripe 价格 ID。必填,除非使用 lookupKey。 |
lookupKey | string | Stripe 价格查找键。作为 priceId 的替代方案。 |
annualDiscountPriceId | string | 年费结算的价格 ID。 |
annualDiscountLookupKey | string | 年费结算的 Stripe 价格查找键。 |
limits | object | 计划的限制(例如 { projects: 10, storage: 5 })。 |
group | string | 用于对计划进行分类的组名。 |
freeTrial | object | 试用配置。请参见下面。 |
免费试用配置
【Free Trial Configuration】
| 选项 | 类型 | 描述 |
|---|---|---|
days | number | 试用天数。必填。 |
onTrialStart | function | 当试用开始时调用。接收 subscription。 |
onTrialEnd | function | 当试用结束时调用。接收 { subscription } 和上下文。 |
onTrialExpired | function | 当试用期到期未转化时调用。接收 subscription 和上下文。 |
组织选项
【Organization Options】
| 选项 | 类型 | 描述 |
|---|---|---|
enabled | boolean | 启用组织客户支持。必填。 |
getCustomerCreateParams | function | 自定义组织的 Stripe 客户创建参数。接收 organization 和上下文。 |
onCustomerCreate | function | 在创建组织客户后调用。接收 { stripeCustomer, organization } 和上下文。 |
高级用法
【Advanced Usage】
与组织一起使用
【Using with Organizations】
Stripe 插件与 组织插件 集成,使组织能够作为 Stripe 客户。不是以个人用户为单位,而是以组织作为订阅的计费实体。这对于 B2B 服务非常有用,因为计费与组织相关,而不是单个用户。
【The Stripe plugin integrates with the organization plugin to enable organizations as Stripe Customers. Instead of individual users, organizations become the billing entity for subscriptions. This is useful for B2B services where billing is tied to the organization rather than individual user.】
当启用组织客户时:
- 当一个组织首次订阅时,会自动创建一个 Stripe 客户账户
- 组织名称更改会同步到 Stripe 客户
- 有有效订阅的组织无法被删除
启用组织客户
【Enabling Organization Customer】
要启用组织客户,请将 organization.enabled 设置为 true 并确保已安装组织插件:
【To enable Organization Customer, set organization.enabled to true and ensure the organization plugin is installed:】
plugins: [
organization(),
stripe({
// ... other options
subscription: {
enabled: true,
plans: [...],
},
organization: {
enabled: true
}
})
]创建组织订阅
【Creating Organization Subscriptions】
即使启用了组织客户,用户订阅仍然可用并且是默认选项。要将组织用作计费实体,请传递 customerType: "organization":
【Even with Organization Customer enabled, user subscriptions remain available and are the default. To use the organization as the billing entity, pass customerType: "organization":】
await authClient.subscription.upgrade({
plan: "team",
referenceId: activeOrg.id,
customerType: "organization",
seats: 10,
successUrl: "/org/billing/success",
cancelUrl: "/org/billing"
});授权
【Authorization】
请确保实现 authorizeReference 函数,以验证用户是否有权限管理该组织的订阅:
【Make sure to implement the authorizeReference function to verify that the user has permission to manage subscriptions for the organization:】
subscription: {
// ... other subscription options
authorizeReference: async ({ user, referenceId, action }) => {
const member = await db.members.findFirst({
where: {
userId: user.id,
organizationId: referenceId
}
});
return member?.role === "owner" || member?.role === "admin";
}
}组织账单电子邮件
【Organization Billing Email】
与用户不同,组织的计费电子邮件不会自动同步,因为组织本身没有唯一的电子邮件。组织通常使用一个独立的计费电子邮件,与用户账户分开。
要在结账后更改计费电子邮件,可以通过 Stripe 仪表板更新,或使用 stripeClient 实现自定义逻辑:
【Unlike users, organization billing email is not automatically synced because organization itself doesn't have a unique email. Organizations often use a dedicated billing email separate from user accounts.
To change the billing email after checkout, update it through the Stripe Dashboard or implement custom logic using stripeClient:】
await stripeClient.customers.update(organization.stripeCustomerId, {
email: "billing@company.com"
});自定义结账会话参数
【Custom Checkout Session Parameters】
你可以使用附加参数自定义 Stripe 结账会话:
【You can customize the Stripe Checkout session with additional parameters:】
getCheckoutSessionParams: async ({ user, session, plan, subscription }, ctx) => {
return {
params: {
allow_promotion_codes: true,
tax_id_collection: {
enabled: true
},
billing_address_collection: "required",
custom_text: {
submit: {
message: "We'll start your subscription right away"
}
},
metadata: {
planType: "business",
referralCode: user.metadata?.referralCode
}
},
options: {
idempotencyKey: `sub_${user.id}_${plan.name}_${Date.now()}`
}
};
}税收
【Tax Collection】
要从客户那里收集税号,请将 tax_id_collection 设置为 true:
【To collect tax IDs from the customer, set tax_id_collection to true:】
subscription: {
// ... other options
getCheckoutSessionParams: async ({ user, session, plan, subscription }, ctx) => {
return {
params: {
tax_id_collection: {
enabled: true
}
}
};
}
}自动税务计算
【Automatic Tax Calculation】
要使用客户的所在地启用自动税款计算,请将 automatic_tax 设置为 true。启用此参数会使结账页面收集进行税款计算所需的任何账单地址信息。要使其生效,你首先需要在 Stripe 仪表板中完成税务注册的设置和配置。
【To enable automatic tax calculation using the customer's location, set automatic_tax to true. Enabling this parameter causes Checkout to collect any billing address information necessary for tax calculation. You need to have tax registration setup and configured in the Stripe dashboard first for this to work.】
subscription: {
// ... other options
getCheckoutSessionParams: async ({ user, session, plan, subscription }, ctx) => {
return {
params: {
automatic_tax: {
enabled: true
}
}
};
}
}试用期管理
【Trial Period Management】
Stripe 插件会自动防止用户获得多个免费试用。一旦用户使用过一次试用期(无论是哪个计划),他们将无法在任何计划中再享受额外的试用期。
【The Stripe plugin automatically prevents users from getting multiple free trials. Once a user has used a trial period (regardless of which plan), they will not be eligible for additional trials on any plan.】
工作原理:
- 系统会跟踪每个用户在所有计划中的试用使用情况
- 当用户订阅带有试用的计划时,系统会检查他们的订阅历史
- 如果用户曾经有过试用(由
trialStart/trialEnd字段或trialing状态指示),将不会提供新的试用 - 这可以防止用户滥用,通过取消订阅再重新订阅来获取多次免费试用
示例场景:
- 用户订阅“入门”计划并享有 7 天试用期
- 用户在试用期结束后取消订阅
- 用户尝试订阅“高级”计划 - 将不提供试用期
- 用户将立即被收取高级计划费用
此行为是自动的,无需额外配置。试用资格在订阅创建时确定,无法通过配置进行覆盖。
【This behavior is automatic and requires no additional configuration. The trial eligibility is determined at the time of subscription creation and cannot be overridden through configuration.】
故障排除
【Troubleshooting】
Webhook 问题
【Webhook Issues】
如果网络钩子处理不正确:
【If webhooks aren't being processed correctly:】
- 请检查你的 webhook URL 是否在 Stripe 仪表板中正确配置
- 验证 webhook 签名密钥是否正确
- 确保你已在 Stripe 仪表板中选择了所有必要的事件
- 检查服务器日志中在处理 Webhook 期间的任何错误
订阅状态问题
【Subscription Status Issues】
如果订阅状态未正确更新:
【If subscription statuses aren't updating correctly:】
- 确保 webhook 事件已被接收并处理
- 检查
stripeCustomerId和stripeSubscriptionId字段是否已正确填充 - 核实你的应用与 Stripe 之间的参考 ID 是否匹配
本地测试 Webhooks
【Testing Webhooks Locally】
对于本地开发,你可以使用 Stripe CLI 将 Webhook 转发到本地环境:
【For local development, you can use the Stripe CLI to forward webhooks to your local environment:】
stripe listen --forward-to localhost:3000/api/auth/stripe/webhook这将为你提供一个可以在本地环境中使用的 webhook 签名密钥。
【This will provide you with a webhook signing secret that you can use in your local environment.】