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

Zat*_*t42 7 stripe-payments google-cloud-platform google-cloud-functions

我有一个条纹Webhook,它调用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);
Run Code Online (Sandbox Code Playgroud)

Stripe文档中所述,我必须使用原始主体来执行此安全检查。

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

app.use(require('body-parser').raw({type: '*/*'}));
Run Code Online (Sandbox Code Playgroud)

但是我总是会收到这个错误:

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
Run Code Online (Sandbox Code Playgroud)

Jer*_*yal 59

有2点需要注意:

  1. 确保您使用正确的 Webhook 密钥。每个 webhook url 都是唯一的!

在此输入图像描述

  1. 如果您使用的是 stripe CLI,请将 stripe 密钥更改为 cli

在此输入图像描述


如果使用 Firebase 云功能:

通过req.rawBody而不是req.bodyconstructEvent

const event = stripe.webhooks.constructEvent(
        req.rawBody,
        sig,
        STRIPE_WEBHOOK_SECRET
      );
Run Code Online (Sandbox Code Playgroud)

如果使用Next.jsVercel 函数:

export const config = {
  api: {
    bodyParser: false,
  },
};

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const sig = req.headers["stripe-signature"]!;

  const body = await buffer(req);
  const event = stripeObj.webhooks.constructEvent(body, sig, key);

  console.log("webhook verified");
  switch (event.type) {
    case "checkout.session.completed":

    default:
      console.error(`Unhandled event type ${event.type}`);
  }

  return res.status(200).json({});
}

const buffer = (req: NextApiRequest) => {
  return new Promise<Buffer>((resolve, reject) => {
    const chunks: Buffer[] = [];

    req.on("data", (chunk: Buffer) => {
      chunks.push(chunk);
    });

    req.on("end", () => {
      resolve(Buffer.concat(chunks));
    });

    req.on("error", reject);
  });
};
Run Code Online (Sandbox Code Playgroud)

检查其他服务器:https://github.com/stripe/stripe-node/tree/master/examples/webhook-signing

  • 是的,你是对的...我正在使用 webhook id...非常令人困惑 (5认同)

小智 17

这是对我有用的代码:

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

然后通过 req.rawBody 进行验证

stripe.checkWebHook(req.rawBody, signature);
Run Code Online (Sandbox Code Playgroud)

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


Fáb*_*uza 17

2021 - 解决方案

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

//App.js

this.server.use((req, res, next) => {
  if (req.originalUrl.startsWith('/webhook')) {
    next();
  } else {
    express.json()(req, res, next);
  }
});
Run Code Online (Sandbox Code Playgroud)
//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(...)
  }
)
Run Code Online (Sandbox Code Playgroud)

需要注意的两大警告:

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

尖端:

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

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

Webhook 密钥示例

我希望它对你有帮助。:)

  • 确保本地测试 webhooks 的秘密是正确的是解决我的问题的一个很好的技巧! (4认同)
  • 谢谢。之前我使用了不同的 webhook 密钥 (2认同)

Mat*_*asG 11

这是对我有用的:

添加这一行:

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

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

就在这一行之前:

app.use(bodyParser.json());
Run Code Online (Sandbox Code Playgroud)

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

注意:如果你使用 Express 4.16+ 你可以用 express 替换 bodyParser:

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

然后:

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 });
}
Run Code Online (Sandbox Code Playgroud)

  • 这很棒!我不知道你可以在“app.use()”中指定确切的路径。比使用“verify”检查特殊情况的其他答案要优雅得多(并自行转换原始主体,而不是依赖 Express.js 的内置功能来处理原始内容)。 (3认同)

Dou*_*son 7

Cloud Functions自动解析已知类型的正文内容。如果您要获取JSON,那么它已经被解析并且可以在中使用req.body。您不需要添加其他主体解析中间件。

如果您需要处理原始数据,则应使用req.rawBody,但我认为您无需在此处进行处理。

  • 为了定义`req.rawBody`,我必须做`app.use(express.json({verify: (req,res,buf) =&gt; { req.rawBody = buf }}));`而不是` app.use(express.json());`。或者,我认为我可以在“app.use(express.json())”之前访问“req.body”,但这样我就可以在任何地方访问原始主体。 (4认同)
  • 我只需要在`constructEvent()`中使用`req.rawBody`。愚蠢的错误,谢谢。 (3认同)