fix(core-flows): prevent completing cart from webhook when order already exists (#14108)
* Prevent completing cart when order already exists in processPaymentWorkflow * Add tests * Add changeset
This commit is contained in:
1 parent
d6a9e36bf0
commit
79582bc94e
3 files changed
+206
-3
No files matched your search
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/core-flows": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix(core-flows): prevent completing cart from webhook when order already exists
|
||||||
@@ -1,11 +1,14 @@
|
|||||||
import {
|
import {
|
||||||
addToCartWorkflow,
|
addToCartWorkflow,
|
||||||
|
beginOrderEditOrderWorkflow,
|
||||||
completeCartWorkflow,
|
completeCartWorkflow,
|
||||||
|
confirmOrderEditRequestWorkflow,
|
||||||
createCartWorkflow,
|
createCartWorkflow,
|
||||||
createPaymentCollectionForCartWorkflow,
|
createPaymentCollectionForCartWorkflow,
|
||||||
createPaymentSessionsWorkflow,
|
createPaymentSessionsWorkflow,
|
||||||
getOrderDetailWorkflow,
|
getOrderDetailWorkflow,
|
||||||
listShippingOptionsForCartWorkflow,
|
listShippingOptionsForCartWorkflow,
|
||||||
|
orderEditAddNewItemWorkflow,
|
||||||
processPaymentWorkflow,
|
processPaymentWorkflow,
|
||||||
} from "@medusajs/core-flows"
|
} from "@medusajs/core-flows"
|
||||||
import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
|
import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
|
||||||
@@ -26,6 +29,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
ContainerRegistrationKeys,
|
ContainerRegistrationKeys,
|
||||||
Modules,
|
Modules,
|
||||||
|
PaymentCollectionStatus,
|
||||||
ProductStatus,
|
ProductStatus,
|
||||||
remoteQueryObjectFromString,
|
remoteQueryObjectFromString,
|
||||||
} from "@medusajs/utils"
|
} from "@medusajs/utils"
|
||||||
@@ -1024,6 +1028,187 @@ medusaIntegrationTestRunner({
|
|||||||
"Failed to do something before ending complete cart workflow"
|
"Failed to do something before ending complete cart workflow"
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should avoid completing cart when order already exists", async () => {
|
||||||
|
const salesChannel = await scModuleService.createSalesChannels({
|
||||||
|
name: "Webshop",
|
||||||
|
})
|
||||||
|
|
||||||
|
const location = await stockLocationModule.createStockLocations({
|
||||||
|
name: "Warehouse",
|
||||||
|
})
|
||||||
|
|
||||||
|
const [product] = await productModule.createProducts([
|
||||||
|
{
|
||||||
|
title: "Test product",
|
||||||
|
status: ProductStatus.PUBLISHED,
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
title: "Test variant",
|
||||||
|
manage_inventory: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
const priceSet = await pricingModule.createPriceSets({
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 3000,
|
||||||
|
currency_code: "usd",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
await pricingModule.createPricePreferences({
|
||||||
|
attribute: "currency_code",
|
||||||
|
value: "usd",
|
||||||
|
has_tax_inclusive: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
await remoteLink.create([
|
||||||
|
{
|
||||||
|
[Modules.PRODUCT]: {
|
||||||
|
variant_id: product.variants[0].id,
|
||||||
|
},
|
||||||
|
[Modules.PRICING]: {
|
||||||
|
price_set_id: priceSet.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[Modules.SALES_CHANNEL]: {
|
||||||
|
sales_channel_id: salesChannel.id,
|
||||||
|
},
|
||||||
|
[Modules.STOCK_LOCATION]: {
|
||||||
|
stock_location_id: location.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
// create cart
|
||||||
|
const cart = await cartModuleService.createCarts({
|
||||||
|
currency_code: "usd",
|
||||||
|
sales_channel_id: salesChannel.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
await addToCartWorkflow(appContainer).run({
|
||||||
|
input: {
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
variant_id: product.variants[0].id,
|
||||||
|
quantity: 1,
|
||||||
|
requires_shipping: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
cart_id: cart.id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
await createPaymentCollectionForCartWorkflow(appContainer).run({
|
||||||
|
input: {
|
||||||
|
cart_id: cart.id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const [payCol] = await remoteQuery(
|
||||||
|
remoteQueryObjectFromString({
|
||||||
|
entryPoint: "cart_payment_collection",
|
||||||
|
variables: { filters: { cart_id: cart.id } },
|
||||||
|
fields: ["payment_collection_id"],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
await createPaymentSessionsWorkflow(appContainer).run({
|
||||||
|
input: {
|
||||||
|
payment_collection_id: payCol.payment_collection_id,
|
||||||
|
provider_id: "pp_system_default",
|
||||||
|
context: {},
|
||||||
|
data: {},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const {
|
||||||
|
result: { id: orderId },
|
||||||
|
} = await completeCartWorkflow(appContainer).run({
|
||||||
|
input: {
|
||||||
|
id: cart.id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
await beginOrderEditOrderWorkflow(appContainer).run({
|
||||||
|
input: {
|
||||||
|
order_id: orderId,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await orderEditAddNewItemWorkflow(appContainer).run({
|
||||||
|
input: {
|
||||||
|
order_id: orderId,
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
variant_id: product.variants[0].id,
|
||||||
|
quantity: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await confirmOrderEditRequestWorkflow(appContainer).run({
|
||||||
|
input: { order_id: orderId },
|
||||||
|
})
|
||||||
|
|
||||||
|
const orderPaymentCollections = await remoteQuery(
|
||||||
|
remoteQueryObjectFromString({
|
||||||
|
entryPoint: "order_payment_collection",
|
||||||
|
variables: { filters: { order_id: orderId } },
|
||||||
|
fields: ["payment_collection_id"],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const [pendingPaymentCollection] = await remoteQuery(
|
||||||
|
remoteQueryObjectFromString({
|
||||||
|
entryPoint: "payment_collection",
|
||||||
|
variables: {
|
||||||
|
filters: {
|
||||||
|
id: orderPaymentCollections.map(
|
||||||
|
(orderPayCol) => orderPayCol.payment_collection_id
|
||||||
|
),
|
||||||
|
status: PaymentCollectionStatus.NOT_PAID,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fields: ["id"],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const { result: paymentSession } =
|
||||||
|
await createPaymentSessionsWorkflow(appContainer).run({
|
||||||
|
input: {
|
||||||
|
payment_collection_id: pendingPaymentCollection.id,
|
||||||
|
provider_id: "pp_system_default",
|
||||||
|
context: {},
|
||||||
|
data: {},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
let completeCartCalled = false
|
||||||
|
const workflow = processPaymentWorkflow(appContainer)
|
||||||
|
|
||||||
|
workflow.addAction("track-complete-cart-step", {
|
||||||
|
invoke: async function trackStep({ invoke }) {
|
||||||
|
completeCartCalled = !!invoke["complete-cart-after-payment-step"]
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
await workflow.run({
|
||||||
|
input: {
|
||||||
|
action: "captured",
|
||||||
|
data: {
|
||||||
|
session_id: paymentSession.id,
|
||||||
|
amount: 3000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(completeCartCalled).toBe(false)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -73,10 +73,23 @@ export const processPaymentWorkflow = createWorkflow(
|
|||||||
const cartId = transform(
|
const cartId = transform(
|
||||||
{ cartPaymentCollection },
|
{ cartPaymentCollection },
|
||||||
({ cartPaymentCollection }) => {
|
({ cartPaymentCollection }) => {
|
||||||
return cartPaymentCollection.data[0].cart_id
|
return cartPaymentCollection.data[0]?.cart_id
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const { data: order } = useQueryGraphStep({
|
||||||
|
entity: "order_cart",
|
||||||
|
fields: ["id"],
|
||||||
|
filters: {
|
||||||
|
cart_id: cartId
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
isList: false
|
||||||
|
}
|
||||||
|
}).config({
|
||||||
|
name: "cart-order-query",
|
||||||
|
})
|
||||||
|
|
||||||
when("lock-cart-when-available", { cartId }, ({ cartId }) => {
|
when("lock-cart-when-available", { cartId }, ({ cartId }) => {
|
||||||
return !!cartId
|
return !!cartId
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
@@ -158,8 +171,8 @@ export const processPaymentWorkflow = createWorkflow(
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
when({ cartPaymentCollection }, ({ cartPaymentCollection }) => {
|
when({ cartPaymentCollection, order }, ({ cartPaymentCollection, order }) => {
|
||||||
return !!cartPaymentCollection.data.length
|
return !!cartPaymentCollection.data.length && !order
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
completeCartAfterPaymentStep({
|
completeCartAfterPaymentStep({
|
||||||
cart_id: cartPaymentCollection.data[0].cart_id,
|
cart_id: cartPaymentCollection.data[0].cart_id,
|
||||||
|
|||||||
Reference in new issue
Block a user