如何从 webhook 获得 Stripe“payment_intent.succeeded”响应以处理订单?

Posted

技术标签:

【中文标题】如何从 webhook 获得 Stripe“payment_intent.succeeded”响应以处理订单?【英文标题】:How do I get Stripe's "payment_intent.suceeded" response from the webhook in order to process an order? 【发布时间】:2021-12-16 08:17:03 【问题描述】:

我正在使用快速服务器运行 Vue 应用程序。在客户端,我使用 createPaymentIntent、createPaymentMethod 和 confirmCardPayment api。文档明确指出Payment Intents, verifying statuses

您的集成不应尝试在客户端处理订单履行,因为客户可能会在付款完成后但在履行流程启动之前离开页面。相反,使用 webhook 来监控 payment_intent.succeeded 事件并异步处理其完成,而不是尝试在客户端启动履行。

下单流程如下:

    使用客户详细信息和产品创建订单 - newOrder。 调用 createPaymentIntent(newOrder) 调用stripe.createPaymentMethod(type, card) 从 stripe.confirmCardPayment(secret, payment_method: paymentMethodRequest.paymentMethod.id, 运输:billingDetails,) if result.error - 在客户端向客户显示错误消息 否则,我会处理加载并设置successfulPayment = true 以触发客户端上的操作(动画等)。重要的是,我调用 OrderService.postOrder(newOrder) 将订单发送到后端以便在 Mongo 中进行处理。
    async createOrder() 
      this.loading(true);
      this.$v.$touch();
      if (!this.$v.$invalid) 
        // order item details....
        const cardElement = elements.getElement("card");
        const billingDetails = 
          name: user.name,
          address: 
            city: user.city,
            line1: user.street,
            state: "NY"
          ,
          phone: user.phone
        ;
        try 
          const response = await OrderService.createPaymentIntent(newOrder);

          const secret = response.data.clientSecret;
          this.deliveryFee = response.data.deliveryFee;
          this.totalAmount = response.data.totalAmount;
          const paymentMethodRequest = await stripe.createPaymentMethod(
            type: "card",
            card: cardElement

          );
          const result = await stripe.confirmCardPayment(secret, 
            payment_method: paymentMethodRequest.paymentMethod.id,
            shipping: billingDetails,
            receipt_email: "kevinturney01@gmail.com"
          );

          // ! DO NOT CONFIRM A SUCCESS ON THE CLIENT, RELY ON THE WEBHOOK!!!!!
          // console.log("LINE 443", result, result.error);
          if (result.error) 
            const error = result.error;
            this.showErrorMessage(error);
           else 
            this.successfullPayment = true;
            this.isLoading = false;
            //! NEED TO REDIRECT TO SUCCESSS PAGE
            OrderService.postOrder(newOrder).catch(error => 
              // https://guillim.github.io/vue/2019/03/20/damn-vuejs-observer.html
              this.updateErrors(Object.assign([], error.response.data.errors));
            );
            console.log("success");
            this.updateOrder(newOrder);

          
         catch (error) 
          console.log(error);
        
      

我的 webhook 端点几乎直接来自 Stripe 文档:

/webhook

    static async apiPostWebhookEvent(req, res) 
    let data, eventType
    let paymentIntent
    let event = null
    console.log("HITTING STRIPE WEBHOOK")
    // Check if webhook signing is configured.

    if (process.env.WHSNGROK)  //WHSNGROK hits endpoint
    // Retrieve the event by verifying the signature using the raw body and secret.

    let signature = req.headers['stripe-signature'];
    console.log("SIGNATURE",signature)
    try 
      event = stripe.webhooks.constructEvent(
        req.rawBody,
        signature,
        process.env.WHSNGROK
      );
     catch (err) 
      console.log(`⚠️  Webhook signature verification failed.`);
      return res.sendStatus(400);
    
    data = event.data;
    eventType = event.type;
   else 
    // Webhook signing is recommended, but if the secret is not configured in `config.js`,
    // we can retrieve the event data directly from the request body.
    data = req.body.data;
    eventType = req.body.type;
    console.log("LINE 38 WEBHOOK", eventType)
    console.log("LINE 39", event.type)
  
  // event.type || eventType || eventType.type
  switch (event.type) 
    case 'payment_intent.created':
      paymentIntent = event.data.object;
      break;
    case 'payment_intent.payment_failed':
      paymentIntent = event.data.object;
