fix(): Lock process payment to prevent ingesting payment processing t… (#13977)

* fix(): Lock process payment to prevent ingesting payment processing twice

* Create brave-lamps-beg.md

* fix(): Lock process payment to prevent ingesting payment processing twice

* fix changeset

* resolve conflicts

---------

Co-authored-by: Carlos R. L. Rodrigues <37986729+carlos-r-l-rodrigues@users.noreply.github.com>
This commit is contained in:
Adrien de PerettiandCarlos R. L. Rodrigues authored and GitHub committed 2025-11-07 10:14:56 +01:00
1 parent a9d33bc8d1
commit 9fdc00350a
3 files changed
+40 -3

No files matched your search

+6
View File
@@ -0,0 +1,6 @@
---
"@medusajs/medusa": patch
"@medusajs/core-flows": patch
---
fix(): Lock process payment to prevent ingesting payment processing t…
@@ -1,10 +1,14 @@
import type { WebhookActionResult } from "@medusajs/framework/types" import type { WebhookActionResult } from "@medusajs/framework/types"
import { PaymentActions } from "@medusajs/utils" import { PaymentActions } from "@medusajs/utils"
import { createWorkflow, when } from "@medusajs/workflows-sdk" import { createWorkflow, transform, when } from "@medusajs/workflows-sdk"
import { useQueryGraphStep } from "../../common" import { useQueryGraphStep } from "../../common"
import { authorizePaymentSessionStep } from "../steps" import { authorizePaymentSessionStep } from "../steps"
import { completeCartAfterPaymentStep } from "../steps/complete-cart-after-payment" import { completeCartAfterPaymentStep } from "../steps/complete-cart-after-payment"
import { capturePaymentWorkflow } from "./capture-payment" import { capturePaymentWorkflow } from "./capture-payment"
import { acquireLockStep, releaseLockStep } from "../../locking"
const THIRTY_SECONDS = 30
const TWO_MINUTES = 60 * 2
/** /**
* The data to process a payment from a webhook action. * The data to process a payment from a webhook action.
@@ -66,6 +70,23 @@ export const processPaymentWorkflow = createWorkflow(
name: "cart-payment-query", name: "cart-payment-query",
}) })
const cartId = transform(
{ cartPaymentCollection },
({ cartPaymentCollection }) => {
return cartPaymentCollection.data[0].cart_id
}
)
when("lock-cart-when-available", { cartId }, ({ cartId }) => {
return !!cartId
}).then(() => {
acquireLockStep({
key: cartId,
timeout: THIRTY_SECONDS,
ttl: TWO_MINUTES,
})
})
when({ input, paymentData }, ({ input, paymentData }) => { when({ input, paymentData }, ({ input, paymentData }) => {
return ( return (
input.action === PaymentActions.SUCCESSFUL && !!paymentData.data.length input.action === PaymentActions.SUCCESSFUL && !!paymentData.data.length
@@ -128,6 +149,15 @@ export const processPaymentWorkflow = createWorkflow(
}) })
}) })
// We release before the completion to prevent dead locks
when("release-lock-cart-when-available", { cartId }, ({ cartId }) => {
return !!cartId
}).then(() => {
releaseLockStep({
key: cartId,
})
})
when({ cartPaymentCollection }, ({ cartPaymentCollection }) => { when({ cartPaymentCollection }, ({ cartPaymentCollection }) => {
return !!cartPaymentCollection.data.length return !!cartPaymentCollection.data.length
}).then(() => { }).then(() => {
@@ -1,4 +1,4 @@
import { processPaymentWorkflow } from "@medusajs/core-flows" import { processPaymentWorkflowId } from "@medusajs/core-flows"
import { import {
IPaymentModuleService, IPaymentModuleService,
ProviderWebhookPayload, ProviderWebhookPayload,
@@ -49,7 +49,8 @@ export default async function paymentWebhookhandler({
return return
} }
await processPaymentWorkflow(container).run({ input: processedEvent }) const wfEngine = container.resolve(Modules.WORKFLOW_ENGINE)
await wfEngine.run(processPaymentWorkflowId, { input: processedEvent })
} }
export const config: SubscriberConfig = { export const config: SubscriberConfig = {