在Sails框架中访问Stripe API原始数据及解决Webhook签名验证失败问题
在Sails框架中使用Stripe的常见问题解决
我来帮你解决这两个在Sails中对接Stripe时遇到的问题:
1. 如何访问Stripe API的原始数据?
当你通过Stripe Node SDK调用API时,返回的结果默认是经过封装的对象。如果需要获取Stripe API返回的原始响应数据,可以直接访问返回对象的raw属性——不管是成功响应还是错误信息,都能拿到原始的JSON数据。
举个实际的例子,创建支付Intent时获取原始数据:
const stripe = require('stripe')(sails.config.stripe.secretKey); try { const paymentIntent = await stripe.paymentIntents.create({ amount: 1000, currency: 'usd', }); // 获取API返回的原始响应 const rawApiResponse = paymentIntent.raw; console.log('Stripe原始响应:', rawApiResponse); } catch (err) { // 错误场景下,原始错误数据也在err.raw里 console.error('Stripe错误原始数据:', err.raw); }
2. 解决Stripe Webhook签名验证失败与请求体丢失问题
你遇到的核心问题是:Stripe签名验证需要未被修改的原始请求体,但Sails默认的bodyParser会解析请求体并修改它,直接绕过又会导致req.body丢失。下面是一套完整的解决方案:
步骤1:自定义中间件捕获原始请求体
在config/http.js中添加一个自定义中间件,专门为Stripe Webhook路由捕获原始请求体,同时手动解析出req.body供后续业务使用:
module.exports.http = { middleware: { // 自定义中间件:捕获Stripe Webhook的原始请求体 stripeWebhookRawBody: function(req, res, next) { // 只针对你的Stripe Webhook路由(比如 /webhooks/stripe) if (req.path === '/webhooks/stripe' && req.method === 'POST') { let rawBody = ''; // 监听请求数据事件,拼接原始内容 req.on('data', chunk => { rawBody += chunk.toString(); }); // 请求结束后挂载原始数据,并手动解析JSON req.on('end', () => { req.rawBody = rawBody; try { req.body = JSON.parse(rawBody); } catch (err) { return res.sendStatus(400); } next(); }); } else { // 非Webhook路由直接跳过 next(); } }, // 调整中间件顺序,把自定义的放在bodyParser前面 order: [ 'stripeWebhookRawBody', 'cookieParser', 'session', 'bodyParser', // ...保留你原来的其他中间件 ], // 为Webhook路由绕过默认的bodyParser bodyParser: (function configureBodyParser(){ const skipper = require('skipper'); const defaultBodyParser = skipper(); return function(req, res, next) { // 跳过Stripe Webhook路由的自动解析 if (req.path === '/webhooks/stripe' && req.method === 'POST') { return next(); } return defaultBodyParser(req, res, next); }; })(), } };
步骤2:在控制器中验证签名并处理事件
现在你的Webhook控制器可以同时拿到req.rawBody(用于签名验证)和req.body(用于业务处理)了:
// api/controllers/WebhookController.js module.exports = { stripe: async function(req, res) { const stripe = require('stripe')(sails.config.stripe.secretKey); const webhookSecret = sails.config.stripe.webhookSecret; const signature = req.headers['stripe-signature']; let stripeEvent; try { // 使用原始请求体验证签名 stripeEvent = stripe.webhooks.constructEvent( req.rawBody, signature, webhookSecret ); } catch (err) { console.error('Stripe签名验证失败:', err.message); return res.status(400).send(`Webhook Error: ${err.message}`); } // 根据事件类型处理业务逻辑 switch (stripeEvent.type) { case 'payment_intent.succeeded': const paymentIntent = stripeEvent.data.object; console.log('支付成功:', paymentIntent.id); // 这里添加你的业务逻辑,比如更新订单状态 break; case 'charge.failed': const failedCharge = stripeEvent.data.object; console.log('支付失败:', failedCharge.id); // 处理支付失败的逻辑 break; // 其他你需要处理的事件类型... default: console.log(`未处理的事件类型: ${stripeEvent.type}`); } // 返回200给Stripe,确认收到事件 res.sendStatus(200); } };
关于"重复造轮子"的疑问
目前Sails生态里确实没有官方维护的Stripe Webhook集成组件,大部分开发者都是基于Stripe官方Node SDK自己适配Sails的中间件体系——所以你的实现不算重复造轮子,只是在Sails的框架规则下对接Stripe的标准功能而已。毕竟Stripe的Webhook验证逻辑是固定的,我们只需要在Sails里做好原始请求体的捕获就行。
内容的提问来源于stack exchange,提问作者matt9292
相关产品推荐
相关产品推荐

