chore: Minor restructure of cart completion (#12353)
* chore: Minor revamp of cart completion * wip * wip * temp. remove import * continue on error * chore: Use workflow * fix import * Create beige-actors-tie.md * fix import
This commit is contained in:
6
.changeset/beige-actors-tie.md
Normal file
6
.changeset/beige-actors-tie.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@medusajs/medusa": patch
|
||||
"@medusajs/core-flows": patch
|
||||
---
|
||||
|
||||
chore: Minor revamp of cart completion
|
||||
@@ -1,11 +1,10 @@
|
||||
import { createStep, StepResponse } from "@medusajs/framework/workflows-sdk"
|
||||
import { IPaymentModuleService, Logger } from "@medusajs/framework/types"
|
||||
import {
|
||||
ContainerRegistrationKeys,
|
||||
Modules,
|
||||
PaymentSessionStatus,
|
||||
} from "@medusajs/framework/utils"
|
||||
import { Modules } from "@medusajs/framework/utils"
|
||||
import { Logger } from "@medusajs/framework/types"
|
||||
import { IPaymentModuleService } from "@medusajs/framework/types"
|
||||
import { createStep, StepResponse } from "@medusajs/framework/workflows-sdk"
|
||||
|
||||
/**
|
||||
* The payment session's details for compensation.
|
||||
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
useQueryGraphStep,
|
||||
useRemoteQueryStep,
|
||||
} from "../../common"
|
||||
import { addOrderTransactionStep } from "../../order/steps/add-order-transaction"
|
||||
import { createOrdersStep } from "../../order/steps/create-orders"
|
||||
import { authorizePaymentSessionStep } from "../../payment/steps/authorize-payment-session"
|
||||
import { registerUsageStep } from "../../promotion/steps/register-usage"
|
||||
@@ -32,6 +33,7 @@ import {
|
||||
validateCartPaymentsStep,
|
||||
validateShippingStep,
|
||||
} from "../steps"
|
||||
import { compensatePaymentIfNeededStep } from "../steps/compensate-payment-if-needed"
|
||||
import { reserveInventoryStep } from "../steps/reserve-inventory"
|
||||
import { completeCartFields } from "../utils/fields"
|
||||
import { prepareConfirmInventoryInput } from "../utils/prepare-confirm-inventory-input"
|
||||
@@ -41,7 +43,6 @@ import {
|
||||
PrepareLineItemDataInput,
|
||||
prepareTaxLinesData,
|
||||
} from "../utils/prepare-line-item-data"
|
||||
import { compensatePaymentIfNeededStep } from "../steps/compensate-payment-if-needed"
|
||||
/**
|
||||
* The data to complete a cart and place an order.
|
||||
*/
|
||||
@@ -88,7 +89,7 @@ export const completeCartWorkflow = createWorkflow(
|
||||
{
|
||||
name: completeCartWorkflowId,
|
||||
store: true,
|
||||
idempotent: true,
|
||||
idempotent: false,
|
||||
retentionTime: THREE_DAYS,
|
||||
},
|
||||
(input: WorkflowData<CompleteCartWorkflowInput>) => {
|
||||
@@ -152,16 +153,6 @@ export const completeCartWorkflow = createWorkflow(
|
||||
|
||||
validateShippingStep({ cart, shippingOptions })
|
||||
|
||||
createHook("beforePaymentAuthorization", {
|
||||
input,
|
||||
})
|
||||
|
||||
const payment = authorizePaymentSessionStep({
|
||||
// We choose the first payment session, as there will only be one active payment session
|
||||
// This might change in the future.
|
||||
id: paymentSessions![0].id,
|
||||
})
|
||||
|
||||
const { variants, sales_channel_id } = transform({ cart }, (data) => {
|
||||
const variantsMap: Record<string, any> = {}
|
||||
const allItems = data.cart?.items?.map((item) => {
|
||||
@@ -181,19 +172,7 @@ export const completeCartWorkflow = createWorkflow(
|
||||
}
|
||||
})
|
||||
|
||||
const cartToOrder = transform({ cart, payment }, ({ cart, payment }) => {
|
||||
const transactions =
|
||||
(payment &&
|
||||
payment?.captures?.map((capture) => {
|
||||
return {
|
||||
amount: capture.raw_amount ?? capture.amount,
|
||||
currency_code: payment.currency_code,
|
||||
reference: "capture",
|
||||
reference_id: capture.id,
|
||||
}
|
||||
})) ??
|
||||
[]
|
||||
|
||||
const cartToOrder = transform({ cart }, ({ cart }) => {
|
||||
const allItems = (cart.items ?? []).map((item) => {
|
||||
const input: PrepareLineItemDataInput = {
|
||||
item,
|
||||
@@ -259,7 +238,6 @@ export const completeCartWorkflow = createWorkflow(
|
||||
shipping_methods: shippingMethods,
|
||||
metadata: cart.metadata,
|
||||
promo_codes: promoCodes,
|
||||
transactions,
|
||||
credit_lines: creditLines,
|
||||
}
|
||||
})
|
||||
@@ -298,39 +276,6 @@ export const completeCartWorkflow = createWorkflow(
|
||||
}
|
||||
})
|
||||
|
||||
const linksToCreate = transform(
|
||||
{ cart, createdOrder },
|
||||
({ cart, createdOrder }) => {
|
||||
const links: Record<string, any>[] = [
|
||||
{
|
||||
[Modules.ORDER]: { order_id: createdOrder.id },
|
||||
[Modules.CART]: { cart_id: cart.id },
|
||||
},
|
||||
]
|
||||
|
||||
if (isDefined(cart.payment_collection?.id)) {
|
||||
links.push({
|
||||
[Modules.ORDER]: { order_id: createdOrder.id },
|
||||
[Modules.PAYMENT]: {
|
||||
payment_collection_id: cart.payment_collection.id,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return links
|
||||
}
|
||||
)
|
||||
|
||||
parallelize(
|
||||
createRemoteLinkStep(linksToCreate),
|
||||
updateCartsStep([updateCompletedAt]),
|
||||
reserveInventoryStep(formatedInventoryItems),
|
||||
emitEventStep({
|
||||
eventName: OrderWorkflowEvents.PLACED,
|
||||
data: { id: createdOrder.id },
|
||||
})
|
||||
)
|
||||
|
||||
const promotionUsage = transform(
|
||||
{ cart },
|
||||
({ cart }: { cart: CartWorkflowDTO }) => {
|
||||
@@ -362,7 +307,74 @@ export const completeCartWorkflow = createWorkflow(
|
||||
}
|
||||
)
|
||||
|
||||
registerUsageStep(promotionUsage)
|
||||
const linksToCreate = transform(
|
||||
{ cart, createdOrder },
|
||||
({ cart, createdOrder }) => {
|
||||
const links: Record<string, any>[] = [
|
||||
{
|
||||
[Modules.ORDER]: { order_id: createdOrder.id },
|
||||
[Modules.CART]: { cart_id: cart.id },
|
||||
},
|
||||
]
|
||||
|
||||
if (isDefined(cart.payment_collection?.id)) {
|
||||
links.push({
|
||||
[Modules.ORDER]: { order_id: createdOrder.id },
|
||||
[Modules.PAYMENT]: {
|
||||
payment_collection_id: cart.payment_collection.id,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return links
|
||||
}
|
||||
)
|
||||
|
||||
parallelize(
|
||||
createRemoteLinkStep(linksToCreate),
|
||||
updateCartsStep([updateCompletedAt]),
|
||||
reserveInventoryStep(formatedInventoryItems),
|
||||
registerUsageStep(promotionUsage),
|
||||
emitEventStep({
|
||||
eventName: OrderWorkflowEvents.PLACED,
|
||||
data: { id: createdOrder.id },
|
||||
})
|
||||
)
|
||||
|
||||
createHook("beforePaymentAuthorization", {
|
||||
input,
|
||||
})
|
||||
|
||||
// We authorize payment sessions at the very end of the workflow to minimize the risk of
|
||||
// canceling the payment in the compensation flow. The only operations that can trigger it
|
||||
// is creating the transactions, the workflow hook, and the linking.
|
||||
const payment = authorizePaymentSessionStep({
|
||||
// We choose the first payment session, as there will only be one active payment session
|
||||
// This might change in the future.
|
||||
id: paymentSessions![0].id,
|
||||
})
|
||||
|
||||
const orderTransactions = transform(
|
||||
{ payment, createdOrder },
|
||||
({ payment, createdOrder }) => {
|
||||
const transactions =
|
||||
(payment &&
|
||||
payment?.captures?.map((capture) => {
|
||||
return {
|
||||
order_id: createdOrder.id,
|
||||
amount: capture.raw_amount ?? capture.amount,
|
||||
currency_code: payment.currency_code,
|
||||
reference: "capture",
|
||||
reference_id: capture.id,
|
||||
}
|
||||
})) ??
|
||||
[]
|
||||
|
||||
return transactions
|
||||
}
|
||||
)
|
||||
|
||||
addOrderTransactionStep(orderTransactions)
|
||||
|
||||
createHook("orderCreated", {
|
||||
order_id: createdOrder.id,
|
||||
|
||||
@@ -5,12 +5,16 @@ import { StepResponse, createStep } from "@medusajs/framework/workflows-sdk"
|
||||
/**
|
||||
* The transaction(s) to add to the order.
|
||||
*/
|
||||
export type AddOrderTransactionStepInput = CreateOrderTransactionDTO | CreateOrderTransactionDTO[]
|
||||
export type AddOrderTransactionStepInput =
|
||||
| CreateOrderTransactionDTO
|
||||
| CreateOrderTransactionDTO[]
|
||||
|
||||
/**
|
||||
* The added order transaction(s).
|
||||
*/
|
||||
export type AddOrderTransactionStepOutput = CreateOrderTransactionDTO | CreateOrderTransactionDTO[]
|
||||
export type AddOrderTransactionStepOutput =
|
||||
| CreateOrderTransactionDTO
|
||||
| CreateOrderTransactionDTO[]
|
||||
|
||||
export const addOrderTransactionStepId = "add-order-transaction"
|
||||
/**
|
||||
@@ -18,14 +22,15 @@ export const addOrderTransactionStepId = "add-order-transaction"
|
||||
*/
|
||||
export const addOrderTransactionStep = createStep(
|
||||
addOrderTransactionStepId,
|
||||
async (
|
||||
data: AddOrderTransactionStepInput,
|
||||
{ container }
|
||||
) => {
|
||||
async (data: AddOrderTransactionStepInput, { container }) => {
|
||||
const service = container.resolve(Modules.ORDER)
|
||||
|
||||
const trxsData = Array.isArray(data) ? data : [data]
|
||||
|
||||
if (!trxsData.length) {
|
||||
return new StepResponse(null)
|
||||
}
|
||||
|
||||
for (const trx of trxsData) {
|
||||
const existing = await service.listOrderTransactions(
|
||||
{
|
||||
@@ -46,7 +51,9 @@ export const addOrderTransactionStep = createStep(
|
||||
const created = await service.addOrderTransactions(trxsData)
|
||||
|
||||
return new StepResponse(
|
||||
(Array.isArray(data) ? created : created[0]) as AddOrderTransactionStepOutput,
|
||||
(Array.isArray(data)
|
||||
? created
|
||||
: created[0]) as AddOrderTransactionStepOutput,
|
||||
created.map((c) => c.id)
|
||||
)
|
||||
},
|
||||
|
||||
@@ -2,3 +2,4 @@ export * from "./capture-payment"
|
||||
export * from "./process-payment"
|
||||
export * from "./refund-payment"
|
||||
export * from "./refund-payments"
|
||||
|
||||
|
||||
@@ -102,9 +102,7 @@ export const processPaymentWorkflow = createWorkflow(
|
||||
}).then(() => {
|
||||
completeCartWorkflow
|
||||
.runAsStep({
|
||||
input: {
|
||||
id: cartPaymentCollection.data[0].cart_id,
|
||||
},
|
||||
input: { id: cartPaymentCollection.data[0].cart_id },
|
||||
})
|
||||
.config({
|
||||
continueOnPermanentFailure: true, // Continue payment processing even if cart completion fails
|
||||
|
||||
@@ -35,10 +35,13 @@ export default async function paymentWebhookhandler({
|
||||
|
||||
const processedEvent = await paymentService.getWebhookActionAndData(input)
|
||||
|
||||
if (!processedEvent.data) {
|
||||
return
|
||||
}
|
||||
|
||||
if (
|
||||
processedEvent?.action === PaymentActions.NOT_SUPPORTED ||
|
||||
// Currently none of these are handled by the processPaymentWorkflow, so we ignore them.
|
||||
// Remove once the processPaymentWorkflow is handling them.
|
||||
// We currently don't handle these payment statuses in the processPayment function.
|
||||
processedEvent?.action === PaymentActions.CANCELED ||
|
||||
processedEvent?.action === PaymentActions.FAILED ||
|
||||
processedEvent?.action === PaymentActions.REQUIRES_MORE
|
||||
@@ -46,13 +49,7 @@ export default async function paymentWebhookhandler({
|
||||
return
|
||||
}
|
||||
|
||||
if (!processedEvent.data) {
|
||||
return
|
||||
}
|
||||
|
||||
await processPaymentWorkflow(container).run({
|
||||
input: processedEvent,
|
||||
})
|
||||
await processPaymentWorkflow(container).run({ input: processedEvent })
|
||||
}
|
||||
|
||||
export const config: SubscriberConfig = {
|
||||
|
||||
Reference in New Issue
Block a user