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

Javascript 如何将来自用户表单的POST请求延迟到收到来自Stripe的webhook POST之后?,javascript,asynchronous,async-await,stripe-payments,Javascript,Asynchronous,Async Await,Stripe Payments,我希望用户在处理来自前端表单的POST请求之前支付费用。我有一个Stripe webhook,可以在后端正常工作,但我不确定如何将表单的前端发布延迟到收到付款确认之后 在下面的代码中,现在,createTour和createTourPay同时运行。我希望先执行createTourPay,并且createTourPay仅在从webhook向我的应用程序发送条带后触发。我怎样才能做到这一点 控制器文件(webhook): exports.webhookCheckout = (req, res, ne

我希望用户在处理来自前端表单的POST请求之前支付费用。我有一个Stripe webhook,可以在后端正常工作,但我不确定如何将表单的前端发布延迟到收到付款确认之后

在下面的代码中,现在,
createTour
createTourPay
同时运行。我希望先执行
createTourPay
,并且
createTourPay
仅在从webhook向我的应用程序发送条带后触发。我怎样才能做到这一点

控制器文件(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 });
  }
};
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);
  }
};
前端JS文件:

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 });
  }
};
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);
  }
};

概括地说:不要这样做。事实上,您应该在系统中创建一些挂起/未付版本的“tour”(或任何其他产品/服务),然后在创建时将唯一id(例如:
tour_123
)附加到签出会话,或者使用
客户端参考id
()或
元数据
():

然后,您将使用webhook检查这些值,并更新您自己的数据库,以表明已付款,并且您可以向客户付款(发货、发送代码、允许访问服务等)

如果您真的想进行更同步的流程,您可以在授权和创建旅行团实体后,使用排序您的客户体验

编辑:关于安全性的说明


对于诸如创建“付费”巡演之类的受限操作,您永远不应该信任客户端逻辑。例如,一个有动力的用户可以简单地调用您的
/api/v1/tours
创建端点,而不必经过您的支付流程。除非您验证付款并在服务器上跟踪该状态,否则您将无法知道这些付款中的哪一个实际支付给了您。

啊,很有趣。因此,首先创建旅游,将唯一ID传递到签出,然后更新DB中的字段以确认付款。在前端,我可以将其设置为只显示“付费:真实”或类似内容的旅游。一般来说,我理解正确吗?没错!我刚才还添加了一个关于客户端逻辑和安全性的说明。您确实需要在后端管理此未付/已付状态以使其安全,否则,一个坏演员可能会操纵您的客户端代码,在不付费的情况下发出创建旅游的请求。而且,您可以让您的客户端在
GET/api/tours
上从后端检索旅游列表,并且它可能是一个刚付费的列表,或付费和待定等。这是由您决定的产品。请记住,用户可能会在结帐时取消或关闭浏览器,而从不付款,因此您将有一些待处理的项目需要管理。例如,您可以在客户仪表板中显示这些无报酬的旅行,并显示一个链接,以便再次前往结帐付款(这次不创建新的!),说明他们的浏览器是否第一次崩溃或互联网是否第一次断开。太棒了,感谢您的帮助!!:)我能够按照建议将其设置为工作状态。我还没有搞乱“待定”项,但现在,我已经在数据库中首先创建了列表,成功创建后,将打开条带签出窗口以获取唯一的列表ID。非常酷的东西!再次感谢您的帮助!非常欢迎——很高兴听到这对你有帮助!:D