【问题标题】:Stripe Error: No signatures found matching the expected signature for payload条带错误:未找到与有效负载的预期签名匹配的签名
【发布时间】:2019-05-22 18:52:49
【问题描述】:

我有一个调用 Firebase 函数的条带网络钩子。在这个函数中,我需要验证这个请求是否来自 Stripe 服务器。这是代码:

const functions = require('firebase-functions');
const bodyParser = require('body-parser');
const stripe = require("stripe")("sk_test_****");
const endpointSecret = 'whsec_****';
const app = require('express')();

app.use(bodyParser.json({
    verify: function (req, res, buf) {
        var url = req.originalUrl;
        if (url.startsWith('/webhook')) {
            req.rawBody = buf.toString()
        }
    }
}));

app.post('/webhook/example', (req, res) => {
    let sig = req.headers["stripe-signature"];

    try {
        console.log(req.bodyRaw)
        let event = stripe.webhooks.constructEvent(req.body, sig, endpointSecret);
        console.log(event);
        res.status(200).end()

        // Do something with event
    }
    catch (err) {
        console.log(err);
        res.status(400).end()
    }
});

exports.app = functions.https.onRequest(app);

正如Stripe Documentation 中提到的,我必须使用原始正文来执行此安全检查。

我已尝试使用我当前的代码和:

app.use(require('body-parser').raw({type: '*/*'}));

但我总是收到这个错误:

Error: No signatures found matching the expected signature for payload. Are you passing the raw request body you received from Stripe? https://github.com/stripe/stripe-node#webhook-signing

