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:
1 parent
41f46b7723
commit
469ecef3c5
7 files changed
+103
-83
No files matched your search
@@ -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 {
|
import {
|
||||||
ContainerRegistrationKeys,
|
ContainerRegistrationKeys,
|
||||||
|
Modules,
|
||||||
PaymentSessionStatus,
|
PaymentSessionStatus,
|
||||||
} from "@medusajs/framework/utils"
|
} from "@medusajs/framework/utils"
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
import { createStep, StepResponse } from "@medusajs/framework/workflows-sdk"
|
||||||
import { Logger } from "@medusajs/framework/types"
|
|
||||||
import { IPaymentModuleService } from "@medusajs/framework/types"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The payment session's details for compensation.
|
* The payment session's details for compensation.
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import {
|
|||||||
useQueryGraphStep,
|
useQueryGraphStep,
|
||||||
useRemoteQueryStep,
|
useRemoteQueryStep,
|
||||||
} from "../../common"
|
} from "../../common"
|
||||||
|
import { addOrderTransactionStep } from "../../order/steps/add-order-transaction"
|
||||||
import { createOrdersStep } from "../../order/steps/create-orders"
|
import { createOrdersStep } from "../../order/steps/create-orders"
|
||||||
import { authorizePaymentSessionStep } from "../../payment/steps/authorize-payment-session"
|
import { authorizePaymentSessionStep } from "../../payment/steps/authorize-payment-session"
|
||||||
import { registerUsageStep } from "../../promotion/steps/register-usage"
|
import { registerUsageStep } from "../../promotion/steps/register-usage"
|
||||||
@@ -32,6 +33,7 @@ import {
|
|||||||
validateCartPaymentsStep,
|
validateCartPaymentsStep,
|
||||||
validateShippingStep,
|
validateShippingStep,
|
||||||
} from "../steps"
|
} from "../steps"
|
||||||
|
import { compensatePaymentIfNeededStep } from "../steps/compensate-payment-if-needed"
|
||||||
import { reserveInventoryStep } from "../steps/reserve-inventory"
|
import { reserveInventoryStep } from "../steps/reserve-inventory"
|
||||||
import { completeCartFields } from "../utils/fields"
|
import { completeCartFields } from "../utils/fields"
|
||||||
import { prepareConfirmInventoryInput } from "../utils/prepare-confirm-inventory-input"
|
import { prepareConfirmInventoryInput } from "../utils/prepare-confirm-inventory-input"
|
||||||
@@ -41,7 +43,6 @@ import {
|
|||||||
PrepareLineItemDataInput,
|
PrepareLineItemDataInput,
|
||||||
prepareTaxLinesData,
|
prepareTaxLinesData,
|
||||||
} from "../utils/prepare-line-item-data"
|
} from "../utils/prepare-line-item-data"
|
||||||
import { compensatePaymentIfNeededStep } from "../steps/compensate-payment-if-needed"
|
|
||||||
/**
|
/**
|
||||||
* The data to complete a cart and place an order.
|
* The data to complete a cart and place an order.
|
||||||
*/
|
*/
|
||||||
@@ -88,7 +89,7 @@ export const completeCartWorkflow = createWorkflow(
|
|||||||
{
|
{
|
||||||
name: completeCartWorkflowId,
|
name: completeCartWorkflowId,
|
||||||
store: true,
|
store: true,
|
||||||
idempotent: true,
|
idempotent: false,
|
||||||
retentionTime: THREE_DAYS,
|
retentionTime: THREE_DAYS,
|
||||||
},
|
},
|
||||||
(input: WorkflowData<CompleteCartWorkflowInput>) => {
|
(input: WorkflowData<CompleteCartWorkflowInput>) => {
|
||||||
@@ -152,16 +153,6 @@ export const completeCartWorkflow = createWorkflow(
|
|||||||
|
|
||||||
validateShippingStep({ cart, shippingOptions })
|
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 { variants, sales_channel_id } = transform({ cart }, (data) => {
|
||||||
const variantsMap: Record<string, any> = {}
|
const variantsMap: Record<string, any> = {}
|
||||||
const allItems = data.cart?.items?.map((item) => {
|
const allItems = data.cart?.items?.map((item) => {
|
||||||
@@ -181,19 +172,7 @@ export const completeCartWorkflow = createWorkflow(
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const cartToOrder = transform({ cart, payment }, ({ cart, payment }) => {
|
const cartToOrder = transform({ cart }, ({ cart }) => {
|
||||||
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 allItems = (cart.items ?? []).map((item) => {
|
const allItems = (cart.items ?? []).map((item) => {
|
||||||
const input: PrepareLineItemDataInput = {
|
const input: PrepareLineItemDataInput = {
|
||||||
item,
|
item,
|
||||||
@@ -259,7 +238,6 @@ export const completeCartWorkflow = createWorkflow(
|
|||||||
shipping_methods: shippingMethods,
|
shipping_methods: shippingMethods,
|
||||||
metadata: cart.metadata,
|
metadata: cart.metadata,
|
||||||
promo_codes: promoCodes,
|
promo_codes: promoCodes,
|
||||||
transactions,
|
|
||||||
credit_lines: creditLines,
|
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(
|
const promotionUsage = transform(
|
||||||
{ cart },
|
{ cart },
|
||||||
({ cart }: { cart: CartWorkflowDTO }) => {
|
({ 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", {
|
createHook("orderCreated", {
|
||||||
order_id: createdOrder.id,
|
order_id: createdOrder.id,
|
||||||
|
|||||||
@@ -5,12 +5,16 @@ import { StepResponse, createStep } from "@medusajs/framework/workflows-sdk"
|
|||||||
/**
|
/**
|
||||||
* The transaction(s) to add to the order.
|
* The transaction(s) to add to the order.
|
||||||
*/
|
*/
|
||||||
export type AddOrderTransactionStepInput = CreateOrderTransactionDTO | CreateOrderTransactionDTO[]
|
export type AddOrderTransactionStepInput =
|
||||||
|
| CreateOrderTransactionDTO
|
||||||
|
| CreateOrderTransactionDTO[]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The added order transaction(s).
|
* The added order transaction(s).
|
||||||
*/
|
*/
|
||||||
export type AddOrderTransactionStepOutput = CreateOrderTransactionDTO | CreateOrderTransactionDTO[]
|
export type AddOrderTransactionStepOutput =
|
||||||
|
| CreateOrderTransactionDTO
|
||||||
|
| CreateOrderTransactionDTO[]
|
||||||
|
|
||||||
export const addOrderTransactionStepId = "add-order-transaction"
|
export const addOrderTransactionStepId = "add-order-transaction"
|
||||||
/**
|
/**
|
||||||
@@ -18,14 +22,15 @@ export const addOrderTransactionStepId = "add-order-transaction"
|
|||||||
*/
|
*/
|
||||||
export const addOrderTransactionStep = createStep(
|
export const addOrderTransactionStep = createStep(
|
||||||
addOrderTransactionStepId,
|
addOrderTransactionStepId,
|
||||||
async (
|
async (data: AddOrderTransactionStepInput, { container }) => {
|
||||||
data: AddOrderTransactionStepInput,
|
|
||||||
{ container }
|
|
||||||
) => {
|
|
||||||
const service = container.resolve(Modules.ORDER)
|
const service = container.resolve(Modules.ORDER)
|
||||||
|
|
||||||
const trxsData = Array.isArray(data) ? data : [data]
|
const trxsData = Array.isArray(data) ? data : [data]
|
||||||
|
|
||||||
|
if (!trxsData.length) {
|
||||||
|
return new StepResponse(null)
|
||||||
|
}
|
||||||
|
|
||||||
for (const trx of trxsData) {
|
for (const trx of trxsData) {
|
||||||
const existing = await service.listOrderTransactions(
|
const existing = await service.listOrderTransactions(
|
||||||
{
|
{
|
||||||
@@ -46,7 +51,9 @@ export const addOrderTransactionStep = createStep(
|
|||||||
const created = await service.addOrderTransactions(trxsData)
|
const created = await service.addOrderTransactions(trxsData)
|
||||||
|
|
||||||
return new StepResponse(
|
return new StepResponse(
|
||||||
(Array.isArray(data) ? created : created[0]) as AddOrderTransactionStepOutput,
|
(Array.isArray(data)
|
||||||
|
? created
|
||||||
|
: created[0]) as AddOrderTransactionStepOutput,
|
||||||
created.map((c) => c.id)
|
created.map((c) => c.id)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,3 +2,4 @@ export * from "./capture-payment"
|
|||||||
export * from "./process-payment"
|
export * from "./process-payment"
|
||||||
export * from "./refund-payment"
|
export * from "./refund-payment"
|
||||||
export * from "./refund-payments"
|
export * from "./refund-payments"
|
||||||
|
|
||||||
@@ -102,9 +102,7 @@ export const processPaymentWorkflow = createWorkflow(
|
|||||||
}).then(() => {
|
}).then(() => {
|
||||||
completeCartWorkflow
|
completeCartWorkflow
|
||||||
.runAsStep({
|
.runAsStep({
|
||||||
input: {
|
input: { id: cartPaymentCollection.data[0].cart_id },
|
||||||
id: cartPaymentCollection.data[0].cart_id,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
.config({
|
.config({
|
||||||
continueOnPermanentFailure: true, // Continue payment processing even if cart completion fails
|
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)
|
const processedEvent = await paymentService.getWebhookActionAndData(input)
|
||||||
|
|
||||||
|
if (!processedEvent.data) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
processedEvent?.action === PaymentActions.NOT_SUPPORTED ||
|
processedEvent?.action === PaymentActions.NOT_SUPPORTED ||
|
||||||
// Currently none of these are handled by the processPaymentWorkflow, so we ignore them.
|
// We currently don't handle these payment statuses in the processPayment function.
|
||||||
// Remove once the processPaymentWorkflow is handling them.
|
|
||||||
processedEvent?.action === PaymentActions.CANCELED ||
|
processedEvent?.action === PaymentActions.CANCELED ||
|
||||||
processedEvent?.action === PaymentActions.FAILED ||
|
processedEvent?.action === PaymentActions.FAILED ||
|
||||||
processedEvent?.action === PaymentActions.REQUIRES_MORE
|
processedEvent?.action === PaymentActions.REQUIRES_MORE
|
||||||
@@ -46,13 +49,7 @@ export default async function paymentWebhookhandler({
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!processedEvent.data) {
|
await processPaymentWorkflow(container).run({ input: processedEvent })
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
await processPaymentWorkflow(container).run({
|
|
||||||
input: processedEvent,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const config: SubscriberConfig = {
|
export const config: SubscriberConfig = {
|
||||||
|
|||||||
Reference in new issue
Block a user