如何从 Webhook 获得 Stripe 的 "payment_intent.suceeded" 响应以处理订单?
How do I get Stripe's "payment_intent.suceeded" response from the webhook in order to process an order?
我是 运行 带有快速服务器的 Vue 应用程序。在客户端,我正在使用 createPaymentIntent、createPaymentMethod 和 confirmCardPayment api。文档明确指出 Payment Intents, verifying statuses
Your integration shouldn’t attempt to handle order fulfillment on the client side because it is possible for customers to leave the page after payment is complete but before the fulfillment process initiates. Instead, use webhooks to monitor the payment_intent.succeeded event and handle its completion asynchronously instead of attempting to initiate fulfillment on the client side.
下单流程如下:
- 使用客户详细信息和产品创建订单 - 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 文档:
/网络钩子
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 中,如何从 webhook 获取 payment_intent.succeeded 到我的客户端(我知道这是 Stripe 向我的服务器发出的 post 请求)。那,而不是在客户端调用 confirmCardPayment() 并依赖该结果?是否可以从那里调用 post 我的新订单,还是我仍然需要从客户端调用我的 OrderService.postOrder(newOrder)。
我认为检查 webhook 是否已命中您的服务器的最简洁方法是让客户端每隔 X 秒对服务器执行一次 ping 操作以检查订单状态,或者您可以在客户端之间建立网络套接字连接和服务器,以便服务器可以向客户端推送一条消息,告诉它 paymentIntent
已成功(或任何其他状态消息)。
这些选项中的任何一个都可以,尽管我认为 webhook 在服务器上触发对订单的更新比客户端在收到响应后这样做更安全。否则,如果客户端和服务器之间的连接断开,则订单可能已付款但未标记为已付款,然后客户端打算执行的任何其他相关操作都不会发生。一旦用户为订单付款,它基本上消除了对客户端的任何依赖,以便完成订单工作流。
我是 运行 带有快速服务器的 Vue 应用程序。在客户端,我正在使用 createPaymentIntent、createPaymentMethod 和 confirmCardPayment api。文档明确指出 Payment Intents, verifying statuses
Your integration shouldn’t attempt to handle order fulfillment on the client side because it is possible for customers to leave the page after payment is complete but before the fulfillment process initiates. Instead, use webhooks to monitor the payment_intent.succeeded event and handle its completion asynchronously instead of attempting to initiate fulfillment on the client side.
下单流程如下:
- 使用客户详细信息和产品创建订单 - 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 文档:
/网络钩子
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 中,如何从 webhook 获取 payment_intent.succeeded 到我的客户端(我知道这是 Stripe 向我的服务器发出的 post 请求)。那,而不是在客户端调用 confirmCardPayment() 并依赖该结果?是否可以从那里调用 post 我的新订单,还是我仍然需要从客户端调用我的 OrderService.postOrder(newOrder)。
我认为检查 webhook 是否已命中您的服务器的最简洁方法是让客户端每隔 X 秒对服务器执行一次 ping 操作以检查订单状态,或者您可以在客户端之间建立网络套接字连接和服务器,以便服务器可以向客户端推送一条消息,告诉它 paymentIntent
已成功(或任何其他状态消息)。
这些选项中的任何一个都可以,尽管我认为 webhook 在服务器上触发对订单的更新比客户端在收到响应后这样做更安全。否则,如果客户端和服务器之间的连接断开,则订单可能已付款但未标记为已付款,然后客户端打算执行的任何其他相关操作都不会发生。一旦用户为订单付款,它基本上消除了对客户端的任何依赖,以便完成订单工作流。