如何给 Next.js 添加 Stripe 订阅(完整指南)
By make1day · Updated 2026-11-07
这是「在 Next.js 中接入 Stripe 订阅」最短、且不会在生产翻车的路径。四个部件,没有灌水。
1. 创建产品与价格
在 Stripe 后台创建带周期性价格的 Product,把 Price ID 作为 STRIPE_PRICE_ID_PRO 写进环境变量,绝不硬编码进源码。
2. 结账路由
POST /api/checkout 读取套餐、解析价格 id、创建 mode 为 subscription 的 Checkout Session,带上 client_reference_id 与明确的 success_url / cancel_url,把托管页 URL 返回浏览器。创建会话前要求已登录——游客结账会找不到该给谁发授权。
3. Webhook 是真相来源
在解析 JSON 之前,先用签名头校验原始 body。处理 checkout.session.completed 与 customer.subscription.deleted,然后更新数据库。两条规则:
- 验签失败(缺头、HMAC 不对、JSON 损坏)必须抛错并返回 HTTP 400。用 200 确认伪造请求是安全漏洞。
- 记录已处理事件 id,任何写库之前先跳过重放。Stripe 会重试,没有这一步就会出现重复升级与幽灵退款。
4. 功能权限
每个受保护路由或服务端动作都从数据库读套餐,绝不读客户端状态。套餐未激活返回 402。AI 接口、源码 ZIP 下载、席位限制都挂在这一个检查上。
5. 跑通整个闭环
用测试卡 4242 4242 4242 4242 在测试模式完成结账,确认套餐翻转,再取消并确认翻回。然后用篡改签名的 Webhook 打一次,确认返回 400。若一份 env 就能跑通,计费就是真的。
好的模板会把第 2–4 步预先接好,并把它们放在与支付商无关的接口后面,这样以后加 Creem、PayPal、Waffo 或 Lemon Squeezy 时不必重写应用代码。
FAQ
验签之前可以先解析 body 吗?+
不行。先用签名头校验原始字节,再解析。先解析再重新序列化会改变字节,导致 HMAC 校验失败。
为什么游客结账会发不出授权?+
Webhook 按用户 id 或付款邮箱发放权限。若没人登录、付款邮箱又对不上账户,license 就没有可认领的人。创建会话前要求登录。
被忽略的 Webhook 响应是什么意思?+
返回 200 并标记 ignored,表示事件合法已验签但不属于我们处理的类型。它绝不能用于验签失败——那些必须是 400。
All frequently asked questions · Markdown version (for AI agents)