From 3cf8365a83720d39212e847081dd863246ef2c96 Mon Sep 17 00:00:00 2001 From: fPolic Date: Wed, 17 Jan 2024 09:42:28 +0100 Subject: [PATCH 01/36] wip: setup --- packages/payment/src/services/index.ts | 1 + .../src/services/payment-collection.ts | 39 +++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 packages/payment/src/services/payment-collection.ts diff --git a/packages/payment/src/services/index.ts b/packages/payment/src/services/index.ts index a4f5ea37aea2e..dc38616342ad9 100644 --- a/packages/payment/src/services/index.ts +++ b/packages/payment/src/services/index.ts @@ -1 +1,2 @@ export { default as PaymentModuleService } from "./payment-module" +export { default as PaymentCollection } from "./payment-collection" diff --git a/packages/payment/src/services/payment-collection.ts b/packages/payment/src/services/payment-collection.ts new file mode 100644 index 0000000000000..9b55d1d30e48e --- /dev/null +++ b/packages/payment/src/services/payment-collection.ts @@ -0,0 +1,39 @@ +import { PaymentCollection } from "@models" +import { Context, DAL } from "@medusajs/types" +import { + doNotForceTransaction, + InjectTransactionManager, + MedusaContext, + shouldForceTransaction, +} from "@medusajs/utils" + +type InjectedDependencies = { + paymentCollectionRepository: DAL.RepositoryService +} + +export default class PaymentCollectionService< + T extends PaymentCollection = PaymentCollection +> { + protected readonly paymentCollectionRepository_: DAL.RepositoryService + + constructor({ paymentCollectionRepository }: InjectedDependencies) { + this.paymentCollectionRepository_ = paymentCollectionRepository + } + + @InjectTransactionManager( + shouldForceTransaction, + "paymentCollectionRepository_" + ) + create() {} + + @InjectTransactionManager( + doNotForceTransaction, + "paymentCollectionRepository_" + ) + async delete( + ids: string[], + @MedusaContext() sharedContext: Context = {} + ): Promise { + await this.paymentCollectionRepository_.delete(ids, sharedContext) + } +} From b83ef419fcc1df3bf39df457116b9f4caadeed00 Mon Sep 17 00:00:00 2001 From: fPolic Date: Wed, 17 Jan 2024 10:53:07 +0100 Subject: [PATCH 02/36] feat: create --- .../services/payment-module/index.spec.ts | 74 +++++++++++++++++++ packages/payment/src/initialize/index.ts | 7 +- packages/payment/src/joiner-config.ts | 3 +- packages/payment/src/loaders/container.ts | 7 +- packages/payment/src/repositories/index.ts | 2 + .../src/repositories/payment-collection.ts | 10 +++ .../src/services/payment-collection.ts | 15 +++- .../payment/src/services/payment-module.ts | 48 +++++++++++- 8 files changed, 157 insertions(+), 9 deletions(-) create mode 100644 packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts create mode 100644 packages/payment/src/repositories/payment-collection.ts diff --git a/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts b/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts new file mode 100644 index 0000000000000..8171882683899 --- /dev/null +++ b/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts @@ -0,0 +1,74 @@ +import { IPaymentModuleService } from "@medusajs/types" +import { SqlEntityManager } from "@mikro-orm/postgresql" + +import { initialize } from "../../../../src/initialize" +import { DB_URL, MikroOrmWrapper } from "../../../utils" + +jest.setTimeout(30000) + +describe("Payment Module Service", () => { + let service: IPaymentModuleService + let repositoryManager: SqlEntityManager + + beforeEach(async () => { + await MikroOrmWrapper.setupDatabase() + repositoryManager = await MikroOrmWrapper.forkManager() + + service = await initialize({ + database: { + clientUrl: DB_URL, + schema: process.env.MEDUSA_PAYMNET_DB_SCHEMA, + }, + }) + }) + + afterEach(async () => { + await MikroOrmWrapper.clearDatabase() + }) + + describe("create", () => { + it("should throw an error when required params are not passed", async () => { + let error = await service + .createPaymentCollection([ + { + amount: 200, + } as any, + ]) + .catch((e) => e) + + expect(error.message).toContain( + "Value for PaymentCollection.currency_code is required, 'undefined' found" + ) + + error = await service + .createPaymentCollection([ + { + currency_code: "USD", + } as any, + ]) + .catch((e) => e) + + expect(error.message).toContain( + "Value for PaymentCollection.amount is required, 'undefined' found" + ) + }) + + it("should create a payment collection successfully", async () => { + const [createdPaymentCollection] = await service.createPaymentCollection([ + { currency_code: "USD", amount: 200 }, + ]) + + expect(createdPaymentCollection).toEqual( + expect.objectContaining({ + id: expect.any(String), + status: "not_paid", + payment_providers: [], + payment_sessions: [], + payments: [], + currency_code: "USD", + amount: 200, + }) + ) + }) + }) +}) diff --git a/packages/payment/src/initialize/index.ts b/packages/payment/src/initialize/index.ts index 37830dcf8aba5..28208d3db37b5 100644 --- a/packages/payment/src/initialize/index.ts +++ b/packages/payment/src/initialize/index.ts @@ -6,11 +6,16 @@ import { Modules, } from "@medusajs/modules-sdk" import { IPaymentModuleService, ModulesSdkTypes } from "@medusajs/types" + import { moduleDefinition } from "../module-definition" import { InitializeModuleInjectableDependencies } from "../types" export const initialize = async ( - options?: ModulesSdkTypes.ModuleBootstrapDeclaration, + options?: + | ModulesSdkTypes.ModuleServiceInitializeOptions + | ModulesSdkTypes.ModuleServiceInitializeCustomDataLayerOptions + | ExternalModuleDeclaration + | InternalModuleDeclaration, injectedDependencies?: InitializeModuleInjectableDependencies ): Promise => { const loaded = await MedusaModule.bootstrap({ diff --git a/packages/payment/src/joiner-config.ts b/packages/payment/src/joiner-config.ts index a04381a6ef63f..4bffc28e732bf 100644 --- a/packages/payment/src/joiner-config.ts +++ b/packages/payment/src/joiner-config.ts @@ -1,10 +1,11 @@ import { Modules } from "@medusajs/modules-sdk" import { ModuleJoinerConfig } from "@medusajs/types" import { MapToConfig } from "@medusajs/utils" -import { Payment } from "@models" +import { Payment, PaymentCollection } from "@models" export const LinkableKeys = { payment_id: Payment.name, + payment_collection_id: PaymentCollection.name, } const entityLinkableKeysMap: MapToConfig = {} diff --git a/packages/payment/src/loaders/container.ts b/packages/payment/src/loaders/container.ts index ba1b855344a42..63c4045bf8a5b 100644 --- a/packages/payment/src/loaders/container.ts +++ b/packages/payment/src/loaders/container.ts @@ -18,7 +18,9 @@ export default async ({ )?.repositories container.register({ - // paymentService: asClass(defaultServices.PaymentService).singleton(), + paymentCollectionService: asClass( + defaultServices.PaymentCollection + ).singleton(), }) if (customRepositories) { @@ -35,5 +37,8 @@ export default async ({ function loadDefaultRepositories({ container }) { container.register({ baseRepository: asClass(defaultRepositories.BaseRepository).singleton(), + paymentCollectionRepository: asClass( + defaultRepositories.PaymentCollectionRepository + ).singleton(), }) } diff --git a/packages/payment/src/repositories/index.ts b/packages/payment/src/repositories/index.ts index 147c9cc259fa4..543b6508f757c 100644 --- a/packages/payment/src/repositories/index.ts +++ b/packages/payment/src/repositories/index.ts @@ -1 +1,3 @@ export { MikroOrmBaseRepository as BaseRepository } from "@medusajs/utils" + +export { PaymentCollectionRepository } from "./payment-collection" diff --git a/packages/payment/src/repositories/payment-collection.ts b/packages/payment/src/repositories/payment-collection.ts new file mode 100644 index 0000000000000..2f5af5d9f2beb --- /dev/null +++ b/packages/payment/src/repositories/payment-collection.ts @@ -0,0 +1,10 @@ +import { PaymentCollectionDTO } from "@medusajs/types" +import { DALUtils } from "@medusajs/utils" +import { PaymentCollection } from "@models" + +export class PaymentCollectionRepository extends DALUtils.mikroOrmBaseRepositoryFactory< + PaymentCollection, + { + create: PaymentCollectionDTO + } +>(PaymentCollection) {} diff --git a/packages/payment/src/services/payment-collection.ts b/packages/payment/src/services/payment-collection.ts index 9b55d1d30e48e..d6c72a9d6ba6f 100644 --- a/packages/payment/src/services/payment-collection.ts +++ b/packages/payment/src/services/payment-collection.ts @@ -1,5 +1,5 @@ import { PaymentCollection } from "@models" -import { Context, DAL } from "@medusajs/types" +import { Context, DAL, PaymentCollectionDTO } from "@medusajs/types" import { doNotForceTransaction, InjectTransactionManager, @@ -7,12 +7,14 @@ import { shouldForceTransaction, } from "@medusajs/utils" +import { PaymentCollectionRepository } from "@repositories" + type InjectedDependencies = { paymentCollectionRepository: DAL.RepositoryService } export default class PaymentCollectionService< - T extends PaymentCollection = PaymentCollection + TEntity extends PaymentCollection = PaymentCollection > { protected readonly paymentCollectionRepository_: DAL.RepositoryService @@ -24,7 +26,14 @@ export default class PaymentCollectionService< shouldForceTransaction, "paymentCollectionRepository_" ) - create() {} + async create( + data: PaymentCollectionDTO[], + @MedusaContext() sharedContext?: Context + ): Promise { + return (await ( + this.paymentCollectionRepository_ as PaymentCollectionRepository + ).create(data, sharedContext)) as TEntity[] + } @InjectTransactionManager( doNotForceTransaction, diff --git a/packages/payment/src/services/payment-module.ts b/packages/payment/src/services/payment-module.ts index 9dc1a7a406f44..f73d4439df88c 100644 --- a/packages/payment/src/services/payment-module.ts +++ b/packages/payment/src/services/payment-module.ts @@ -1,8 +1,15 @@ import { + Context, + CreatePaymentCollectionDTO, DAL, InternalModuleDeclaration, + IPaymentModuleService, ModuleJoinerConfig, + PaymentCollectionDTO, } from "@medusajs/types" +import { InjectTransactionManager, MedusaContext } from "@medusajs/utils" + +import * as services from "@services" import { Payment } from "@models" @@ -10,20 +17,55 @@ import { joinerConfig } from "../joiner-config" type InjectedDependencies = { baseRepository: DAL.RepositoryService + paymentCollectionService: services.PaymentCollection } -// TODO: implement IPaymentModule -export default class PaymentModule { +export default class PaymentModule + implements IPaymentModuleService +{ protected baseRepository_: DAL.RepositoryService + protected paymentCollectionService_: services.PaymentCollection constructor( - { baseRepository }: InjectedDependencies, + { baseRepository, paymentCollectionService }: InjectedDependencies, protected readonly moduleDeclaration: InternalModuleDeclaration ) { this.baseRepository_ = baseRepository + + this.paymentCollectionService_ = paymentCollectionService } __joinerConfig(): ModuleJoinerConfig { return joinerConfig } + + createPaymentCollection( + data: CreatePaymentCollectionDTO, + sharedContext?: Context + ): Promise + + createPaymentCollection( + data: CreatePaymentCollectionDTO[], + sharedContext?: Context + ): Promise + + @InjectTransactionManager("baseRepository_") + async createPaymentCollection( + data: CreatePaymentCollectionDTO | CreatePaymentCollectionDTO[], + @MedusaContext() sharedContext?: Context + ): Promise { + const input = Array.isArray(data) ? data : [data] + + const collections = await this.paymentCollectionService_.create( + input, + sharedContext + ) + + return await this.baseRepository_.serialize( + Array.isArray(data) ? collections : collections[0], + { + populate: true, + } + ) + } } From 426d60976055bbdd35cf464633686d0f9a8b000f Mon Sep 17 00:00:00 2001 From: fPolic Date: Wed, 17 Jan 2024 12:10:23 +0100 Subject: [PATCH 03/36] feat: delete PC --- .../services/payment-module/index.spec.ts | 17 +++++++++++++- .../src/services/payment-collection.ts | 17 +++----------- .../payment/src/services/payment-module.ts | 22 +++++++++++++++++++ 3 files changed, 41 insertions(+), 15 deletions(-) diff --git a/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts b/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts index 8171882683899..9b5e133591bc5 100644 --- a/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts +++ b/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts @@ -32,6 +32,7 @@ describe("Payment Module Service", () => { .createPaymentCollection([ { amount: 200, + region_id: "req_123", } as any, ]) .catch((e) => e) @@ -44,6 +45,7 @@ describe("Payment Module Service", () => { .createPaymentCollection([ { currency_code: "USD", + region_id: "req_123", } as any, ]) .catch((e) => e) @@ -51,11 +53,24 @@ describe("Payment Module Service", () => { expect(error.message).toContain( "Value for PaymentCollection.amount is required, 'undefined' found" ) + + error = await service + .createPaymentCollection([ + { + currency_code: "USD", + amount: 200, + } as any, + ]) + .catch((e) => e) + + expect(error.message).toContain( + "Value for PaymentCollection.region_id is required, 'undefined' found" + ) }) it("should create a payment collection successfully", async () => { const [createdPaymentCollection] = await service.createPaymentCollection([ - { currency_code: "USD", amount: 200 }, + { currency_code: "USD", amount: 200, region_id: "reg_123" }, ]) expect(createdPaymentCollection).toEqual( diff --git a/packages/payment/src/services/payment-collection.ts b/packages/payment/src/services/payment-collection.ts index d6c72a9d6ba6f..5f662472667eb 100644 --- a/packages/payment/src/services/payment-collection.ts +++ b/packages/payment/src/services/payment-collection.ts @@ -1,11 +1,6 @@ import { PaymentCollection } from "@models" import { Context, DAL, PaymentCollectionDTO } from "@medusajs/types" -import { - doNotForceTransaction, - InjectTransactionManager, - MedusaContext, - shouldForceTransaction, -} from "@medusajs/utils" +import { InjectTransactionManager, MedusaContext } from "@medusajs/utils" import { PaymentCollectionRepository } from "@repositories" @@ -22,10 +17,7 @@ export default class PaymentCollectionService< this.paymentCollectionRepository_ = paymentCollectionRepository } - @InjectTransactionManager( - shouldForceTransaction, - "paymentCollectionRepository_" - ) + @InjectTransactionManager("paymentCollectionRepository_") async create( data: PaymentCollectionDTO[], @MedusaContext() sharedContext?: Context @@ -35,10 +27,7 @@ export default class PaymentCollectionService< ).create(data, sharedContext)) as TEntity[] } - @InjectTransactionManager( - doNotForceTransaction, - "paymentCollectionRepository_" - ) + @InjectTransactionManager("paymentCollectionRepository_") async delete( ids: string[], @MedusaContext() sharedContext: Context = {} diff --git a/packages/payment/src/services/payment-module.ts b/packages/payment/src/services/payment-module.ts index f73d4439df88c..a2020be4db448 100644 --- a/packages/payment/src/services/payment-module.ts +++ b/packages/payment/src/services/payment-module.ts @@ -68,4 +68,26 @@ export default class PaymentModule } ) } + + deletePaymentCollection( + paymentCollectionId: string[], + sharedContext?: Context + ): Promise + deletePaymentCollection( + paymentCollectionId: string, + sharedContext?: Context + ): Promise + + @InjectTransactionManager("baseRepository_") + async deletePaymentCollection( + ids: string | string[], + data: CreatePaymentCollectionDTO | CreatePaymentCollectionDTO[], + @MedusaContext() sharedContext?: Context + ) { + const paymentCollectionIds = Array.isArray(ids) ? ids : [ids] + await this.paymentCollectionService_.delete( + paymentCollectionIds, + sharedContext + ) + } } From 71d5d8f8549540b4d89ed13d582afd4c77eb9fb8 Mon Sep 17 00:00:00 2001 From: fPolic Date: Wed, 17 Jan 2024 12:12:37 +0100 Subject: [PATCH 04/36] fix; param --- packages/payment/src/services/payment-module.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/payment/src/services/payment-module.ts b/packages/payment/src/services/payment-module.ts index a2020be4db448..6a019e5bc1785 100644 --- a/packages/payment/src/services/payment-module.ts +++ b/packages/payment/src/services/payment-module.ts @@ -81,7 +81,6 @@ export default class PaymentModule @InjectTransactionManager("baseRepository_") async deletePaymentCollection( ids: string | string[], - data: CreatePaymentCollectionDTO | CreatePaymentCollectionDTO[], @MedusaContext() sharedContext?: Context ) { const paymentCollectionIds = Array.isArray(ids) ? ids : [ids] From fee5e19e6ed4741eda8a79f1c9396571816c573b Mon Sep 17 00:00:00 2001 From: fPolic Date: Wed, 17 Jan 2024 12:23:52 +0100 Subject: [PATCH 05/36] feat: list methods --- .../src/services/payment-collection.ts | 49 ++++++++++++++++++- .../payment/src/services/payment-module.ts | 48 +++++++++++++++++- 2 files changed, 94 insertions(+), 3 deletions(-) diff --git a/packages/payment/src/services/payment-collection.ts b/packages/payment/src/services/payment-collection.ts index 5f662472667eb..f9a7cd2908e20 100644 --- a/packages/payment/src/services/payment-collection.ts +++ b/packages/payment/src/services/payment-collection.ts @@ -1,6 +1,17 @@ import { PaymentCollection } from "@models" -import { Context, DAL, PaymentCollectionDTO } from "@medusajs/types" -import { InjectTransactionManager, MedusaContext } from "@medusajs/utils" +import { + Context, + DAL, + FilterablePaymentCollectionProps, + FindConfig, + PaymentCollectionDTO, +} from "@medusajs/types" +import { + InjectManager, + InjectTransactionManager, + MedusaContext, + ModulesSdkUtils, +} from "@medusajs/utils" import { PaymentCollectionRepository } from "@repositories" @@ -34,4 +45,38 @@ export default class PaymentCollectionService< ): Promise { await this.paymentCollectionRepository_.delete(ids, sharedContext) } + + @InjectManager("paymentCollectionRepository_") + async list( + filters: FilterablePaymentCollectionProps = {}, + config: FindConfig = {}, + @MedusaContext() sharedContext: Context = {} + ): Promise { + const queryOptions = ModulesSdkUtils.buildQuery( + filters, + config + ) + + return (await this.paymentCollectionRepository_.find( + queryOptions, + sharedContext + )) as TEntity[] + } + + @InjectManager("paymentCollectionRepository_") + async listAndCount( + filters: FilterablePaymentCollectionProps = {}, + config: FindConfig = {}, + @MedusaContext() sharedContext: Context = {} + ): Promise<[TEntity[], number]> { + const queryOptions = ModulesSdkUtils.buildQuery( + filters, + config + ) + + return (await this.paymentCollectionRepository_.findAndCount( + queryOptions, + sharedContext + )) as [TEntity[], number] + } } diff --git a/packages/payment/src/services/payment-module.ts b/packages/payment/src/services/payment-module.ts index 6a019e5bc1785..87ad93a034054 100644 --- a/packages/payment/src/services/payment-module.ts +++ b/packages/payment/src/services/payment-module.ts @@ -2,12 +2,18 @@ import { Context, CreatePaymentCollectionDTO, DAL, + FilterablePaymentCollectionProps, + FindConfig, InternalModuleDeclaration, IPaymentModuleService, ModuleJoinerConfig, PaymentCollectionDTO, } from "@medusajs/types" -import { InjectTransactionManager, MedusaContext } from "@medusajs/utils" +import { + InjectManager, + InjectTransactionManager, + MedusaContext, +} from "@medusajs/utils" import * as services from "@services" @@ -89,4 +95,44 @@ export default class PaymentModule sharedContext ) } + + @InjectManager("baseRepository_") + async listPaymentCollections( + filters?: FilterablePaymentCollectionProps, + config?: FindConfig, + @MedusaContext() sharedContext?: Context + ): Promise { + const paymentCollections = await this.paymentCollectionService_.list( + filters, + config, + sharedContext + ) + + return await this.baseRepository_.serialize( + paymentCollections, + { populate: true } + ) + } + + @InjectManager("baseRepository_") + async listAndCountPaymentCollections( + filters?: FilterablePaymentCollectionProps, + config?: FindConfig, + @MedusaContext() sharedContext?: Context + ): Promise<[PaymentCollectionDTO[], number]> { + const [paymentCollections, count] = + await this.paymentCollectionService_.listAndCount( + filters, + config, + sharedContext + ) + + return [ + await this.baseRepository_.serialize( + paymentCollections, + { populate: true } + ), + count, + ] + } } From d00fc3ca89ca1579dfc1e76c881860ecbb377795 Mon Sep 17 00:00:00 2001 From: fPolic Date: Wed, 17 Jan 2024 19:59:35 +0100 Subject: [PATCH 06/36] feat: update, list, tests wip --- .../__fixtures__/payment-collection/data.ts | 20 +++++++ .../__fixtures__/payment-collection/index.ts | 22 ++++++++ .../services/payment-module/index.spec.ts | 52 +++++++++++++++++++ .../src/repositories/payment-collection.ts | 4 +- .../src/services/payment-collection.ts | 13 ++++- .../payment/src/services/payment-module.ts | 29 +++++++++++ 6 files changed, 137 insertions(+), 3 deletions(-) create mode 100644 packages/payment/integration-tests/__fixtures__/payment-collection/data.ts create mode 100644 packages/payment/integration-tests/__fixtures__/payment-collection/index.ts diff --git a/packages/payment/integration-tests/__fixtures__/payment-collection/data.ts b/packages/payment/integration-tests/__fixtures__/payment-collection/data.ts new file mode 100644 index 0000000000000..692d57bc3e49f --- /dev/null +++ b/packages/payment/integration-tests/__fixtures__/payment-collection/data.ts @@ -0,0 +1,20 @@ +export const defaultPaymentCollectionData = [ + { + id: "pay-col-id-1", + amount: 100, + region_id: "region-id-1", + currency_code: "usd", + }, + { + id: "pay-col-id-2", + amount: 200, + region_id: "region-id-1", + currency_code: "usd", + }, + { + id: "pay-col-id-3", + amount: 300, + region_id: "region-id-2", + currency_code: "usd", + }, +] diff --git a/packages/payment/integration-tests/__fixtures__/payment-collection/index.ts b/packages/payment/integration-tests/__fixtures__/payment-collection/index.ts new file mode 100644 index 0000000000000..3700ffd5df4e0 --- /dev/null +++ b/packages/payment/integration-tests/__fixtures__/payment-collection/index.ts @@ -0,0 +1,22 @@ +import { CreatePaymentCollectionDTO } from "@medusajs/types" +import { SqlEntityManager } from "@mikro-orm/postgresql" + +import { PaymentCollection } from "../../../src/models" +import { defaultPaymentCollectionData } from "./data" + +export * from "./data" + +export async function createPaymentCollections( + manager: SqlEntityManager, + paymentCollectionData: CreatePaymentCollectionDTO[] = defaultPaymentCollectionData +): Promise { + const collections: PaymentCollection[] = [] + + for (let data of paymentCollectionData) { + let collection = manager.create(PaymentCollection, data) + + await manager.persistAndFlush(collection) + } + + return collections +} diff --git a/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts b/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts index 9b5e133591bc5..ed61e57081d17 100644 --- a/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts +++ b/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts @@ -3,6 +3,7 @@ import { SqlEntityManager } from "@mikro-orm/postgresql" import { initialize } from "../../../../src/initialize" import { DB_URL, MikroOrmWrapper } from "../../../utils" +import { createPaymentCollections } from "../../../__fixtures__/payment-collection" jest.setTimeout(30000) @@ -20,6 +21,8 @@ describe("Payment Module Service", () => { schema: process.env.MEDUSA_PAYMNET_DB_SCHEMA, }, }) + + await createPaymentCollections(repositoryManager) }) afterEach(async () => { @@ -86,4 +89,53 @@ describe("Payment Module Service", () => { ) }) }) + + describe("delete", () => { + it("should delete a Payment Collection", async () => { + let collection = await service.listPaymentCollections({ + id: ["pay-col-id-1"], + }) + + expect(collection.length).toEqual(1) + + await service.deletePaymentCollection(["pay-col-id-1"]) + + collection = await service.listPaymentCollections({ + id: ["pay-col-id-1"], + }) + + expect(collection.length).toEqual(0) + }) + }) + + describe("list", () => { + it("should list and count Payment Collection", async () => { + let [collections, count] = await service.listAndCountPaymentCollections() + + expect(count).toEqual(3) + + expect(collections).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: "pay-col-id-1", + amount: 100, + region_id: "region-id-1", + currency_code: "usd", + }), + expect.objectContaining({ + id: "pay-col-id-2", + amount: 200, + region_id: "region-id-1", + currency_code: "usd", + }), + expect.objectContaining({ + id: "pay-col-id-3", + amount: 300, + region_id: "region-id-2", + currency_code: "usd", + }), + ]) + ) + }) + }) }) diff --git a/packages/payment/src/repositories/payment-collection.ts b/packages/payment/src/repositories/payment-collection.ts index 2f5af5d9f2beb..c0476465163ae 100644 --- a/packages/payment/src/repositories/payment-collection.ts +++ b/packages/payment/src/repositories/payment-collection.ts @@ -1,10 +1,10 @@ -import { PaymentCollectionDTO } from "@medusajs/types" +import { CreatePaymentCollectionDTO } from "@medusajs/types" import { DALUtils } from "@medusajs/utils" import { PaymentCollection } from "@models" export class PaymentCollectionRepository extends DALUtils.mikroOrmBaseRepositoryFactory< PaymentCollection, { - create: PaymentCollectionDTO + create: CreatePaymentCollectionDTO } >(PaymentCollection) {} diff --git a/packages/payment/src/services/payment-collection.ts b/packages/payment/src/services/payment-collection.ts index f9a7cd2908e20..360eff93440ab 100644 --- a/packages/payment/src/services/payment-collection.ts +++ b/packages/payment/src/services/payment-collection.ts @@ -1,6 +1,7 @@ import { PaymentCollection } from "@models" import { Context, + CreatePaymentCollectionDTO, DAL, FilterablePaymentCollectionProps, FindConfig, @@ -30,7 +31,7 @@ export default class PaymentCollectionService< @InjectTransactionManager("paymentCollectionRepository_") async create( - data: PaymentCollectionDTO[], + data: CreatePaymentCollectionDTO[], @MedusaContext() sharedContext?: Context ): Promise { return (await ( @@ -38,6 +39,16 @@ export default class PaymentCollectionService< ).create(data, sharedContext)) as TEntity[] } + @InjectTransactionManager("paymentCollectionRepository_") + async update( + data: CreatePaymentCollectionDTO[], + @MedusaContext() sharedContext?: Context + ) { + return (await ( + this.paymentCollectionRepository_ as PaymentCollectionRepository + ).update(data, sharedContext)) as TEntity[] + } + @InjectTransactionManager("paymentCollectionRepository_") async delete( ids: string[], diff --git a/packages/payment/src/services/payment-module.ts b/packages/payment/src/services/payment-module.ts index 87ad93a034054..0f04fc6cad609 100644 --- a/packages/payment/src/services/payment-module.ts +++ b/packages/payment/src/services/payment-module.ts @@ -8,6 +8,7 @@ import { IPaymentModuleService, ModuleJoinerConfig, PaymentCollectionDTO, + UpdatePaymentCollectionDTO, } from "@medusajs/types" import { InjectManager, @@ -75,6 +76,34 @@ export default class PaymentModule ) } + updatePaymentCollection( + data: UpdatePaymentCollectionDTO[], + sharedContext?: Context + ): Promise + updatePaymentCollection( + data: UpdatePaymentCollectionDTO, + sharedContext?: Context + ): Promise + + @InjectTransactionManager("baseRepository_") + async updatePaymentCollection( + data: UpdatePaymentCollectionDTO | UpdatePaymentCollectionDTO[], + sharedContext?: Context + ): Promise { + const input = Array.isArray(data) ? data : [data] + const result = await this.paymentCollectionService_.update( + input, + sharedContext + ) + + return await this.baseRepository_.serialize( + Array.isArray(data) ? result : result[0], + { + populate: true, + } + ) + } + deletePaymentCollection( paymentCollectionId: string[], sharedContext?: Context From 66044a5700b4dfcede0b71841d02b48bc3a4f1fb Mon Sep 17 00:00:00 2001 From: fPolic Date: Thu, 18 Jan 2024 10:22:11 +0100 Subject: [PATCH 07/36] feat: list tests --- .../services/payment-module/index.spec.ts | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts b/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts index ed61e57081d17..94d93ec5cb56e 100644 --- a/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts +++ b/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts @@ -137,5 +137,51 @@ describe("Payment Module Service", () => { ]) ) }) + + it("should list Payment Collections by region_id", async () => { + let collections = await service.listPaymentCollections( + { + region_id: "region-id-1", + }, + { select: ["id", "amount", "region_id"] } + ) + + expect(collections.length).toEqual(2) + + expect(collections).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: "pay-col-id-1", + amount: 100, + region_id: "region-id-1", + }), + expect.objectContaining({ + id: "pay-col-id-2", + amount: 200, + region_id: "region-id-1", + }), + ]) + ) + }) + + it("should list Payment Collections by id", async () => { + let collections = await service.listPaymentCollections( + { + id: "pay-col-id-1", + }, + { select: ["id", "amount"] } + ) + + expect(collections.length).toEqual(1) + + expect(collections).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: "pay-col-id-1", + amount: 100, + }), + ]) + ) + }) }) }) From 8cf85816261f6d2363b7dfec341e22994c261540 Mon Sep 17 00:00:00 2001 From: fPolic Date: Thu, 18 Jan 2024 13:36:46 +0100 Subject: [PATCH 08/36] feat: update, retrieve, tests --- .../services/payment-module/index.spec.ts | 55 +++++--- .../src/repositories/payment-collection.ts | 6 +- .../src/services/payment-collection.ts | 24 +++- .../payment/src/services/payment-module.ts | 120 +++++++++++++++++- packages/types/src/payment/mutations.ts | 20 ++- 5 files changed, 201 insertions(+), 24 deletions(-) diff --git a/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts b/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts index 94d93ec5cb56e..98ff66df31647 100644 --- a/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts +++ b/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts @@ -108,6 +108,31 @@ describe("Payment Module Service", () => { }) }) + describe("retrieve", () => { + it("should retrieve a Payment Collection", async () => { + let collection = await service.retrievePaymentCollection("pay-col-id-2") + + expect(collection).toEqual( + expect.objectContaining({ + id: "pay-col-id-2", + amount: 200, + region_id: "region-id-1", + currency_code: "usd", + }) + ) + }) + + it("should fail to retrieve a non existent Payment Collection", async () => { + let error = await service + .retrievePaymentCollection("pay-col-id-not-exists") + .catch((e) => e) + + expect(error.message).toContain( + "PaymentCollection with id: pay-col-id-not-exists was not found" + ) + }) + }) + describe("list", () => { it("should list and count Payment Collection", async () => { let [collections, count] = await service.listAndCountPaymentCollections() @@ -163,24 +188,24 @@ describe("Payment Module Service", () => { ]) ) }) + }) - it("should list Payment Collections by id", async () => { - let collections = await service.listPaymentCollections( - { - id: "pay-col-id-1", - }, - { select: ["id", "amount"] } - ) + describe("update", () => { + it("should update a Payment Collection", async () => { + await service.updatePaymentCollection({ + id: "pay-col-id-2", + currency_code: "eur", + authorized_amount: 200, + }) - expect(collections.length).toEqual(1) + const collection = await service.retrievePaymentCollection("pay-col-id-2") - expect(collections).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: "pay-col-id-1", - amount: 100, - }), - ]) + expect(collection).toEqual( + expect.objectContaining({ + id: "pay-col-id-2", + authorized_amount: 200, + currency_code: "eur", + }) ) }) }) diff --git a/packages/payment/src/repositories/payment-collection.ts b/packages/payment/src/repositories/payment-collection.ts index c0476465163ae..887c8e15e3c37 100644 --- a/packages/payment/src/repositories/payment-collection.ts +++ b/packages/payment/src/repositories/payment-collection.ts @@ -1,4 +1,7 @@ -import { CreatePaymentCollectionDTO } from "@medusajs/types" +import { + CreatePaymentCollectionDTO, + UpdatePaymentCollectionDTO, +} from "@medusajs/types" import { DALUtils } from "@medusajs/utils" import { PaymentCollection } from "@models" @@ -6,5 +9,6 @@ export class PaymentCollectionRepository extends DALUtils.mikroOrmBaseRepository PaymentCollection, { create: CreatePaymentCollectionDTO + update: UpdatePaymentCollectionDTO } >(PaymentCollection) {} diff --git a/packages/payment/src/services/payment-collection.ts b/packages/payment/src/services/payment-collection.ts index 360eff93440ab..312971c29b0cd 100644 --- a/packages/payment/src/services/payment-collection.ts +++ b/packages/payment/src/services/payment-collection.ts @@ -6,12 +6,15 @@ import { FilterablePaymentCollectionProps, FindConfig, PaymentCollectionDTO, + UpdatePaymentCollectionDTO, } from "@medusajs/types" import { InjectManager, InjectTransactionManager, MedusaContext, + MedusaError, ModulesSdkUtils, + retrieveEntity, } from "@medusajs/utils" import { PaymentCollectionRepository } from "@repositories" @@ -29,6 +32,21 @@ export default class PaymentCollectionService< this.paymentCollectionRepository_ = paymentCollectionRepository } + @InjectManager("paymentCollectionRepository_") + async retrieve( + id: string, + config: FindConfig = {}, + @MedusaContext() sharedContext: Context = {} + ): Promise { + return (await retrieveEntity({ + id: id, + entityName: PaymentCollection.name, + repository: this.paymentCollectionRepository_, + config, + sharedContext, + })) as TEntity + } + @InjectTransactionManager("paymentCollectionRepository_") async create( data: CreatePaymentCollectionDTO[], @@ -41,9 +59,9 @@ export default class PaymentCollectionService< @InjectTransactionManager("paymentCollectionRepository_") async update( - data: CreatePaymentCollectionDTO[], - @MedusaContext() sharedContext?: Context - ) { + data: UpdatePaymentCollectionDTO[], + @MedusaContext() sharedContext: Context = {} + ): Promise { return (await ( this.paymentCollectionRepository_ as PaymentCollectionRepository ).update(data, sharedContext)) as TEntity[] diff --git a/packages/payment/src/services/payment-module.ts b/packages/payment/src/services/payment-module.ts index 0f04fc6cad609..91952672bbb6c 100644 --- a/packages/payment/src/services/payment-module.ts +++ b/packages/payment/src/services/payment-module.ts @@ -1,6 +1,8 @@ import { Context, CreatePaymentCollectionDTO, + CreatePaymentDTO, + CreatePaymentSessionDTO, DAL, FilterablePaymentCollectionProps, FindConfig, @@ -8,7 +10,10 @@ import { IPaymentModuleService, ModuleJoinerConfig, PaymentCollectionDTO, + PaymentDTO, + SetPaymentSessionsDTO, UpdatePaymentCollectionDTO, + UpdatePaymentDTO, } from "@medusajs/types" import { InjectManager, @@ -16,9 +21,8 @@ import { MedusaContext, } from "@medusajs/utils" -import * as services from "@services" - import { Payment } from "@models" +import * as services from "@services" import { joinerConfig } from "../joiner-config" @@ -125,6 +129,24 @@ export default class PaymentModule ) } + @InjectManager("baseRepository_") + async retrievePaymentCollection( + paymentCollectionId: string, + config: FindConfig = {}, + @MedusaContext() sharedContext: Context = {} + ): Promise { + const paymentCollection = await this.paymentCollectionService_.retrieve( + paymentCollectionId, + config, + sharedContext + ) + + return await this.baseRepository_.serialize( + paymentCollection, + { populate: true } + ) + } + @InjectManager("baseRepository_") async listPaymentCollections( filters?: FilterablePaymentCollectionProps, @@ -164,4 +186,98 @@ export default class PaymentModule count, ] } + + /** + * TODO + */ + + authorizePaymentCollection( + paymentCollectionId: string, + sharedContext?: Context | undefined + ): Promise { + throw new Error("Method not implemented.") + } + completePaymentCollection( + paymentCollectionId: string, + sharedContext?: Context | undefined + ): Promise { + throw new Error("Method not implemented.") + } + createPayment(data: CreatePaymentDTO): Promise + createPayment(data: CreatePaymentDTO[]): Promise + createPayment( + data: unknown + ): + | Promise + | Promise { + throw new Error("Method not implemented.") + } + capturePayment( + paymentId: string, + amount: number, + sharedContext?: Context | undefined + ): Promise { + throw new Error("Method not implemented.") + } + refundPayment( + paymentId: string, + amount: number, + sharedContext?: Context | undefined + ): Promise { + throw new Error("Method not implemented.") + } + updatePayment( + data: UpdatePaymentDTO, + sharedContext?: Context | undefined + ): Promise + updatePayment( + data: UpdatePaymentDTO[], + sharedContext?: Context | undefined + ): Promise + updatePayment( + data: unknown, + sharedContext?: unknown + ): + | Promise + | Promise { + throw new Error("Method not implemented.") + } + createPaymentSession( + paymentCollectionId: string, + data: CreatePaymentSessionDTO, + sharedContext?: Context | undefined + ): Promise + createPaymentSession( + paymentCollectionId: string, + data: CreatePaymentSessionDTO[], + sharedContext?: Context | undefined + ): Promise + createPaymentSession( + paymentCollectionId: unknown, + data: unknown, + sharedContext?: unknown + ): Promise { + throw new Error("Method not implemented.") + } + authorizePaymentSessions( + paymentCollectionId: string, + sessionIds: string[], + sharedContext?: Context | undefined + ): Promise { + throw new Error("Method not implemented.") + } + completePaymentSessions( + paymentCollectionId: string, + sessionIds: string[], + sharedContext?: Context | undefined + ): Promise { + throw new Error("Method not implemented.") + } + setPaymentSessions( + paymentCollectionId: string, + data: SetPaymentSessionsDTO[], + sharedContext?: Context | undefined + ): Promise { + throw new Error("Method not implemented.") + } } diff --git a/packages/types/src/payment/mutations.ts b/packages/types/src/payment/mutations.ts index 0359f7ba00b76..6e7abd1e100fa 100644 --- a/packages/types/src/payment/mutations.ts +++ b/packages/types/src/payment/mutations.ts @@ -1,15 +1,25 @@ /** - * TODO + * Payment Collection */ - export interface CreatePaymentCollectionDTO { region_id: string currency_code: string amount: number + + metadata?: Record } export interface UpdatePaymentCollectionDTO - extends CreatePaymentCollectionDTO {} + extends Partial { + id: string + + authorized_amount?: number + refunded_amount?: number +} + +/** + * Payment + */ export interface CreatePaymentDTO { amount: number @@ -30,6 +40,10 @@ export interface UpdatePaymentDTO { customer_id?: string } +/** + * Payment Session + */ + export interface CreatePaymentSessionDTO { amount: number currency_code: string From b1c9851d2e4279275b04e82a4e2dd7bdf3227f56 Mon Sep 17 00:00:00 2001 From: fPolic Date: Thu, 18 Jan 2024 17:28:59 +0100 Subject: [PATCH 09/36] refactor: use abstract service --- .../src/services/payment-collection.ts | 105 ++---------------- 1 file changed, 10 insertions(+), 95 deletions(-) diff --git a/packages/payment/src/services/payment-collection.ts b/packages/payment/src/services/payment-collection.ts index 312971c29b0cd..abeef3e6625e7 100644 --- a/packages/payment/src/services/payment-collection.ts +++ b/packages/payment/src/services/payment-collection.ts @@ -1,23 +1,10 @@ import { PaymentCollection } from "@models" import { - Context, CreatePaymentCollectionDTO, DAL, - FilterablePaymentCollectionProps, - FindConfig, - PaymentCollectionDTO, UpdatePaymentCollectionDTO, } from "@medusajs/types" -import { - InjectManager, - InjectTransactionManager, - MedusaContext, - MedusaError, - ModulesSdkUtils, - retrieveEntity, -} from "@medusajs/utils" - -import { PaymentCollectionRepository } from "@repositories" +import { ModulesSdkUtils } from "@medusajs/utils" type InjectedDependencies = { paymentCollectionRepository: DAL.RepositoryService @@ -25,87 +12,15 @@ type InjectedDependencies = { export default class PaymentCollectionService< TEntity extends PaymentCollection = PaymentCollection -> { - protected readonly paymentCollectionRepository_: DAL.RepositoryService - - constructor({ paymentCollectionRepository }: InjectedDependencies) { - this.paymentCollectionRepository_ = paymentCollectionRepository - } - - @InjectManager("paymentCollectionRepository_") - async retrieve( - id: string, - config: FindConfig = {}, - @MedusaContext() sharedContext: Context = {} - ): Promise { - return (await retrieveEntity({ - id: id, - entityName: PaymentCollection.name, - repository: this.paymentCollectionRepository_, - config, - sharedContext, - })) as TEntity - } - - @InjectTransactionManager("paymentCollectionRepository_") - async create( - data: CreatePaymentCollectionDTO[], - @MedusaContext() sharedContext?: Context - ): Promise { - return (await ( - this.paymentCollectionRepository_ as PaymentCollectionRepository - ).create(data, sharedContext)) as TEntity[] - } - - @InjectTransactionManager("paymentCollectionRepository_") - async update( - data: UpdatePaymentCollectionDTO[], - @MedusaContext() sharedContext: Context = {} - ): Promise { - return (await ( - this.paymentCollectionRepository_ as PaymentCollectionRepository - ).update(data, sharedContext)) as TEntity[] - } - - @InjectTransactionManager("paymentCollectionRepository_") - async delete( - ids: string[], - @MedusaContext() sharedContext: Context = {} - ): Promise { - await this.paymentCollectionRepository_.delete(ids, sharedContext) - } - - @InjectManager("paymentCollectionRepository_") - async list( - filters: FilterablePaymentCollectionProps = {}, - config: FindConfig = {}, - @MedusaContext() sharedContext: Context = {} - ): Promise { - const queryOptions = ModulesSdkUtils.buildQuery( - filters, - config - ) - - return (await this.paymentCollectionRepository_.find( - queryOptions, - sharedContext - )) as TEntity[] +> extends ModulesSdkUtils.abstractServiceFactory< + InjectedDependencies, + { + create: CreatePaymentCollectionDTO + update: UpdatePaymentCollectionDTO } - - @InjectManager("paymentCollectionRepository_") - async listAndCount( - filters: FilterablePaymentCollectionProps = {}, - config: FindConfig = {}, - @MedusaContext() sharedContext: Context = {} - ): Promise<[TEntity[], number]> { - const queryOptions = ModulesSdkUtils.buildQuery( - filters, - config - ) - - return (await this.paymentCollectionRepository_.findAndCount( - queryOptions, - sharedContext - )) as [TEntity[], number] +>(PaymentCollection) { + constructor(container: InjectedDependencies) { + // @ts-ignore + super(...arguments) } } From ebd8ea63d853496eeb2d3bc0e7d1b05893027ffe Mon Sep 17 00:00:00 2001 From: fPolic Date: Fri, 19 Jan 2024 11:06:33 +0100 Subject: [PATCH 10/36] fix: add missing props for update --- packages/types/src/payment/mutations.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/types/src/payment/mutations.ts b/packages/types/src/payment/mutations.ts index 6e7abd1e100fa..7f34bdd3eab70 100644 --- a/packages/types/src/payment/mutations.ts +++ b/packages/types/src/payment/mutations.ts @@ -1,3 +1,5 @@ +import { PaymentCollectionStatus } from "@medusajs/utils" + /** * Payment Collection */ @@ -15,6 +17,8 @@ export interface UpdatePaymentCollectionDTO authorized_amount?: number refunded_amount?: number + completed_at?: number + status?: PaymentCollectionStatus } /** From 9fd471593fe0f6fb4704bcf39d4d8fa4993fc9c9 Mon Sep 17 00:00:00 2001 From: fPolic Date: Fri, 19 Jan 2024 11:27:02 +0100 Subject: [PATCH 11/36] fix: move enum --- packages/types/src/payment/common.ts | 28 +++++++++++++++++++++++++ packages/types/src/payment/mutations.ts | 2 -- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/packages/types/src/payment/common.ts b/packages/types/src/payment/common.ts index 01788395ed35b..5f4b303bff64f 100644 --- a/packages/types/src/payment/common.ts +++ b/packages/types/src/payment/common.ts @@ -3,6 +3,34 @@ import { OperatorMap } from "../dal/utils" /* ********** PAYMENT COLLECTION ********** */ +/** + * @enum + * + * The payment collection's status. + */ +export enum PaymentCollectionStatus { + /** + * The payment collection isn't paid. + */ + NOT_PAID = "not_paid", + /** + * The payment collection is awaiting payment. + */ + AWAITING = "awaiting", + /** + * The payment collection is authorized. + */ + AUTHORIZED = "authorized", + /** + * Some of the payments in the payment collection are authorized. + */ + PARTIALLY_AUTHORIZED = "partially_authorized", + /** + * The payment collection is canceled. + */ + CANCELED = "canceled", +} + export interface PaymentCollectionDTO { /** * The ID of the Payment Collection diff --git a/packages/types/src/payment/mutations.ts b/packages/types/src/payment/mutations.ts index 7f34bdd3eab70..35807a10d8451 100644 --- a/packages/types/src/payment/mutations.ts +++ b/packages/types/src/payment/mutations.ts @@ -1,5 +1,3 @@ -import { PaymentCollectionStatus } from "@medusajs/utils" - /** * Payment Collection */ From c7da017aa70034900dad1d2f919b977c9c3c15dc Mon Sep 17 00:00:00 2001 From: fPolic Date: Fri, 19 Jan 2024 11:30:17 +0100 Subject: [PATCH 12/36] chore: import --- packages/types/src/payment/mutations.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/types/src/payment/mutations.ts b/packages/types/src/payment/mutations.ts index 35807a10d8451..c5cfa3e9b9bdc 100644 --- a/packages/types/src/payment/mutations.ts +++ b/packages/types/src/payment/mutations.ts @@ -1,3 +1,5 @@ +import { PaymentCollectionStatus } from "./common" + /** * Payment Collection */ From 083ca5f7193ddfad51b356cf3d2e0d4b2399d38e Mon Sep 17 00:00:00 2001 From: fPolic Date: Fri, 19 Jan 2024 13:07:38 +0100 Subject: [PATCH 13/36] wip: Payment and PaymentSession services, creation of entities and tests --- .../__fixtures__/payment/data.ts | 27 ++ .../__fixtures__/payment/index.ts | 22 + .../services/payment-module/index.spec.ts | 408 +++++++++++------- packages/payment/src/loaders/container.ts | 8 + .../payment/src/models/payment-session.ts | 5 +- packages/payment/src/repositories/index.ts | 2 + .../src/repositories/payment-session.ts | 10 + packages/payment/src/repositories/payment.ts | 11 + packages/payment/src/services/index.ts | 5 +- .../payment/src/services/payment-module.ts | 99 +++-- .../payment/src/services/payment-session.ts | 21 + packages/payment/src/services/payment.ts | 22 + packages/types/src/payment/mutations.ts | 3 + 13 files changed, 455 insertions(+), 188 deletions(-) create mode 100644 packages/payment/integration-tests/__fixtures__/payment/data.ts create mode 100644 packages/payment/integration-tests/__fixtures__/payment/index.ts create mode 100644 packages/payment/src/repositories/payment-session.ts create mode 100644 packages/payment/src/repositories/payment.ts create mode 100644 packages/payment/src/services/payment-session.ts create mode 100644 packages/payment/src/services/payment.ts diff --git a/packages/payment/integration-tests/__fixtures__/payment/data.ts b/packages/payment/integration-tests/__fixtures__/payment/data.ts new file mode 100644 index 0000000000000..e8bbcbc421b2e --- /dev/null +++ b/packages/payment/integration-tests/__fixtures__/payment/data.ts @@ -0,0 +1,27 @@ +export const defaultPaymentData = [ + { + id: "pay-id-1", + amount: 100, + provider_id: "stripe", + currency_code: "usd", + session: { id: "session-1" }, + payment_collection: { id: "pay_col-1" }, + data: { + data_key: "val", + }, + }, + // { + // id: "pay-id-2", + // amount: 200, + // provider_id: "manual", + // currency_code: "eur", + // data: {}, + // }, + // { + // id: "pay-id-3", + // amount: 300, + // provider_id: "manual", + // currency_code: "usd", + // data: {}, + // }, +] diff --git a/packages/payment/integration-tests/__fixtures__/payment/index.ts b/packages/payment/integration-tests/__fixtures__/payment/index.ts new file mode 100644 index 0000000000000..776cd6eee7eb6 --- /dev/null +++ b/packages/payment/integration-tests/__fixtures__/payment/index.ts @@ -0,0 +1,22 @@ +import { CreatePaymentDTO } from "@medusajs/types" +import { SqlEntityManager } from "@mikro-orm/postgresql" + +import { Payment } from "../../../src/models" +import { defaultPaymentData } from "./data" + +export * from "./data" + +export async function createPayment( + manager: SqlEntityManager, + paymentData: CreatePaymentDTO[] = defaultPaymentData +): Promise { + const collections: Payment[] = [] + + for (let data of paymentData) { + let collection = manager.create(Payment, data) + + await manager.persistAndFlush(collection) + } + + return collections +} diff --git a/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts b/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts index 98ff66df31647..f72d6255dfd7c 100644 --- a/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts +++ b/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts @@ -1,7 +1,7 @@ import { IPaymentModuleService } from "@medusajs/types" import { SqlEntityManager } from "@mikro-orm/postgresql" -import { initialize } from "../../../../src/initialize" +import { initialize } from "../../../../src" import { DB_URL, MikroOrmWrapper } from "../../../utils" import { createPaymentCollections } from "../../../__fixtures__/payment-collection" @@ -9,204 +9,302 @@ jest.setTimeout(30000) describe("Payment Module Service", () => { let service: IPaymentModuleService - let repositoryManager: SqlEntityManager - beforeEach(async () => { - await MikroOrmWrapper.setupDatabase() - repositoryManager = await MikroOrmWrapper.forkManager() + describe("PaymentCollection", () => { + let repositoryManager: SqlEntityManager - service = await initialize({ - database: { - clientUrl: DB_URL, - schema: process.env.MEDUSA_PAYMNET_DB_SCHEMA, - }, - }) - - await createPaymentCollections(repositoryManager) - }) + beforeEach(async () => { + await MikroOrmWrapper.setupDatabase() + repositoryManager = await MikroOrmWrapper.forkManager() - afterEach(async () => { - await MikroOrmWrapper.clearDatabase() - }) + service = await initialize({ + database: { + clientUrl: DB_URL, + schema: process.env.MEDUSA_PAYMNET_DB_SCHEMA, + }, + }) - describe("create", () => { - it("should throw an error when required params are not passed", async () => { - let error = await service - .createPaymentCollection([ - { - amount: 200, - region_id: "req_123", - } as any, - ]) - .catch((e) => e) + await createPaymentCollections(repositoryManager) + }) - expect(error.message).toContain( - "Value for PaymentCollection.currency_code is required, 'undefined' found" - ) + afterEach(async () => { + await MikroOrmWrapper.clearDatabase() + }) - error = await service - .createPaymentCollection([ - { - currency_code: "USD", - region_id: "req_123", - } as any, - ]) - .catch((e) => e) + describe("create", () => { + it("should throw an error when required params are not passed", async () => { + let error = await service + .createPaymentCollection([ + { + amount: 200, + region_id: "req_123", + } as any, + ]) + .catch((e) => e) + + expect(error.message).toContain( + "Value for PaymentCollection.currency_code is required, 'undefined' found" + ) + + error = await service + .createPaymentCollection([ + { + currency_code: "USD", + region_id: "req_123", + } as any, + ]) + .catch((e) => e) + + expect(error.message).toContain( + "Value for PaymentCollection.amount is required, 'undefined' found" + ) + + error = await service + .createPaymentCollection([ + { + currency_code: "USD", + amount: 200, + } as any, + ]) + .catch((e) => e) + + expect(error.message).toContain( + "Value for PaymentCollection.region_id is required, 'undefined' found" + ) + }) - expect(error.message).toContain( - "Value for PaymentCollection.amount is required, 'undefined' found" - ) + it("should create a payment collection successfully", async () => { + const [createdPaymentCollection] = + await service.createPaymentCollection([ + { currency_code: "USD", amount: 200, region_id: "reg_123" }, + ]) - error = await service - .createPaymentCollection([ - { + expect(createdPaymentCollection).toEqual( + expect.objectContaining({ + id: expect.any(String), + status: "not_paid", + payment_providers: [], + payment_sessions: [], + payments: [], currency_code: "USD", amount: 200, - } as any, - ]) - .catch((e) => e) - - expect(error.message).toContain( - "Value for PaymentCollection.region_id is required, 'undefined' found" - ) + }) + ) + }) }) - it("should create a payment collection successfully", async () => { - const [createdPaymentCollection] = await service.createPaymentCollection([ - { currency_code: "USD", amount: 200, region_id: "reg_123" }, - ]) - - expect(createdPaymentCollection).toEqual( - expect.objectContaining({ - id: expect.any(String), - status: "not_paid", - payment_providers: [], - payment_sessions: [], - payments: [], - currency_code: "USD", - amount: 200, + describe("delete", () => { + it("should delete a Payment Collection", async () => { + let collection = await service.listPaymentCollections({ + id: ["pay-col-id-1"], }) - ) + + expect(collection.length).toEqual(1) + + await service.deletePaymentCollection(["pay-col-id-1"]) + + collection = await service.listPaymentCollections({ + id: ["pay-col-id-1"], + }) + + expect(collection.length).toEqual(0) + }) }) - }) - describe("delete", () => { - it("should delete a Payment Collection", async () => { - let collection = await service.listPaymentCollections({ - id: ["pay-col-id-1"], + describe("retrieve", () => { + it("should retrieve a Payment Collection", async () => { + let collection = await service.retrievePaymentCollection("pay-col-id-2") + + expect(collection).toEqual( + expect.objectContaining({ + id: "pay-col-id-2", + amount: 200, + region_id: "region-id-1", + currency_code: "usd", + }) + ) }) - expect(collection.length).toEqual(1) + it("should fail to retrieve a non existent Payment Collection", async () => { + let error = await service + .retrievePaymentCollection("pay-col-id-not-exists") + .catch((e) => e) - await service.deletePaymentCollection(["pay-col-id-1"]) + expect(error.message).toContain( + "PaymentCollection with id: pay-col-id-not-exists was not found" + ) + }) + }) - collection = await service.listPaymentCollections({ - id: ["pay-col-id-1"], + describe("list", () => { + it("should list and count Payment Collection", async () => { + let [collections, count] = + await service.listAndCountPaymentCollections() + + expect(count).toEqual(3) + + expect(collections).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: "pay-col-id-1", + amount: 100, + region_id: "region-id-1", + currency_code: "usd", + }), + expect.objectContaining({ + id: "pay-col-id-2", + amount: 200, + region_id: "region-id-1", + currency_code: "usd", + }), + expect.objectContaining({ + id: "pay-col-id-3", + amount: 300, + region_id: "region-id-2", + currency_code: "usd", + }), + ]) + ) }) - expect(collection.length).toEqual(0) + it("should list Payment Collections by region_id", async () => { + let collections = await service.listPaymentCollections( + { + region_id: "region-id-1", + }, + { select: ["id", "amount", "region_id"] } + ) + + expect(collections.length).toEqual(2) + + expect(collections).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: "pay-col-id-1", + amount: 100, + region_id: "region-id-1", + }), + expect.objectContaining({ + id: "pay-col-id-2", + amount: 200, + region_id: "region-id-1", + }), + ]) + ) + }) }) - }) - describe("retrieve", () => { - it("should retrieve a Payment Collection", async () => { - let collection = await service.retrievePaymentCollection("pay-col-id-2") - - expect(collection).toEqual( - expect.objectContaining({ + describe("update", () => { + it("should update a Payment Collection", async () => { + await service.updatePaymentCollection({ id: "pay-col-id-2", - amount: 200, - region_id: "region-id-1", - currency_code: "usd", + currency_code: "eur", + authorized_amount: 200, }) - ) - }) - it("should fail to retrieve a non existent Payment Collection", async () => { - let error = await service - .retrievePaymentCollection("pay-col-id-not-exists") - .catch((e) => e) + const collection = await service.retrievePaymentCollection( + "pay-col-id-2" + ) - expect(error.message).toContain( - "PaymentCollection with id: pay-col-id-not-exists was not found" - ) + expect(collection).toEqual( + expect.objectContaining({ + id: "pay-col-id-2", + authorized_amount: 200, + currency_code: "eur", + }) + ) + }) }) }) - describe("list", () => { - it("should list and count Payment Collection", async () => { - let [collections, count] = await service.listAndCountPaymentCollections() + describe("PaymentSession", () => { + let repositoryManager: SqlEntityManager - expect(count).toEqual(3) + beforeEach(async () => { + await MikroOrmWrapper.setupDatabase() + repositoryManager = await MikroOrmWrapper.forkManager() - expect(collections).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: "pay-col-id-1", - amount: 100, - region_id: "region-id-1", - currency_code: "usd", - }), - expect.objectContaining({ - id: "pay-col-id-2", - amount: 200, - region_id: "region-id-1", - currency_code: "usd", - }), - expect.objectContaining({ - id: "pay-col-id-3", - amount: 300, - region_id: "region-id-2", - currency_code: "usd", - }), - ]) - ) + service = await initialize({ + database: { + clientUrl: DB_URL, + schema: process.env.MEDUSA_PAYMNET_DB_SCHEMA, + }, + }) + + await createPaymentCollections(repositoryManager) }) - it("should list Payment Collections by region_id", async () => { - let collections = await service.listPaymentCollections( - { - region_id: "region-id-1", - }, - { select: ["id", "amount", "region_id"] } - ) + afterEach(async () => { + await MikroOrmWrapper.clearDatabase() + }) - expect(collections.length).toEqual(2) + describe("create", () => { + it("should create a payment session successfully", async () => { + const createdSession = await service.createPaymentSession( + "pay-col-id-1", + { + amount: 200, + provider_id: "manual", + currency_code: "usd", + } + ) - expect(collections).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: "pay-col-id-1", - amount: 100, - region_id: "region-id-1", - }), + console.log(createdSession) + + expect(createdSession).toEqual( expect.objectContaining({ - id: "pay-col-id-2", - amount: 200, - region_id: "region-id-1", - }), - ]) - ) + id: expect.any(String), + }) + ) + }) }) }) - describe("update", () => { - it("should update a Payment Collection", async () => { - await service.updatePaymentCollection({ - id: "pay-col-id-2", - currency_code: "eur", - authorized_amount: 200, + describe("Payment", () => { + let repositoryManager: SqlEntityManager + + beforeEach(async () => { + await MikroOrmWrapper.setupDatabase() + repositoryManager = await MikroOrmWrapper.forkManager() + + service = await initialize({ + database: { + clientUrl: DB_URL, + schema: process.env.MEDUSA_PAYMNET_DB_SCHEMA, + }, }) - const collection = await service.retrievePaymentCollection("pay-col-id-2") + await createPaymentCollections(repositoryManager) + }) - expect(collection).toEqual( - expect.objectContaining({ - id: "pay-col-id-2", - authorized_amount: 200, - currency_code: "eur", + afterEach(async () => { + await MikroOrmWrapper.clearDatabase() + }) + + describe("create", () => { + it("should create a payment successfully", async () => { + const coll = await service.createPaymentCollection({ + currency_code: "usd", + amount: 200, + region_id: "reg", }) - ) + const [createdPayment] = await service.createPayment({ + data: {}, + amount: 200, + provider_id: "manual", + currency_code: "usd", + payment_collection_id: coll.id, + payment_session_id: "ses_123", + }) + + console.log(createdPayment) + + expect(createdPayment).toEqual( + expect.objectContaining({ + id: expect.any(String), + }) + ) + }) }) }) }) diff --git a/packages/payment/src/loaders/container.ts b/packages/payment/src/loaders/container.ts index 63c4045bf8a5b..e1e0c9ae4b143 100644 --- a/packages/payment/src/loaders/container.ts +++ b/packages/payment/src/loaders/container.ts @@ -21,6 +21,8 @@ export default async ({ paymentCollectionService: asClass( defaultServices.PaymentCollection ).singleton(), + paymentService: asClass(defaultServices.Payment).singleton(), + paymentSessionService: asClass(defaultServices.PaymentSession).singleton(), }) if (customRepositories) { @@ -40,5 +42,11 @@ function loadDefaultRepositories({ container }) { paymentCollectionRepository: asClass( defaultRepositories.PaymentCollectionRepository ).singleton(), + paymentRepository: asClass( + defaultRepositories.PaymentRepository + ).singleton(), + paymentSessionRepository: asClass( + defaultRepositories.PaymentSessionRepository + ).singleton(), }) } diff --git a/packages/payment/src/models/payment-session.ts b/packages/payment/src/models/payment-session.ts index f001852af5098..b0b14fa771142 100644 --- a/packages/payment/src/models/payment-session.ts +++ b/packages/payment/src/models/payment-session.ts @@ -5,6 +5,7 @@ import { ManyToOne, OneToOne, OnInit, + OptionalProps, PrimaryKey, Property, } from "@mikro-orm/core" @@ -15,6 +16,8 @@ import Payment from "./payment" @Entity({ tableName: "payment_session" }) export default class PaymentSession { + [OptionalProps]?: "status" + @PrimaryKey({ columnType: "text" }) id: string @@ -36,7 +39,7 @@ export default class PaymentSession { @Enum({ items: () => PaymentSessionStatus, }) - status: PaymentSessionStatus + status: PaymentSessionStatus = PaymentSessionStatus.PENDING @Property({ columnType: "timestamptz", diff --git a/packages/payment/src/repositories/index.ts b/packages/payment/src/repositories/index.ts index 543b6508f757c..56bd1dac36358 100644 --- a/packages/payment/src/repositories/index.ts +++ b/packages/payment/src/repositories/index.ts @@ -1,3 +1,5 @@ export { MikroOrmBaseRepository as BaseRepository } from "@medusajs/utils" +export { PaymentRepository } from "./payment" +export { PaymentSessionRepository } from "./payment-session" export { PaymentCollectionRepository } from "./payment-collection" diff --git a/packages/payment/src/repositories/payment-session.ts b/packages/payment/src/repositories/payment-session.ts new file mode 100644 index 0000000000000..08f7395f23b1c --- /dev/null +++ b/packages/payment/src/repositories/payment-session.ts @@ -0,0 +1,10 @@ +import { CreatePaymentSessionDTO } from "@medusajs/types" +import { DALUtils } from "@medusajs/utils" +import { PaymentSession } from "@models" + +export class PaymentSessionRepository extends DALUtils.mikroOrmBaseRepositoryFactory< + PaymentSession, + { + create: CreatePaymentSessionDTO + } +>(PaymentSession) {} diff --git a/packages/payment/src/repositories/payment.ts b/packages/payment/src/repositories/payment.ts new file mode 100644 index 0000000000000..bfae74580ae47 --- /dev/null +++ b/packages/payment/src/repositories/payment.ts @@ -0,0 +1,11 @@ +import { CreatePaymentDTO, UpdatePaymentDTO } from "@medusajs/types" +import { DALUtils } from "@medusajs/utils" +import { Payment } from "@models" + +export class PaymentRepository extends DALUtils.mikroOrmBaseRepositoryFactory< + Payment, + { + create: CreatePaymentDTO + update: UpdatePaymentDTO + } +>(Payment) {} diff --git a/packages/payment/src/services/index.ts b/packages/payment/src/services/index.ts index dc38616342ad9..a89fd1092733d 100644 --- a/packages/payment/src/services/index.ts +++ b/packages/payment/src/services/index.ts @@ -1,2 +1,5 @@ -export { default as PaymentModuleService } from "./payment-module" +export { default as Payment } from "./payment" +export { default as PaymentSession } from "./payment-session" export { default as PaymentCollection } from "./payment-collection" + +export { default as PaymentModuleService } from "./payment-module" diff --git a/packages/payment/src/services/payment-module.ts b/packages/payment/src/services/payment-module.ts index 91952672bbb6c..6f48783db11fd 100644 --- a/packages/payment/src/services/payment-module.ts +++ b/packages/payment/src/services/payment-module.ts @@ -21,7 +21,6 @@ import { MedusaContext, } from "@medusajs/utils" -import { Payment } from "@models" import * as services from "@services" import { joinerConfig } from "../joiner-config" @@ -29,20 +28,30 @@ import { joinerConfig } from "../joiner-config" type InjectedDependencies = { baseRepository: DAL.RepositoryService paymentCollectionService: services.PaymentCollection + paymentService: services.Payment + paymentSessionService: services.PaymentSession } -export default class PaymentModule - implements IPaymentModuleService -{ +export default class PaymentModule implements IPaymentModuleService { protected baseRepository_: DAL.RepositoryService + + protected paymentService_: services.Payment + protected paymentSessionService_: services.PaymentSession protected paymentCollectionService_: services.PaymentCollection constructor( - { baseRepository, paymentCollectionService }: InjectedDependencies, + { + baseRepository, + paymentService, + paymentSessionService, + paymentCollectionService, + }: InjectedDependencies, protected readonly moduleDeclaration: InternalModuleDeclaration ) { this.baseRepository_ = baseRepository + this.paymentService_ = paymentService + this.paymentSessionService_ = paymentSessionService this.paymentCollectionService_ = paymentCollectionService } @@ -186,6 +195,58 @@ export default class PaymentModule count, ] } + createPayment(data: CreatePaymentDTO): Promise + createPayment(data: CreatePaymentDTO[]): Promise + async createPayment( + data: CreatePaymentDTO | CreatePaymentDTO[], + @MedusaContext() sharedContext?: Context + ): Promise { + let input = Array.isArray(data) ? data : [data] + + input = input.map((inputData) => ({ + payment_collection: inputData.payment_collection_id, + payment_session: inputData.payment_session_id, + ...inputData, + })) + + const payments = await this.paymentService_.create(input, sharedContext) + + return await this.baseRepository_.serialize( + Array.isArray(data) ? payments : payments[0], + { + populate: true, + } + ) + } + + createPaymentSession( + paymentCollectionId: string, + data: CreatePaymentSessionDTO, + sharedContext?: Context | undefined + ): Promise + createPaymentSession( + paymentCollectionId: string, + data: CreatePaymentSessionDTO[], + sharedContext?: Context | undefined + ): Promise + async createPaymentSession( + paymentCollectionId: string, + data: CreatePaymentSessionDTO | CreatePaymentSessionDTO[], + @MedusaContext() sharedContext?: Context + ): Promise { + let input = Array.isArray(data) ? data : [data] + + input = input.map((inputData) => ({ + payment_collection: paymentCollectionId, + ...inputData, + })) + + await this.paymentSessionService_.create(input, sharedContext) + + return await this.retrievePaymentCollection(paymentCollectionId, { + relations: ["payment_sessions"], + }) + } /** * TODO @@ -203,15 +264,7 @@ export default class PaymentModule ): Promise { throw new Error("Method not implemented.") } - createPayment(data: CreatePaymentDTO): Promise - createPayment(data: CreatePaymentDTO[]): Promise - createPayment( - data: unknown - ): - | Promise - | Promise { - throw new Error("Method not implemented.") - } + capturePayment( paymentId: string, amount: number, @@ -242,23 +295,7 @@ export default class PaymentModule | Promise { throw new Error("Method not implemented.") } - createPaymentSession( - paymentCollectionId: string, - data: CreatePaymentSessionDTO, - sharedContext?: Context | undefined - ): Promise - createPaymentSession( - paymentCollectionId: string, - data: CreatePaymentSessionDTO[], - sharedContext?: Context | undefined - ): Promise - createPaymentSession( - paymentCollectionId: unknown, - data: unknown, - sharedContext?: unknown - ): Promise { - throw new Error("Method not implemented.") - } + authorizePaymentSessions( paymentCollectionId: string, sessionIds: string[], diff --git a/packages/payment/src/services/payment-session.ts b/packages/payment/src/services/payment-session.ts new file mode 100644 index 0000000000000..359d1f244d895 --- /dev/null +++ b/packages/payment/src/services/payment-session.ts @@ -0,0 +1,21 @@ +import { PaymentSession } from "@models" +import { CreatePaymentSessionDTO, DAL } from "@medusajs/types" +import { ModulesSdkUtils } from "@medusajs/utils" + +type InjectedDependencies = { + paymentSessionRepository: DAL.RepositoryService +} + +export default class PaymentSessionService< + TEntity extends PaymentSession = PaymentSession +> extends ModulesSdkUtils.abstractServiceFactory< + InjectedDependencies, + { + create: CreatePaymentSessionDTO + } +>(PaymentSession) { + constructor(container: InjectedDependencies) { + // @ts-ignore + super(...arguments) + } +} diff --git a/packages/payment/src/services/payment.ts b/packages/payment/src/services/payment.ts new file mode 100644 index 0000000000000..70464b98c53db --- /dev/null +++ b/packages/payment/src/services/payment.ts @@ -0,0 +1,22 @@ +import { Payment } from "@models" +import { CreatePaymentDTO, DAL, UpdatePaymentDTO } from "@medusajs/types" +import { ModulesSdkUtils } from "@medusajs/utils" + +type InjectedDependencies = { + paymentRepository: DAL.RepositoryService +} + +export default class PaymentService< + TEntity extends Payment = Payment +> extends ModulesSdkUtils.abstractServiceFactory< + InjectedDependencies, + { + create: CreatePaymentDTO + update: UpdatePaymentDTO + } +>(Payment) { + constructor(container: InjectedDependencies) { + // @ts-ignore + super(...arguments) + } +} diff --git a/packages/types/src/payment/mutations.ts b/packages/types/src/payment/mutations.ts index c5cfa3e9b9bdc..d5a42680c0569 100644 --- a/packages/types/src/payment/mutations.ts +++ b/packages/types/src/payment/mutations.ts @@ -31,6 +31,9 @@ export interface CreatePaymentDTO { provider_id: string data: Record + payment_session_id: string + payment_collection_id: string + cart_id?: string order_id?: string order_edit_id?: string From f430524629e0b1b599e5f34cbcce7760d136e891 Mon Sep 17 00:00:00 2001 From: fPolic Date: Fri, 19 Jan 2024 17:41:54 +0100 Subject: [PATCH 14/36] feat: create Session and Payment --- .../services/payment-module/index.spec.ts | 61 +++++++++++++++---- .../payment/src/models/payment-session.ts | 3 +- packages/payment/src/models/payment.ts | 4 +- .../payment/src/services/payment-module.ts | 25 ++++++-- packages/types/src/payment/common.ts | 13 +++- packages/types/src/payment/service.ts | 10 ++- .../modules-sdk/abstract-service-factory.ts | 2 +- 7 files changed, 93 insertions(+), 25 deletions(-) diff --git a/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts b/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts index f72d6255dfd7c..546ecb0efcdc1 100644 --- a/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts +++ b/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts @@ -240,7 +240,7 @@ describe("Payment Module Service", () => { describe("create", () => { it("should create a payment session successfully", async () => { - const createdSession = await service.createPaymentSession( + const paymentCollection = await service.createPaymentSession( "pay-col-id-1", { amount: 200, @@ -249,11 +249,25 @@ describe("Payment Module Service", () => { } ) - console.log(createdSession) - - expect(createdSession).toEqual( + expect(paymentCollection).toEqual( expect.objectContaining({ - id: expect.any(String), + id: "pay-col-id-1", + status: "not_paid", + payment_sessions: [ + { + id: expect.any(String), + currency_code: "usd", + amount: 200, + provider_id: "manual", + status: "pending", + payment_collection: expect.objectContaining({ + id: "pay-col-id-1", + }), + authorized_at: null, + payment: null, + data: null, + }, + ], }) ) }) @@ -273,8 +287,6 @@ describe("Payment Module Service", () => { schema: process.env.MEDUSA_PAYMNET_DB_SCHEMA, }, }) - - await createPaymentCollections(repositoryManager) }) afterEach(async () => { @@ -283,25 +295,48 @@ describe("Payment Module Service", () => { describe("create", () => { it("should create a payment successfully", async () => { - const coll = await service.createPaymentCollection({ + let paymentCollection = await service.createPaymentCollection({ currency_code: "usd", amount: 200, region_id: "reg", }) - const [createdPayment] = await service.createPayment({ + + paymentCollection = await service.createPaymentSession( + paymentCollection.id, + { + amount: 200, + provider_id: "manual", + currency_code: "usd", + } + ) + + const createdPayment = await service.createPayment({ data: {}, amount: 200, provider_id: "manual", currency_code: "usd", - payment_collection_id: coll.id, - payment_session_id: "ses_123", + payment_collection_id: paymentCollection.id, + payment_session_id: paymentCollection.payment_sessions[0].id, }) - console.log(createdPayment) - expect(createdPayment).toEqual( expect.objectContaining({ id: expect.any(String), + authorized_amount: null, + cart_id: null, + order_id: null, + order_edit_id: null, + customer_id: null, + data: {}, + deleted_at: null, + captured_at: null, + canceled_at: null, + refunds: [], + captures: [], + amount: 200, + currency_code: "usd", + provider_id: "manual", + payment_collection: paymentCollection.id, }) ) }) diff --git a/packages/payment/src/models/payment-session.ts b/packages/payment/src/models/payment-session.ts index b0b14fa771142..a0baaaed99347 100644 --- a/packages/payment/src/models/payment-session.ts +++ b/packages/payment/src/models/payment-session.ts @@ -57,8 +57,9 @@ export default class PaymentSession { entity: () => Payment, mappedBy: (payment) => payment.session, cascade: ["soft-remove"] as any, + nullable: true, }) - payment!: Payment + payment?: Payment | null @BeforeCreate() onCreate() { diff --git a/packages/payment/src/models/payment.ts b/packages/payment/src/models/payment.ts index 4647be64ac245..9a336d7b6fc1c 100644 --- a/packages/payment/src/models/payment.ts +++ b/packages/payment/src/models/payment.ts @@ -116,10 +116,10 @@ export default class Payment { index: "IDX_payment_payment_collection_id", fieldName: "payment_collection_id", }) - payment_collection: PaymentCollection + payment_collection!: PaymentCollection @OneToOne({ owner: true, fieldName: "session_id" }) - session: PaymentSession + session!: PaymentSession /** COMPUTED PROPERTIES START **/ diff --git a/packages/payment/src/services/payment-module.ts b/packages/payment/src/services/payment-module.ts index 6f48783db11fd..e924c309e6d03 100644 --- a/packages/payment/src/services/payment-module.ts +++ b/packages/payment/src/services/payment-module.ts @@ -195,8 +195,17 @@ export default class PaymentModule implements IPaymentModuleService { count, ] } - createPayment(data: CreatePaymentDTO): Promise - createPayment(data: CreatePaymentDTO[]): Promise + + createPayment( + data: CreatePaymentDTO, + sharedContext?: Context + ): Promise + createPayment( + data: CreatePaymentDTO[], + sharedContext?: Context + ): Promise + + @InjectManager("baseRepository_") // TODO: USE TX MANAGER AFTER FIX async createPayment( data: CreatePaymentDTO | CreatePaymentDTO[], @MedusaContext() sharedContext?: Context @@ -229,6 +238,8 @@ export default class PaymentModule implements IPaymentModuleService { data: CreatePaymentSessionDTO[], sharedContext?: Context | undefined ): Promise + + @InjectTransactionManager("baseRepository_") async createPaymentSession( paymentCollectionId: string, data: CreatePaymentSessionDTO | CreatePaymentSessionDTO[], @@ -243,9 +254,13 @@ export default class PaymentModule implements IPaymentModuleService { await this.paymentSessionService_.create(input, sharedContext) - return await this.retrievePaymentCollection(paymentCollectionId, { - relations: ["payment_sessions"], - }) + return await this.retrievePaymentCollection( + paymentCollectionId, + { + relations: ["payment_sessions"], + }, + sharedContext + ) } /** diff --git a/packages/types/src/payment/common.ts b/packages/types/src/payment/common.ts index 5f4b303bff64f..595d10e791c1f 100644 --- a/packages/types/src/payment/common.ts +++ b/packages/types/src/payment/common.ts @@ -36,6 +36,8 @@ export interface PaymentCollectionDTO { * The ID of the Payment Collection */ id: string + + payment_sessions: PaymentSessionDTO[] } export interface FilterablePaymentCollectionProps @@ -52,7 +54,16 @@ export interface FilterablePaymentCollectionProps export interface PaymentDTO { /** - * The ID of the Payment Collection + * The ID of the Payment + */ + id: string +} + +/* ********** PAYMENT ********** */ + +export interface PaymentSessionDTO { + /** + * The ID of the Payment Session */ id: string } diff --git a/packages/types/src/payment/service.ts b/packages/types/src/payment/service.ts index 94951f55455c0..71e313defc03b 100644 --- a/packages/types/src/payment/service.ts +++ b/packages/types/src/payment/service.ts @@ -75,8 +75,14 @@ export interface IPaymentModuleService extends IModuleService { /* ********** PAYMENT ********** */ - createPayment(data: CreatePaymentDTO): Promise - createPayment(data: CreatePaymentDTO[]): Promise + createPayment( + data: CreatePaymentDTO, + sharedContext?: Context + ): Promise + createPayment( + data: CreatePaymentDTO[], + sharedContext?: Context + ): Promise capturePayment( paymentId: string, diff --git a/packages/utils/src/modules-sdk/abstract-service-factory.ts b/packages/utils/src/modules-sdk/abstract-service-factory.ts index b176d6ff45151..0898871a80f9a 100644 --- a/packages/utils/src/modules-sdk/abstract-service-factory.ts +++ b/packages/utils/src/modules-sdk/abstract-service-factory.ts @@ -186,7 +186,7 @@ export function abstractServiceFactory< )) as [TEntity[], number] } - @InjectTransactionManager(shouldForceTransaction, propertyRepositoryName) + @InjectManager(propertyRepositoryName) // TODO: REVERT TO USE TX MANAGER AFTER FIX async create( data: TDTOs["create"][], @MedusaContext() sharedContext: Context = {} From 045c27e2eaefaf3dc252c993eed516e977de9fa4 Mon Sep 17 00:00:00 2001 From: fPolic Date: Fri, 19 Jan 2024 17:56:25 +0100 Subject: [PATCH 15/36] wip: update Payment --- .../services/payment-module/index.spec.ts | 63 +++++++++++++++++++ .../payment/src/services/payment-module.ts | 41 +++++++----- packages/types/src/payment/mutations.ts | 4 ++ packages/types/src/payment/service.ts | 3 + 4 files changed, 95 insertions(+), 16 deletions(-) diff --git a/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts b/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts index 546ecb0efcdc1..318fe91df9f1d 100644 --- a/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts +++ b/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts @@ -341,5 +341,68 @@ describe("Payment Module Service", () => { ) }) }) + + describe("update", () => { + it("should update a payment successfully", async () => { + // TODO: refactor when factory is added + + let paymentCollection = await service.createPaymentCollection({ + currency_code: "usd", + amount: 200, + region_id: "reg", + }) + + paymentCollection = await service.createPaymentSession( + paymentCollection.id, + { + amount: 200, + provider_id: "manual", + currency_code: "usd", + } + ) + + const createdPayment = await service.createPayment({ + data: {}, + amount: 200, + provider_id: "manual", + currency_code: "usd", + payment_collection_id: paymentCollection.id, + payment_session_id: paymentCollection.payment_sessions[0].id, + }) + + expect(createdPayment).toEqual( + expect.objectContaining({ + id: expect.any(String), + authorized_amount: null, + cart_id: null, + order_id: null, + order_edit_id: null, + customer_id: null, + data: {}, + deleted_at: null, + captured_at: null, + canceled_at: null, + refunds: [], + captures: [], + amount: 200, + currency_code: "usd", + provider_id: "manual", + payment_collection: paymentCollection.id, + }) + ) + + const updatedPayment = await service.updatePayment({ + id: createdPayment.id, + cart_id: "new-cart", + }) + + expect(updatedPayment).toEqual( + expect.objectContaining({ + id: createdPayment.id, + cart_id: "new-cart", + }) + ) + }) + }) }) }) diff --git a/packages/payment/src/services/payment-module.ts b/packages/payment/src/services/payment-module.ts index e924c309e6d03..033fa98a64066 100644 --- a/packages/payment/src/services/payment-module.ts +++ b/packages/payment/src/services/payment-module.ts @@ -228,6 +228,31 @@ export default class PaymentModule implements IPaymentModuleService { ) } + updatePayment( + data: UpdatePaymentDTO, + sharedContext?: Context | undefined + ): Promise + updatePayment( + data: UpdatePaymentDTO[], + sharedContext?: Context | undefined + ): Promise + + @InjectTransactionManager("baseRepository_") + async updatePayment( + data: UpdatePaymentDTO | UpdatePaymentDTO[], + @MedusaContext() sharedContext?: Context + ): Promise { + const input = Array.isArray(data) ? data : [data] + const result = await this.paymentService_.update(input, sharedContext) + + return await this.baseRepository_.serialize( + Array.isArray(data) ? result : result[0], + { + populate: true, + } + ) + } + createPaymentSession( paymentCollectionId: string, data: CreatePaymentSessionDTO, @@ -294,22 +319,6 @@ export default class PaymentModule implements IPaymentModuleService { ): Promise { throw new Error("Method not implemented.") } - updatePayment( - data: UpdatePaymentDTO, - sharedContext?: Context | undefined - ): Promise - updatePayment( - data: UpdatePaymentDTO[], - sharedContext?: Context | undefined - ): Promise - updatePayment( - data: unknown, - sharedContext?: unknown - ): - | Promise - | Promise { - throw new Error("Method not implemented.") - } authorizePaymentSessions( paymentCollectionId: string, diff --git a/packages/types/src/payment/mutations.ts b/packages/types/src/payment/mutations.ts index d5a42680c0569..d5d37643edef4 100644 --- a/packages/types/src/payment/mutations.ts +++ b/packages/types/src/payment/mutations.ts @@ -41,10 +41,14 @@ export interface CreatePaymentDTO { } export interface UpdatePaymentDTO { + id: string + cart_id?: string order_id?: string order_edit_id?: string customer_id?: string + + data?: Record } /** diff --git a/packages/types/src/payment/service.ts b/packages/types/src/payment/service.ts index 71e313defc03b..4aded4bb5b14b 100644 --- a/packages/types/src/payment/service.ts +++ b/packages/types/src/payment/service.ts @@ -89,12 +89,15 @@ export interface IPaymentModuleService extends IModuleService { amount: number, sharedContext?: Context ): Promise + refundPayment( paymentId: string, amount: number, sharedContext?: Context ): Promise + cancelPayment(paymentId: string, sharedContext?: Context): Promise + updatePayment( data: UpdatePaymentDTO, sharedContext?: Context From 5e696507ffb2cdcea299c0c4c9f302408efd1516 Mon Sep 17 00:00:00 2001 From: fPolic Date: Fri, 19 Jan 2024 18:49:18 +0100 Subject: [PATCH 16/36] wip: Refund & Capture --- .../services/payment-module/index.spec.ts | 35 +++++++++++ packages/payment/src/loaders/container.ts | 4 ++ packages/payment/src/repositories/capture.ts | 11 ++++ packages/payment/src/repositories/index.ts | 2 + packages/payment/src/repositories/refund.ts | 11 ++++ .../payment/src/services/payment-module.ts | 61 ++++++++++++++++--- packages/payment/src/services/payment.ts | 45 +++++++++++++- packages/types/src/payment/common.ts | 40 ++++++++++++ packages/types/src/payment/mutations.ts | 14 +++++ packages/types/src/payment/service.ts | 8 +-- 10 files changed, 215 insertions(+), 16 deletions(-) create mode 100644 packages/payment/src/repositories/capture.ts create mode 100644 packages/payment/src/repositories/refund.ts diff --git a/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts b/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts index 318fe91df9f1d..a2ac6a43a1a03 100644 --- a/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts +++ b/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts @@ -277,6 +277,8 @@ describe("Payment Module Service", () => { describe("Payment", () => { let repositoryManager: SqlEntityManager + let testPayment + beforeEach(async () => { await MikroOrmWrapper.setupDatabase() repositoryManager = await MikroOrmWrapper.forkManager() @@ -287,6 +289,30 @@ describe("Payment Module Service", () => { schema: process.env.MEDUSA_PAYMNET_DB_SCHEMA, }, }) + + let paymentCollection = await service.createPaymentCollection({ + currency_code: "usd", + amount: 200, + region_id: "reg", + }) + + paymentCollection = await service.createPaymentSession( + paymentCollection.id, + { + amount: 200, + provider_id: "manual", + currency_code: "usd", + } + ) + + testPayment = await service.createPayment({ + data: {}, + amount: 200, + provider_id: "manual", + currency_code: "usd", + payment_collection_id: paymentCollection.id, + payment_session_id: paymentCollection.payment_sessions[0].id, + }) }) afterEach(async () => { @@ -404,5 +430,14 @@ describe("Payment Module Service", () => { ) }) }) + + describe("capture", () => { + it("should capture a payment successfully", async () => { + const capturedPayment = await service.capturePayment({ + amount: 100, + payment_id: testPayment.id, + }) + }) + }) }) }) diff --git a/packages/payment/src/loaders/container.ts b/packages/payment/src/loaders/container.ts index e1e0c9ae4b143..66a188c37aad8 100644 --- a/packages/payment/src/loaders/container.ts +++ b/packages/payment/src/loaders/container.ts @@ -48,5 +48,9 @@ function loadDefaultRepositories({ container }) { paymentSessionRepository: asClass( defaultRepositories.PaymentSessionRepository ).singleton(), + captureRepository: asClass( + defaultRepositories.CaptureRepository + ).singleton(), + refundRepository: asClass(defaultRepositories.RefundRepository).singleton(), }) } diff --git a/packages/payment/src/repositories/capture.ts b/packages/payment/src/repositories/capture.ts new file mode 100644 index 0000000000000..d9138874c58d1 --- /dev/null +++ b/packages/payment/src/repositories/capture.ts @@ -0,0 +1,11 @@ +import { DALUtils } from "@medusajs/utils" + +import { Capture } from "@models" +import { CreateCaptureDTO } from "@medusajs/types" + +export class CaptureRepository extends DALUtils.mikroOrmBaseRepositoryFactory< + Capture, + { + create: CreateCaptureDTO + } +>(Capture) {} diff --git a/packages/payment/src/repositories/index.ts b/packages/payment/src/repositories/index.ts index 56bd1dac36358..ece4c84ffb7fd 100644 --- a/packages/payment/src/repositories/index.ts +++ b/packages/payment/src/repositories/index.ts @@ -1,5 +1,7 @@ export { MikroOrmBaseRepository as BaseRepository } from "@medusajs/utils" export { PaymentRepository } from "./payment" +export { CaptureRepository } from "./capture" +export { RefundRepository } from "./refund" export { PaymentSessionRepository } from "./payment-session" export { PaymentCollectionRepository } from "./payment-collection" diff --git a/packages/payment/src/repositories/refund.ts b/packages/payment/src/repositories/refund.ts new file mode 100644 index 0000000000000..26ee4ad150d4e --- /dev/null +++ b/packages/payment/src/repositories/refund.ts @@ -0,0 +1,11 @@ +import { DALUtils } from "@medusajs/utils" + +import { Refund } from "@models" +import { CreateRefundDTO } from "@medusajs/types" + +export class RefundRepository extends DALUtils.mikroOrmBaseRepositoryFactory< + Refund, + { + create: CreateRefundDTO + } +>(Refund) {} diff --git a/packages/payment/src/services/payment-module.ts b/packages/payment/src/services/payment-module.ts index 033fa98a64066..ff91d7af21930 100644 --- a/packages/payment/src/services/payment-module.ts +++ b/packages/payment/src/services/payment-module.ts @@ -1,8 +1,10 @@ import { Context, + CreateCaptureDTO, CreatePaymentCollectionDTO, CreatePaymentDTO, CreatePaymentSessionDTO, + CreateRefundDTO, DAL, FilterablePaymentCollectionProps, FindConfig, @@ -253,6 +255,53 @@ export default class PaymentModule implements IPaymentModuleService { ) } + @InjectTransactionManager("baseRepository_") + async capturePayment( + data: CreateCaptureDTO, + @MedusaContext() sharedContext?: Context + ): Promise { + // TODO 1. check if (payment.captured_amount + amount > payment.authorized_amount) {} + + // TODO: 2. set captured_at if fully captured? + // TODO: 3. set PaymentCollection status + + await this.paymentService_.capture(data, sharedContext) + + const payment = await this.paymentService_.retrieve( + data.payment_id, + { + relations: ["captures"], + }, + sharedContext + ) + + return await this.baseRepository_.serialize(payment, { + populate: true, + }) + } + + @InjectTransactionManager("baseRepository_") + async refund( + data: CreateRefundDTO, + @MedusaContext() sharedContext?: Context + ): Promise { + // TODO 1. check if (payment.captured_amount - amount > 0) {} + + await this.paymentService_.refund(data, sharedContext) + + const payment = await this.paymentService_.retrieve( + data.payment_id, + { + relations: ["refunds"], + }, + sharedContext + ) + + return await this.baseRepository_.serialize(payment, { + populate: true, + }) + } + createPaymentSession( paymentCollectionId: string, data: CreatePaymentSessionDTO, @@ -305,17 +354,9 @@ export default class PaymentModule implements IPaymentModuleService { throw new Error("Method not implemented.") } - capturePayment( - paymentId: string, - amount: number, - sharedContext?: Context | undefined - ): Promise { - throw new Error("Method not implemented.") - } - refundPayment( + cancelPayment( paymentId: string, - amount: number, - sharedContext?: Context | undefined + sharedContext?: Context ): Promise { throw new Error("Method not implemented.") } diff --git a/packages/payment/src/services/payment.ts b/packages/payment/src/services/payment.ts index 70464b98c53db..847cf38a0f309 100644 --- a/packages/payment/src/services/payment.ts +++ b/packages/payment/src/services/payment.ts @@ -1,9 +1,24 @@ import { Payment } from "@models" -import { CreatePaymentDTO, DAL, UpdatePaymentDTO } from "@medusajs/types" -import { ModulesSdkUtils } from "@medusajs/utils" +import { + CaptureDTO, + Context, + CreateCaptureDTO, + CreatePaymentDTO, + CreateRefundDTO, + DAL, + RefundDTO, + UpdatePaymentDTO, +} from "@medusajs/types" +import { + InjectTransactionManager, + MedusaContext, + ModulesSdkUtils, +} from "@medusajs/utils" type InjectedDependencies = { paymentRepository: DAL.RepositoryService + captureRepository: DAL.RepositoryService + refundRepository: DAL.RepositoryService } export default class PaymentService< @@ -15,8 +30,34 @@ export default class PaymentService< update: UpdatePaymentDTO } >(Payment) { + protected captureRepository_: DAL.RepositoryService + protected refundRepository_: DAL.RepositoryService + constructor(container: InjectedDependencies) { // @ts-ignore super(...arguments) + + this.captureRepository_ = container.captureRepository + this.refundRepository_ = container.refundRepository + } + + @InjectTransactionManager("captureRepository_") + async capture( + data: CreateCaptureDTO, + @MedusaContext() sharedContext?: Context + ): Promise { + const created = await this.captureRepository_.create([data]) + + return created[0] + } + + @InjectTransactionManager("refundRepository_") + async refund( + data: CreateRefundDTO, + @MedusaContext() sharedContext?: Context + ): Promise { + const created = await this.refundRepository_.create([data]) + + return created[0] } } diff --git a/packages/types/src/payment/common.ts b/packages/types/src/payment/common.ts index 595d10e791c1f..e9497bcac9cc8 100644 --- a/packages/types/src/payment/common.ts +++ b/packages/types/src/payment/common.ts @@ -57,6 +57,46 @@ export interface PaymentDTO { * The ID of the Payment */ id: string + + captures: CaptureDTO[] + + refunds: RefundDTO[] +} + +export interface CaptureDTO { + /** + * The ID of the Capture + */ + id: string + + /** + * Captured amount + */ + amount: number + + created_at: Date + + created_by?: string + + payment: PaymentDTO +} + +export interface RefundDTO { + /** + * The ID of the Refund + */ + id: string + + /** + * Refunded amount + */ + amount: number + + created_at: Date + + created_by?: string + + payment: PaymentDTO } /* ********** PAYMENT ********** */ diff --git a/packages/types/src/payment/mutations.ts b/packages/types/src/payment/mutations.ts index d5d37643edef4..3f2a0c4dea727 100644 --- a/packages/types/src/payment/mutations.ts +++ b/packages/types/src/payment/mutations.ts @@ -51,6 +51,20 @@ export interface UpdatePaymentDTO { data?: Record } +export interface CreateCaptureDTO { + amount: number + payment_id: string + + captured_by?: string +} + +export interface CreateRefundDTO { + amount: number + payment_id: string + + captured_by?: string +} + /** * Payment Session */ diff --git a/packages/types/src/payment/service.ts b/packages/types/src/payment/service.ts index 4aded4bb5b14b..9524121d11917 100644 --- a/packages/types/src/payment/service.ts +++ b/packages/types/src/payment/service.ts @@ -1,9 +1,11 @@ import { IModuleService } from "../modules-sdk" import { Context } from "../shared-context" import { + CreateCaptureDTO, CreatePaymentCollectionDTO, CreatePaymentDTO, CreatePaymentSessionDTO, + CreateRefundDTO, SetPaymentSessionsDTO, UpdatePaymentCollectionDTO, UpdatePaymentDTO, @@ -85,14 +87,12 @@ export interface IPaymentModuleService extends IModuleService { ): Promise capturePayment( - paymentId: string, - amount: number, + data: CreateCaptureDTO, sharedContext?: Context ): Promise refundPayment( - paymentId: string, - amount: number, + data: CreateRefundDTO, sharedContext?: Context ): Promise From 172a5e02e9dd3ce517f2bf5de751b3e1c7755a16 Mon Sep 17 00:00:00 2001 From: fPolic Date: Mon, 22 Jan 2024 11:40:33 +0100 Subject: [PATCH 17/36] fix: create, refactor model, finalise test --- .../services/payment-module/index.spec.ts | 47 ++++++++++--------- .../payment/src/models/payment-session.ts | 2 +- packages/payment/src/models/payment.ts | 2 +- .../payment/src/services/payment-module.ts | 4 +- .../modules-sdk/abstract-service-factory.ts | 2 +- 5 files changed, 29 insertions(+), 28 deletions(-) diff --git a/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts b/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts index a2ac6a43a1a03..b49df711152ad 100644 --- a/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts +++ b/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts @@ -290,29 +290,29 @@ describe("Payment Module Service", () => { }, }) - let paymentCollection = await service.createPaymentCollection({ - currency_code: "usd", - amount: 200, - region_id: "reg", - }) - - paymentCollection = await service.createPaymentSession( - paymentCollection.id, - { - amount: 200, - provider_id: "manual", - currency_code: "usd", - } - ) - - testPayment = await service.createPayment({ - data: {}, - amount: 200, - provider_id: "manual", - currency_code: "usd", - payment_collection_id: paymentCollection.id, - payment_session_id: paymentCollection.payment_sessions[0].id, - }) + // let paymentCollection = await service.createPaymentCollection({ + // currency_code: "usd", + // amount: 200, + // region_id: "reg", + // }) + // + // paymentCollection = await service.createPaymentSession( + // paymentCollection.id, + // { + // amount: 200, + // provider_id: "manual", + // currency_code: "usd", + // } + // ) + // + // testPayment = await service.createPayment({ + // data: {}, + // amount: 200, + // provider_id: "manual", + // currency_code: "usd", + // payment_collection_id: paymentCollection.id, + // payment_session_id: paymentCollection.payment_sessions[0].id, + // }) }) afterEach(async () => { @@ -363,6 +363,7 @@ describe("Payment Module Service", () => { currency_code: "usd", provider_id: "manual", payment_collection: paymentCollection.id, + payment_session: paymentCollection.payment_sessions[0].id, }) ) }) diff --git a/packages/payment/src/models/payment-session.ts b/packages/payment/src/models/payment-session.ts index a0baaaed99347..736f2d2b015e1 100644 --- a/packages/payment/src/models/payment-session.ts +++ b/packages/payment/src/models/payment-session.ts @@ -55,7 +55,7 @@ export default class PaymentSession { @OneToOne({ entity: () => Payment, - mappedBy: (payment) => payment.session, + mappedBy: (payment) => payment.payment_session, cascade: ["soft-remove"] as any, nullable: true, }) diff --git a/packages/payment/src/models/payment.ts b/packages/payment/src/models/payment.ts index 9a336d7b6fc1c..d419488c2fc71 100644 --- a/packages/payment/src/models/payment.ts +++ b/packages/payment/src/models/payment.ts @@ -119,7 +119,7 @@ export default class Payment { payment_collection!: PaymentCollection @OneToOne({ owner: true, fieldName: "session_id" }) - session!: PaymentSession + payment_session!: PaymentSession /** COMPUTED PROPERTIES START **/ diff --git a/packages/payment/src/services/payment-module.ts b/packages/payment/src/services/payment-module.ts index ff91d7af21930..92decca82b735 100644 --- a/packages/payment/src/services/payment-module.ts +++ b/packages/payment/src/services/payment-module.ts @@ -207,7 +207,7 @@ export default class PaymentModule implements IPaymentModuleService { sharedContext?: Context ): Promise - @InjectManager("baseRepository_") // TODO: USE TX MANAGER AFTER FIX + @InjectTransactionManager("baseRepository_") async createPayment( data: CreatePaymentDTO | CreatePaymentDTO[], @MedusaContext() sharedContext?: Context @@ -281,7 +281,7 @@ export default class PaymentModule implements IPaymentModuleService { } @InjectTransactionManager("baseRepository_") - async refund( + async refundPayment( data: CreateRefundDTO, @MedusaContext() sharedContext?: Context ): Promise { diff --git a/packages/utils/src/modules-sdk/abstract-service-factory.ts b/packages/utils/src/modules-sdk/abstract-service-factory.ts index 0898871a80f9a..b176d6ff45151 100644 --- a/packages/utils/src/modules-sdk/abstract-service-factory.ts +++ b/packages/utils/src/modules-sdk/abstract-service-factory.ts @@ -186,7 +186,7 @@ export function abstractServiceFactory< )) as [TEntity[], number] } - @InjectManager(propertyRepositoryName) // TODO: REVERT TO USE TX MANAGER AFTER FIX + @InjectTransactionManager(shouldForceTransaction, propertyRepositoryName) async create( data: TDTOs["create"][], @MedusaContext() sharedContext: Context = {} From c1a20afc536f770ad0cb9d6654d36b9688026519 Mon Sep 17 00:00:00 2001 From: fPolic Date: Mon, 22 Jan 2024 13:11:52 +0100 Subject: [PATCH 18/36] fix: tests --- .../integration-tests/__fixtures__/data.ts | 65 ++++++++++++ .../integration-tests/__fixtures__/index.ts | 57 +++++++++++ .../__fixtures__/payment-collection/data.ts | 20 ---- .../__fixtures__/payment-collection/index.ts | 22 ----- .../__fixtures__/payment/data.ts | 27 ----- .../__fixtures__/payment/index.ts | 22 ----- .../services/payment-module/index.spec.ts | 99 ++++--------------- .../payment/src/models/payment-session.ts | 1 + packages/payment/src/models/payment.ts | 1 + 9 files changed, 143 insertions(+), 171 deletions(-) create mode 100644 packages/payment/integration-tests/__fixtures__/data.ts create mode 100644 packages/payment/integration-tests/__fixtures__/index.ts delete mode 100644 packages/payment/integration-tests/__fixtures__/payment-collection/data.ts delete mode 100644 packages/payment/integration-tests/__fixtures__/payment-collection/index.ts delete mode 100644 packages/payment/integration-tests/__fixtures__/payment/data.ts delete mode 100644 packages/payment/integration-tests/__fixtures__/payment/index.ts diff --git a/packages/payment/integration-tests/__fixtures__/data.ts b/packages/payment/integration-tests/__fixtures__/data.ts new file mode 100644 index 0000000000000..5b17385bf1ee3 --- /dev/null +++ b/packages/payment/integration-tests/__fixtures__/data.ts @@ -0,0 +1,65 @@ +export const defaultPaymentCollectionData = [ + { + id: "pay-col-id-1", + amount: 100, + region_id: "region-id-1", + currency_code: "usd", + }, + { + id: "pay-col-id-2", + amount: 200, + region_id: "region-id-1", + currency_code: "usd", + }, + { + id: "pay-col-id-3", + amount: 300, + region_id: "region-id-2", + currency_code: "usd", + }, +] + +export const defaultPaymentSessionData = [ + { + id: "pay-sess-id-1", + amount: 100, + currency_code: "usd", + provider_id: "manual", + payment_collection: "pay-col-id-1", + }, + { + id: "pay-sess-id-2", + amount: 100, + currency_code: "usd", + provider_id: "manual", + payment_collection: "pay-col-id-2", + }, + { + id: "pay-sess-id-3", + amount: 100, + currency_code: "usd", + provider_id: "manual", + payment_collection: "pay-col-id-2", + }, +] + +export const defaultPaymentData = [ + { + id: "pay-id-1", + amount: 100, + currency_code: "usd", + payment_collection: "pay-col-id-1", + payment_session: "pay-sess-id-1", + provider_id: "manual", + data: {}, + }, + { + id: "pay-id-2", + amount: 100, + currency_code: "usd", + payment_collection: "pay-col-id-2", + payment_session: "pay-sess-id-2", + provider_id: "manual", + data: {}, + }, +] diff --git a/packages/payment/integration-tests/__fixtures__/index.ts b/packages/payment/integration-tests/__fixtures__/index.ts new file mode 100644 index 0000000000000..585af6d7edfbb --- /dev/null +++ b/packages/payment/integration-tests/__fixtures__/index.ts @@ -0,0 +1,57 @@ +import { SqlEntityManager } from "@mikro-orm/postgresql" + +import { Payment, PaymentSession } from "@models" + +import { PaymentCollection } from "../../src/models" +import { + defaultPaymentCollectionData, + defaultPaymentData, + defaultPaymentSessionData, +} from "./data" + +export * from "./data" + +export async function createPaymentCollections( + manager: SqlEntityManager, + paymentCollectionData = defaultPaymentCollectionData +): Promise { + const collections: PaymentCollection[] = [] + + for (let data of paymentCollectionData) { + let collection = manager.create(PaymentCollection, data) + + await manager.persistAndFlush(collection) + } + + return collections +} + +export async function createPaymentSessions( + manager: SqlEntityManager, + paymentCollectionData = defaultPaymentSessionData +): Promise { + const collections: PaymentSession[] = [] + + for (let data of paymentCollectionData) { + let collection = manager.create(PaymentSession, data) + + await manager.persistAndFlush(collection) + } + + return collections +} + +export async function createPayments( + manager: SqlEntityManager, + paymentCollectionData = defaultPaymentData +): Promise { + const collections: Payment[] = [] + + for (let data of paymentCollectionData) { + let collection = manager.create(Payment, data) + + await manager.persistAndFlush(collection) + } + + return collections +} diff --git a/packages/payment/integration-tests/__fixtures__/payment-collection/data.ts b/packages/payment/integration-tests/__fixtures__/payment-collection/data.ts deleted file mode 100644 index 692d57bc3e49f..0000000000000 --- a/packages/payment/integration-tests/__fixtures__/payment-collection/data.ts +++ /dev/null @@ -1,20 +0,0 @@ -export const defaultPaymentCollectionData = [ - { - id: "pay-col-id-1", - amount: 100, - region_id: "region-id-1", - currency_code: "usd", - }, - { - id: "pay-col-id-2", - amount: 200, - region_id: "region-id-1", - currency_code: "usd", - }, - { - id: "pay-col-id-3", - amount: 300, - region_id: "region-id-2", - currency_code: "usd", - }, -] diff --git a/packages/payment/integration-tests/__fixtures__/payment-collection/index.ts b/packages/payment/integration-tests/__fixtures__/payment-collection/index.ts deleted file mode 100644 index 3700ffd5df4e0..0000000000000 --- a/packages/payment/integration-tests/__fixtures__/payment-collection/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { CreatePaymentCollectionDTO } from "@medusajs/types" -import { SqlEntityManager } from "@mikro-orm/postgresql" - -import { PaymentCollection } from "../../../src/models" -import { defaultPaymentCollectionData } from "./data" - -export * from "./data" - -export async function createPaymentCollections( - manager: SqlEntityManager, - paymentCollectionData: CreatePaymentCollectionDTO[] = defaultPaymentCollectionData -): Promise { - const collections: PaymentCollection[] = [] - - for (let data of paymentCollectionData) { - let collection = manager.create(PaymentCollection, data) - - await manager.persistAndFlush(collection) - } - - return collections -} diff --git a/packages/payment/integration-tests/__fixtures__/payment/data.ts b/packages/payment/integration-tests/__fixtures__/payment/data.ts deleted file mode 100644 index e8bbcbc421b2e..0000000000000 --- a/packages/payment/integration-tests/__fixtures__/payment/data.ts +++ /dev/null @@ -1,27 +0,0 @@ -export const defaultPaymentData = [ - { - id: "pay-id-1", - amount: 100, - provider_id: "stripe", - currency_code: "usd", - session: { id: "session-1" }, - payment_collection: { id: "pay_col-1" }, - data: { - data_key: "val", - }, - }, - // { - // id: "pay-id-2", - // amount: 200, - // provider_id: "manual", - // currency_code: "eur", - // data: {}, - // }, - // { - // id: "pay-id-3", - // amount: 300, - // provider_id: "manual", - // currency_code: "usd", - // data: {}, - // }, -] diff --git a/packages/payment/integration-tests/__fixtures__/payment/index.ts b/packages/payment/integration-tests/__fixtures__/payment/index.ts deleted file mode 100644 index 776cd6eee7eb6..0000000000000 --- a/packages/payment/integration-tests/__fixtures__/payment/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { CreatePaymentDTO } from "@medusajs/types" -import { SqlEntityManager } from "@mikro-orm/postgresql" - -import { Payment } from "../../../src/models" -import { defaultPaymentData } from "./data" - -export * from "./data" - -export async function createPayment( - manager: SqlEntityManager, - paymentData: CreatePaymentDTO[] = defaultPaymentData -): Promise { - const collections: Payment[] = [] - - for (let data of paymentData) { - let collection = manager.create(Payment, data) - - await manager.persistAndFlush(collection) - } - - return collections -} diff --git a/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts b/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts index b49df711152ad..0a7865262f31e 100644 --- a/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts +++ b/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts @@ -3,7 +3,11 @@ import { SqlEntityManager } from "@mikro-orm/postgresql" import { initialize } from "../../../../src" import { DB_URL, MikroOrmWrapper } from "../../../utils" -import { createPaymentCollections } from "../../../__fixtures__/payment-collection" +import { + createPaymentCollections, + createPaymentSessions, + createPayments, +} from "../../../__fixtures__" jest.setTimeout(30000) @@ -25,6 +29,8 @@ describe("Payment Module Service", () => { }) await createPaymentCollections(repositoryManager) + await createPaymentSessions(repositoryManager) + await createPayments(repositoryManager) }) afterEach(async () => { @@ -232,6 +238,7 @@ describe("Payment Module Service", () => { }) await createPaymentCollections(repositoryManager) + await createPaymentSessions(repositoryManager) }) afterEach(async () => { @@ -253,21 +260,20 @@ describe("Payment Module Service", () => { expect.objectContaining({ id: "pay-col-id-1", status: "not_paid", - payment_sessions: [ + payment_sessions: expect.arrayContaining([ { id: expect.any(String), + data: null, + status: "pending", + authorized_at: null, currency_code: "usd", amount: 200, provider_id: "manual", - status: "pending", payment_collection: expect.objectContaining({ - id: "pay-col-id-1", + id: paymentCollection.id, }), - authorized_at: null, - payment: null, - data: null, }, - ], + ]), }) ) }) @@ -290,29 +296,9 @@ describe("Payment Module Service", () => { }, }) - // let paymentCollection = await service.createPaymentCollection({ - // currency_code: "usd", - // amount: 200, - // region_id: "reg", - // }) - // - // paymentCollection = await service.createPaymentSession( - // paymentCollection.id, - // { - // amount: 200, - // provider_id: "manual", - // currency_code: "usd", - // } - // ) - // - // testPayment = await service.createPayment({ - // data: {}, - // amount: 200, - // provider_id: "manual", - // currency_code: "usd", - // payment_collection_id: paymentCollection.id, - // payment_session_id: paymentCollection.payment_sessions[0].id, - // }) + await createPaymentCollections(repositoryManager) + await createPaymentSessions(repositoryManager) + await createPayments(repositoryManager) }) afterEach(async () => { @@ -371,61 +357,14 @@ describe("Payment Module Service", () => { describe("update", () => { it("should update a payment successfully", async () => { - // TODO: refactor when factory is added - - let paymentCollection = await service.createPaymentCollection({ - currency_code: "usd", - amount: 200, - region_id: "reg", - }) - - paymentCollection = await service.createPaymentSession( - paymentCollection.id, - { - amount: 200, - provider_id: "manual", - currency_code: "usd", - } - ) - - const createdPayment = await service.createPayment({ - data: {}, - amount: 200, - provider_id: "manual", - currency_code: "usd", - payment_collection_id: paymentCollection.id, - payment_session_id: paymentCollection.payment_sessions[0].id, - }) - - expect(createdPayment).toEqual( - expect.objectContaining({ - id: expect.any(String), - authorized_amount: null, - cart_id: null, - order_id: null, - order_edit_id: null, - customer_id: null, - data: {}, - deleted_at: null, - captured_at: null, - canceled_at: null, - refunds: [], - captures: [], - amount: 200, - currency_code: "usd", - provider_id: "manual", - payment_collection: paymentCollection.id, - }) - ) - const updatedPayment = await service.updatePayment({ - id: createdPayment.id, + id: "pay-id-1", cart_id: "new-cart", }) expect(updatedPayment).toEqual( expect.objectContaining({ - id: createdPayment.id, + id: "pay-id-1", cart_id: "new-cart", }) ) diff --git a/packages/payment/src/models/payment-session.ts b/packages/payment/src/models/payment-session.ts index 736f2d2b015e1..1e11878ae12e0 100644 --- a/packages/payment/src/models/payment-session.ts +++ b/packages/payment/src/models/payment-session.ts @@ -50,6 +50,7 @@ export default class PaymentSession { @ManyToOne({ index: "IDX_payment_session_payment_collection_id", fieldName: "payment_collection_id", + onDelete: "cascade", }) payment_collection!: PaymentCollection diff --git a/packages/payment/src/models/payment.ts b/packages/payment/src/models/payment.ts index d419488c2fc71..f27bc37d48ceb 100644 --- a/packages/payment/src/models/payment.ts +++ b/packages/payment/src/models/payment.ts @@ -115,6 +115,7 @@ export default class Payment { @ManyToOne({ index: "IDX_payment_payment_collection_id", fieldName: "payment_collection_id", + onDelete: "cascade", }) payment_collection!: PaymentCollection From 1ce795e49dc053be1e83f74437a91f9ee6ce6a05 Mon Sep 17 00:00:00 2001 From: fPolic Date: Mon, 22 Jan 2024 14:49:20 +0100 Subject: [PATCH 19/36] feat: Capture/Refund and tests --- .../services/payment-module/index.spec.ts | 68 ++++++++++++++++++- packages/payment/src/models/capture.ts | 2 +- packages/payment/src/models/refund.ts | 2 +- packages/payment/src/services/payment.ts | 20 +++++- 4 files changed, 85 insertions(+), 7 deletions(-) diff --git a/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts b/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts index 0a7865262f31e..f3a5bebf84297 100644 --- a/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts +++ b/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts @@ -283,8 +283,6 @@ describe("Payment Module Service", () => { describe("Payment", () => { let repositoryManager: SqlEntityManager - let testPayment - beforeEach(async () => { await MikroOrmWrapper.setupDatabase() repositoryManager = await MikroOrmWrapper.forkManager() @@ -375,8 +373,72 @@ describe("Payment Module Service", () => { it("should capture a payment successfully", async () => { const capturedPayment = await service.capturePayment({ amount: 100, - payment_id: testPayment.id, + payment_id: "pay-id-1", + }) + + expect(capturedPayment).toEqual( + expect.objectContaining({ + id: "pay-id-1", + amount: 100, + currency_code: "usd", + provider_id: "manual", + data: {}, + + captures: [ + expect.objectContaining({ + created_by: null, + amount: 100, + }), + ], + + // TODO + authorized_amount: null, + captured_at: null, + + cart_id: null, + order_id: null, + order_edit_id: null, + customer_id: null, + deleted_at: null, + canceled_at: null, + }) + ) + }) + }) + + describe("refund", () => { + it("should refund a payment successfully", async () => { + const refundedPayment = await service.refundPayment({ + amount: 100, + payment_id: "pay-id-1", }) + + expect(refundedPayment).toEqual( + expect.objectContaining({ + id: "pay-id-1", + amount: 100, + currency_code: "usd", + provider_id: "manual", + data: {}, + + refunds: [ + expect.objectContaining({ + created_by: null, + amount: 100, + }), + ], + + authorized_amount: null, + captured_at: null, + + cart_id: null, + order_id: null, + order_edit_id: null, + customer_id: null, + deleted_at: null, + canceled_at: null, + }) + ) }) }) }) diff --git a/packages/payment/src/models/capture.ts b/packages/payment/src/models/capture.ts index 5c8d1eb53a7e8..f0d5be2a25cf8 100644 --- a/packages/payment/src/models/capture.ts +++ b/packages/payment/src/models/capture.ts @@ -31,7 +31,7 @@ export default class Capture { index: "IDX_capture_payment_id", fieldName: "payment_id", }) - payment: Payment + payment!: Payment @Property({ onCreate: () => new Date(), diff --git a/packages/payment/src/models/refund.ts b/packages/payment/src/models/refund.ts index 4f99da250f72b..8213eff127f20 100644 --- a/packages/payment/src/models/refund.ts +++ b/packages/payment/src/models/refund.ts @@ -26,7 +26,7 @@ export default class Refund { index: "IDX_refund_payment_id", fieldName: "payment_id", }) - payment: Payment + payment!: Payment @Property({ onCreate: () => new Date(), diff --git a/packages/payment/src/services/payment.ts b/packages/payment/src/services/payment.ts index 847cf38a0f309..244c17a4a1ddd 100644 --- a/packages/payment/src/services/payment.ts +++ b/packages/payment/src/services/payment.ts @@ -46,7 +46,15 @@ export default class PaymentService< data: CreateCaptureDTO, @MedusaContext() sharedContext?: Context ): Promise { - const created = await this.captureRepository_.create([data]) + const created = await this.captureRepository_.create( + [ + { + payment: data.payment_id, + ...data, + }, + ], + sharedContext + ) return created[0] } @@ -56,7 +64,15 @@ export default class PaymentService< data: CreateRefundDTO, @MedusaContext() sharedContext?: Context ): Promise { - const created = await this.refundRepository_.create([data]) + const created = await this.refundRepository_.create( + [ + { + payment: data.payment_id, + ...data, + }, + ], + sharedContext + ) return created[0] } From 19e5551444b0ec8e55ca56f1acc511f827450ae7 Mon Sep 17 00:00:00 2001 From: fPolic Date: Mon, 22 Jan 2024 15:07:19 +0100 Subject: [PATCH 20/36] refactor: new repo approach --- packages/payment/src/repositories/capture.ts | 11 ---- packages/payment/src/repositories/index.ts | 6 --- .../src/repositories/payment-collection.ts | 14 ----- .../src/repositories/payment-session.ts | 10 ---- packages/payment/src/repositories/payment.ts | 11 ---- packages/payment/src/repositories/refund.ts | 11 ---- packages/payment/src/services/index.ts | 7 ++- .../payment/src/services/payment-module.ts | 14 ++--- packages/payment/src/types/repositories.ts | 51 ++++++++++++++++++- 9 files changed, 60 insertions(+), 75 deletions(-) delete mode 100644 packages/payment/src/repositories/capture.ts delete mode 100644 packages/payment/src/repositories/payment-collection.ts delete mode 100644 packages/payment/src/repositories/payment-session.ts delete mode 100644 packages/payment/src/repositories/payment.ts delete mode 100644 packages/payment/src/repositories/refund.ts diff --git a/packages/payment/src/repositories/capture.ts b/packages/payment/src/repositories/capture.ts deleted file mode 100644 index d9138874c58d1..0000000000000 --- a/packages/payment/src/repositories/capture.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { DALUtils } from "@medusajs/utils" - -import { Capture } from "@models" -import { CreateCaptureDTO } from "@medusajs/types" - -export class CaptureRepository extends DALUtils.mikroOrmBaseRepositoryFactory< - Capture, - { - create: CreateCaptureDTO - } ->(Capture) {} diff --git a/packages/payment/src/repositories/index.ts b/packages/payment/src/repositories/index.ts index ece4c84ffb7fd..147c9cc259fa4 100644 --- a/packages/payment/src/repositories/index.ts +++ b/packages/payment/src/repositories/index.ts @@ -1,7 +1 @@ export { MikroOrmBaseRepository as BaseRepository } from "@medusajs/utils" - -export { PaymentRepository } from "./payment" -export { CaptureRepository } from "./capture" -export { RefundRepository } from "./refund" -export { PaymentSessionRepository } from "./payment-session" -export { PaymentCollectionRepository } from "./payment-collection" diff --git a/packages/payment/src/repositories/payment-collection.ts b/packages/payment/src/repositories/payment-collection.ts deleted file mode 100644 index 887c8e15e3c37..0000000000000 --- a/packages/payment/src/repositories/payment-collection.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { - CreatePaymentCollectionDTO, - UpdatePaymentCollectionDTO, -} from "@medusajs/types" -import { DALUtils } from "@medusajs/utils" -import { PaymentCollection } from "@models" - -export class PaymentCollectionRepository extends DALUtils.mikroOrmBaseRepositoryFactory< - PaymentCollection, - { - create: CreatePaymentCollectionDTO - update: UpdatePaymentCollectionDTO - } ->(PaymentCollection) {} diff --git a/packages/payment/src/repositories/payment-session.ts b/packages/payment/src/repositories/payment-session.ts deleted file mode 100644 index 08f7395f23b1c..0000000000000 --- a/packages/payment/src/repositories/payment-session.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { CreatePaymentSessionDTO } from "@medusajs/types" -import { DALUtils } from "@medusajs/utils" -import { PaymentSession } from "@models" - -export class PaymentSessionRepository extends DALUtils.mikroOrmBaseRepositoryFactory< - PaymentSession, - { - create: CreatePaymentSessionDTO - } ->(PaymentSession) {} diff --git a/packages/payment/src/repositories/payment.ts b/packages/payment/src/repositories/payment.ts deleted file mode 100644 index bfae74580ae47..0000000000000 --- a/packages/payment/src/repositories/payment.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { CreatePaymentDTO, UpdatePaymentDTO } from "@medusajs/types" -import { DALUtils } from "@medusajs/utils" -import { Payment } from "@models" - -export class PaymentRepository extends DALUtils.mikroOrmBaseRepositoryFactory< - Payment, - { - create: CreatePaymentDTO - update: UpdatePaymentDTO - } ->(Payment) {} diff --git a/packages/payment/src/repositories/refund.ts b/packages/payment/src/repositories/refund.ts deleted file mode 100644 index 26ee4ad150d4e..0000000000000 --- a/packages/payment/src/repositories/refund.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { DALUtils } from "@medusajs/utils" - -import { Refund } from "@models" -import { CreateRefundDTO } from "@medusajs/types" - -export class RefundRepository extends DALUtils.mikroOrmBaseRepositoryFactory< - Refund, - { - create: CreateRefundDTO - } ->(Refund) {} diff --git a/packages/payment/src/services/index.ts b/packages/payment/src/services/index.ts index 35dc9115fe938..39afcb4fb0ff8 100644 --- a/packages/payment/src/services/index.ts +++ b/packages/payment/src/services/index.ts @@ -1,6 +1,5 @@ -export { default as Payment } from "./payment" -export { default as PaymentSession } from "./payment-session" -export { default as PaymentCollection } from "./payment-collection" - export { default as PaymentModuleService } from "./payment-module" + +export { default as PaymentService } from "./payment" +export { default as PaymentSessionService } from "./payment-session" export { default as PaymentCollectionService } from "./payment-collection" diff --git a/packages/payment/src/services/payment-module.ts b/packages/payment/src/services/payment-module.ts index 92decca82b735..4355947705475 100644 --- a/packages/payment/src/services/payment-module.ts +++ b/packages/payment/src/services/payment-module.ts @@ -29,17 +29,17 @@ import { joinerConfig } from "../joiner-config" type InjectedDependencies = { baseRepository: DAL.RepositoryService - paymentCollectionService: services.PaymentCollection - paymentService: services.Payment - paymentSessionService: services.PaymentSession + paymentCollectionService: services.PaymentCollectionService + paymentService: services.PaymentService + paymentSessionService: services.PaymentSessionService } -export default class PaymentModule implements IPaymentModuleService { +export default class PaymentModuleService implements IPaymentModuleService { protected baseRepository_: DAL.RepositoryService - protected paymentService_: services.Payment - protected paymentSessionService_: services.PaymentSession - protected paymentCollectionService_: services.PaymentCollection + protected paymentService_: services.PaymentService + protected paymentSessionService_: services.PaymentSessionService + protected paymentCollectionService_: services.PaymentCollectionService constructor( { diff --git a/packages/payment/src/types/repositories.ts b/packages/payment/src/types/repositories.ts index cc84166e9a0d9..25e8ab2ef1c15 100644 --- a/packages/payment/src/types/repositories.ts +++ b/packages/payment/src/types/repositories.ts @@ -2,9 +2,20 @@ import { DAL, CreatePaymentCollectionDTO, UpdatePaymentCollectionDTO, + CreatePaymentDTO, + UpdatePaymentDTO, + CreatePaymentSessionDTO, + CreateCaptureDTO, + CreateRefundDTO, } from "@medusajs/types" -import { PaymentCollection } from "@models" +import { + Capture, + Payment, + PaymentCollection, + PaymentSession, + Refund, +} from "@models" // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface IPaymentCollectionRepository< @@ -16,3 +27,41 @@ export interface IPaymentCollectionRepository< update: UpdatePaymentCollectionDTO } > {} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface IPaymentRepository + extends DAL.RepositoryService< + TEntity, + { + create: CreatePaymentDTO + update: UpdatePaymentDTO + } + > {} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface IPaymentSessionRepository< + TEntity extends PaymentSession = PaymentSession +> extends DAL.RepositoryService< + TEntity, + { + create: CreatePaymentSessionDTO + } + > {} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ICaptureRepository + extends DAL.RepositoryService< + TEntity, + { + create: CreateCaptureDTO + } + > {} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface IRefundRepository + extends DAL.RepositoryService< + TEntity, + { + create: CreateRefundDTO + } + > {} From 1cab80be1cad968fd48ad0de6417c9b29d62ebf7 Mon Sep 17 00:00:00 2001 From: fPolic Date: Mon, 22 Jan 2024 18:24:05 +0100 Subject: [PATCH 21/36] chore: Collection and Payment DTOs --- packages/types/src/payment/common.ts | 160 ++++++++++++++++++++++++++- 1 file changed, 157 insertions(+), 3 deletions(-) diff --git a/packages/types/src/payment/common.ts b/packages/types/src/payment/common.ts index e9497bcac9cc8..021138cb461a8 100644 --- a/packages/types/src/payment/common.ts +++ b/packages/types/src/payment/common.ts @@ -37,7 +37,75 @@ export interface PaymentCollectionDTO { */ id: string - payment_sessions: PaymentSessionDTO[] + /** + * The currency of the payments/sessions associated with payment collection + */ + currency_code: string + /** + * The id of the region + */ + region_id: string + + /** + * The amount + */ + amount: number + + /** + * The amount authorized within associated payment sessions + */ + authorized_amount?: number + + /** + * The amount refunded from associated payments + */ + refunded_amount?: number + + /** + * When the payment collection was completed + */ + completed_at?: string | Date + + /** + * When the payment collection was created + */ + created_at?: string | Date + + /** + * When the payment collection was updated + */ + updated_at?: string | Date + + /** + * Holds custom data in key-value pairs + */ + metadata?: Record | null + + /** + * The status of the payment collection + */ + status: PaymentCollectionStatus + + /** + * The payment provider for the payments + * + * @expandable + */ + payment_providers: PaymentProviderDTO[] + + /** + * The associated payment sessions + * + * @expandable + */ + payment_sessions?: PaymentSessionDTO[] + + /** + * The associated payments + * + * @expandable + */ + payments?: PaymentDTO[] } export interface FilterablePaymentCollectionProps @@ -58,9 +126,90 @@ export interface PaymentDTO { */ id: string - captures: CaptureDTO[] + /** + * The payment amount + */ + amount: number + + authorized_amount?: number - refunds: RefundDTO[] + /** + * Payment currency + */ + currency_code: string + + /** + * The ID of payment provider + */ + provider_id: string + + cart_id?: string + order_id?: string + order_edit_id?: string + customer_id?: string + + /** + * Payment provider data + */ + data?: Record + + /** + * When the payment collection was created + */ + created_at?: string | Date + + /** + * When the payment collection was updated + */ + updated_at?: string | Date + + /** + * When the payment was captured + */ + captured_at?: string | Date + + /** + * When the payment was canceled + */ + canceled_at?: string | Date + + /** + * The sum of the associated captures + */ + captured_amount?: number + + /** + * The sum of the associated refunds + */ + refunded_amount?: number + + /** + * The associated payment captures + * + * @expandable + */ + captures?: CaptureDTO[] + + /** + * The associated refunds of the payment + * + * @expandable + */ + refunds?: RefundDTO[] + + /** + * The payment collection the payment is associated with + * + * @expandable + */ + payment_collection?: PaymentCollectionDTO + + /** + * The payment session from which the payment is created + * + * @expandable + */ + payment_session?: PaymentSessionDTO } export interface CaptureDTO { @@ -107,3 +256,8 @@ export interface PaymentSessionDTO { */ id: string } + +export interface PaymentProviderDTO { + id: string + is_enabled: string +} From 360f7231a74a240b8dae4904ea6d06198ab46760 Mon Sep 17 00:00:00 2001 From: fPolic Date: Tue, 23 Jan 2024 17:39:58 +0100 Subject: [PATCH 22/36] wip: computing refunded and captured amounts --- .../integration-tests/__fixtures__/data.ts | 2 + .../__fixtures__/payment-collection/data.ts | 20 ----- .../__fixtures__/payment-collection/index.ts | 22 ------ .../services/payment-module/index.spec.ts | 77 +++++++++++++------ .../__tests__/services/payment/index.spec.ts | 68 ++++++++++++++++ packages/payment/src/models/payment.ts | 4 +- .../payment/src/services/payment-module.ts | 70 +++++++++++++++-- packages/payment/src/services/payment.ts | 73 ++++++++++++++++++ packages/types/src/payment/mutations.ts | 2 + 9 files changed, 264 insertions(+), 74 deletions(-) delete mode 100644 packages/payment/integration-tests/__fixtures__/payment-collection/data.ts delete mode 100644 packages/payment/integration-tests/__fixtures__/payment-collection/index.ts create mode 100644 packages/payment/integration-tests/__tests__/services/payment/index.spec.ts diff --git a/packages/payment/integration-tests/__fixtures__/data.ts b/packages/payment/integration-tests/__fixtures__/data.ts index 5b17385bf1ee3..8dad208fded0d 100644 --- a/packages/payment/integration-tests/__fixtures__/data.ts +++ b/packages/payment/integration-tests/__fixtures__/data.ts @@ -51,11 +51,13 @@ export const defaultPaymentData = [ payment_collection: "pay-col-id-1", payment_session: "pay-sess-id-1", provider_id: "manual", + authorized_amount: 100, data: {}, }, { id: "pay-id-2", amount: 100, + authorized_amount: 100, currency_code: "usd", payment_collection: "pay-col-id-2", payment_session: "pay-sess-id-2", diff --git a/packages/payment/integration-tests/__fixtures__/payment-collection/data.ts b/packages/payment/integration-tests/__fixtures__/payment-collection/data.ts deleted file mode 100644 index 692d57bc3e49f..0000000000000 --- a/packages/payment/integration-tests/__fixtures__/payment-collection/data.ts +++ /dev/null @@ -1,20 +0,0 @@ -export const defaultPaymentCollectionData = [ - { - id: "pay-col-id-1", - amount: 100, - region_id: "region-id-1", - currency_code: "usd", - }, - { - id: "pay-col-id-2", - amount: 200, - region_id: "region-id-1", - currency_code: "usd", - }, - { - id: "pay-col-id-3", - amount: 300, - region_id: "region-id-2", - currency_code: "usd", - }, -] diff --git a/packages/payment/integration-tests/__fixtures__/payment-collection/index.ts b/packages/payment/integration-tests/__fixtures__/payment-collection/index.ts deleted file mode 100644 index 3700ffd5df4e0..0000000000000 --- a/packages/payment/integration-tests/__fixtures__/payment-collection/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { CreatePaymentCollectionDTO } from "@medusajs/types" -import { SqlEntityManager } from "@mikro-orm/postgresql" - -import { PaymentCollection } from "../../../src/models" -import { defaultPaymentCollectionData } from "./data" - -export * from "./data" - -export async function createPaymentCollections( - manager: SqlEntityManager, - paymentCollectionData: CreatePaymentCollectionDTO[] = defaultPaymentCollectionData -): Promise { - const collections: PaymentCollection[] = [] - - for (let data of paymentCollectionData) { - let collection = manager.create(PaymentCollection, data) - - await manager.persistAndFlush(collection) - } - - return collections -} diff --git a/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts b/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts index f3a5bebf84297..69a8bc8890041 100644 --- a/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts +++ b/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts @@ -380,9 +380,6 @@ describe("Payment Module Service", () => { expect.objectContaining({ id: "pay-id-1", amount: 100, - currency_code: "usd", - provider_id: "manual", - data: {}, captures: [ expect.objectContaining({ @@ -391,23 +388,49 @@ describe("Payment Module Service", () => { }), ], - // TODO - authorized_amount: null, - captured_at: null, + captured_amount: 100, + captured_at: expect.any(Date), + }) + ) + }) - cart_id: null, - order_id: null, - order_edit_id: null, - customer_id: null, - deleted_at: null, - canceled_at: null, + it("should fail to capture amount greater than authorized", async () => { + const error = await service + .capturePayment({ + amount: 200, + payment_id: "pay-id-1", }) + .catch((e) => e) + + expect(error.message).toEqual( + "Total captured amount exceeds authorised amount." ) }) + + it("should fail to capture already captured payment", 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) + + expect(error.message).toEqual("The payment is already fully captured.") + }) }) describe("refund", () => { it("should refund a payment successfully", async () => { + await service.capturePayment({ + amount: 100, + payment_id: "pay-id-1", + }) + const refundedPayment = await service.refundPayment({ amount: 100, payment_id: "pay-id-1", @@ -417,27 +440,33 @@ describe("Payment Module Service", () => { expect.objectContaining({ id: "pay-id-1", amount: 100, - currency_code: "usd", - provider_id: "manual", - data: {}, - refunds: [ expect.objectContaining({ created_by: null, amount: 100, }), ], + captured_amount: 100, + refunded_amount: 100, + }) + ) + }) - authorized_amount: null, - captured_at: null, + it("should throw if refund is greater than captured amount", async () => { + await service.capturePayment({ + amount: 50, + payment_id: "pay-id-1", + }) - cart_id: null, - order_id: null, - order_edit_id: null, - customer_id: null, - deleted_at: null, - canceled_at: null, + const error = await service + .refundPayment({ + amount: 100, + payment_id: "pay-id-1", }) + .catch((e) => e) + + expect(error.message).toEqual( + "Refund amount cannot be greater than the amount captured on the payment." ) }) }) diff --git a/packages/payment/integration-tests/__tests__/services/payment/index.spec.ts b/packages/payment/integration-tests/__tests__/services/payment/index.spec.ts new file mode 100644 index 0000000000000..cbc2a68cea1c9 --- /dev/null +++ b/packages/payment/integration-tests/__tests__/services/payment/index.spec.ts @@ -0,0 +1,68 @@ +import { SqlEntityManager } from "@mikro-orm/postgresql" +import { asValue } from "awilix" +import { + createPaymentCollections, + createPaymentSessions, + createPayments, +} from "../../../__fixtures__" +import { createMedusaContainer } from "@medusajs/utils" +import { PaymentService } from "@services" + +import { MikroOrmWrapper } from "../../../utils" +import ContainerLoader from "../../../../src/loaders/container" + +jest.setTimeout(30000) + +describe("Payment Service", () => { + let service: PaymentService + let testManager: SqlEntityManager + let repositoryManager: SqlEntityManager + + beforeEach(async () => { + await MikroOrmWrapper.setupDatabase() + repositoryManager = await MikroOrmWrapper.forkManager() + testManager = await MikroOrmWrapper.forkManager() + + const container = createMedusaContainer() + container.register("manager", asValue(repositoryManager)) + + await ContainerLoader({ container }) + + service = container.resolve("paymentService") + + await createPaymentCollections(testManager) + await createPaymentSessions(testManager) + await createPayments(testManager) + }) + + afterEach(async () => { + await MikroOrmWrapper.clearDatabase() + }) + + describe("retrieve with amounts", () => { + it("should retrieve a payment with computed amounts", async () => { + let payment: any = await service.retrieve("pay-id-1", { + select: ["captured_amount"], + }) + + expect(payment.captured_amount).toEqual(0) + + await service.capture({ amount: 50, payment_id: "pay-id-1" }) + + payment = await service.retrieve("pay-id-1", { + select: ["captured_amount"], + }) + + expect(payment.captured_amount).toEqual(50) + + await service.capture({ amount: 25, payment_id: "pay-id-1" }) + + payment = await service.retrieve("pay-id-1", { + select: ["captured_amount", "refunded_amount"], + }) + + expect(payment.captured_amount).toEqual(75) + expect(payment.refunded_amount).toEqual(0) + }) + }) +}) diff --git a/packages/payment/src/models/payment.ts b/packages/payment/src/models/payment.ts index f27bc37d48ceb..c8dfd2619b8ec 100644 --- a/packages/payment/src/models/payment.ts +++ b/packages/payment/src/models/payment.ts @@ -124,8 +124,8 @@ export default class Payment { /** COMPUTED PROPERTIES START **/ - // captured_amount: number // sum of the associated captures - // refunded_amount: number // sum of the associated refunds + captured_amount: number // sum of the associated captures + refunded_amount: number // sum of the associated refunds /** COMPUTED PROPERTIES END **/ diff --git a/packages/payment/src/services/payment-module.ts b/packages/payment/src/services/payment-module.ts index 4355947705475..36b3641570a2d 100644 --- a/packages/payment/src/services/payment-module.ts +++ b/packages/payment/src/services/payment-module.ts @@ -21,6 +21,7 @@ import { InjectManager, InjectTransactionManager, MedusaContext, + MedusaError, } from "@medusajs/utils" import * as services from "@services" @@ -260,16 +261,59 @@ export default class PaymentModuleService implements IPaymentModuleService { data: CreateCaptureDTO, @MedusaContext() sharedContext?: Context ): Promise { - // TODO 1. check if (payment.captured_amount + amount > payment.authorized_amount) {} + let payment = await this.paymentService_.retrieve( + data.payment_id, + { + select: [ + "amount", + "authorized_amount", + "captured_amount", + "captured_at", + ], + relations: ["captures"], + }, + sharedContext + ) - // TODO: 2. set captured_at if fully captured? - // TODO: 3. set PaymentCollection status + if (payment.captured_at) { + throw new MedusaError( + MedusaError.Types.INVALID_DATA, + "The payment is already fully captured." + ) + } + + if ( + Number(payment.captured_amount) + data.amount > + Number(payment.authorized_amount) + ) { + throw new MedusaError( + MedusaError.Types.INVALID_DATA, + "Total captured amount exceeds authorised amount." + ) + } await this.paymentService_.capture(data, sharedContext) - const payment = await this.paymentService_.retrieve( + if ( + Number(payment.captured_amount) + data.amount === + Number(payment.amount) + ) { + await this.paymentService_.update([ + { id: payment.id, captured_at: new Date() }, + ]) + + // TODO: set PaymentCollection status if fully captured + } + + payment = await this.paymentService_.retrieve( data.payment_id, { + select: [ + "amount", + "authorized_amount", + "captured_amount", + "captured_at", + ], relations: ["captures"], }, sharedContext @@ -285,13 +329,27 @@ export default class PaymentModuleService implements IPaymentModuleService { data: CreateRefundDTO, @MedusaContext() sharedContext?: Context ): Promise { - // TODO 1. check if (payment.captured_amount - amount > 0) {} + let payment = await this.paymentService_.retrieve( + data.payment_id, + { + select: ["captured_amount"], + }, + sharedContext + ) + + if (payment.captured_amount < data.amount) { + throw new MedusaError( + MedusaError.Types.INVALID_DATA, + "Refund amount cannot be greater than the amount captured on the payment." + ) + } await this.paymentService_.refund(data, sharedContext) - const payment = await this.paymentService_.retrieve( + payment = await this.paymentService_.retrieve( data.payment_id, { + select: ["amount", "refunded_amount"], relations: ["refunds"], }, sharedContext diff --git a/packages/payment/src/services/payment.ts b/packages/payment/src/services/payment.ts index 244c17a4a1ddd..688249b8a6bbd 100644 --- a/packages/payment/src/services/payment.ts +++ b/packages/payment/src/services/payment.ts @@ -6,10 +6,12 @@ import { CreatePaymentDTO, CreateRefundDTO, DAL, + FindConfig, RefundDTO, UpdatePaymentDTO, } from "@medusajs/types" import { + InjectManager, InjectTransactionManager, MedusaContext, ModulesSdkUtils, @@ -32,15 +34,86 @@ export default class PaymentService< >(Payment) { protected captureRepository_: DAL.RepositoryService protected refundRepository_: DAL.RepositoryService + protected paymentRepository_: DAL.RepositoryService constructor(container: InjectedDependencies) { // @ts-ignore super(...arguments) + this.paymentRepository_ = container.paymentRepository this.captureRepository_ = container.captureRepository this.refundRepository_ = container.refundRepository } + protected transformQueryForAmounts(config: FindConfig) { + const select = config?.select + let relations = config?.relations + const amountsSelect: string[] = [] + + if (select?.includes("captured_amount")) { + amountsSelect.push("captured_amount") + if (!relations?.includes("captures")) { + if (!relations) { + relations = ["captures"] + } else { + relations?.push("captures") + } + } + } + + if (select?.includes("refunded_amount")) { + amountsSelect.push("refunded_amount") + if (!relations?.includes("refunds")) { + if (!relations) { + relations = ["refunds"] + } else { + relations?.push("refunds") + } + } + } + + return { select, relations, amountsSelect } + } + + protected decorateAmounts(payment: TEntity, amountsSelected: string[]) { + const amounts = {} + + for (const amountName of amountsSelected) { + if (amountName === "captured_amount") { + amounts["captured_amount"] = payment.captures.reduce( + (sum, capture) => sum + Number(capture.amount), // TODO: revisit this + 0 + ) + } + if (amountName === "refunded_amount") { + amounts["refunded_amount"] = payment.refunds.reduce( + (sum, refund) => sum + Number(refund.amount), // TODO: revisit this + 0 + ) + } + } + + return Object.assign(payment, amounts) as TEntity + } + + @InjectManager("paymentRepository_") + async retrieve( + id: string, + config: FindConfig = {}, + @MedusaContext() sharedContext?: Context + ): Promise { + const { select, relations, amountsSelect } = + this.transformQueryForAmounts(config) + + const result = await super.retrieve( + id, + { ...config, select, relations }, + sharedContext + ) + + return this.decorateAmounts(result, amountsSelect) as unknown as TEntity + } + @InjectTransactionManager("captureRepository_") async capture( data: CreateCaptureDTO, diff --git a/packages/types/src/payment/mutations.ts b/packages/types/src/payment/mutations.ts index 3f2a0c4dea727..e65c55b5b7ed9 100644 --- a/packages/types/src/payment/mutations.ts +++ b/packages/types/src/payment/mutations.ts @@ -48,6 +48,8 @@ export interface UpdatePaymentDTO { order_edit_id?: string customer_id?: string + captured_at?: Date + data?: Record } From ad103ea1ae84453670440bac7223c1134c869ca7 Mon Sep 17 00:00:00 2001 From: fPolic Date: Wed, 24 Jan 2024 11:08:58 +0100 Subject: [PATCH 23/36] fix: temp fix for build --- packages/payment/src/services/payment.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/payment/src/services/payment.ts b/packages/payment/src/services/payment.ts index 688249b8a6bbd..b131ddef9fa77 100644 --- a/packages/payment/src/services/payment.ts +++ b/packages/payment/src/services/payment.ts @@ -99,7 +99,7 @@ export default class PaymentService< @InjectManager("paymentRepository_") async retrieve( id: string, - config: FindConfig = {}, + config: FindConfig = {}, // TODO: fix type when overriding @MedusaContext() sharedContext?: Context ): Promise { const { select, relations, amountsSelect } = @@ -111,7 +111,7 @@ export default class PaymentService< sharedContext ) - return this.decorateAmounts(result, amountsSelect) as unknown as TEntity + return this.decorateAmounts(result, amountsSelect) } @InjectTransactionManager("captureRepository_") From 16c5ee63ed6b3909f331cc6e103f90cbcf779930 Mon Sep 17 00:00:00 2001 From: fPolic Date: Wed, 24 Jan 2024 11:30:09 +0100 Subject: [PATCH 24/36] refactor: add todos --- packages/payment/src/services/payment.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/payment/src/services/payment.ts b/packages/payment/src/services/payment.ts index b131ddef9fa77..52bb2742242fc 100644 --- a/packages/payment/src/services/payment.ts +++ b/packages/payment/src/services/payment.ts @@ -45,6 +45,9 @@ export default class PaymentService< this.refundRepository_ = container.refundRepository } + /** + * NOTE TODO - TEMP IMPL. THAT WILL CHANGE + */ protected transformQueryForAmounts(config: FindConfig) { const select = config?.select let relations = config?.relations @@ -75,19 +78,22 @@ export default class PaymentService< return { select, relations, amountsSelect } } + /** + * NOTE TODO - TEMP IMPL. THAT WILL CHANGE + */ protected decorateAmounts(payment: TEntity, amountsSelected: string[]) { const amounts = {} for (const amountName of amountsSelected) { if (amountName === "captured_amount") { amounts["captured_amount"] = payment.captures.reduce( - (sum, capture) => sum + Number(capture.amount), // TODO: revisit this + (sum, capture) => sum + Number(capture.amount), 0 ) } if (amountName === "refunded_amount") { amounts["refunded_amount"] = payment.refunds.reduce( - (sum, refund) => sum + Number(refund.amount), // TODO: revisit this + (sum, refund) => sum + Number(refund.amount), 0 ) } @@ -96,6 +102,9 @@ export default class PaymentService< return Object.assign(payment, amounts) as TEntity } + /** + * NOTE TODO - TEMP IMPL. THAT WILL CHANGE + */ @InjectManager("paymentRepository_") async retrieve( id: string, From 8abf73083f7b7a1d8bd7e7554f61f7790cee3387 Mon Sep 17 00:00:00 2001 From: fPolic Date: Wed, 24 Jan 2024 11:44:18 +0100 Subject: [PATCH 25/36] fix: params --- packages/payment/src/services/payment-module.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/payment/src/services/payment-module.ts b/packages/payment/src/services/payment-module.ts index 36b3641570a2d..02c0c2a24d1d1 100644 --- a/packages/payment/src/services/payment-module.ts +++ b/packages/payment/src/services/payment-module.ts @@ -161,8 +161,8 @@ export default class PaymentModuleService implements IPaymentModuleService { @InjectManager("baseRepository_") async listPaymentCollections( - filters?: FilterablePaymentCollectionProps, - config?: FindConfig, + filters: FilterablePaymentCollectionProps = {}, + config: FindConfig = {}, @MedusaContext() sharedContext?: Context ): Promise { const paymentCollections = await this.paymentCollectionService_.list( @@ -179,8 +179,8 @@ export default class PaymentModuleService implements IPaymentModuleService { @InjectManager("baseRepository_") async listAndCountPaymentCollections( - filters?: FilterablePaymentCollectionProps, - config?: FindConfig, + filters: FilterablePaymentCollectionProps = {}, + config: FindConfig = {}, @MedusaContext() sharedContext?: Context ): Promise<[PaymentCollectionDTO[], number]> { const [paymentCollections, count] = From c761f8cae49c03266a8ea0aaa7cec88af09caa50 Mon Sep 17 00:00:00 2001 From: fPolic Date: Wed, 24 Jan 2024 11:46:41 +0100 Subject: [PATCH 26/36] fix: return param --- packages/payment/src/services/payment-module.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/payment/src/services/payment-module.ts b/packages/payment/src/services/payment-module.ts index 02c0c2a24d1d1..27fbd513d5b5a 100644 --- a/packages/payment/src/services/payment-module.ts +++ b/packages/payment/src/services/payment-module.ts @@ -133,7 +133,7 @@ export default class PaymentModuleService implements IPaymentModuleService { async deletePaymentCollection( ids: string | string[], @MedusaContext() sharedContext?: Context - ) { + ): Promise { const paymentCollectionIds = Array.isArray(ids) ? ids : [ids] await this.paymentCollectionService_.delete( paymentCollectionIds, From 821ab18712f0ccbfcc3f1c8e8616f9484ef0543f Mon Sep 17 00:00:00 2001 From: fPolic Date: Wed, 24 Jan 2024 13:25:14 +0100 Subject: [PATCH 27/36] fix: refactor test util --- .../integration-tests/__fixtures__/index.ts | 62 +++++++++---------- .../services/payment-module/index.spec.ts | 4 +- 2 files changed, 31 insertions(+), 35 deletions(-) diff --git a/packages/payment/integration-tests/__fixtures__/index.ts b/packages/payment/integration-tests/__fixtures__/index.ts index 585af6d7edfbb..ee87a7d8a970b 100644 --- a/packages/payment/integration-tests/__fixtures__/index.ts +++ b/packages/payment/integration-tests/__fixtures__/index.ts @@ -1,8 +1,8 @@ +import { EntityName } from "@mikro-orm/core" import { SqlEntityManager } from "@mikro-orm/postgresql" -import { Payment, PaymentSession } from "@models" +import { Payment, PaymentSession, PaymentCollection } from "@models" -import { PaymentCollection } from "../../src/models" import { defaultPaymentCollectionData, defaultPaymentData, @@ -11,47 +11,43 @@ import { export * from "./data" +async function createEntities< + T extends EntityName +>(manager: SqlEntityManager, entity: T, data: any[]) { + const created: T[] = [] + for (let record of data) { + created.push(manager.create(entity, record)) + } + + await manager.persistAndFlush(created) + return created +} + export async function createPaymentCollections( manager: SqlEntityManager, paymentCollectionData = defaultPaymentCollectionData ): Promise { - const collections: PaymentCollection[] = [] - - for (let data of paymentCollectionData) { - let collection = manager.create(PaymentCollection, data) - - await manager.persistAndFlush(collection) - } - - return collections + return await createEntities( + manager, + PaymentCollection, + paymentCollectionData + ) } export async function createPaymentSessions( manager: SqlEntityManager, - paymentCollectionData = defaultPaymentSessionData -): Promise { - const collections: PaymentSession[] = [] - - for (let data of paymentCollectionData) { - let collection = manager.create(PaymentSession, data) - - await manager.persistAndFlush(collection) - } - - return collections + paymentSessionData = defaultPaymentSessionData +): Promise { + return await createEntities( + manager, + PaymentSession, + paymentSessionData + ) } export async function createPayments( manager: SqlEntityManager, - paymentCollectionData = defaultPaymentData -): Promise { - const collections: Payment[] = [] - - for (let data of paymentCollectionData) { - let collection = manager.create(Payment, data) - - await manager.persistAndFlush(collection) - } - - return collections + paymentData = defaultPaymentData +): Promise { + return await createEntities(manager, Payment, paymentData) } diff --git a/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts b/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts index 69a8bc8890041..c1444f13c74e0 100644 --- a/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts +++ b/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts @@ -326,7 +326,7 @@ describe("Payment Module Service", () => { provider_id: "manual", currency_code: "usd", payment_collection_id: paymentCollection.id, - payment_session_id: paymentCollection.payment_sessions[0].id, + payment_session_id: paymentCollection.payment_sessions![0].id, }) expect(createdPayment).toEqual( @@ -347,7 +347,7 @@ describe("Payment Module Service", () => { currency_code: "usd", provider_id: "manual", payment_collection: paymentCollection.id, - payment_session: paymentCollection.payment_sessions[0].id, + payment_session: paymentCollection.payment_sessions![0].id, }) ) }) From 918bb38582fdc6da4fbcd0c9ffb837a8fd16892a Mon Sep 17 00:00:00 2001 From: fPolic Date: Thu, 25 Jan 2024 10:01:22 +0100 Subject: [PATCH 28/36] fix: conflicts --- .../services/payment-module/index.spec.ts | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts b/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts index 3805426a77031..5492803a26070 100644 --- a/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts +++ b/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts @@ -18,22 +18,22 @@ describe("Payment Module Service", () => { let service: IPaymentModuleService describe("PaymentCollection", () => { - let repositoryManager: SqlEntityManager - let shutdownFunc: () => Promise + let repositoryManager: SqlEntityManager + let shutdownFunc: () => Promise - beforeAll(async () => { - const initModulesConfig = getInitModuleConfig() + beforeAll(async () => { + const initModulesConfig = getInitModuleConfig() - const { medusaApp, shutdown } = await initModules(initModulesConfig) + const { medusaApp, shutdown } = await initModules(initModulesConfig) - service = medusaApp.modules[Modules.PAYMENT] + service = medusaApp.modules[Modules.PAYMENT] - shutdownFunc = shutdown - }) + shutdownFunc = shutdown + }) - afterAll(async () => { - await shutdownFunc() - }) + afterAll(async () => { + await shutdownFunc() + }) beforeEach(async () => { await MikroOrmWrapper.setupDatabase() From c98844a3c87d869328123cfac240627bc6ce6081 Mon Sep 17 00:00:00 2001 From: fPolic Date: Thu, 25 Jan 2024 12:05:54 +0100 Subject: [PATCH 29/36] feat: capture bulk --- .../services/payment-module/index.spec.ts | 65 ++++++++- .../payment/src/services/payment-module.ts | 125 +++++++++++++----- packages/payment/src/services/payment.ts | 57 +++++--- packages/types/src/payment/service.ts | 12 ++ 4 files changed, 200 insertions(+), 59 deletions(-) diff --git a/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts b/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts index 5492803a26070..773383ef1b438 100644 --- a/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts +++ b/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts @@ -412,6 +412,69 @@ describe("Payment Module Service", () => { ) }) + it("should capture payments in bulk successfully", async () => { + const capturedPayments = await service.capturePayment([ + { + amount: 50, // partially captured + payment_id: "pay-id-1", + }, + { + amount: 100, // fully captured + payment_id: "pay-id-2", + }, + ]) + + expect(capturedPayments).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: "pay-id-1", + amount: 100, + authorized_amount: 100, + captured_at: null, + captures: [ + expect.objectContaining({ + created_by: null, + amount: 50, + }), + ], + captured_amount: 50, + }), + expect.objectContaining({ + id: "pay-id-2", + amount: 100, + authorized_amount: 100, + captured_at: expect.any(Date), + captures: [ + expect.objectContaining({ + created_by: null, + amount: 100, + }), + ], + captured_amount: 100, + }), + ]) + ) + }) + + it("should fail to capture payments in bulk if one of the captures fail", async () => { + const error = await service + .capturePayment([ + { + amount: 50, + payment_id: "pay-id-1", + }, + { + amount: 200, // exceeds authorized amount + payment_id: "pay-id-2", + }, + ]) + .catch((e) => e) + + expect(error.message).toEqual( + "Total captured amount for payment: pay-id-2 exceeds authorised amount." + ) + }) + it("should fail to capture amount greater than authorized", async () => { const error = await service .capturePayment({ @@ -421,7 +484,7 @@ describe("Payment Module Service", () => { .catch((e) => e) expect(error.message).toEqual( - "Total captured amount exceeds authorised amount." + "Total captured amount for payment: pay-id-1 exceeds authorised amount." ) }) diff --git a/packages/payment/src/services/payment-module.ts b/packages/payment/src/services/payment-module.ts index 27fbd513d5b5a..f229e199dc344 100644 --- a/packages/payment/src/services/payment-module.ts +++ b/packages/payment/src/services/payment-module.ts @@ -27,6 +27,7 @@ import { import * as services from "@services" import { joinerConfig } from "../joiner-config" +import { Payment } from "@models" type InjectedDependencies = { baseRepository: DAL.RepositoryService @@ -256,13 +257,37 @@ export default class PaymentModuleService implements IPaymentModuleService { ) } - @InjectTransactionManager("baseRepository_") - async capturePayment( + capturePayment( data: CreateCaptureDTO, + sharedContext?: Context + ): Promise + capturePayment( + data: CreateCaptureDTO[], + sharedContext?: Context + ): Promise + + @InjectManager("baseRepository_") + async capturePayment( + data: CreateCaptureDTO | CreateCaptureDTO[], + @MedusaContext() sharedContext: Context = {} + ): Promise { + const input = Array.isArray(data) ? data : [data] + + const payments = await this.capturePaymentBulk_(input, sharedContext) + + return await this.baseRepository_.serialize( + Array.isArray(data) ? payments : payments[0], + { populate: true } + ) + } + + @InjectTransactionManager("baseRepository_") + protected async capturePaymentBulk_( + data: CreateCaptureDTO[], @MedusaContext() sharedContext?: Context - ): Promise { - let payment = await this.paymentService_.retrieve( - data.payment_id, + ): Promise { + let payments = await this.paymentService_.list( + { id: data.map((d) => d.payment_id) }, { select: [ "amount", @@ -275,38 +300,52 @@ export default class PaymentModuleService implements IPaymentModuleService { sharedContext ) - if (payment.captured_at) { - throw new MedusaError( - MedusaError.Types.INVALID_DATA, - "The payment is already fully captured." - ) - } + for (const payment of payments) { + const input = data.find((d) => d.payment_id === payment.id)! - if ( - Number(payment.captured_amount) + data.amount > - Number(payment.authorized_amount) - ) { - throw new MedusaError( - MedusaError.Types.INVALID_DATA, - "Total captured amount exceeds authorised amount." - ) + if (payment.captured_at) { + throw new MedusaError( + MedusaError.Types.INVALID_DATA, + "The payment is already fully captured." + ) + } + + if ( + Number(payment.captured_amount) + input.amount > + Number(payment.authorized_amount) + ) { + throw new MedusaError( + MedusaError.Types.INVALID_DATA, + `Total captured amount for payment: ${payment.id} exceeds authorised amount.` + ) + } } await this.paymentService_.capture(data, sharedContext) - if ( - Number(payment.captured_amount) + data.amount === - Number(payment.amount) - ) { - await this.paymentService_.update([ - { id: payment.id, captured_at: new Date() }, - ]) + let fullyCapturedPaymentsId: string[] = [] + for (const payment of payments) { + const input = data.find((d) => d.payment_id === payment.id)! + if ( + Number(payment.captured_amount) + input.amount === + Number(payment.amount) + ) { + fullyCapturedPaymentsId.push(payment.id) + } + } - // TODO: set PaymentCollection status if fully captured + if (fullyCapturedPaymentsId.length) { + await this.paymentService_.update( + fullyCapturedPaymentsId.map((id) => ({ id, captured_at: new Date() })), + sharedContext + ) } - payment = await this.paymentService_.retrieve( - data.payment_id, + // TODO: set PaymentCollection status if fully captured + + return await this.paymentService_.list( + { id: data.map((d) => d.payment_id) }, + // TODO: temp - will be removed { select: [ "amount", @@ -318,12 +357,17 @@ export default class PaymentModuleService implements IPaymentModuleService { }, sharedContext ) - - return await this.baseRepository_.serialize(payment, { - populate: true, - }) } + // refundPayment( + // data: CreateRefundDTO, + // sharedContext?: Context + // ): Promise + // refundPayment( + // data: CreateRefundDTO[], + // sharedContext?: Context + // ): Promise + @InjectTransactionManager("baseRepository_") async refundPayment( data: CreateRefundDTO, @@ -344,7 +388,10 @@ export default class PaymentModuleService implements IPaymentModuleService { ) } - await this.paymentService_.refund(data, sharedContext) + await this.paymentService_.refund( + data as unknown as CreateRefundDTO[], // TODO + sharedContext + ) payment = await this.paymentService_.retrieve( data.payment_id, @@ -412,10 +459,16 @@ export default class PaymentModuleService implements IPaymentModuleService { throw new Error("Method not implemented.") } + cancelPayment(paymentId: string, sharedContext?: Context): Promise cancelPayment( - paymentId: string, + paymentId: string[], sharedContext?: Context - ): Promise { + ): Promise + + cancelPayment( + paymentId: string | string[], + sharedContext?: Context + ): Promise { throw new Error("Method not implemented.") } diff --git a/packages/payment/src/services/payment.ts b/packages/payment/src/services/payment.ts index 52bb2742242fc..79b2fd534467b 100644 --- a/packages/payment/src/services/payment.ts +++ b/packages/payment/src/services/payment.ts @@ -123,39 +123,52 @@ export default class PaymentService< return this.decorateAmounts(result, amountsSelect) } + /** + * NOTE TODO - TEMP IMPL. THAT WILL CHANGE + */ + @InjectManager("paymentRepository_") + async list( + filters: {} = {}, + config: FindConfig = {}, + @MedusaContext() sharedContext?: Context + ): Promise { + const { select, relations, amountsSelect } = + this.transformQueryForAmounts(config) + + const result = await super.list( + filters, + { ...config, select, relations }, + sharedContext + ) + + return result.map((r) => this.decorateAmounts(r, amountsSelect)) + } + @InjectTransactionManager("captureRepository_") async capture( - data: CreateCaptureDTO, + data: CreateCaptureDTO[], @MedusaContext() sharedContext?: Context - ): Promise { - const created = await this.captureRepository_.create( - [ - { - payment: data.payment_id, - ...data, - }, - ], + ): Promise { + return await this.captureRepository_.create( + data.map((input) => ({ + payment: input.payment_id, + ...input, + })), sharedContext ) - - return created[0] } @InjectTransactionManager("refundRepository_") async refund( - data: CreateRefundDTO, + data: CreateRefundDTO[], @MedusaContext() sharedContext?: Context - ): Promise { - const created = await this.refundRepository_.create( - [ - { - payment: data.payment_id, - ...data, - }, - ], + ): Promise { + return await this.refundRepository_.create( + data.map((input) => ({ + payment: input.payment_id, + ...input, + })), sharedContext ) - - return created[0] } } diff --git a/packages/types/src/payment/service.ts b/packages/types/src/payment/service.ts index 9524121d11917..25c4a90bd22dd 100644 --- a/packages/types/src/payment/service.ts +++ b/packages/types/src/payment/service.ts @@ -90,13 +90,25 @@ export interface IPaymentModuleService extends IModuleService { data: CreateCaptureDTO, sharedContext?: Context ): Promise + capturePayment( + data: CreateCaptureDTO[], + sharedContext?: Context + ): Promise refundPayment( data: CreateRefundDTO, sharedContext?: Context ): Promise + // refundPayment( + // data: CreateRefundDTO[], + // sharedContext?: Context + // ): Promise cancelPayment(paymentId: string, sharedContext?: Context): Promise + cancelPayment( + paymentId: string[], + sharedContext?: Context + ): Promise updatePayment( data: UpdatePaymentDTO, From 3d81950fe4e61ecca467e0319f57d1a85580ace7 Mon Sep 17 00:00:00 2001 From: fPolic Date: Thu, 25 Jan 2024 14:12:38 +0100 Subject: [PATCH 30/36] feat: refund bulk --- .../services/payment-module/index.spec.ts | 57 ++++++++++---- .../payment/src/services/payment-module.ts | 76 +++++++++++-------- packages/types/src/payment/service.ts | 8 +- 3 files changed, 91 insertions(+), 50 deletions(-) diff --git a/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts b/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts index 773383ef1b438..bcc7663645e5e 100644 --- a/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts +++ b/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts @@ -506,30 +506,55 @@ describe("Payment Module Service", () => { }) describe("refund", () => { - it("should refund a payment successfully", async () => { + it("should refund a payments in bulk successfully", async () => { await service.capturePayment({ amount: 100, payment_id: "pay-id-1", }) - const refundedPayment = await service.refundPayment({ + await service.capturePayment({ amount: 100, - payment_id: "pay-id-1", + payment_id: "pay-id-2", }) - expect(refundedPayment).toEqual( - expect.objectContaining({ - id: "pay-id-1", + const refundedPayment = await service.refundPayment([ + { amount: 100, - refunds: [ - expect.objectContaining({ - created_by: null, - amount: 100, - }), - ], - captured_amount: 100, - refunded_amount: 100, - }) + payment_id: "pay-id-1", + }, + { + amount: 100, + payment_id: "pay-id-2", + }, + ]) + + expect(refundedPayment).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: "pay-id-1", + amount: 100, + refunds: [ + expect.objectContaining({ + created_by: null, + amount: 100, + }), + ], + captured_amount: 100, + refunded_amount: 100, + }), + expect.objectContaining({ + id: "pay-id-2", + amount: 100, + refunds: [ + expect.objectContaining({ + created_by: null, + amount: 100, + }), + ], + captured_amount: 100, + refunded_amount: 100, + }), + ]) ) }) @@ -547,7 +572,7 @@ describe("Payment Module Service", () => { .catch((e) => e) expect(error.message).toEqual( - "Refund amount cannot be greater than the amount captured on the payment." + "Refund amount for payment: pay-id-1 cannot be greater than the amount captured on the payment." ) }) }) diff --git a/packages/payment/src/services/payment-module.ts b/packages/payment/src/services/payment-module.ts index f229e199dc344..4e2cd29bbe164 100644 --- a/packages/payment/src/services/payment-module.ts +++ b/packages/payment/src/services/payment-module.ts @@ -299,9 +299,10 @@ export default class PaymentModuleService implements IPaymentModuleService { }, sharedContext ) + const inputMap = new Map(data.map((d) => [d.payment_id, d])) for (const payment of payments) { - const input = data.find((d) => d.payment_id === payment.id)! + const input = inputMap.get(payment.id)! if (payment.captured_at) { throw new MedusaError( @@ -325,7 +326,8 @@ export default class PaymentModuleService implements IPaymentModuleService { let fullyCapturedPaymentsId: string[] = [] for (const payment of payments) { - const input = data.find((d) => d.payment_id === payment.id)! + const input = inputMap.get(payment.id)! + if ( Number(payment.captured_amount) + input.amount === Number(payment.amount) @@ -359,52 +361,66 @@ export default class PaymentModuleService implements IPaymentModuleService { ) } - // refundPayment( - // data: CreateRefundDTO, - // sharedContext?: Context - // ): Promise - // refundPayment( - // data: CreateRefundDTO[], - // sharedContext?: Context - // ): Promise + refundPayment( + data: CreateRefundDTO, + sharedContext?: Context + ): Promise + refundPayment( + data: CreateRefundDTO[], + sharedContext?: Context + ): Promise - @InjectTransactionManager("baseRepository_") + @InjectManager("baseRepository_") async refundPayment( - data: CreateRefundDTO, + data: CreateRefundDTO | CreateRefundDTO[], + @MedusaContext() sharedContext?: Context + ): Promise { + const input = Array.isArray(data) ? data : [data] + + const payments = await this.refundPaymentBulk_(input, sharedContext) + + return await this.baseRepository_.serialize( + Array.isArray(data) ? payments : payments[0], + { populate: true } + ) + } + + @InjectTransactionManager("baseRepository_") + async refundPaymentBulk_( + data: CreateRefundDTO[], @MedusaContext() sharedContext?: Context - ): Promise { - let payment = await this.paymentService_.retrieve( - data.payment_id, + ): Promise { + const payments = await this.paymentService_.list( + { id: data.map(({ payment_id }) => payment_id) }, + // TODO: temp - will be removed { select: ["captured_amount"], }, sharedContext ) - if (payment.captured_amount < data.amount) { - throw new MedusaError( - MedusaError.Types.INVALID_DATA, - "Refund amount cannot be greater than the amount captured on the payment." - ) + const inputMap = new Map(data.map((d) => [d.payment_id, d])) + + for (const payment of payments) { + const input = inputMap.get(payment.id)! + if (payment.captured_amount < input.amount) { + throw new MedusaError( + MedusaError.Types.INVALID_DATA, + `Refund amount for payment: ${payment.id} cannot be greater than the amount captured on the payment.` + ) + } } - await this.paymentService_.refund( - data as unknown as CreateRefundDTO[], // TODO - sharedContext - ) + await this.paymentService_.refund(data, sharedContext) - payment = await this.paymentService_.retrieve( - data.payment_id, + return await this.paymentService_.list( + { id: data.map(({ payment_id }) => payment_id) }, { select: ["amount", "refunded_amount"], relations: ["refunds"], }, sharedContext ) - - return await this.baseRepository_.serialize(payment, { - populate: true, - }) } createPaymentSession( diff --git a/packages/types/src/payment/service.ts b/packages/types/src/payment/service.ts index 25c4a90bd22dd..394ccf7b6c037 100644 --- a/packages/types/src/payment/service.ts +++ b/packages/types/src/payment/service.ts @@ -99,10 +99,10 @@ export interface IPaymentModuleService extends IModuleService { data: CreateRefundDTO, sharedContext?: Context ): Promise - // refundPayment( - // data: CreateRefundDTO[], - // sharedContext?: Context - // ): Promise + refundPayment( + data: CreateRefundDTO[], + sharedContext?: Context + ): Promise cancelPayment(paymentId: string, sharedContext?: Context): Promise cancelPayment( From facf7f83f65f149c02b8f37e765bf1f1538b0f76 Mon Sep 17 00:00:00 2001 From: fPolic Date: Thu, 25 Jan 2024 14:24:33 +0100 Subject: [PATCH 31/36] fix: payment service test --- .../__tests__/services/payment/index.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/payment/integration-tests/__tests__/services/payment/index.spec.ts b/packages/payment/integration-tests/__tests__/services/payment/index.spec.ts index cbc2a68cea1c9..99fef88bf0900 100644 --- a/packages/payment/integration-tests/__tests__/services/payment/index.spec.ts +++ b/packages/payment/integration-tests/__tests__/services/payment/index.spec.ts @@ -47,7 +47,7 @@ describe("Payment Service", () => { expect(payment.captured_amount).toEqual(0) - await service.capture({ amount: 50, payment_id: "pay-id-1" }) + await service.capture([{ amount: 50, payment_id: "pay-id-1" }]) payment = await service.retrieve("pay-id-1", { select: ["captured_amount"], @@ -55,7 +55,7 @@ describe("Payment Service", () => { expect(payment.captured_amount).toEqual(50) - await service.capture({ amount: 25, payment_id: "pay-id-1" }) + await service.capture([{ amount: 25, payment_id: "pay-id-1" }]) payment = await service.retrieve("pay-id-1", { select: ["captured_amount", "refunded_amount"], From 75468c8605b0c5b72f9c4e17d096443ac838cbb6 Mon Sep 17 00:00:00 2001 From: fPolic Date: Mon, 5 Feb 2024 16:03:27 +0100 Subject: [PATCH 32/36] chore: migrate to new abstract service --- .../services/payment-module/index.spec.ts | 8 +- packages/payment/src/services/index.ts | 2 - .../payment/src/services/payment-module.ts | 132 +++--------------- .../payment/src/services/payment-session.ts | 21 --- packages/payment/src/services/payment.ts | 20 +-- 5 files changed, 31 insertions(+), 152 deletions(-) delete mode 100644 packages/payment/src/services/payment-session.ts diff --git a/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts b/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts index fc9157f40c95c..453f3c8835373 100644 --- a/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts +++ b/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts @@ -364,8 +364,12 @@ describe("Payment Module Service", () => { amount: 200, currency_code: "usd", provider_id: "manual", - payment_collection: paymentCollection.id, - payment_session: paymentCollection.payment_sessions![0].id, + payment_collection: expect.objectContaining({ + id: paymentCollection.id, + }), + payment_session: expect.objectContaining({ + id: paymentCollection.payment_sessions![0].id, + }), }) ) }) diff --git a/packages/payment/src/services/index.ts b/packages/payment/src/services/index.ts index 39afcb4fb0ff8..32b3a98e8de59 100644 --- a/packages/payment/src/services/index.ts +++ b/packages/payment/src/services/index.ts @@ -1,5 +1,3 @@ export { default as PaymentModuleService } from "./payment-module" export { default as PaymentService } from "./payment" -export { default as PaymentSessionService } from "./payment-session" -export { default as PaymentCollectionService } from "./payment-collection" diff --git a/packages/payment/src/services/payment-module.ts b/packages/payment/src/services/payment-module.ts index 0c9ea954b0fcb..b11c0b8eaedd1 100644 --- a/packages/payment/src/services/payment-module.ts +++ b/packages/payment/src/services/payment-module.ts @@ -1,4 +1,5 @@ import { + CaptureDTO, Context, CreateCaptureDTO, CreatePaymentCollectionDTO, @@ -6,12 +7,15 @@ import { CreatePaymentSessionDTO, CreateRefundDTO, DAL, + FilterablePaymentCollectionProps, + FindConfig, InternalModuleDeclaration, IPaymentModuleService, ModuleJoinerConfig, ModulesSdkTypes, PaymentCollectionDTO, PaymentDTO, + PaymentSessionDTO, SetPaymentSessionsDTO, UpdatePaymentCollectionDTO, UpdatePaymentDTO, @@ -21,62 +25,41 @@ import { MedusaContext, ModulesSdkUtils, MedusaError, + InjectManager, } from "@medusajs/utils" import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config" -import { - Capture, - Payment, - PaymentCollection, - PaymentMethodToken, - PaymentProvider, - PaymentSession, - Refund, -} from "@models" -import * as services from "@services" - -import { joinerConfig } from "../joiner-config" -import { Payment } from "@models" +import { Payment, PaymentCollection, PaymentSession } from "@models" + +import PaymentService from "./payment" type InjectedDependencies = { baseRepository: DAL.RepositoryService - paymentCollectionService: ModulesSdkTypes.InternalModuleService - paymentService: ModulesSdkTypes.InternalModuleService + paymentService: PaymentService paymentSessionService: ModulesSdkTypes.InternalModuleService paymentCollectionService: ModulesSdkTypes.InternalModuleService } -const generateMethodForModels = [ - Capture, - PaymentCollection, - PaymentMethodToken, - PaymentProvider, - PaymentSession, - Refund, -] +const generateMethodForModels = [PaymentCollection, PaymentSession] export default class PaymentModuleService< - TPaymentCollection extends PaymentCollection = PaymentCollection + TPaymentCollection extends PaymentCollection = PaymentCollection, + TPayment extends Payment = Payment > extends ModulesSdkUtils.abstractModuleServiceFactory< - // TODO revisit when moving forward frane InjectedDependencies, - PaymentDTO, + PaymentCollectionDTO, { - Capture: { dto: any } - PaymentCollection: { dto: any } - PaymentMethodToken: { dto: any } - PaymentProvider: { dto: any } - PaymentSession: { dto: any } - Refund: { dto: any } + PaymentCollection: { dto: PaymentCollectionDTO } + PaymentSession: { dto: PaymentSessionDTO } } - >(Payment, generateMethodForModels, entityNameToLinkableKeysMap) + >(PaymentCollection, generateMethodForModels, entityNameToLinkableKeysMap) implements IPaymentModuleService { protected baseRepository_: DAL.RepositoryService - protected paymentService_: services.PaymentService - protected paymentSessionService_: services.PaymentSessionService + protected paymentService_: PaymentService + protected paymentSessionService_: ModulesSdkTypes.InternalModuleService protected paymentCollectionService_: ModulesSdkTypes.InternalModuleService constructor( @@ -160,85 +143,6 @@ export default class PaymentModuleService< ) } - deletePaymentCollection( - paymentCollectionId: string[], - sharedContext?: Context - ): Promise - deletePaymentCollection( - paymentCollectionId: string, - sharedContext?: Context - ): Promise - - @InjectTransactionManager("baseRepository_") - async deletePaymentCollection( - ids: string | string[], - @MedusaContext() sharedContext?: Context - ): Promise { - const paymentCollectionIds = Array.isArray(ids) ? ids : [ids] - await this.paymentCollectionService_.delete( - paymentCollectionIds, - sharedContext - ) - } - - @InjectManager("baseRepository_") - async retrievePaymentCollection( - paymentCollectionId: string, - config: FindConfig = {}, - @MedusaContext() sharedContext: Context = {} - ): Promise { - const paymentCollection = await this.paymentCollectionService_.retrieve( - paymentCollectionId, - config, - sharedContext - ) - - return await this.baseRepository_.serialize( - paymentCollection, - { populate: true } - ) - } - - @InjectManager("baseRepository_") - async listPaymentCollections( - filters: FilterablePaymentCollectionProps = {}, - config: FindConfig = {}, - @MedusaContext() sharedContext?: Context - ): Promise { - const paymentCollections = await this.paymentCollectionService_.list( - filters, - config, - sharedContext - ) - - return await this.baseRepository_.serialize( - paymentCollections, - { populate: true } - ) - } - - @InjectManager("baseRepository_") - async listAndCountPaymentCollections( - filters: FilterablePaymentCollectionProps = {}, - config: FindConfig = {}, - @MedusaContext() sharedContext?: Context - ): Promise<[PaymentCollectionDTO[], number]> { - const [paymentCollections, count] = - await this.paymentCollectionService_.listAndCount( - filters, - config, - sharedContext - ) - - return [ - await this.baseRepository_.serialize( - paymentCollections, - { populate: true } - ), - count, - ] - } - createPayment( data: CreatePaymentDTO, sharedContext?: Context diff --git a/packages/payment/src/services/payment-session.ts b/packages/payment/src/services/payment-session.ts deleted file mode 100644 index 359d1f244d895..0000000000000 --- a/packages/payment/src/services/payment-session.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { PaymentSession } from "@models" -import { CreatePaymentSessionDTO, DAL } from "@medusajs/types" -import { ModulesSdkUtils } from "@medusajs/utils" - -type InjectedDependencies = { - paymentSessionRepository: DAL.RepositoryService -} - -export default class PaymentSessionService< - TEntity extends PaymentSession = PaymentSession -> extends ModulesSdkUtils.abstractServiceFactory< - InjectedDependencies, - { - create: CreatePaymentSessionDTO - } ->(PaymentSession) { - constructor(container: InjectedDependencies) { - // @ts-ignore - super(...arguments) - } -} diff --git a/packages/payment/src/services/payment.ts b/packages/payment/src/services/payment.ts index 79b2fd534467b..07ea2b18f0f4f 100644 --- a/packages/payment/src/services/payment.ts +++ b/packages/payment/src/services/payment.ts @@ -3,16 +3,15 @@ import { CaptureDTO, Context, CreateCaptureDTO, - CreatePaymentDTO, CreateRefundDTO, DAL, FindConfig, RefundDTO, - UpdatePaymentDTO, } from "@medusajs/types" import { InjectManager, InjectTransactionManager, + isObject, MedusaContext, ModulesSdkUtils, } from "@medusajs/utils" @@ -25,13 +24,9 @@ type InjectedDependencies = { export default class PaymentService< TEntity extends Payment = Payment -> extends ModulesSdkUtils.abstractServiceFactory< - InjectedDependencies, - { - create: CreatePaymentDTO - update: UpdatePaymentDTO - } ->(Payment) { +> extends ModulesSdkUtils.internalModuleServiceFactory( + Payment +) { protected captureRepository_: DAL.RepositoryService protected refundRepository_: DAL.RepositoryService protected paymentRepository_: DAL.RepositoryService @@ -105,17 +100,16 @@ export default class PaymentService< /** * NOTE TODO - TEMP IMPL. THAT WILL CHANGE */ - @InjectManager("paymentRepository_") async retrieve( - id: string, + id: string | object, config: FindConfig = {}, // TODO: fix type when overriding - @MedusaContext() sharedContext?: Context + sharedContext: Context = {} ): Promise { const { select, relations, amountsSelect } = this.transformQueryForAmounts(config) const result = await super.retrieve( - id, + isObject(id) ? id : { id }, { ...config, select, relations }, sharedContext ) From a17609e7c60709e3d2ca1abc19ed290dc9fa27d9 Mon Sep 17 00:00:00 2001 From: fPolic Date: Mon, 5 Feb 2024 16:36:08 +0100 Subject: [PATCH 33/36] fix: address comments --- packages/payment/src/services/payment-module.ts | 2 +- packages/types/src/payment/mutations.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/payment/src/services/payment-module.ts b/packages/payment/src/services/payment-module.ts index b11c0b8eaedd1..153cbcb691675 100644 --- a/packages/payment/src/services/payment-module.ts +++ b/packages/payment/src/services/payment-module.ts @@ -260,7 +260,7 @@ export default class PaymentModuleService< ) { throw new MedusaError( MedusaError.Types.INVALID_DATA, - `Total captured amount for payment: ${payment.id} exceeds authorised amount.` + `Total captured amount for payment: ${payment.id} exceeds authorized amount.` ) } } diff --git a/packages/types/src/payment/mutations.ts b/packages/types/src/payment/mutations.ts index e65c55b5b7ed9..60bd52a5da373 100644 --- a/packages/types/src/payment/mutations.ts +++ b/packages/types/src/payment/mutations.ts @@ -64,7 +64,7 @@ export interface CreateRefundDTO { amount: number payment_id: string - captured_by?: string + created_by?: string } /** From 81ac0cc825ce57aadb84787c8e83a04277b7d49b Mon Sep 17 00:00:00 2001 From: fPolic Date: Mon, 5 Feb 2024 18:50:12 +0100 Subject: [PATCH 34/36] refactor: remove payment service --- .../services/payment-module/index.spec.ts | 148 +++++++-------- .../__tests__/services/payment/index.spec.ts | 68 ------- packages/payment/src/services/index.ts | 2 - .../payment/src/services/payment-module.ts | 102 ++++++----- packages/payment/src/services/payment.ts | 168 ------------------ 5 files changed, 130 insertions(+), 358 deletions(-) delete mode 100644 packages/payment/integration-tests/__tests__/services/payment/index.spec.ts delete mode 100644 packages/payment/src/services/payment.ts diff --git a/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts b/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts index 453f3c8835373..b671f78c42f85 100644 --- a/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts +++ b/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts @@ -410,8 +410,9 @@ describe("Payment Module Service", () => { }), ], - captured_amount: 100, - captured_at: expect.any(Date), + // TODO: uncomment when totals calculations are implemented + // captured_amount: 100, + // captured_at: expect.any(Date), }) ) }) @@ -441,72 +442,73 @@ describe("Payment Module Service", () => { amount: 50, }), ], - captured_amount: 50, + // captured_amount: 50, }), expect.objectContaining({ id: "pay-id-2", amount: 100, authorized_amount: 100, - captured_at: expect.any(Date), + // captured_at: expect.any(Date), captures: [ expect.objectContaining({ created_by: null, amount: 100, }), ], - captured_amount: 100, + // captured_amount: 100, }), ]) ) }) - it("should fail to capture payments in bulk if one of the captures fail", async () => { - const error = await service - .capturePayment([ - { - amount: 50, - payment_id: "pay-id-1", - }, - { - amount: 200, // exceeds authorized amount - payment_id: "pay-id-2", - }, - ]) - .catch((e) => e) - - expect(error.message).toEqual( - "Total captured amount for payment: pay-id-2 exceeds authorised amount." - ) - }) - - it("should fail to capture amount greater than authorized", async () => { - const error = await service - .capturePayment({ - amount: 200, - payment_id: "pay-id-1", - }) - .catch((e) => e) - - expect(error.message).toEqual( - "Total captured amount for payment: pay-id-1 exceeds authorised amount." - ) - }) - - it("should fail to capture already captured payment", 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) - - expect(error.message).toEqual("The payment is already fully captured.") - }) + // TODO: uncomment when totals are implemented + // it("should fail to capture payments in bulk if one of the captures fail", async () => { + // const error = await service + // .capturePayment([ + // { + // amount: 50, + // payment_id: "pay-id-1", + // }, + // { + // amount: 200, // exceeds authorized amount + // payment_id: "pay-id-2", + // }, + // ]) + // .catch((e) => e) + // + // expect(error.message).toEqual( + // "Total captured amount for payment: pay-id-2 exceeds authorized amount." + // ) + // }) + + // it("should fail to capture amount greater than authorized", async () => { + // const error = await service + // .capturePayment({ + // amount: 200, + // payment_id: "pay-id-1", + // }) + // .catch((e) => e) + // + // expect(error.message).toEqual( + // "Total captured amount for payment: pay-id-1 exceeds authorized amount." + // ) + // }) + + // it("should fail to capture already captured payment", 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) + // + // expect(error.message).toEqual("The payment is already fully captured.") + // }) }) describe("refund", () => { @@ -543,8 +545,8 @@ describe("Payment Module Service", () => { amount: 100, }), ], - captured_amount: 100, - refunded_amount: 100, + // captured_amount: 100, + // refunded_amount: 100, }), expect.objectContaining({ id: "pay-id-2", @@ -555,30 +557,30 @@ describe("Payment Module Service", () => { amount: 100, }), ], - captured_amount: 100, - refunded_amount: 100, + // captured_amount: 100, + // refunded_amount: 100, }), ]) ) }) - it("should throw if refund is greater than captured amount", async () => { - await service.capturePayment({ - amount: 50, - payment_id: "pay-id-1", - }) - - const error = await service - .refundPayment({ - amount: 100, - payment_id: "pay-id-1", - }) - .catch((e) => e) - - expect(error.message).toEqual( - "Refund amount for payment: pay-id-1 cannot be greater than the amount captured on the payment." - ) - }) + // it("should throw if refund is greater than captured amount", async () => { + // await service.capturePayment({ + // amount: 50, + // payment_id: "pay-id-1", + // }) + // + // const error = await service + // .refundPayment({ + // amount: 100, + // payment_id: "pay-id-1", + // }) + // .catch((e) => e) + // + // expect(error.message).toEqual( + // "Refund amount for payment: pay-id-1 cannot be greater than the amount captured on the payment." + // ) + // }) }) }) }) diff --git a/packages/payment/integration-tests/__tests__/services/payment/index.spec.ts b/packages/payment/integration-tests/__tests__/services/payment/index.spec.ts deleted file mode 100644 index 99fef88bf0900..0000000000000 --- a/packages/payment/integration-tests/__tests__/services/payment/index.spec.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { SqlEntityManager } from "@mikro-orm/postgresql" -import { asValue } from "awilix" -import { - createPaymentCollections, - createPaymentSessions, - createPayments, -} from "../../../__fixtures__" -import { createMedusaContainer } from "@medusajs/utils" -import { PaymentService } from "@services" - -import { MikroOrmWrapper } from "../../../utils" -import ContainerLoader from "../../../../src/loaders/container" - -jest.setTimeout(30000) - -describe("Payment Service", () => { - let service: PaymentService - let testManager: SqlEntityManager - let repositoryManager: SqlEntityManager - - beforeEach(async () => { - await MikroOrmWrapper.setupDatabase() - repositoryManager = await MikroOrmWrapper.forkManager() - testManager = await MikroOrmWrapper.forkManager() - - const container = createMedusaContainer() - container.register("manager", asValue(repositoryManager)) - - await ContainerLoader({ container }) - - service = container.resolve("paymentService") - - await createPaymentCollections(testManager) - await createPaymentSessions(testManager) - await createPayments(testManager) - }) - - afterEach(async () => { - await MikroOrmWrapper.clearDatabase() - }) - - describe("retrieve with amounts", () => { - it("should retrieve a payment with computed amounts", async () => { - let payment: any = await service.retrieve("pay-id-1", { - select: ["captured_amount"], - }) - - expect(payment.captured_amount).toEqual(0) - - await service.capture([{ amount: 50, payment_id: "pay-id-1" }]) - - payment = await service.retrieve("pay-id-1", { - select: ["captured_amount"], - }) - - expect(payment.captured_amount).toEqual(50) - - await service.capture([{ amount: 25, payment_id: "pay-id-1" }]) - - payment = await service.retrieve("pay-id-1", { - select: ["captured_amount", "refunded_amount"], - }) - - expect(payment.captured_amount).toEqual(75) - expect(payment.refunded_amount).toEqual(0) - }) - }) -}) diff --git a/packages/payment/src/services/index.ts b/packages/payment/src/services/index.ts index 32b3a98e8de59..a4f5ea37aea2e 100644 --- a/packages/payment/src/services/index.ts +++ b/packages/payment/src/services/index.ts @@ -1,3 +1 @@ export { default as PaymentModuleService } from "./payment-module" - -export { default as PaymentService } from "./payment" diff --git a/packages/payment/src/services/payment-module.ts b/packages/payment/src/services/payment-module.ts index 153cbcb691675..49ce4acc77cf9 100644 --- a/packages/payment/src/services/payment-module.ts +++ b/packages/payment/src/services/payment-module.ts @@ -7,8 +7,6 @@ import { CreatePaymentSessionDTO, CreateRefundDTO, DAL, - FilterablePaymentCollectionProps, - FindConfig, InternalModuleDeclaration, IPaymentModuleService, ModuleJoinerConfig, @@ -16,6 +14,7 @@ import { PaymentCollectionDTO, PaymentDTO, PaymentSessionDTO, + RefundDTO, SetPaymentSessionsDTO, UpdatePaymentCollectionDTO, UpdatePaymentDTO, @@ -29,13 +28,19 @@ import { } from "@medusajs/utils" import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config" -import { Payment, PaymentCollection, PaymentSession } from "@models" - -import PaymentService from "./payment" +import { + Capture, + Payment, + PaymentCollection, + PaymentSession, + Refund, +} from "@models" type InjectedDependencies = { baseRepository: DAL.RepositoryService - paymentService: PaymentService + paymentService: ModulesSdkTypes.InternalModuleService + captureService: ModulesSdkTypes.InternalModuleService + refundService: ModulesSdkTypes.InternalModuleService paymentSessionService: ModulesSdkTypes.InternalModuleService paymentCollectionService: ModulesSdkTypes.InternalModuleService } @@ -44,7 +49,10 @@ const generateMethodForModels = [PaymentCollection, PaymentSession] export default class PaymentModuleService< TPaymentCollection extends PaymentCollection = PaymentCollection, - TPayment extends Payment = Payment + TPayment extends Payment = Payment, + TCapture extends Capture = Capture, + TRefund extends Refund = Refund, + TPaymentSession extends PaymentSession = PaymentSession > extends ModulesSdkUtils.abstractModuleServiceFactory< InjectedDependencies, @@ -52,20 +60,27 @@ export default class PaymentModuleService< { PaymentCollection: { dto: PaymentCollectionDTO } PaymentSession: { dto: PaymentSessionDTO } + Payment: { dto: PaymentDTO } + Capture: { dto: CaptureDTO } + Refund: { dto: RefundDTO } } >(PaymentCollection, generateMethodForModels, entityNameToLinkableKeysMap) implements IPaymentModuleService { protected baseRepository_: DAL.RepositoryService - protected paymentService_: PaymentService - protected paymentSessionService_: ModulesSdkTypes.InternalModuleService + protected paymentService_: ModulesSdkTypes.InternalModuleService + protected captureService_: ModulesSdkTypes.InternalModuleService + protected refundService_: ModulesSdkTypes.InternalModuleService + protected paymentSessionService_: ModulesSdkTypes.InternalModuleService protected paymentCollectionService_: ModulesSdkTypes.InternalModuleService constructor( { baseRepository, paymentService, + captureService, + refundService, paymentSessionService, paymentCollectionService, }: InjectedDependencies, @@ -76,6 +91,8 @@ export default class PaymentModuleService< this.baseRepository_ = baseRepository + this.refundService_ = refundService + this.captureService_ = captureService this.paymentService_ = paymentService this.paymentSessionService_ = paymentSessionService this.paymentCollectionService_ = paymentCollectionService @@ -231,15 +248,7 @@ export default class PaymentModuleService< ): Promise { let payments = await this.paymentService_.list( { id: data.map((d) => d.payment_id) }, - { - select: [ - "amount", - "authorized_amount", - "captured_amount", - "captured_at", - ], - relations: ["captures"], - }, + {}, sharedContext ) const inputMap = new Map(data.map((d) => [d.payment_id, d])) @@ -254,29 +263,32 @@ export default class PaymentModuleService< ) } - if ( - Number(payment.captured_amount) + input.amount > - Number(payment.authorized_amount) - ) { - throw new MedusaError( - MedusaError.Types.INVALID_DATA, - `Total captured amount for payment: ${payment.id} exceeds authorized amount.` - ) - } + // TODO: revisit when https://github.com/medusajs/medusa/pull/6253 is merged + // if (payment.captured_amount + input.amount > payment.authorized_amount) { + // throw new MedusaError( + // MedusaError.Types.INVALID_DATA, + // `Total captured amount for payment: ${payment.id} exceeds authorized amount.` + // ) + // } } - await this.paymentService_.capture(data, sharedContext) + await this.captureService_.create( + data.map((d) => ({ + payment: d.payment_id, + amount: d.amount, + captured_by: d.captured_by, + })), + sharedContext + ) let fullyCapturedPaymentsId: string[] = [] for (const payment of payments) { const input = inputMap.get(payment.id)! - if ( - Number(payment.captured_amount) + input.amount === - Number(payment.amount) - ) { - fullyCapturedPaymentsId.push(payment.id) - } + // TODO: revisit when https://github.com/medusajs/medusa/pull/6253 is merged + // if (payment.captured_amount + input.amount === payment.amount) { + // fullyCapturedPaymentsId.push(payment.id) + // } } if (fullyCapturedPaymentsId.length) { @@ -290,14 +302,7 @@ export default class PaymentModuleService< return await this.paymentService_.list( { id: data.map((d) => d.payment_id) }, - // TODO: temp - will be removed { - select: [ - "amount", - "authorized_amount", - "captured_amount", - "captured_at", - ], relations: ["captures"], }, sharedContext @@ -335,10 +340,7 @@ export default class PaymentModuleService< ): Promise { const payments = await this.paymentService_.list( { id: data.map(({ payment_id }) => payment_id) }, - // TODO: temp - will be removed - { - select: ["captured_amount"], - }, + {}, sharedContext ) @@ -354,12 +356,18 @@ export default class PaymentModuleService< } } - await this.paymentService_.refund(data, sharedContext) + await this.refundService_.create( + data.map((d) => ({ + payment: d.payment_id, + amount: d.amount, + captured_by: d.captured_by, + })), + sharedContext + ) return await this.paymentService_.list( { id: data.map(({ payment_id }) => payment_id) }, { - select: ["amount", "refunded_amount"], relations: ["refunds"], }, sharedContext diff --git a/packages/payment/src/services/payment.ts b/packages/payment/src/services/payment.ts deleted file mode 100644 index 07ea2b18f0f4f..0000000000000 --- a/packages/payment/src/services/payment.ts +++ /dev/null @@ -1,168 +0,0 @@ -import { Payment } from "@models" -import { - CaptureDTO, - Context, - CreateCaptureDTO, - CreateRefundDTO, - DAL, - FindConfig, - RefundDTO, -} from "@medusajs/types" -import { - InjectManager, - InjectTransactionManager, - isObject, - MedusaContext, - ModulesSdkUtils, -} from "@medusajs/utils" - -type InjectedDependencies = { - paymentRepository: DAL.RepositoryService - captureRepository: DAL.RepositoryService - refundRepository: DAL.RepositoryService -} - -export default class PaymentService< - TEntity extends Payment = Payment -> extends ModulesSdkUtils.internalModuleServiceFactory( - Payment -) { - protected captureRepository_: DAL.RepositoryService - protected refundRepository_: DAL.RepositoryService - protected paymentRepository_: DAL.RepositoryService - - constructor(container: InjectedDependencies) { - // @ts-ignore - super(...arguments) - - this.paymentRepository_ = container.paymentRepository - this.captureRepository_ = container.captureRepository - this.refundRepository_ = container.refundRepository - } - - /** - * NOTE TODO - TEMP IMPL. THAT WILL CHANGE - */ - protected transformQueryForAmounts(config: FindConfig) { - const select = config?.select - let relations = config?.relations - const amountsSelect: string[] = [] - - if (select?.includes("captured_amount")) { - amountsSelect.push("captured_amount") - if (!relations?.includes("captures")) { - if (!relations) { - relations = ["captures"] - } else { - relations?.push("captures") - } - } - } - - if (select?.includes("refunded_amount")) { - amountsSelect.push("refunded_amount") - if (!relations?.includes("refunds")) { - if (!relations) { - relations = ["refunds"] - } else { - relations?.push("refunds") - } - } - } - - return { select, relations, amountsSelect } - } - - /** - * NOTE TODO - TEMP IMPL. THAT WILL CHANGE - */ - protected decorateAmounts(payment: TEntity, amountsSelected: string[]) { - const amounts = {} - - for (const amountName of amountsSelected) { - if (amountName === "captured_amount") { - amounts["captured_amount"] = payment.captures.reduce( - (sum, capture) => sum + Number(capture.amount), - 0 - ) - } - if (amountName === "refunded_amount") { - amounts["refunded_amount"] = payment.refunds.reduce( - (sum, refund) => sum + Number(refund.amount), - 0 - ) - } - } - - return Object.assign(payment, amounts) as TEntity - } - - /** - * NOTE TODO - TEMP IMPL. THAT WILL CHANGE - */ - async retrieve( - id: string | object, - config: FindConfig = {}, // TODO: fix type when overriding - sharedContext: Context = {} - ): Promise { - const { select, relations, amountsSelect } = - this.transformQueryForAmounts(config) - - const result = await super.retrieve( - isObject(id) ? id : { id }, - { ...config, select, relations }, - sharedContext - ) - - return this.decorateAmounts(result, amountsSelect) - } - - /** - * NOTE TODO - TEMP IMPL. THAT WILL CHANGE - */ - @InjectManager("paymentRepository_") - async list( - filters: {} = {}, - config: FindConfig = {}, - @MedusaContext() sharedContext?: Context - ): Promise { - const { select, relations, amountsSelect } = - this.transformQueryForAmounts(config) - - const result = await super.list( - filters, - { ...config, select, relations }, - sharedContext - ) - - return result.map((r) => this.decorateAmounts(r, amountsSelect)) - } - - @InjectTransactionManager("captureRepository_") - async capture( - data: CreateCaptureDTO[], - @MedusaContext() sharedContext?: Context - ): Promise { - return await this.captureRepository_.create( - data.map((input) => ({ - payment: input.payment_id, - ...input, - })), - sharedContext - ) - } - - @InjectTransactionManager("refundRepository_") - async refund( - data: CreateRefundDTO[], - @MedusaContext() sharedContext?: Context - ): Promise { - return await this.refundRepository_.create( - data.map((input) => ({ - payment: input.payment_id, - ...input, - })), - sharedContext - ) - } -} From c1dea05348f1b0971aa81435aa1401b4e4038d18 Mon Sep 17 00:00:00 2001 From: fPolic Date: Mon, 5 Feb 2024 19:01:24 +0100 Subject: [PATCH 35/36] fix: revisit DTOs, add todos --- .../payment/src/services/payment-module.ts | 19 ++++++++++--------- packages/types/src/payment/mutations.ts | 3 --- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/packages/payment/src/services/payment-module.ts b/packages/payment/src/services/payment-module.ts index 49ce4acc77cf9..3ae9904571f10 100644 --- a/packages/payment/src/services/payment-module.ts +++ b/packages/payment/src/services/payment-module.ts @@ -346,15 +346,16 @@ export default class PaymentModuleService< const inputMap = new Map(data.map((d) => [d.payment_id, d])) - for (const payment of payments) { - const input = inputMap.get(payment.id)! - if (payment.captured_amount < input.amount) { - throw new MedusaError( - MedusaError.Types.INVALID_DATA, - `Refund amount for payment: ${payment.id} cannot be greater than the amount captured on the payment.` - ) - } - } + // TODO: revisit when https://github.com/medusajs/medusa/pull/6253 is merged + // for (const payment of payments) { + // const input = inputMap.get(payment.id)! + // if (payment.captured_amount < input.amount) { + // throw new MedusaError( + // MedusaError.Types.INVALID_DATA, + // `Refund amount for payment: ${payment.id} cannot be greater than the amount captured on the payment.` + // ) + // } + // } await this.refundService_.create( data.map((d) => ({ diff --git a/packages/types/src/payment/mutations.ts b/packages/types/src/payment/mutations.ts index 60bd52a5da373..92d6ed6487ef3 100644 --- a/packages/types/src/payment/mutations.ts +++ b/packages/types/src/payment/mutations.ts @@ -17,7 +17,6 @@ export interface UpdatePaymentCollectionDTO authorized_amount?: number refunded_amount?: number - completed_at?: number status?: PaymentCollectionStatus } @@ -48,8 +47,6 @@ export interface UpdatePaymentDTO { order_edit_id?: string customer_id?: string - captured_at?: Date - data?: Record } From 11e60bfc7b4556d31e04cd6e0d92c77090a19351 Mon Sep 17 00:00:00 2001 From: fPolic Date: Mon, 5 Feb 2024 19:50:47 +0100 Subject: [PATCH 36/36] fix: build --- packages/payment/src/services/payment-module.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/payment/src/services/payment-module.ts b/packages/payment/src/services/payment-module.ts index 3ae9904571f10..6f5b2b075af89 100644 --- a/packages/payment/src/services/payment-module.ts +++ b/packages/payment/src/services/payment-module.ts @@ -361,7 +361,7 @@ export default class PaymentModuleService< data.map((d) => ({ payment: d.payment_id, amount: d.amount, - captured_by: d.captured_by, + captured_by: d.created_by, })), sharedContext )