简体   繁体   English

Stripe webhook 作为中间件

[英]Stripe webhook as a middleware

In my Nodejs/Express API, when the user checks out and the stripe checkout is successful, I want to send them some success responses and call some functions.在我的 Nodejs/Express API 中,当用户签出并且条带签出成功时,我想向他们发送一些成功响应并调用一些函数。

I have tried to access the checkout.session.completed event with the webhook to determine if the checkout process was complete and then send a response but the problem is that the webhook route and checkout route are different from each other and I have no idea how I can implement the webhook functionality as a middleware so that the checkout.session.completed event gets cheked in the same checkout route.我尝试使用 webhook 访问checkout.session.completed事件以确定结帐过程是否完成,然后发送响应,但问题是 webhook 路由和结帐路由彼此不同,我不知道如何我可以将 webhook 功能实现为中间件,以便checkout.session.completed事件在同一结帐路线中得到检查。

This is the checkout functionality which goes into the checkout route (cartsRouter.post("/:cart_id/checkout", checkoutCart) :这是进入结帐路线的结帐功能(cartsRouter.post("/:cart_id/checkout", checkoutCart)

// Checkout
const checkoutCart = async (req, res, next) => {
  const user_id = req.user.user_id;

  try {
    // Check if cart isn't empty
    const cart = await db.query(selectCartProducts, [user_id]);
    if (!cart.rows.length) {
      res.status(200).json({ message: "Cart Is Empty" });
    } else {
      // Check if shipping address has been provided in the user info
      const address = await db.query(checkAddress, [user_id]);
      if (!address.rows.length) {
        missingAddressError(next);
      } else {
        // Stripe
        const session = await stripe.checkout.sessions.create({
          line_items: cart.rows.map((product) => {
            return {
              price_data: {
                currency: "usd",
                product_data: {
                  name: product.name,
                  images: product.img_urls,
                },
                unit_amount: product.price * 100,
              },
              quantity: product.quantity,
            };
          }),
          mode: "payment",
          success_url: process.env.ADDRESS2,
          cancel_url: process.env.ADDRESS1,
        });

        res.status(200).json(session.url);
      }
    }
  } catch (err) {
    next(err);
  }
};

And this is the webhook route:这是webhook路由:

cartsRouter.post(
  "/checkout/webhook",
  express.raw({ type: "application/json" }),
  async (req, res, next) => {
    const cart_id = req.user.cart_id;
    const user_id = req.user.user_id;

    const sig = req.headers["stripe-signature"];

    let event;

    try {
      event = stripe.webhooks.constructEvent(
        req.body,
        sig,
        process.env.ENDPOINT_SECRET
      );
    } catch (err) {
      res.status(400).send(`Webhook Error: ${err.message}`);
      return;
    }

    // Handle the checkout.session.completed event
    if (event.type === "checkout.session.completed") {
      // Fulfill the purchase...
      try {
        // Get cart
        const cart = await db.query(selectCartProducts, [user_id]);

        // Calculate the total price of the products based on their quantity
        const total_price = cart.rows
          .reduce((acc, item) => {
            return acc + parseFloat(item.price) * item.quantity;
          }, 0)
          .toFixed(2);

        // Create new order
        const order = await db.query(insertOrder, [
          user_id,
          total_price,
          "Complete",
        ]);
        const order_id = order.rows[0].order_id;

        // Move products from cart to order history
        await Promise.all(
          cart.rows.map(async (product) => {
            await db.query(insertProductIntoOrder, [
              order_id,
              product.product_id,
              product.quantity,
              product.color,
            ]);
            // Delete products from cart after adding them to order history (empty cart)
            await db.query(deleteProductFromCart, [
              cart_id,
              product.product_id,
            ]);
          })
        );

        return res.status(201).json({
          message: "Order Submitted Successfully",
          order: {
            order_number: order.rows[0].order_number,
            total_price: order.rows[0].total_price,
            status: order.rows[0].status,
          },
        });
      } catch (err) {
        console.error(err.message);
        return next(err);
      }
    }

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

I'm looking for a way to combine these two routes so that while still being on the checkout route, the checkout.session.completed event in the webhook gets checked and appropriate responses get sent to the client.我正在寻找一种方法来组合这两条路线,以便在仍然在结帐路线上的同时,检查 webhook 中的checkout.session.completed事件并将适当的响应发送到客户端。

Is there any way to do this?有没有办法做到这一点?

After a successful payment or completion, your customer is redirected back from Checkout to your success_url that you configured.成功付款或完成后,您的客户将从 Checkout 重定向回您配置的success_url At that point, you can confirm that they paid successfully without waiting for the webhook itself.此时,您可以确认他们已成功支付,而无需等待 webhook 本身。 Or you can poll your server until it's done processing the webhook otherwise.或者,您可以轮询您的服务器,直到它完成对 webhook 的处理。

This is outlined in Checkout's main documentation here where they explain that redirect.这在 Checkout 的主要文档中进行了概述,他们在此处解释了该重定向。 While it just shows a basic success page, you can do fulfillment here too as long as you securely check that they completed the Session.虽然它只显示一个基本的成功页面,但只要您安全地检查他们是否完成了会话,您也可以在此处完成操作。 The idea would be to use a cookie to recognize them after a redirect for example and then use the Retrieve Session API to check its status.例如,这个想法是在重定向后使用 cookie 来识别它们,然后使用 Retrieve Session API检查其状态。

Make sure to always have the webhook as a fallback though since it's possible for the customer to close the tab before the redirect completes.确保始终将 webhook 作为后备,因为客户可以在重定向完成之前关闭选项卡。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM