Next.js中Stripe Webhook签名匹配失败问题求助
Next.js Pages API路由Stripe Webhook签名不匹配问题解决思路
核心排查与修复步骤
1. 先禁用Next.js自动解析请求体(关键第一步)
Pages API路由默认会自动解析JSON请求体,这会破坏Stripe要求的原始字节流,直接导致签名匹配失败。在你的API路由文件顶部加这段配置即可——Vercel完全支持pages路由的config配置,放心用:
export const config = { api: { bodyParser: false, }, };
2. 正确读取原始请求体的两种靠谱方法
方法一:用raw-body库(省心高效)
先安装依赖:
npm install raw-body
读取时必须指定encoding: null,确保拿到的是原始Buffer而非字符串:
import getRawBody from 'raw-body'; import Stripe from 'stripe'; const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, { apiVersion: '2024-04-10', }); export const config = { api: { bodyParser: false, }, }; export default async function handler(req, res) { if (req.method !== 'POST') return res.status(405).end(); try { const rawBody = await getRawBody(req, { length: req.headers['content-length'], limit: '2mb', encoding: null, // 必须返回Buffer,禁止转字符串 }); const signature = req.headers['stripe-signature']; const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET; const event = stripe.webhooks.constructEvent(rawBody, signature, webhookSecret); // 写入你的事件处理逻辑,比如支付成功后的业务操作 switch (event.type) { case 'payment_intent.succeeded': // 处理逻辑示例 break; default: console.log(`未处理的事件类型:${event.type}`); } res.status(200).json({ received: true }); } catch (err) { console.error(err); return res.status(400).send(`Webhook错误:${err.message}`); } }
方法二:手动拼接Buffer(无需第三方库)
不想装依赖的话,直接手动读取请求流拼接:
import Stripe from 'stripe'; const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, { apiVersion: '2024-04-10', }); export const config = { api: { bodyParser: false, }, }; export default async function handler(req, res) { if (req.method !== 'POST') return res.status(405).end(); try { const rawBody = await new Promise((resolve, reject) => { const chunks = []; req.on('data', chunk => chunks.push(chunk)); req.on('end', () => resolve(Buffer.concat(chunks))); req.on('error', reject); }); const signature = req.headers['stripe-signature']; const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET; const event = stripe.webhooks.constructEvent(rawBody, signature, webhookSecret); // 事件处理逻辑... res.status(200).json({ received: true }); } catch (err) { console.error(err); return res.status(400).send(`Webhook错误:${err.message}`); } }
3. 环境变量与Stripe控制台配置检查
- 确认
STRIPE_WEBHOOK_SECRET是Stripe控制台生成的Webhook签名密钥,不是普通API密钥,测试/生产环境密钥要对应,不能混用。 - 检查Stripe控制台中Webhook的端点URL,必须和部署后的API路由URL完全一致,生产环境必须使用HTTPS。
- 确保Stripe控制台Webhook的API版本设置为
2024-04-10,和代码中的配置完全对齐。
4. Vercel部署后的额外检查
- 进入Vercel项目的环境变量页面,确认
STRIPE_SECRET_KEY和STRIPE_WEBHOOK_SECRET配置正确,无拼写错误。 - 若使用了Vercel中间件或Edge Functions,禁止提前解析请求体,避免原始数据被修改。
5. 本地测试验证
用Stripe CLI模拟Webhook事件,排查本地是否正常:
# 启动转发,将Stripe事件转发到本地API路由 stripe listen --forward-to localhost:3000/api/webhook # 触发测试事件 stripe trigger payment_intent.succeeded
如果本地能正常解析,部署后出问题,大概率是环境变量或Stripe控制台的URL/密钥配置错误。
内容的提问来源于stack exchange,提问作者realfranser
相关产品推荐
相关产品推荐

