diff --git a/integration-tests/http/__tests__/payment/admin/payment.spec.ts b/integration-tests/http/__tests__/payment/admin/payment.spec.ts index 6d412d098d..248aba2fb8 100644 --- a/integration-tests/http/__tests__/payment/admin/payment.spec.ts +++ b/integration-tests/http/__tests__/payment/admin/payment.spec.ts @@ -1,11 +1,12 @@ import { ClaimType } from "@medusajs/utils" -import { adminHeaders } from "../../../../helpers/create-admin-user" - import { medusaIntegrationTestRunner } from "medusa-test-utils" -import { createAdminUser } from "../../../../helpers/create-admin-user" +import { + adminHeaders, + createAdminUser, +} from "../../../../helpers/create-admin-user" import { createOrderSeeder } from "../../fixtures/order" -jest.setTimeout(30000) +jest.setTimeout(50000) medusaIntegrationTestRunner({ testSuite: ({ dbConnection, getContainer, api }) => { @@ -78,6 +79,92 @@ medusaIntegrationTestRunner({ expect(response.status).toEqual(200) }) + it("should throw if capture amount is greater than authorized amount", async () => { + const payment = order.payment_collections[0].payments[0] + + const response = await api.post( + `/admin/payments/${payment.id}/capture`, + { amount: 75 }, + adminHeaders + ) + + expect(response.data.payment).toEqual( + expect.objectContaining({ + id: payment.id, + captured_at: null, // not fully captured yet + captures: [ + expect.objectContaining({ + id: expect.any(String), + amount: 75, + }), + ], + refunds: [], + amount: 100, + }) + ) + expect(response.status).toEqual(200) + + const errResponse = await api + .post( + `/admin/payments/${payment.id}/capture`, + { amount: 75 }, + adminHeaders + ) + .catch((e) => e) + + expect(errResponse.response.data.message).toEqual( + "You cannot capture more than the authorized amount substracted by what is already captured." + ) + }) + + it("should return payment if payment is already fully captured", async () => { + const payment = order.payment_collections[0].payments[0] + + const response = await api.post( + `/admin/payments/${payment.id}/capture`, + undefined, + adminHeaders + ) + + expect(response.data.payment).toEqual( + expect.objectContaining({ + id: payment.id, + captured_at: expect.any(String), + captures: [ + expect.objectContaining({ + id: expect.any(String), + amount: 100, + }), + ], + refunds: [], + amount: 100, + }) + ) + expect(response.status).toEqual(200) + + const anotherResponse = await api.post( + `/admin/payments/${payment.id}/capture`, + undefined, + adminHeaders + ) + + expect(anotherResponse.data.payment).toEqual( + expect.objectContaining({ + id: payment.id, + captured_at: expect.any(String), + captures: [ + expect.objectContaining({ + id: expect.any(String), + amount: 100, + }), + ], + refunds: [], + amount: 100, + }) + ) + expect(anotherResponse.status).toEqual(200) + }) + it("should refund a captured payment", async () => { const payment = order.payment_collections[0].payments[0] diff --git a/packages/modules/payment/integration-tests/__tests__/services/payment-module/index.spec.ts b/packages/modules/payment/integration-tests/__tests__/services/payment-module/index.spec.ts index d0e22efc8a..43bc178af9 100644 --- a/packages/modules/payment/integration-tests/__tests__/services/payment-module/index.spec.ts +++ b/packages/modules/payment/integration-tests/__tests__/services/payment-module/index.spec.ts @@ -636,21 +636,28 @@ moduleIntegrationTestRunner({ ) }) - it("should fail to capture already captured payment", async () => { + it("should return payment if payment is already captured", async () => { await service.capturePayment({ amount: 100, payment_id: "pay-id-1", }) - const error = await service - .capturePayment({ - amount: 100, - payment_id: "pay-id-1", - }) - .catch((e) => e) + const capturedPayment = await service.capturePayment({ + amount: 100, + payment_id: "pay-id-1", + }) - expect(error.message).toEqual( - "You cannot capture more than the authorized amount substracted by what is already captured." + expect(capturedPayment).toEqual( + expect.objectContaining({ + id: "pay-id-1", + amount: 100, + captures: [ + expect.objectContaining({ + amount: 100, + }), + ], + captured_at: expect.any(Date), + }) ) }) diff --git a/packages/modules/payment/src/services/payment-module.ts b/packages/modules/payment/src/services/payment-module.ts index ba93b7ed81..d27487f45c 100644 --- a/packages/modules/payment/src/services/payment-module.ts +++ b/packages/modules/payment/src/services/payment-module.ts @@ -583,7 +583,7 @@ export default class PaymentModuleService data: CreateCaptureDTO, @MedusaContext() sharedContext: Context = {} ): Promise { - const [payment, isFullyCaptured] = await this.capturePayment_( + const { payment, isFullyCaptured, capture } = await this.capturePayment_( data, sharedContext ) @@ -595,7 +595,9 @@ export default class PaymentModuleService sharedContext ) } catch (error) { - await super.deleteCaptures(data.payment_id, sharedContext) + if (capture?.id) { + await super.deleteCaptures({ id: capture.id }, sharedContext) + } throw error } @@ -615,7 +617,11 @@ export default class PaymentModuleService private async capturePayment_( data: CreateCaptureDTO, @MedusaContext() sharedContext: Context = {} - ): Promise<[Payment, boolean]> { + ): Promise<{ + payment: Payment + isFullyCaptured: boolean + capture?: Capture + }> { const payment = await this.paymentService_.retrieve( data.payment_id, { @@ -626,6 +632,7 @@ export default class PaymentModuleService "payment_collection_id", "amount", "raw_amount", + "captured_at", "canceled_at", ], relations: ["captures.raw_amount"], @@ -646,14 +653,7 @@ export default class PaymentModuleService } if (payment.captured_at) { - return [ - (await this.retrievePayment( - data.payment_id, - { relations: ["captures"] }, - sharedContext - )) as unknown as Payment, - true, - ] + return { payment, isFullyCaptured: true } } const capturedAmount = payment.captures.reduce((captureAmount, next) => { @@ -677,7 +677,7 @@ export default class PaymentModuleService ) const isFullyCaptured = MathBN.gte(totalCaptured, authorizedAmount) - await this.captureService_.create( + const capture = await this.captureService_.create( { payment: data.payment_id, amount: data.amount, @@ -686,7 +686,7 @@ export default class PaymentModuleService sharedContext ) - return [payment, isFullyCaptured] + return { payment, isFullyCaptured, capture } } @InjectManager() private async capturePaymentFromProvider_(