feat(core-flows,typers,utils,medusa): add payment auth step to complete cart workflow - [complete cart part 3] (#7248)
* chore: authorize payment sessions for cart * chore: add spec for cart returns * fix: Correctly select fields for cart * chore: fix specs + address comments --------- Co-authored-by: Stevche Radevski <sradevski@live.com> Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com> Co-authored-by: Carlos R. L. Rodrigues <37986729+carlos-r-l-rodrigues@users.noreply.github.com> Co-authored-by: Carlos R. L. Rodrigues <rodrigolr@gmail.com>
This commit is contained in:
co-authored by
Stevche Radevski
Oli Juhl
Carlos R. L. Rodrigues
Carlos R. L. Rodrigues
parent
5228b14ca9
commit
0430e63b0b
@@ -21,5 +21,6 @@ export * from "./retrieve-cart-with-links"
|
||||
export * from "./set-tax-lines-for-items"
|
||||
export * from "./update-cart-promotions"
|
||||
export * from "./update-carts"
|
||||
export * from "./validate-cart-payments"
|
||||
export * from "./validate-cart-shipping-options"
|
||||
export * from "./validate-variant-prices"
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import { CartWorkflowDTO } from "@medusajs/types"
|
||||
import { isPresent, MedusaError, PaymentSessionStatus } from "@medusajs/utils"
|
||||
import { createStep, StepResponse } from "@medusajs/workflows-sdk"
|
||||
|
||||
interface StepInput {
|
||||
cart: CartWorkflowDTO
|
||||
}
|
||||
|
||||
export const validateCartPaymentsStepId = "validate-cart-payments"
|
||||
export const validateCartPaymentsStep = createStep(
|
||||
validateCartPaymentsStepId,
|
||||
async (data: StepInput) => {
|
||||
const {
|
||||
cart: { payment_collection: paymentCollection },
|
||||
} = data
|
||||
|
||||
if (!isPresent(paymentCollection)) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Payment collection has not been initiated for cart`
|
||||
)
|
||||
}
|
||||
|
||||
// We check if any of these payment sessions are present in the cart
|
||||
// If not, we throw an error for the consumer to provide a processable payment session
|
||||
const processablePaymentStatuses = [
|
||||
PaymentSessionStatus.PENDING,
|
||||
PaymentSessionStatus.REQUIRES_MORE,
|
||||
]
|
||||
|
||||
const paymentsToProcess = paymentCollection.payment_sessions?.filter((ps) =>
|
||||
processablePaymentStatuses.includes(ps.status)
|
||||
)
|
||||
|
||||
if (!paymentsToProcess?.length) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Payment sessions are required to complete cart`
|
||||
)
|
||||
}
|
||||
|
||||
return new StepResponse(paymentsToProcess)
|
||||
}
|
||||
)
|
||||
@@ -5,23 +5,12 @@ import {
|
||||
transform,
|
||||
} from "@medusajs/workflows-sdk"
|
||||
import { useRemoteQueryStep } from "../../../common"
|
||||
import { createOrderFromCartStep } from "../steps"
|
||||
import { authorizePaymentSessionStep } from "../../../payment/steps/authorize-payment-session"
|
||||
import { createOrderFromCartStep, validateCartPaymentsStep } from "../steps"
|
||||
import { reserveInventoryStep } from "../steps/reserve-inventory"
|
||||
import { updateTaxLinesStep } from "../steps/update-tax-lines"
|
||||
import { completeCartFields } from "../utils/fields"
|
||||
import { confirmVariantInventoryWorkflow } from "./confirm-variant-inventory"
|
||||
|
||||
/*
|
||||
- [] Create Tax Lines
|
||||
- [] Authorize Payment
|
||||
- fail:
|
||||
- [] Delete Tax lines
|
||||
- [] Reserve Item from inventory (if enabled)
|
||||
- fail:
|
||||
- [] Delete reservations
|
||||
- [] Cancel Payment
|
||||
- [] Create order
|
||||
*/
|
||||
export const completeCartWorkflowId = "complete-cart"
|
||||
export const completeCartWorkflow = createWorkflow(
|
||||
completeCartWorkflowId,
|
||||
@@ -33,6 +22,15 @@ export const completeCartWorkflow = createWorkflow(
|
||||
list: false,
|
||||
})
|
||||
|
||||
const paymentSessions = validateCartPaymentsStep({ cart })
|
||||
|
||||
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,
|
||||
context: { cart_id: cart.id },
|
||||
})
|
||||
|
||||
const { variants, items, sales_channel_id } = transform(
|
||||
{ cart },
|
||||
(data) => {
|
||||
@@ -44,7 +42,6 @@ export const completeCartWorkflow = createWorkflow(
|
||||
variant_id: item.variant_id,
|
||||
quantity: item.quantity,
|
||||
})
|
||||
|
||||
allVariants.push(item.variant)
|
||||
})
|
||||
|
||||
@@ -64,8 +61,6 @@ export const completeCartWorkflow = createWorkflow(
|
||||
},
|
||||
})
|
||||
|
||||
updateTaxLinesStep({ cart_or_cart_id: cart, force_tax_calculation: true })
|
||||
|
||||
reserveInventoryStep(formatedInventoryItems)
|
||||
|
||||
const finalCart = useRemoteQueryStep({
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { IPaymentModuleService, Logger, PaymentDTO } from "@medusajs/types"
|
||||
import {
|
||||
ContainerRegistrationKeys,
|
||||
MedusaError,
|
||||
PaymentSessionStatus,
|
||||
} from "@medusajs/utils"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
type StepInput = {
|
||||
id: string
|
||||
context: Record<string, unknown>
|
||||
}
|
||||
|
||||
export const authorizePaymentSessionStepId = "authorize-payment-session-step"
|
||||
export const authorizePaymentSessionStep = createStep(
|
||||
authorizePaymentSessionStepId,
|
||||
async (input: StepInput, { container }) => {
|
||||
let payment: PaymentDTO | undefined
|
||||
const logger = container.resolve<Logger>(ContainerRegistrationKeys.LOGGER)
|
||||
const paymentModule = container.resolve<IPaymentModuleService>(
|
||||
ModuleRegistrationName.PAYMENT
|
||||
)
|
||||
|
||||
try {
|
||||
payment = await paymentModule.authorizePaymentSession(
|
||||
input.id,
|
||||
input.context || {}
|
||||
)
|
||||
} catch (e) {
|
||||
logger.error(
|
||||
`Error was thrown trying to authorize payment session - ${input.id} - ${e}`
|
||||
)
|
||||
}
|
||||
|
||||
const paymentSession = await paymentModule.retrievePaymentSession(input.id)
|
||||
|
||||
// Throw a special error type when the status is requires_more as it requires a specific further action
|
||||
// from the consumer
|
||||
if (paymentSession.status === PaymentSessionStatus.REQUIRES_MORE) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.PAYMENT_REQUIRES_MORE_ERROR,
|
||||
`More information is required for payment`
|
||||
)
|
||||
}
|
||||
|
||||
// If any other error other than requires_more shows up, this usually requires the consumer to create a new payment session
|
||||
// This could also be a system error thats caused by invalid setup or a failure in connecting to external providers
|
||||
if (paymentSession.status !== PaymentSessionStatus.AUTHORIZED || !payment) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.PAYMENT_AUTHORIZATION_ERROR,
|
||||
`Payment authorization failed`
|
||||
)
|
||||
}
|
||||
|
||||
return new StepResponse(payment)
|
||||
},
|
||||
// If payment or any other part of complete cart fails post payment step, we cancel any payments made
|
||||
async (payment, { container }) => {
|
||||
if (!payment) {
|
||||
return
|
||||
}
|
||||
|
||||
const logger = container.resolve<Logger>(ContainerRegistrationKeys.LOGGER)
|
||||
const paymentModule = container.resolve<IPaymentModuleService>(
|
||||
ModuleRegistrationName.PAYMENT
|
||||
)
|
||||
|
||||
// If the payment session status is requires_more, we don't have to revert the payment.
|
||||
// Return the same status for the cart completion to be re-run.
|
||||
if (
|
||||
payment.payment_session &&
|
||||
payment.payment_session.status === PaymentSessionStatus.REQUIRES_MORE
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await paymentModule.cancelPayment(payment.id)
|
||||
} catch (e) {
|
||||
logger.error(
|
||||
`Error was thrown trying to cancel payment - ${payment.id} - ${e}`
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
Reference in New Issue
Block a user