条带错误:未找到与有效负载的预期签名匹配的签名

Posted

技术标签:

【中文标题】条带错误:未找到与有效负载的预期签名匹配的签名【英文标题】: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

【问题讨论】:

【参考方案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】:

需要注意的两件事:

    req.rawBody 而不是req.body 传递给constructEvent
const event = stripe.webhooks.constructEvent(
        req.rawBody,
        sig,
        STRIPE_WEBHOOK_SECRET
      );
    确保您使用的是正确的 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;
    ,
  )
);

【讨论】:

这比***.com/a/54956700/6541288 或***.com/a/53899407/6541288 好在哪里?【参考方案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 ))

【讨论】:

以上是关于条带错误:未找到与有效负载的预期签名匹配的签名的主要内容,如果未能解决你的问题,请参考以下文章

带有 Google Cloud Functions 的 Stripe webhook 不断给出 Webhook 错误:未找到与有效负载的预期签名匹配的签名

未找到与有效负载的预期签名匹配的签名

代码签名错误:未找到匹配的配置文件:未找到与适用的签名身份匹配的配置文件

sdk-3.1.1.js:1 未捕获类型错误:无法在“URL”上执行“createObjectURL”:未找到与提供的签名匹配的函数

无法将数据从 Stripe webhook 发送到 Firebase

代码签名错误 - XCode 中不提供配置文件