Stripe 支付全指南:一次性付款与订阅的正确建模(Next.js 全栈实践)
目录
在 SaaS、内容订阅和工具类产品里,Stripe 几乎是事实标准。但它的对象多、概念多,很多团队在 MVP 阶段就踩坑:选错集成方式、订阅建模混乱、Webhook 处理不稳,最后被迫重构。
本文从工程视角出发,给出一套可扩展的 Stripe 支付落地方案,覆盖一次性支付与订阅的核心差异、关键对象与数据库结构。
一、先厘清两个层次:集成方式 vs 业务模式
1. 集成方式(Integration)
你如何把 Stripe 接进网站:
| 集成方式 | 说明 | 推荐度 |
|---|---|---|
| Checkout | Stripe 托管收银台(跳转) | ⭐⭐⭐⭐⭐ |
| Payment Element / Elements | 自定义支付页面(嵌入) | ⭐⭐⭐ |
| Payment Links | 无代码支付链接 | ⭐⭐ |
| Invoicing | 发票/对公 | ⭐⭐ |
结论: 大多数 MVP 或早期 SaaS 优先选择 Checkout,简单、稳定、合规成本低。
2. 业务模式(Billing Model)
用户怎么付钱:
| 模式 | Stripe 对象 |
|---|---|
| 一次性支付 | PaymentIntent |
| 按月订阅 | Subscription + Invoice |
| 按年订阅 | Subscription + Invoice |
按月和按年本质是同一套系统,只是 Price 的 recurring.interval 不同。
二、一次性支付:模型简单,但要保证可追溯
适用场景:购买单次服务、点数包、一次性交付。
推荐流程(Checkout):
- 后端创建 Checkout Session(
mode=payment) - 前端跳转
session.url - 支付成功后 Webhook 确认
- 业务订单状态更新
关键对象:
Checkout Session(cs_...)PaymentIntent(pi_...)Charge(ch_...)
要点:不要用前端回调当作支付成功的唯一依据,Webhook 才是事实来源。
三、订阅支付:价格与订阅分离建模
1. Stripe 正确建模方式
- Product:产品,例如 Pro Plan
- Price:价格,月付/年付是两个 Price
- Subscription:用户订阅记录
示例:
pro_monthly→recurring.interval = monthpro_yearly→recurring.interval = year
2. 订阅流程(Checkout)
- 创建 Checkout Session(
mode=subscription) - 传入
line_items.price - 用户完成支付
- Stripe 创建 Subscription
- 每期续费生成 Invoice
- Webhook 驱动订阅状态
四、一次性 vs 订阅:核心差异一览
| 维度 | 一次性支付 | 订阅(月/年) |
|---|---|---|
| Checkout mode | payment | subscription |
| 核心对象 | PaymentIntent | Subscription + Invoice |
| 是否自动续费 | 否 | 是 |
| 是否需要订阅表 | 否 | 是 |
| Webhook 复杂度 | 低 | 中 |
五、关键 Key 与环境变量
1. Secret Key(后端)
STRIPE_SECRET_KEY=sk_...用途:创建 Session / PaymentIntent / Subscription、退款、查询、处理 Webhook。
2. Publishable Key(前端)
NEXT_PUBLIC_STRIPE_PUBLIC_KEY=pk_...仅在使用 Payment Element 等前端支付组件时需要。若只使用 Checkout 跳转,通常可以不使用。
3. Webhook Secret(生产必备)
STRIPE_WEBHOOK_SECRET=whsec_...用于验证 Webhook 签名,防止伪造请求。
六、最小但可持续的数据库设计
推荐 4 张表,避免早期重构:
| 表名 | 作用 |
|---|---|
| billing_orders | 业务订单(统一入口) |
| billing_payments | 支付流水(对账/退款) |
| billing_subscriptions | 订阅状态(激活/取消/到期) |
| stripe_events | Webhook 幂等与审计 |
为什么要分表?
- 订单、支付、订阅是三个维度
- 订阅会周期性扣款
- Webhook 可能重复投递
七、Webhook 是支付系统的事实来源
前端成功页 ≠ 支付成功。必须以 Stripe Webhook 事件为准:
一次性支付关注:
checkout.session.completedpayment_intent.succeeded
订阅关注:
customer.subscription.createdcustomer.subscription.updatedinvoice.paidinvoice.payment_failed
建议做法:
- 对
stripe_events表做幂等去重 - 使用
event.id保证一次处理 - 处理失败要支持重试
八、Next.js 实践建议(简要)
- API Route 创建 Checkout Session
- Server Actions/Route Handler 接收 Webhook
- 中间层(如
billingservice)屏蔽 Stripe 细节 - 状态机 驱动订单/订阅状态迁移
九、落地清单(检查你的实现是否完整)
- 是否明确区分一次性支付与订阅
- 是否用 Checkout 做 MVP
- 是否记录订单、支付、订阅三类实体
- 是否由 Webhook 作为最终支付事实
- 是否做了幂等和失败重试
如果你的业务后续需要支持试用、优惠券、多币种或企业对公开票,建议在现有模型上扩展,而不是推翻重写。
WenHaoFree