简体   繁体   English

如何延迟来自用户表单的 POST 请求,直到收到来自 Stripe 的 webhook POST 之后?

[英]How can I delay a POST request from a user form until AFTER a webhook POST from Stripe is received?

I want users to pay a fee before a POST request from a front end form is processed.我希望用户在处理来自前端表单的 POST 请求之前支付费用。 I have a Stripe webhook that works fine on the backend, but I'm not sure how to delay the front end posting of the form until after the payment confirmation is received.我有一个在后端可以正常工作的 Stripe webhook,但我不确定如何将表单的前端发布延迟到收到付款确认之后。

In the code below, right now, createTour and createTourPay run at the same time.在下面的代码中,现在createTourcreateTourPay同时运行。 I would like for createTourPay to execute first, and the createTour only triggers after Stripe posts to my application from the webhook.我希望createTourPay首先执行,并且createTour仅在 Stripe 从 webhook 发布到我的应用程序后触发。 How can I achieve this?我怎样才能做到这一点?

Controller File (webhook): Controller 文件(webhook):

exports.webhookCheckout = (req, res, next) => {
  const signature = req.headers['stripe-signature'];
  let event;
  try {
    event = stripe.webhooks.constructEvent(
      req.body,
      signature,
      process.env.STRIPE_WEBHOOK_SECRET
    );
  } catch (err) {
    return res.status(400).send(`Webhook error: ${err.message}`);
  }
  if (
    event.type === 'checkout.session.completed' &&
    event.line_items.name === 'New Job Purchase'
  ) {
    res.status(200).json({ recieved: true });
    // Somehow, I want this to trigger the execution of the POST request in my front end JS file.
  } else {
    if (event.type === 'checkout.session.completed')
      createBookingCheckout(event.data.object);
    res.status(200).json({ recieved: true });
  }
};

Front end JS file:前端JS文件:

export const createTourPay = async myForm => {
  try {
    // 1) Get the checkout session from API response
    const session = await axios(`/api/v1/tours/tour-pay`);
    const complete = 1;
    // console.log(session);
    // 2) Create checkout form + charge the credit card
    await stripe.redirectToCheckout({
      sessionId: session.data.session.id
    });
  } catch (err) {
    // console.log(err);
    showAlert('error', err);
  }
};

export const createTour = async myForm => {
  try {
    const startLocation = {
      type: 'Point',
      coordinates: [-10.185942, 95.774772],
      address: '123 Main Street',
      description: 'Candy Land'
    };

    const res = await axios({
      method: 'POST',
      headers: {
        'Content-Type': `multipart/form-data; boundary=${myForm._boundary}`
      },
      url: '/api/v1/tours',
      data: myForm
    });

    if (res.data.status === 'success') {
      showAlert('success', 'NEW TOUR CREATED!');
      window.setTimeout(() => {
        location.assign('/');
      }, 1500);
    }
  } catch (err) {
    showAlert('error', err.response.data.message);
  }
};

Broadly: don't do this.广泛地说:不要这样做。 Instead, you in fact should create some pending/unpaid version of the "tour" (or any other product/service) in your system, then attach the unique id (eg: tour_123 ) to the Checkout session when you create it, either using the client_reference_id ( doc ) or metadata ( doc ):相反,您实际上应该在系统中创建一些待定/未付费版本的“旅游”(或任何其他产品/服务),然后在创建时将唯一 ID(例如: tour_123 )附加到结帐 session ,或者使用client_reference_id ( doc ) 或metadata ( doc ):

const session = await stripe.checkout.sessions.create({
  // ... other params
  client_reference_id: 'tour_123',
  metadata: { tour_id: 'tour_123' },
});

Then you'd use the webhook to inspect those values, and update your own database to indicate the payment has been made and that you can fulfill the order to the customer (ship product, send codes, allow access to service etc).然后,您将使用 webhook 来检查这些值,并更新您自己的数据库以指示已付款并且您可以向客户履行订单(运送产品、发送代码、允许访问服务等)。

If you really want to proceed with a more synchronous flow, you can use separate auth and capture to sequence your customer experience and capture the funds later after authorizing and creating your tour entity.如果您真的想继续使用更同步的流程,您可以使用单独的身份验证和捕获来对您的客户体验进行排序,并在授权和创建您的旅游实体后捕获资金

Edit: a note about security编辑:关于安全的说明

You should never trust client-side logic for restricted operations like creating a "paid" tour.您永远不应该相信客户端逻辑来进行受限操作,例如创建“付费”旅游。 A motivated user could, for example, simply call your /api/v1/tours create endpoint without ever going through your payment flow.例如,有动力的用户可以简单地调用您的/api/v1/tours创建端点,而无需通过您的支付流程。 Unless you validate a payment and track that state on your server you won't be able to know which of these had actually paid you.除非您验证付款并在您的服务器上跟踪 state,否则您将无法知道其中哪些实际支付了您。

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

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