NextJS 13.2.3集成Stripe Webhook签名验证报错求助
NextJS 13.2.3 Stripe Webhook 签名验证问题解决方案
问题背景
结账流程可正常完成并跳转至successUrl,但添加Stripe签名验证后出现两类报错:
- 初始使用
req.json()解析Payload时,触发StripeSignatureVerificationError,提示需传入原始请求体的字符串/Buffer,而非解析后的JS对象 - 禁用bodyParser尝试获取原始请求体后,又出现
No signatures found matching the expected signature for payload,使用micro/raw-body库时遇到req.body为ReadableStream的类型错误
核心原因
Stripe Webhook签名验证完全依赖原始请求体的字节内容,任何对请求体的解析(如req.json())或二次转换(如JSON.stringify())都会改变原始内容的格式(空格、键顺序、数值表示等),导致签名不匹配;同时NextJS 13 App Router默认自动解析请求体,需手动禁用并直接获取原始内容。
正确实现代码(App Router)
在app/api/webhook/stripe/route.ts中编写如下代码:
import { NextRequest, NextResponse } from 'next/server'; import Stripe from 'stripe'; // 初始化Stripe客户端(API版本需与Stripe Dashboard一致) const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, { apiVersion: '2023-10-16', }); // 禁用NextJS自动解析请求体 export const config = { api: { bodyParser: false, }, }; export async function POST(req: NextRequest) { // 获取原始请求体字符串 const rawBody = await req.text(); // 获取Stripe签名头 const signature = req.headers.get('stripe-signature'); const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET!; let event: Stripe.Event; try { // 验证签名并构造Stripe事件 event = stripe.webhooks.constructEvent(rawBody, signature!, webhookSecret); } catch (err) { console.error('签名验证失败:', err); return NextResponse.json({ error: 'Webhook签名验证失败' }, { status: 400 }); } // 处理具体事件类型 switch (event.type) { case 'checkout.session.completed': const session = event.data.object as Stripe.Checkout.Session; // 示例逻辑:更新订单状态、发送用户通知等 console.log('结账完成,Session ID:', session.id); break; // 可添加其他事件处理逻辑(如payment_intent.succeeded等) default: console.log(`未处理事件类型: ${event.type}`); } return NextResponse.json({ received: true }, { status: 200 }); }
常见问题排查
签名不匹配
- 确认
STRIPE_WEBHOOK_SECRET是Stripe Dashboard中对应Webhook端点的签名密钥(不是API密钥) - 本地测试必须使用Stripe CLI转发:
stripe listen --forward-to localhost:3000/api/webhook/stripe,避免手动模拟请求导致签名错误 - 检查中间件/反向代理是否修改了
stripe-signature请求头或请求体
- 确认
类型错误
- NextJS 13 App Router中
req为NextRequest,无需引入micro或raw-body,直接用req.text()/req.arrayBuffer()获取原始内容即可,避免ReadableStream类型问题
- NextJS 13 App Router中
初始解析后转换无效
req.json()解析后再JSON.stringify()会改变原始请求体的格式,Stripe签名基于原始字节,因此必须使用未解析的原始内容
内容的提问来源于stack exchange,提问作者quigly
相关产品推荐
相关产品推荐

