diff --git a/.changeset/upset-keys-deny.md b/.changeset/upset-keys-deny.md new file mode 100644 index 0000000000..182bef3129 --- /dev/null +++ b/.changeset/upset-keys-deny.md @@ -0,0 +1,5 @@ +--- +"@medusajs/core-flows": patch +--- + +feat(core-flows): Allow payment session status captured to be processable upon cart completion diff --git a/integration-tests/http/__tests__/cart/store/cart.spec.ts b/integration-tests/http/__tests__/cart/store/cart.spec.ts index d6a2421a4b..323efeb82b 100644 --- a/integration-tests/http/__tests__/cart/store/cart.spec.ts +++ b/integration-tests/http/__tests__/cart/store/cart.spec.ts @@ -2,6 +2,7 @@ import { createCartCreditLinesWorkflow } from "@medusajs/core-flows" import { medusaIntegrationTestRunner } from "@medusajs/test-utils" import { Modules, + PaymentSessionStatus, PriceListStatus, PriceListType, ProductStatus, @@ -2003,6 +2004,68 @@ medusaIntegrationTestRunner({ ) }) + it("should successfully complete cart with pre existing captured payment session", async () => { + const paymentModule = appContainer.resolve(Modules.PAYMENT) + + const paymentCollection = ( + await api.post( + `/store/payment-collections`, + { cart_id: cart.id }, + storeHeaders + ) + ).data.payment_collection + + const paymentSession = await api + .post( + `/store/payment-collections/${paymentCollection.id}/payment-sessions`, + { provider_id: "pp_system_default" }, + storeHeaders + ) + .then((res) => res.data.payment_collection.payment_sessions[0]) + + // Authorize the payment session (creates a payment) + const payment = await paymentModule.authorizePaymentSession( + paymentSession.id, + {} + ) + + // Capture the payment + await paymentModule.capturePayment({ + payment_id: payment.id, + }) + + const updatedPaymentSession = + await paymentModule.retrievePaymentSession(paymentSession.id, { + relations: ["payment", "payment.captures"], + }) + expect(updatedPaymentSession.payment.captures).toHaveLength(1) + expect(updatedPaymentSession.status).toBe( + PaymentSessionStatus.AUTHORIZED + ) + + // Complete the cart + const response = await api.post( + `/store/carts/${cart.id}/complete`, + {}, + storeHeaders + ) + + expect(response.status).toEqual(200) + expect(response.data.order).toEqual( + expect.objectContaining({ + id: expect.any(String), + currency_code: "usd", + items: expect.arrayContaining([ + expect.objectContaining({ + unit_price: 1500, + compare_at_unit_price: null, + quantity: 1, + }), + ]), + }) + ) + }) + it("should successfully complete cart with credit lines alone", async () => { const oldCart = ( await api.get(`/store/carts/${cart.id}`, storeHeaders) diff --git a/packages/core/core-flows/src/cart/steps/validate-cart-payments.ts b/packages/core/core-flows/src/cart/steps/validate-cart-payments.ts index 4f3e64ef22..f1a9535dbd 100644 --- a/packages/core/core-flows/src/cart/steps/validate-cart-payments.ts +++ b/packages/core/core-flows/src/cart/steps/validate-cart-payments.ts @@ -20,7 +20,7 @@ export interface ValidateCartPaymentsStepInput { export const validateCartPaymentsStepId = "validate-cart-payments" /** * This step validates a cart's payment sessions. Their status must - * be `pending` or `requires_more`. If not valid, the step throws an error. + * be `pending`, `requires_more`, `authorized`, or `captured`. If not valid, the step throws an error. * * :::tip * @@ -62,6 +62,7 @@ export const validateCartPaymentsStep = createStep( PaymentSessionStatus.PENDING, PaymentSessionStatus.REQUIRES_MORE, PaymentSessionStatus.AUTHORIZED, // E.g. payment was authorized, but the cart was not completed + PaymentSessionStatus.CAPTURED, // E.g. payment was captured, but the cart was not completed ] const paymentsToProcess = paymentCollection.payment_sessions?.filter((ps) =>