本地环境使用Stripe Webhooks遇签名验证错误,请求返回400求助
问题:Stripe Webhook签名验证失败(无stripe-signature header)
调用/webhook端点时返回以下错误响应:
{ "type": "StripeSignatureVerificationError", "raw": { "message": "No stripe-signature header value was provided." }, "payload": {} }
代码实现
stripe.service.ts
import config from 'config'; import Stripe from 'stripe'; const stripeSecretKey = config.get<string>('stripeSecretKey'); export const stripe = new Stripe(stripeSecretKey, { apiVersion: '2023-10-16', typescript: true, });
webhook.controller.ts
import config from 'config'; import { Request, Response, NextFunction } from 'express'; import { stripe } from '@/services/stripe.service'; const stripeWebhookSigningSecret = config.get<string>('stripeWebhookSigningSecret'); export const webhookController = async ( req: Request, res: Response, next: NextFunction ) => { const payload = req.body; const sig = req.headers['stripe-signature'] as string; let event; try { event = stripe.webhooks.constructEvent( payload, sig, stripeWebhookSigningSecret ); } catch (error) { return res.status(400).send(error); } console.log(event.type); console.log(event.data.object); return res.json({ received: true }); };
routes.ts
app.post( '/webhook', bodyParser.raw({ type: 'application/json' }), webhookController );
CLI操作流程
- npm run dev > API is running on http://localhost:3000. - stripe login - stripe listen --forward-to localhost:3000/webhook > Ready! You are using Stripe API Version [2022-11-15]. Your webhook signing secret is whsec_xxx 2023-10-26 23:43:26 --> charge.succeeded [evt_xxx] 2023-10-26 23:43:26 <-- [400] POST http://localhost:3000/webhook [evt_xxx] 2023-10-26 23:43:26 --> payment_intent.succeeded [evt_xxx] 2023-10-26 23:43:26 <-- [400] POST http://localhost:3000/webhook [evt_xxx] 2023-10-26 23:43:26 --> payment_intent.created [evt_xxx] 2023-10-26 23:43:26 <-- [400] POST http://localhost:3000/webhook [evt_xxx] - stripe trigger payment_intent.succeeded > Setting up fixture for: payment_intent Running fixture for: payment_intent Trigger succeeded! Check dashboard for event details.
解决方案
1. 确认签名密钥完全匹配
复制stripe listen输出的whsec_xxx签名密钥,确保它和你配置的stripeWebhookSigningSecret完全一致,不要包含多余空格或字符。
2. 排查Express中间件冲突
如果你的应用全局使用了bodyParser.json(),必须确保webhook路由的bodyParser.raw()优先级更高:
- 要么将webhook路由放在全局
bodyParser.json()中间件之前 - 要么在全局中间件中排除/webhook路径,示例:
app.use((req, res, next) => { if (req.path === '/webhook') { return next(); } bodyParser.json()(req, res, next); });
因为一旦JSON解析器先处理请求,req.body会被转换成JSON对象,而非原始的Buffer/字符串,导致签名验证失败。
3. 统一Stripe API版本
Stripe CLI当前使用的API版本是2022-11-15,但代码中指定的是2023-10-16,虽然这不是直接导致签名缺失的原因,但版本不一致可能引发其他问题,建议统一:
stripe listen --forward-to localhost:3000/webhook --api-version 2023-10-16
4. 验证请求头是否被拦截
在webhook控制器中添加日志,确认stripe-signature头是否存在:
export const webhookController = async (req: Request, res: Response, next: NextFunction) => { // 先打印所有请求头 console.log('Request headers:', req.headers); // 后续代码不变... };
如果头不存在,检查是否有反向代理(如Nginx)或其他中间件移除了该请求头。
内容的提问来源于stack exchange,提问作者g4rf4z
相关产品推荐
相关产品推荐

