From e09f6e8a1e4a759fe70664bea0538c61b7cea70a Mon Sep 17 00:00:00 2001 From: Adrien de Peretti Date: Sun, 20 Nov 2022 20:47:15 +0100 Subject: [PATCH] fix(medusa-payment-stripe): handle webhook sirialization failure (#2607) --- .changeset/moody-tips-hang.md | 6 +++ .../src/api/routes/hooks/stripe.js | 47 ++++++++++++++++--- .../medusa/src/services/payment-provider.ts | 4 ++ 3 files changed, 50 insertions(+), 7 deletions(-) create mode 100644 .changeset/moody-tips-hang.md diff --git a/.changeset/moody-tips-hang.md b/.changeset/moody-tips-hang.md new file mode 100644 index 0000000000000..5d89c19659946 --- /dev/null +++ b/.changeset/moody-tips-hang.md @@ -0,0 +1,6 @@ +--- +"medusa-payment-stripe": patch +"@medusajs/medusa": patch +--- + +fix(medusa-payment-stripe): handle webhook sirialization failure diff --git a/packages/medusa-payment-stripe/src/api/routes/hooks/stripe.js b/packages/medusa-payment-stripe/src/api/routes/hooks/stripe.js index 33936889dc81a..1ea4aa6c3a46e 100644 --- a/packages/medusa-payment-stripe/src/api/routes/hooks/stripe.js +++ b/packages/medusa-payment-stripe/src/api/routes/hooks/stripe.js @@ -1,3 +1,5 @@ +import { PostgresError } from "@medusajs/medusa/src/utils" + export default async (req, res) => { const signature = req.headers["stripe-signature"] @@ -16,7 +18,6 @@ export default async (req, res) => { async function handleCartPayments(event, req, res, cartId) { const manager = req.scope.resolve("manager") - const cartService = req.scope.resolve("cartService") const orderService = req.scope.resolve("orderService") const order = await orderService @@ -33,13 +34,26 @@ export default async (req, res) => { } break case "payment_intent.amount_capturable_updated": - if (!order) { + try { await manager.transaction(async (manager) => { - const cartServiceTx = cartService.withTransaction(manager) - await cartServiceTx.setPaymentSession(cartId, "stripe") - await cartServiceTx.authorizePayment(cartId) - await orderService.withTransaction(manager).createFromCart(cartId) + await paymentIntentAmountCapturableEventHandler({ + order, + cartId, + container: req.scope, + transactionManager: manager, + }) }) + } catch (err) { + let message = `Stripe webhook ${event} handling failed\n${ + err?.detail ?? err?.message + }` + if (err?.code === PostgresError.SERIALIZATION_FAILURE) { + message = `Stripe webhook ${event} handle failed. This can happen when this webhook is triggered during a cart completion and can be ignored. This event should be retried automatically.\n${ + err?.detail ?? err?.message + }` + } + this.logger_.warn(message) + return res.sendStatus(409) } break default: @@ -85,8 +99,27 @@ export default async (req, res) => { const resourceId = paymentIntent.metadata.resource_id if (isPaymentCollection(resourceId)) { - await handlePaymentCollection(event, req, res, resourceId, paymentIntentId) + await handlePaymentCollection(event, req, res, resourceId, paymentIntent.id) } else { await handleCartPayments(event, req, res, cartId ?? resourceId) } } + +async function paymentIntentAmountCapturableEventHandler({ + order, + cartId, + container, + transactionManager, +}) { + if (!order) { + const cartService = container.resolve("cartService") + const orderService = container.resolve("orderService") + + const cartServiceTx = cartService.withTransaction(transactionManager) + await cartServiceTx.setPaymentSession(cartId, "stripe") + await cartServiceTx.authorizePayment(cartId) + await orderService + .withTransaction(transactionManager) + .createFromCart(cartId) + } +} diff --git a/packages/medusa/src/services/payment-provider.ts b/packages/medusa/src/services/payment-provider.ts index 34133874bcb8d..430c54bbc3575 100644 --- a/packages/medusa/src/services/payment-provider.ts +++ b/packages/medusa/src/services/payment-provider.ts @@ -20,6 +20,7 @@ import { PaymentProviderDataInput } from "../types/payment-collection" import { FlagRouter } from "../utils/flag-router" import OrderEditingFeatureFlag from "../loaders/feature-flags/order-editing" import PaymentService from "./payment" +import { Logger } from "../types/global" type PaymentProviderKey = `pp_${string}` | "systemPaymentProviderService" type InjectedDependencies = { @@ -30,6 +31,7 @@ type InjectedDependencies = { refundRepository: typeof RefundRepository paymentService: PaymentService featureFlagRouter: FlagRouter + logger: Logger } & { [key in `${PaymentProviderKey}`]: | AbstractPaymentService @@ -48,6 +50,7 @@ export default class PaymentProviderService extends TransactionBaseService { protected readonly paymentProviderRepository_: typeof PaymentProviderRepository protected readonly paymentRepository_: typeof PaymentRepository protected readonly refundRepository_: typeof RefundRepository + protected readonly logger_: Logger protected readonly featureFlagRouter_: FlagRouter @@ -61,6 +64,7 @@ export default class PaymentProviderService extends TransactionBaseService { this.paymentRepository_ = container.paymentRepository this.refundRepository_ = container.refundRepository this.featureFlagRouter_ = container.featureFlagRouter + this.logger_ = container.logger } async registerInstalledProviders(providerIds: string[]): Promise {