===

Stripe 是很多 SaaS 产品的默认支付方案。它能处理信用卡、订阅、发票、税务、收据、客户门户和 Webhook,适合从早期产品一路扩展到更复杂的商业化阶段。
但 Stripe 集成不能只理解成"接一个付款按钮"。真正可靠的 Stripe 集成,要处理产品价格、Checkout Session、Customer、Webhook、订单履约、订阅状态、支付失败和客户自助管理。
本文基于 Stripe 官方文档整理:
- Checkout quickstarts
- Checkout fulfillment
- Checkout Sessions API
- Using webhooks with subscriptions
- Customer portal
先决定你要卖什么
接 Stripe 前,先明确你的商业模型。
你是卖一次性商品,还是卖 SaaS 订阅?是一个价格,还是多个套餐?是按月付费,还是按年付费?是否有免费试用?是否需要团队席位?是否需要优惠码?
Stripe 里通常会有 Product 和 Price。Product 表示你卖的产品或套餐,Price 表示价格、币种、计费周期。
比如:
bash
Product:Pro Plan
Price:$19 / month
Price:$190 / year
不要把价格只写死在代码里。更稳妥的做法,是在 Stripe Dashboard 配好产品和价格,在你的系统里保存对应的 price_id。
优先使用 Stripe Checkout
早期 SaaS 最推荐使用 Stripe Checkout,而不是从零自定义支付表单。
Stripe Checkout 是 Stripe 托管的支付页。你在服务端创建 Checkout Session,前端把用户跳转到 Stripe 提供的 URL,用户在 Stripe 页面完成付款。
这样你可以少处理很多敏感支付细节,也能更快支持多种支付方式、税务、优惠码和订阅。
只有当你对支付页面有很强定制需求时,才考虑更复杂的嵌入式或自定义方案。
配置密钥和环境
Stripe 有测试环境和生产环境,也有不同类型的密钥。
常见环境变量:
ini
STRIPE_SECRET_KEY=sk_test_xxx
STRIPE_WEBHOOK_SECRET=whsec_xxx
STRIPE_PRICE_PRO_MONTHLY=price_xxx
STRIPE_PRICE_PRO_YEARLY=price_xxx
SECRET_KEY 只能放服务端。前端不能接触 secret key。
测试环境和生产环境的 key、price_id、webhook secret 都要分开。很多支付问题不是代码错了,而是测试 key、生产 price、错误 webhook secret 混用。
创建 Checkout Session
当用户点击"购买"或"升级"时,前端应该请求你的服务端。服务端根据用户身份、套餐和价格创建 Checkout Session。
Stripe 官方 API 文档说明,Checkout Session 代表一次用户付款或订阅的会话。创建后,你可以把用户重定向到 Session 的 URL。
核心参数通常包括:
mode:payment 或 subscription
line_items:购买的 price 和数量
success_url:支付完成后跳转地址
cancel_url:用户取消后跳转地址
customer 或 customer_email:关联客户
metadata:你的本地业务信息
对 SaaS 订阅,常用 mode=subscription;一次性付款用 mode=payment。
metadata 很重要
Stripe 是支付系统,你的系统才是业务系统。两者之间需要关联。
创建 Checkout Session 时,建议在 metadata 里放入本地业务信息,比如:
user_id
plan
team_id
order_id
这样 Webhook 回来时,你能知道这笔支付对应哪个用户、哪个团队、哪个订单。
但不要在 metadata 里放敏感信息或过大的数据。metadata 应该只放用于关联和排查的轻量字段。
不要只依赖 success_url
很多新手会在用户跳转到 success_url 后开通权限,这是非常危险的。
用户跳转成功不等于你一定可靠地完成了支付处理。用户可能关闭页面,网络可能中断,跳转可能失败,或者有人伪造访问成功页。
Stripe 官方 fulfillment 文档明确建议:自动履约必须使用 Webhook,确保每笔支付都能被处理。成功页可以让用户立刻看到结果,但不能作为唯一依据。
正确做法是:支付完成后,Stripe 发送 Webhook,你的服务端验证事件,确认支付状态,然后开通权限。
配置 Webhook
Stripe 支付集成的核心,是 Webhook。
常见事件包括:
checkout.session.completed
invoice.paid
invoice.payment_failed
customer.subscription.updated
customer.subscription.deleted
一次性付款通常重点处理 checkout.session.completed。订阅产品除了 Checkout 完成,还要处理续费成功、支付失败、订阅取消和套餐变更。
Webhook 入口要做签名校验、事件落库、幂等处理和日志记录。不要重复开通同一个订单,也不要因为重复事件给用户重复发放权益。
履约要幂等
Stripe 官方 fulfillment 文档提醒:同一个 Checkout Session 的履约函数可能被调用多次,甚至并发调用。
所以你的履约逻辑必须幂等。
比如你可以用 checkout_session_id 或本地 order_id 做唯一约束。如果订单已经开通过,就直接返回成功;如果没有开通,再执行授权、写入订阅记录、发送邮件。
支付系统里,重复处理比处理失败更危险。重复开通、重复发货、重复发优惠都可能造成损失。
保存 Stripe Customer 和 Subscription
对订阅型 SaaS,你需要在本地保存 Stripe 的关键 ID:
stripe_customer_id
stripe_subscription_id
stripe_price_id
subscription_status
current_period_end
cancel_at_period_end
这些字段能帮助你判断用户当前是否有权限、什么时候到期、是否取消、是否需要提醒更新付款方式。
不要每次用户访问都实时请求 Stripe 判断权限。你应该用 Webhook 同步关键状态到本地数据库,再在本地判断权限。
支付失败要有处理流程
订阅不是只处理第一次付款。
后续续费可能失败,银行卡可能过期,用户可能取消订阅,账单可能需要重试。Stripe 会通过 Webhook 通知这些状态变化。
至少要处理:
invoice.paid:续费成功,延长权益
invoice.payment_failed:支付失败,提醒用户更新付款方式
customer.subscription.deleted:订阅结束,回收或降级权限
customer.subscription.updated:套餐或状态变化
如果你只处理第一次 Checkout 成功,订阅系统很快就会和真实支付状态不一致。
接入 Customer Portal
Stripe Customer Portal 可以让用户自助管理付款方式、发票、订阅取消、升级降级等操作。
Stripe 官方文档说明,Customer Portal 能让客户在一个地方管理支付信息、发票和订阅。
对早期 SaaS 来说,接入 Customer Portal 比自己做完整账单管理后台更省时间,也更可靠。
你需要在服务端创建 portal session,然后把用户跳转到 Stripe 的 portal URL。
本地测试要使用 Stripe CLI
Stripe Webhook 本地调试可以使用 Stripe CLI,把事件转发到你的本地服务。
测试时至少覆盖:
一次性付款成功
订阅创建成功
续费成功
支付失败
订阅取消
Webhook 重复发送
Webhook 签名错误
支付系统不能只测"付款成功"。失败、取消、重复和延迟事件,才是最容易出问题的地方。
一个 Stripe 集成流程
可以按下面流程实现:
markdown
1. 在 Stripe 创建 Product 和 Price
2. 保存 price_id 到你的配置
3. 用户点击购买
4. 服务端创建 Checkout Session
5. 前端跳转到 Stripe Checkout
6. 用户完成支付
7. Stripe 发送 Webhook
8. 服务端验签并幂等处理
9. 更新本地订单或订阅状态
10. 开通用户权限
11. 提供 Customer Portal 管理订阅
这条链路里,最重要的是第 7 到第 10 步。
写在最后
Stripe 集成的难点不在付款按钮,而在支付完成后的业务状态同步。
一个可靠的 SaaS 支付系统,要把 Checkout、Webhook、幂等履约、本地订阅状态、支付失败处理和客户门户串起来。
下一篇,我们继续聊另一个支付方案:PayPal 接入避坑。