Stripe集成Express无法获取raw body导致webhook签名校验失败
问题原因
该问题的核心诱因是全局JSON解析中间件优先级高于路由级raw请求体解析中间件:
如果你在代码中提前全局注册了express.json(),该中间件会在匹配到/stripe_webhook路由前,就把所有Content-Type: application/json的请求体解析为JavaScript对象,后续路由上配置的express.raw()不会重复处理已经被解析过的请求,因此你拿到的request.body始终是JSON对象而非原始Buffer格式,直接导致Stripe签名校验不通过。
修复方案
- 方案1(推荐,改动成本最低):调整中间件和路由的注册顺序,将Stripe webhook路由移动到全局
express.json()注册之前,保证webhook请求优先被raw解析器处理:
// 第一步:优先注册Stripe webhook路由,使用raw解析 router.post( "/stripe_webhook", express.raw({ type: "application/json" }), (request, response) => { updateStripePaymentStatus(request, response); } ); // 第二步:再注册全局JSON解析中间件,供其他普通业务接口使用 app.use(express.json()); // 第三步:注册其余普通业务路由 // app.use('/api/order', orderRouter) // ...其他路由
- 方案2:修改全局JSON解析中间件的逻辑,对Stripe webhook路径直接跳过解析:
app.use((req, res, next) => { // webhook路径跳过JSON解析,交给路由级的raw解析器处理 if (req.path === "/stripe_webhook") { return next(); } // 其余路径正常走JSON解析 express.json()(req, res, next); });
- 现有代码的额外问题修复:
- 签名校验失败的catch分支中,返回400响应后需要加
return终止逻辑,否则代码会继续向下执行,在event为undefined时触发switch逻辑报错:
try { event = stripe.webhooks.constructEvent(request.body, sig, endpointSecret); } catch (err) { console.log(err); // 加return终止后续逻辑执行 return response.status(400).send(`Webhook Error: ${err.message}`); }- 数据库
save()操作前需要加await,同时补全查询为空的判断,避免异步逻辑未执行完就返回响应、或者查询不到文档时触发空指针报错:
case "payment_intent.succeeded": const paymentIntent = event.data.object; const doc = await FileModel.findOne({ paymentId: paymentIntent.id }); if (!doc) { return response.status(404).send('Matching payment record not found'); } doc.paymentStatus = paymentIntent.status; // 加await等待保存完成 await doc.save(); break; - 签名校验失败的catch分支中,返回400响应后需要加
内容的提问来源于stack exchange,提问作者user18472307
相关产品推荐
相关产品推荐

