productId,
模板负责 checkout、webhook、订单入库、积分发放、license 签发和账单门户。
最快路径
大多数项目只需要改三个地方:- 在 Stripe 创建 Products + Prices。
- 把 price ID 填进
.env.local。 - 在
src/config/site.ts调整套餐和积分包。
/checkout,自动进入托管收银台。
配置层次
siteConfig.billing 是新手最先看的配置:
siteConfig.demoPlans 定义示例 SaaS 的 4 档套餐。
siteConfig.payment 是高级 Provider 层:默认启用 Stripe;需要备用通道时
再打开 Creem 或 NOWPayments。
Provider 门面
Provider 代码仍然在同一个契约后面:NormalizedEvent。src/payment/handlers/core.ts 里的共享 handler 负责写
payment、发积分、签 license、记录 affiliate commission。
幂等
Provider 重试 webhook 是常态。payment 表对 invoiceId 和 sessionId
都有唯一索引;handler 用 conflict protection 插入订单,再按 paymentId
幂等发放权益。
所以同一个 webhook 重放,不应该产生重复订单、重复积分、重复 license 或重复佣金。
Provider 选择
| Provider | 适合 | 备注 |
|---|---|---|
| Stripe | 默认 SaaS 收款 | 订阅、一次性、积分包、账单门户。 |
| Creem | 备用卡支付/本地通道 | 当 Stripe 不适合你的市场时再启用。 |
| NOWPayments | 加密货币一次性付款 | 不支持订阅,只适合 invoice 流程。 |
验证
- 在
.env.local配好 Stripe key 和 price ID。 - 运行
pnpm dev。 - 打开
/checkout?productId=pro。 - 完成测试支付。
- 确认
payment表出现记录,并且积分/license 正确发放。 - 重放 webhook;数据库不应该新增重复数据。