使用 Express.js 解决 Stripe 中的 Webhook 签名验证问题

问题描述 投票:0回答:1

我一直在尝试在 Express.js 应用程序中实现 Stripe Webhooks,但我遇到了 Webhook 签名验证问题。我收到的错误消息是:

⚠️  Webhook signature verification failed. Webhook payload must be provided as a string or a Buffer (https://nodejs.org/api/buffer.html) instance representing the _raw_ request body.Payload was provided as a parsed JavaScript object instead. Signature verification is impossible without access to the original signed material.

这是我当前的设置:

index.js

require("dotenv").config();
const express = require("express");
const bodyParser = require("body-parser");
const app = express();
const port = 8080;
const cors = require("cors");
const mongoose = require("mongoose");

app.use(cors());

app.get("/", (req, res) => {
  res.send("Hello, World!");
});

// Use these parsers for other routes
app.use(express.json({ limit: "50mb" }));
app.use(express.urlencoded({ limit: "50mb", extended: true }));

const dbURI = process.env.MONGODB_CONNECTION;

const searchRouter = require("./routes/searchRoutes");
const phoneRouter = require("./routes/phone");
const paymentRouter = require("./routes/payment");
const linkRouter = require("./routes/link");
const pieceRouter = require("./routes/piece");
const userRouter = require("./routes/user");
const emailRouter = require("./routes/email");
const autocompleteRouter = require("./routes/autocomplete");
const webhookRouter = require("./routes/webhook");

app.use("/", searchRouter);
app.use("/phone", phoneRouter);
app.use("/payment", paymentRouter);
app.use("/link", linkRouter);
app.use("/piece", pieceRouter);
app.use("/user", userRouter);
app.use("/email", emailRouter);
app.use("/autocomplete", autocompleteRouter);
app.use("/webhook", webhookRouter);

mongoose
  .connect(dbURI)
  .then(() => {
    console.log("MongoDB connected");
    app.listen(port, () => {
      console.log(`Server running on http://localhost:${port}/`);
    });
  })
  .catch((err) => console.log(err));

webhook.js

require("dotenv").config();
const express = require("express");
const router = express.Router();
const stripe = require("stripe")(process.env.STRIPE_SECRET_TEST_KEY);

const endpointSecret = process.env.STRIPE_TEST_WEBHOOK_KEY;

router.post("/", async (req, res) => {
  const sig = req.headers["stripe-signature"];

  console.log("Webhook received!");

  let event;

  try {
    event = stripe.webhooks.constructEvent(req.body, sig, endpointSecret);
  } catch (err) {
    console.log(`⚠️  Webhook signature verification failed.`, err.message);
    return res.sendStatus(400);
  }

  if (event.type === "checkout.session.completed") {
    const session = event.data.object;

    try {
      const paymentIntent = await stripe.paymentIntents.retrieve(
        session.payment_intent,
        {
          expand: ["charges"],
        }
      );

      const charge = paymentIntent.charges.data[0];
      const transferId = charge.transfer;
      const taxAmount = session.total_details.amount_tax;

      await stripe.transfers.createReversal(transferId, {
        amount: taxAmount,
      });

      console.log(`Successfully reversed tax amount: ${taxAmount}`);
    } catch (error) {
      console.error("Error creating transfer reversal:", error);
    }
  }

  res.sendStatus(200);
});

module.exports = router;

我在这里缺少什么?如何正确处理 Stripe webhook 签名验证的原始请求正文?

到目前为止,我已经尝试了许多中间件并查看了几篇文章,但似乎没有一个对我有用。我不知道这是否重要,但我一直在使用 ngrok 公开本地端点以进行测试。

已经检查过这些文章: Stripe - Webhook 负载必须以字符串或缓冲区的形式提供 https://github.com/stripe/stripe-node/issues/341 https://github.com/stripe/stripe-node#webhook-signing Stripe Webhook 400 错误。原始请求正文问题

javascript node.js express stripe-payments
1个回答
0
投票

您有 bodyParser,这对于 Express 和 Stripe Webhook 签名来说是有问题的。查看此问题并尝试建议的解决方法(假设您将端点重命名为

/stripe-webhooks

app.use(bodyParser.json({
// Because Stripe needs the raw body, we compute it but only when hitting the Stripe callback URL.
verify: function(req,res,buf) {
    var url = req.originalUrl;
    if (url.startsWith('/stripe-webhooks')) {
        req.rawBody = buf.toString()
    }
}}));

或避免使用 bodyParser 并严格遵循 Stripe 提供的示例代码

© www.soinside.com 2019 - 2024. All rights reserved.