Stripe webhook 需要与标准 api 请求不同的 json 主体

Stripe webhook needs different json body than standard api requests

我可以在我的打字稿后端有一个条纹 webhook,或者我可以有条纹 API - 但不能两者都...

我设置了路由器,因此 paymentActions.tsx

中任何带有“/payment/”的 url 调用我的条带处理程序

这里是app.ts:

// app.ts

import bodyParser from "body-parser";
import express from "express";

import { paymentActions } from "../controllers/paymentActions";

const app = express();

app.use(express.static("public"));
app.use(express.urlencoded({ extended: true }));

// IF I DISABLE THIS app.use THE STRIPE WEBHOOK WORKS
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.url;
      //@ts-ignore
      req.rawBody = buf.toString();
    },
  })
);

// APIs
app.use("/payment", paymentActions);

export default app;

在 paymentActions.tsx 我有三个端点。两个 GET 请求处理条带 API 以创建和更新支付意图。这非常有效。

第三个端点是一个 stripe webhook,它 returns 一个 400 响应。如果我禁用上面 app.ts 中的 app.use(bodyparser...),webhook 现在可以工作(响应 200)但是其他两个 GET 请求会抛出错误,因为 json 正文包含“数量是 NaN”。

有什么办法可以解决这个问题吗?谢谢!

// paymentActions.tsx

import express from "express";
import * as stripe from "../clients/stripe";
    
export const paymentActions = express.Router();

// Get payment intent
paymentActions.get<{}>("/intent", async (req, res) => {
  const { amount } = req.body;

  try {
    const paymentIntent = await stripe.createPaymentIntent(amount);
    return res.status(200).json(paymentIntent);
  } catch (err) {
    res.status(400).send(`Webhook Error: ${err.message}`);
    return;
  }
});

// Update payment intent
paymentActions.get<{}>("/updatedIntent", async (req, res) => {
  const { intentId, amount, metadata } = req.body;

  try {
    const paymentIntent = await stripe.updatePaymentIntent(intentId, amount, metadata);
    return res.status(200).json(paymentIntent);
  } catch (err) {
    res.status(400).send(`Webhook Error: ${err.message}`);
    return;
  }
});


// Stripe payment webhook
paymentActions.post(
  "/webhook",
  express.raw({ type: "application/json" }),
  (request, response) => {
    const stripe = require("stripe");
    const endpointSecret = process.env.STRIPE_WEBHOOK_SECRET;

    let event = request.body;
    // Only verify the event if you have an endpoint secret defined.
    // Otherwise use the basic event deserialized with JSON.parse
    if (endpointSecret) {
      // Get the signature sent by Stripe
      const signature = request.headers["stripe-signature"];
      try {
        event = stripe.webhooks.constructEvent(
          request.body,
          signature,
          endpointSecret
        );
      } catch (err) {
        console.log(`⚠️  Webhook signature verification failed.`, err.message);
        return response.sendStatus(400);
      }
    }

    // Handle the event
    switch (event.type) {
      case "payment_intent.succeeded":
        // Handle payment intent
        break;
      default:
        // Unexpected event type
    }

    // Return a 200 response to acknowledge receipt of the event
    response.send();
  }
);

请尝试 app.use(express.json()) 并删除 bodyParse app.use。

不确定这是否是理想的解决方案,但在每个 get 请求的参数中指定 express.json() 解决了这个问题。

// Get payment intent
paymentActions.get<{}>("/intent", express.json(), async (req, res) => {
  const { amount } = req.body;

  try {
    const paymentIntent = await stripe.createPaymentIntent(amount);
    return res.status(200).json(paymentIntent);
  } catch (err) {
    res.status(400).send(`Webhook Error: ${err.message}`);
    return;
  }
});