payment_intent.payment_failed
      const message = intent.last_payment_error && intent.last_payment_error.message;
      console.log('❌ Payment failed.', paymentIntent.id, message);
      break;
    case 'payment_intent.processing':
      paymentIntent = event.data.object;
      break;
    case 'payment_intent.succeeded':
      paymentIntent = event.data.object;
      // Then define and call a function to handle the event payment_intent.succeeded
      // Funds have been captured
      // Fulfill any orders, e-mail receipts, etc
      
      // At this point how do I or can I send the order to MongoDB and get the payment_intent.succeeded confirmation from the webhook to the client?

      console.log('???? Payment captured!', paymentIntent.id);
      break;
    // ... handle other event types
    default:
      console.log(`Unhandled event type $event.type`);
    
  return res.sendStatus(200);
  

总结一下。在 webhook 中的 switch/case 中,我怎样才能将 payment_intent.succeeded 从 webhook 发送到我的客户端(我知道这是 Stripe 向我的服务器发出的 post 请求)。那,而不是在客户端上调用 confirmCardPayment() 并依赖该结果?是否可以从那里调用 post my newOrder 或者我仍然需要从客户端调用我的 OrderService.postOrder(newOrder)。

https://stripe.com/docs/api/payment_intents

webhooks

【问题讨论】:

【参考方案1】:

我认为检查 webhook 是否已访问服务器的最简洁方法是让客户端每 X 秒 ping 服务器以检查订单状态,或者您可以在客户端和服务器之间建立 web-socket 连接这样服务器就可以向客户端推送一条消息,告诉它paymentIntent 已成功(或任何其他状态消息)。

这些选项中的任何一个都可以,尽管我认为 webhook 在服务器上触发对 Order 的更新而不是客户端在收到响应后才这样做会更安全。否则,如果客户端和服务器之间的连接断开,则可能会支付订单但未标记为已支付,然后客户端将执行的任何其他相关操作都不会发生。它基本上消除了对客户端的任何依赖,以便在用户支付订单后完成订单工作流程。

【讨论】:

关于你的第一段@KingJ,在我发布的链接上,验证状态,他们谈论投票。 “从技术上讲,可以使用轮询而不是 webhook 来监视由异步操作引起的更改——重复检索 PaymentIntent 以便您可以检查其状态——但这明显不太可靠......”对于我喜欢的第二个选项,我将如何转移/替换来自我的客户的调用以发布订单,而不是在 webhook 中的“成功”案例中将订单数据和发布到 MongoDB 的调用? 我的轮询建议是让客户端轮询您的服务器,而不是 Stripe 服务器,但同意客户端和服务器之间的 web-socket 连接更好。您可以将用于创建订单的任何代码从 PostOrder 端点复制到一个函数中,当支付意图成功时 webhook 可以触发该函数。然后,您可以使用 web-socket 处理替换客户端中对 OrderService.PostOrder 的调用,并在服务器上添加适当的 web-socket 处理。 您推荐的大部分内容我都知道。从最初的研究来看,我服务器上的 websocket 似乎并不太复杂。另一个问题可能是,当我调用 Orderservice.postOrder(newOrder) 时,我在该路由端点上有我的验证中间件 (express-validator)。所以我想我也可以移动它。

以上是关于如何从 webhook 获得 Stripe“payment_intent.succeeded”响应以处理订单?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 C# WCF 中接收 Stripe api webhook

从 Stripe webhook .Net 中提取元数据

如何使用 ngrok 测试接收 Stripe webhook

如何识别 Stripe webhooks 类型?

Stripe Connect Express Webhook - 如何在用户完成 Stripe Connect Express 表单并被重定向后触发 Stripe Webhook

NextJS Stripe Webhook 原始正文