diff --git a/packages/core/types/src/payment/service.ts b/packages/core/types/src/payment/service.ts index d68a0ec89b43e..22c919fd37c1d 100644 --- a/packages/core/types/src/payment/service.ts +++ b/packages/core/types/src/payment/service.ts @@ -529,6 +529,21 @@ export interface IPaymentModuleService extends IModuleService { sharedContext?: Context ): Promise + /** + * This method cancels a payment session. + * + * @param {string} id - The ID of the payment session. + * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. + * @returns {Promise} The payment session's details. + * + * @example + * const paymentSession = await paymentModuleService.cancelPaymentSession("payses_123") + */ + cancelPaymentSession( + id: string, + sharedContext?: Context + ): Promise + /** * This method retrieves a paginated list of payment sessions based on optional filters and configuration. * diff --git a/packages/modules/payment/src/services/payment-module.ts b/packages/modules/payment/src/services/payment-module.ts index 70c519f0433bd..d8c2ea191e076 100644 --- a/packages/modules/payment/src/services/payment-module.ts +++ b/packages/modules/payment/src/services/payment-module.ts @@ -489,6 +489,77 @@ export default class PaymentModuleService ) } + @InjectManager("baseRepository_") + async cancelPaymentSession( + id: string, + @MedusaContext() sharedContext?: Context + ): Promise { + const paymentSession = await this.paymentSessionService_.retrieve( + id, + { select: ["data", "provider_id"] }, + sharedContext + ) + + await this.handleProviderSessionResponse_( + await this.paymentProviderService_.cancelPayment( + paymentSession.provider_id, + paymentSession.data + ), + sharedContext + ) + + return this.retrievePaymentSession(paymentSession.id, {}, sharedContext) + } + + @InjectManager("baseRepository_") + private async cancelPaymentSession_( + id: string, + data?: PaymentProviderSessionResponse["data"], + @MedusaContext() sharedContext?: Context + ): Promise { + let session = await this.paymentSessionService_.retrieve( + id, + { select: ["status"], relations: ["payment"] }, + sharedContext + ) + + if (session.status === PaymentSessionStatus.CANCELED) { + throw new MedusaError( + MedusaError.Types.INVALID_DATA, + `The payment session: ${session.id} has been canceled.` + ) + } + + if ( + session.status !== PaymentSessionStatus.PENDING && + session.status !== PaymentSessionStatus.REQUIRES_MORE && + session.status !== PaymentSessionStatus.AUTHORIZED + ) { + throw new MedusaError( + MedusaError.Types.INVALID_DATA, + `The payment session: ${session.id} cannot be canceled.` + ) + } + + if (session.payment) { + await this.paymentService_.update( + { + id: session.payment.id, + canceled_at: new Date(), + data, + }, + sharedContext + ) + } + + session = await this.paymentSessionService_.update( + { id, data, status: PaymentSessionStatus.CANCELED }, + sharedContext + ) + + return session + } + @InjectManager("baseRepository_") // @ts-expect-error async retrievePaymentSession( @@ -1075,43 +1146,12 @@ export default class PaymentModuleService break } case "canceled": { - await this.paymentSessionService_.update( - { id: session_id, data, status }, - sharedContext - ) - const session = await this.paymentSessionService_.retrieve( + const session = await this.cancelPaymentSession_( session_id, - { - select: ["payment_collection_id"], - relations: ["payment", "payment.captures"], - }, + data, sharedContext ) - if (session.payment) { - if (session.payment.captures.length > 0) { - await this.paymentService_.update( - { - id: session.payment.id, - captured_at: new Date(), - data, - }, - sharedContext - ) - } else { - await this.paymentService_.update( - { - id: session.payment.id, - canceled_at: new Date(), - data, - }, - sharedContext - ) - } - - await this.maybeUpdatePaymentCollection_( - session.payment_collection_id - ) - } + await this.maybeUpdatePaymentCollection_(session.payment_collection_id) break } default: {