# 如何给 Next.js 添加 Stripe 订阅（完整指南）

> 产品、结账路由、Webhook、功能权限与测试闭环——补上多数指南讲得不够的验签细节。

- Author / publisher: make1day (company)
- Published: 2026-04-28
- Updated: 2026-11-07
- Canonical: /blog/add-stripe-subscriptions-nextjs
- Category: Tutorial
- Tags: how to add Stripe subscriptions to Next.js, Stripe subscriptions Next.js, Next.js subscription billing boilerplate
- Language: zh

这是「在 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。

---

Source: make1day. See /llms.txt and /llms-full.txt for the full machine-readable product description.