【问题讨论】:

    标签: google-cloud-platform google-cloud-functions stripe-payments


    【解决方案1】:

    云函数自动parses body content of known types。如果您正在获取 JSON,那么它已经在 req.body 中解析并可供您使用。您不需要添加其他正文解析中间件。

    如果你需要处理原始数据,你应该使用req.rawBody,但我认为你不需要在这里这样做。

    【讨论】:

    • 我只需要在constructEvent() 中使用req.rawBody。愚蠢的错误,谢谢。
    • 在 Java 中遇到同样的问题,请有任何建议
    • 要定义req.rawBody,我必须使用app.use(express.json({verify: (req,res,buf) => { req.rawBody = buf }})); 而不仅仅是app.use(express.json());。或者,我认为我可以在app.use(express.json()) 之前访问req.body,但这样我可以在任何地方访问原始正文。
    • 抱歉,req.rawBody 很好。我错误地设置了向下箭头。现在我的投票已锁定,我无法切换到向上箭头。
    • @pref 这很奇怪。您应该可以随时更改您的投票。
    【解决方案2】:

    这是对我有用的代码:

    app.use(bodyParser.json({
      verify: function (req, res, buf) {
        var url = req.originalUrl;
        if (url.startsWith('/stripe')) {
           req.rawBody = buf.toString();
        }
      }
    }));
    

    然后通过req.rawBody进行验证

    stripe.checkWebHook(req.rawBody, signature);
    

    参考:https://github.com/stripe/stripe-node/issues/341

    【讨论】:

      【解决方案3】:

      这对我有用:

      添加这一行:

      app.use('/api/subs/stripe-webhook', bodyParser.raw({type: "*/*"}))
      

      (第一个参数指定我们应该在哪个路由上使用原始正文解析器。请参阅app.use() 参考文档。)

      就在这一行之前:

      app.use(bodyParser.json());
      

      (不会影响你所有的操作,只是这个:'/api/subs/stripe-webhook')

      注意:如果您使用的是 Express 4.16+,您可以将 bodyParser 替换为 express:

      app.use('/api/subs/stripe-webhook', express.raw({type: "*/*"}));
      app.use(express.json());
      

      那么:

      const endpointSecret = 'whsec_........'
      
      const stripeWebhook = async (req, res) => {
          const sig = req.headers['stripe-signature'];
      
          let eventSecure = {}
          try {
              eventSecure = stripe.webhooks.constructEvent(req.body, sig, endpointSecret);
              //console.log('eventSecure :', eventSecure);
          }
          catch (err) {
              console.log('err.message :', err.message);
              res.status(400).send(`Webhook Secure Error: ${err.message}`)
              return
          }
          res.status(200).send({ received: true });
      }
      

      【讨论】:

      • 这太棒了!我不知道你可以在app.use() 中指定一个确切的路径。比使用 verify 检查特殊情况的其他答案优雅得多(并自行转换原始主体,而不是依赖 Express.js 的内置功能来处理原始内容)。
      【解决方案4】:

      2021 - 解决方案

      我遇到了这个错误,经过大量研究后,我无法轻易找出问题所在,但最终我可以根据以下架构做到这一点:

      //App.js
      
      this.server.use((req, res, next) => {
        if (req.originalUrl.startsWith('/webhook')) {
          next();
        } else {
          express.json()(req, res, next);
        }
      });
      
      //routes.js
      
      routes.post(
        '/webhook-payment-intent-update',
        bodyParser.raw({ type: 'application/json' }),
      
        //your stripe logic (Im using a controller, but wherever)
        (req, res) => {
          stripe.webhooks.constructEvent(...)
        }
      )
      

      需要注意的两大警告:

      • 确保发送req.headers['stripe-signature']
      • 确保您的endpointSecret 是正确的,否则它仍然会报同样的错误

      提示:

      • 通过安装 Stripe CLI 在本地测试它:https://stripe.com/docs/webhooks/test

      • 在条带仪表板上验证您的密钥,或者您也可以通过如下验证您的条带日志来确定您是否拥有正确的密钥:

      希望对你有帮助。 :)

      【讨论】:

      • 确保本地测试 webhook 的秘密是正确的,这是一个很好的提示,可以解决我的问题!
      • 酷! @Manuel :)。
      • 我不断收到此错误:无法从标头中提取时间戳和签名
      • 什么是真正的 stripe-signature?我应该完全通过条纹签名还是其他东西会出现在那个地方?
      • 谢谢。以前我使用不同的 webhook 密钥
      【解决方案5】:

      晚了,但会帮助别人

      Github answer

          const payload = req.body
          const sig = req.headers['stripe-signature']
          const payloadString = JSON.stringify(payload, null, 2);
          const secret = 'webhook_secret';
          const header = stripe.webhooks.generateTestHeaderString({
                  payload: payloadString,
                  secret,
          });
          
           let event;
           try {
                event = stripe.webhooks.constructEvent(payloadString, header, secret);
          
           } catch (err) {
                  console.log(`Webhook Error: ${err.message}`)
                  return res.status(400).send(`Webhook Error: ${err.message}`);
           }
      
             switch (event.type) {
                 case 'checkout.session.completed': {
             ......
          enter code here
      

      【讨论】:

        【解决方案6】:
        // Use JSON parser for all non-webhook routes
        app.use(
          bodyParser.json({
            verify: (req, res, buf) => {
              const url = req.originalUrl;
              if (url.startsWith('/api/stripe/webhook')) {
                req.rawBody = buf.toString();
              }
            }
          })
        );
        

        上面的代码对于上面的答案看起来很好。但即使是我也犯了一个错误。放同样的东西后,我得到了同样的错误。

        最后,我发现如果您在rawBody 代码下方配置了body-parser,那么它会起作用。

        这样

        // Use JSON parser for all non-webhook routes
        app.use(
          bodyParser.json({
            verify: (req, res, buf) => {
              const url = req.originalUrl;
              if (url.startsWith('/api/stripe/webhook')) {
                req.rawBody = buf.toString();
              }
            }
          })
        );
        
        // Setup express response and body parser configurations
        app.use(express.json());
        app.use(bodyParser.json());
        app.use(bodyParser.urlencoded({ extended: true }));
        

        希望对某人有所帮助。

        【讨论】:

        • 在花了一整天时间尝试了几种解决方案之后,到今天 - 2021 年 7 月 6 日,这个终于为我工作了。虽然有一些小的变化,不推荐使用 body-parser,express.json({...})express.raw({...}) 可以是直接使用。
        • 没错我们可以直接使用它来代替body-parser。谢谢!
        【解决方案7】:

        需要注意的两件事:

        1. req.rawBody 而不是req.body 传递给constructEvent
        const event = stripe.webhooks.constructEvent(
                req.rawBody,
                sig,
                STRIPE_WEBHOOK_SECRET
              );
        
        1. 确保您使用的是正确的 webhook 密码。每个 webhook url 都是唯一的!

        【讨论】:

          【解决方案8】:

          我能够从一个 webhook 获取数据,但不能从第二个 webhook 获取数据:问题是我使用的密钥与第一个 webhook 使用的密钥相同,但我发现每个 webhook 都有不同的关键,这就是我收到相同信息的方式。

          【讨论】:

            【解决方案9】:

            在我重命名了一个 firebase 云函数后,我从 Stripe 仪表板发送了一个测试 webhook 时发生了这种情况。我所有的其他功能都运行良好。通过在终端重新设置解决 firebase 函数:config:set stripe.webhook_signature="你的 webhook 签名密钥" (如果您正在使用它)并重新部署功能 firebase deploy --only 功能

            第二次我通过在条带仪表板中滚动条带签名解决了这个问题。

            【讨论】:

              【解决方案10】:

              请使用此脚本

              app.use(
                bodyParser.json({
                  verify: (req, res, buf) => {
                    req.rawBody = buf;
                  },
                })
              );
              
              【解决方案11】:

              AWS API Gateway + Lambda (Express.js CRUD) 我将它用于 Stripe webhook 端点,它适用于我:

              app.use(require('body-parser').text({ type: "*/*" }));
              

              【讨论】:

                【解决方案12】:

                我最喜欢的是结合以上两个很好的答案。

                那么就可以在构造事件的时候使用req.rawbody了。

                将“webhook”替换为您希望拥有原始正文的任何​​路线。

                app.use(
                  "/webhook",
                  express.json({
                    verify: (req, res, buf) => {
                      req.rawBody = buf.toString();
                    },
                  })
                );
                
                BEFORE
                
                app.use(express.json());
                

                如果您使用路由和控制器,效果会很好。

                【讨论】:

                  【解决方案13】:

                  要在 express 中使用原始正文和单独的中间件中的特定端点,我的解决方案是让路由器将 express.raw 用于 webhook 端点。 -node.js v12 -express.js v4.17.1

                  export const handleBodyRequestParsing = (router: Router): void => {
                    router.use('/your_webhook_endpoint', express.raw({ type: '*/*' }))
                    router.use(express.json({ limit: '100mb' }))
                    router.use(express.urlencoded({ extended: true }))
                  }
                  

                  【讨论】:

                    猜你喜欢
                    • 2020-07-07
                    • 2019-09-04
                    • 2021-02-19
                    • 2019-11-10
                    • 2020-04-25
                    • 2021-07-26
                    • 2021-03-14
                    • 2022-12-12
                    • 1970-01-01
                    相关资源
                    最近更新 更多