name: stripe-handler description: 处理Stripe支付、自定义结账和非标准计划/积分之外的Webhook履约 依赖: [“plans-handler”, “credits-handler”, “inngest-handler”]
Stripe处理器
当您需要使用Stripe实现支付逻辑,特别是用于非标准SaaS订阅计划或非标准积分包(这两者分别由plans-handler和credits-handler处理)的场景时,请使用此技能。
何时使用
- 实现“购买一次性产品”流程(例如,课程、数字商品)。
- 创建自定义“服务”支付。
- 处理自定义元数据的
checkout.session.completed事件。 - 为非标准事件自定义
src/app/api/webhooks/stripe/route.ts。 - 将繁重的Webhook处理卸载到后台任务(通过Inngest)。
流程
1. 识别支付类型
在编写代码之前,确定支付的性质:
- 这是订阅计划吗? -> 使用
plans-handler。 - 这是积分包吗? -> 使用
credits-handler。 - 这是自定义一次性或周期性产品吗? -> 继续使用此技能。
2. 创建结账会话(API/操作)
您需要一个服务器端端点来创建Stripe结账会话。
- 文件:创建新路由(例如
src/app/api/app/orders/create/route.ts)或服务器操作。 - 导入:
import stripe from "@/lib/stripe"; - 元数据:至关重要。始终向会话附加
metadata,以便在Webhook中识别购买类型。metadata: { type: "my_custom_feature", userId: user.id, customId: "..." } - URL:使用标准成功/错误页面,或在需要时使用自定义页面。
- 成功:
${process.env.NEXT_PUBLIC_APP_URL}/app/subscribe/success?session_id={CHECKOUT_SESSION_ID} - 错误:
${process.env.NEXT_PUBLIC_APP_URL}/app/subscribe/error
- 成功:
3. 处理Webhook履约
所有Stripe事件都发送到src/app/api/webhooks/stripe/route.ts。
- 文件:
src/app/api/webhooks/stripe/route.ts - 定位:
onCheckoutSessionCompleted(针对一次性支付)或handleOutsidePlanManagementProductInvoicePaid(针对发票)。 - 实现:
- 从事件对象中提取
metadata。 - 检查
metadata.type是否与您的自定义功能匹配。 - 如果匹配:运行您的履约逻辑。
- 简单逻辑:直接更新数据库。
- 繁重逻辑:分发一个Inngest事件以在后台处理。
- 如果不匹配:让函数回退到标准计划/积分处理。
- 从事件对象中提取
4. 后台处理(Inngest)
生产环境推荐:如果您的履约逻辑涉及多个数据库调用、外部API或可能超时(Stripe期望在<3秒内得到响应):
- 使用
inngest-handler创建一个新函数。 - 在Webhook中,仅分发事件:
await inngest.send({ name: "app/payment.custom_succeeded", data: { sessionId: object.id, metadata } }); - 在
src/inngest/functions/...中处理实际逻辑。
5. 数据库更新
- 如果购买授予对资源的访问权限,请更新相应的模式(例如
orders、courses)。 - 确保履约是幂等的(优雅地处理重复的Webhook事件)。
6. 前端集成
- 使用简单的按钮或表单调用您的API/操作。
- 将用户重定向到返回的
url(Stripe结账页面)。
最佳实践
- 幂等性:Webhook可能会多次触发。确保您的逻辑检查订单是否已履约。
- 元数据:依赖元数据,而不仅仅是产品ID,以实现更清晰的逻辑分离。
- 超时:Stripe Webhook必须快速响应。对于任何耗时>2秒的操作,请使用Inngest。
- 测试:使用
stripe listen在本地测试Webhook。
参考
有关创建会话、处理Webhook和使用Inngest的代码片段,请参阅reference.md。