From 2c0ab601f3bedbfde0893d8598a75047afbd5d0b Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Fri, 21 Nov 2025 18:37:09 +0330 Subject: [PATCH] payment --- package-lock.json | 151 ++++++++++++++++++ package.json | 3 +- src/app.module.ts | 2 + src/core/exceptions/http.exceptions.ts | 1 - .../dto/create-payment-method.input.ts | 19 +++ .../payments/dto/create-payment.input.ts | 7 + .../create-restaurant-payment-method.input.ts | 16 ++ .../dto/update-payment-method.input.ts | 8 + .../payments/dto/update-payment.input.ts | 8 + .../update-restaurant-payment-method.input.ts | 8 + .../entities/payment-method.entity.ts | 31 ++++ .../payments/entities/payment.entity.ts | 7 + .../restaurant-payment-method.entity.ts | 26 +++ src/modules/payments/payments.module.ts | 34 ++++ .../repositories/payment-method.repository.ts | 10 ++ .../restaurant-payment-method.repository.ts | 10 ++ .../resolvers/payment-method.resolver.ts | 35 ++++ .../payments/resolvers/payments.resolver.ts | 35 ++++ .../restaurant-payment-method.resolver.ts | 52 ++++++ .../services/payment-method.service.ts | 48 ++++++ .../payments/services/payments.service.ts | 26 +++ .../restaurant-payment-method.service.ts | 134 ++++++++++++++++ 22 files changed, 669 insertions(+), 2 deletions(-) create mode 100644 src/modules/payments/dto/create-payment-method.input.ts create mode 100644 src/modules/payments/dto/create-payment.input.ts create mode 100644 src/modules/payments/dto/create-restaurant-payment-method.input.ts create mode 100644 src/modules/payments/dto/update-payment-method.input.ts create mode 100644 src/modules/payments/dto/update-payment.input.ts create mode 100644 src/modules/payments/dto/update-restaurant-payment-method.input.ts create mode 100644 src/modules/payments/entities/payment-method.entity.ts create mode 100644 src/modules/payments/entities/payment.entity.ts create mode 100644 src/modules/payments/entities/restaurant-payment-method.entity.ts create mode 100644 src/modules/payments/payments.module.ts create mode 100644 src/modules/payments/repositories/payment-method.repository.ts create mode 100644 src/modules/payments/repositories/restaurant-payment-method.repository.ts create mode 100644 src/modules/payments/resolvers/payment-method.resolver.ts create mode 100644 src/modules/payments/resolvers/payments.resolver.ts create mode 100644 src/modules/payments/resolvers/restaurant-payment-method.resolver.ts create mode 100644 src/modules/payments/services/payment-method.service.ts create mode 100644 src/modules/payments/services/payments.service.ts create mode 100644 src/modules/payments/services/restaurant-payment-method.service.ts diff --git a/package-lock.json b/package-lock.json index 1dac946..b7c7814 100644 --- a/package-lock.json +++ b/package-lock.json @@ -67,6 +67,7 @@ "supertest": "^7.0.0", "ts-jest": "^29.2.5", "ts-loader": "^9.5.2", + "ts-node": "^10.9.2", "typescript": "^5.7.3", "typescript-eslint": "^8.20.0" } @@ -1957,6 +1958,30 @@ "node": ">=0.1.90" } }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@emnapi/core": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.6.0.tgz", @@ -5717,6 +5742,34 @@ "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", "license": "MIT" }, + "node_modules/@tsconfig/node10": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", + "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, "node_modules/@tybys/wasm-util": { "version": "0.10.1", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", @@ -6829,6 +6882,19 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -6991,6 +7057,13 @@ "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", "license": "MIT" }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -7970,6 +8043,13 @@ } } }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, "node_modules/cron-parser": { "version": "4.9.0", "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-4.9.0.tgz", @@ -8175,6 +8255,16 @@ "wrappy": "1" } }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -14281,6 +14371,50 @@ "webpack": "^5.0.0" } }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, "node_modules/tsconfig-paths": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", @@ -14602,6 +14736,13 @@ "uuid": "dist/esm/bin/uuid" } }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, "node_modules/v8-to-istanbul": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", @@ -15127,6 +15268,16 @@ "node": ">=12" } }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 0bb3009..6297f3f 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "license": "UNLICENSED", "scripts": { "db:create": "npx mikro-orm schema:create --run --config ./src/config/mikro-orm.config.dev.ts", - "db:update": "npx mikro-orm schema:update --run --config ./src/config/mikro-orm.config.dev.ts", + "db:update": "npx mikro-orm schema:update --run --config ./src/config/mikro-orm.config.dev.ts ", "db:drop": "npx mikro-orm schema:drop --run --config ./src/config/mikro-orm.config.dev.ts", "db:seed": "npx mikro-orm seeder:run --config ./src/config/mikro-orm.config.dev.ts", "build": "nest build", @@ -82,6 +82,7 @@ "supertest": "^7.0.0", "ts-jest": "^29.2.5", "ts-loader": "^9.5.2", + "ts-node": "^10.9.2", "typescript": "^5.7.3", "typescript-eslint": "^8.20.0" }, diff --git a/src/app.module.ts b/src/app.module.ts index e044f07..8a46d3a 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -20,6 +20,7 @@ import { CartModule } from './modules/cart/cart.module'; import { RolesModule } from './modules/roles/roles.module'; import { GraphQLModule } from '@nestjs/graphql'; import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo'; +import { PaymentsModule } from './modules/payments/payments.module'; @Module({ imports: [ @@ -58,6 +59,7 @@ import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo'; FoodModule, CartModule, RolesModule, + PaymentsModule, ], controllers: [], providers: [], diff --git a/src/core/exceptions/http.exceptions.ts b/src/core/exceptions/http.exceptions.ts index 8cdfad8..5fbff20 100755 --- a/src/core/exceptions/http.exceptions.ts +++ b/src/core/exceptions/http.exceptions.ts @@ -11,7 +11,6 @@ export class HttpExceptionFilter implements ExceptionFilter { // In GraphQL context, host is actually an ExecutionContext const gqlContext = GqlExecutionContext.create(host as ExecutionContext); if (gqlContext && gqlContext.getInfo()) { - console.log('GraphQL context**'); // For GraphQL, let the exception propagate - GraphQL will handle it automatically // We can't use HTTP response methods in GraphQL context return; diff --git a/src/modules/payments/dto/create-payment-method.input.ts b/src/modules/payments/dto/create-payment-method.input.ts new file mode 100644 index 0000000..46418f5 --- /dev/null +++ b/src/modules/payments/dto/create-payment-method.input.ts @@ -0,0 +1,19 @@ +import { InputType, Field, Int } from '@nestjs/graphql'; + +@InputType() +export class CreatePaymentMethodInput { + @Field() + name!: string; + + @Field({ nullable: true }) + description?: string; + + @Field({ nullable: true }) + icon?: string; + + @Field(() => Boolean, { nullable: true, defaultValue: true }) + isActive?: boolean; + + @Field(() => Int, { nullable: true }) + order?: number; +} diff --git a/src/modules/payments/dto/create-payment.input.ts b/src/modules/payments/dto/create-payment.input.ts new file mode 100644 index 0000000..9253191 --- /dev/null +++ b/src/modules/payments/dto/create-payment.input.ts @@ -0,0 +1,7 @@ +import { InputType, Int, Field } from '@nestjs/graphql'; + +@InputType() +export class CreatePaymentInput { + @Field(() => Int, { description: 'Example field (placeholder)' }) + exampleField: number; +} diff --git a/src/modules/payments/dto/create-restaurant-payment-method.input.ts b/src/modules/payments/dto/create-restaurant-payment-method.input.ts new file mode 100644 index 0000000..9d57633 --- /dev/null +++ b/src/modules/payments/dto/create-restaurant-payment-method.input.ts @@ -0,0 +1,16 @@ +import { InputType, Field } from '@nestjs/graphql'; + +@InputType() +export class CreateRestaurantPaymentMethodInput { + @Field() + restaurantId!: string; + + @Field() + paymentMethodId!: string; + + @Field({ nullable: true }) + merchantId?: string; + + @Field({ nullable: true }) + isActive?: boolean; +} diff --git a/src/modules/payments/dto/update-payment-method.input.ts b/src/modules/payments/dto/update-payment-method.input.ts new file mode 100644 index 0000000..efe8b48 --- /dev/null +++ b/src/modules/payments/dto/update-payment-method.input.ts @@ -0,0 +1,8 @@ +import { CreatePaymentMethodInput } from './create-payment-method.input'; +import { InputType, Field, PartialType } from '@nestjs/graphql'; + +@InputType() +export class UpdatePaymentMethodInput extends PartialType(CreatePaymentMethodInput) { + @Field() + id!: string; +} diff --git a/src/modules/payments/dto/update-payment.input.ts b/src/modules/payments/dto/update-payment.input.ts new file mode 100644 index 0000000..39b61b3 --- /dev/null +++ b/src/modules/payments/dto/update-payment.input.ts @@ -0,0 +1,8 @@ +import { CreatePaymentInput } from './create-payment.input'; +import { InputType, Field, Int, PartialType } from '@nestjs/graphql'; + +@InputType() +export class UpdatePaymentInput extends PartialType(CreatePaymentInput) { + @Field(() => Int) + id: number; +} diff --git a/src/modules/payments/dto/update-restaurant-payment-method.input.ts b/src/modules/payments/dto/update-restaurant-payment-method.input.ts new file mode 100644 index 0000000..791d001 --- /dev/null +++ b/src/modules/payments/dto/update-restaurant-payment-method.input.ts @@ -0,0 +1,8 @@ +import { CreateRestaurantPaymentMethodInput } from './create-restaurant-payment-method.input'; +import { InputType, Field, PartialType } from '@nestjs/graphql'; + +@InputType() +export class UpdateRestaurantPaymentMethodInput extends PartialType(CreateRestaurantPaymentMethodInput) { + @Field() + id!: string; +} diff --git a/src/modules/payments/entities/payment-method.entity.ts b/src/modules/payments/entities/payment-method.entity.ts new file mode 100644 index 0000000..5555d1a --- /dev/null +++ b/src/modules/payments/entities/payment-method.entity.ts @@ -0,0 +1,31 @@ +import { Entity, Property } from '@mikro-orm/core'; +import { BaseEntity } from '../../../common/entities/base.entity'; +import { ObjectType, Field, Int } from '@nestjs/graphql'; + +@ObjectType() +@Entity({ tableName: 'payment_methods' }) +export class PaymentMethod extends BaseEntity { + @Field() + @Property() + name!: string; + + @Field() + @Property() + keyName!: string; + + @Field({ nullable: true }) + @Property({ nullable: true }) + description?: string; + + @Field({ nullable: true }) + @Property({ nullable: true }) + icon?: string; + + @Field(() => Boolean) + @Property({ default: true }) + isActive: boolean = true; + + @Field(() => Int, { nullable: true }) + @Property({ type: 'int', nullable: true }) + order?: number; +} diff --git a/src/modules/payments/entities/payment.entity.ts b/src/modules/payments/entities/payment.entity.ts new file mode 100644 index 0000000..67d28ea --- /dev/null +++ b/src/modules/payments/entities/payment.entity.ts @@ -0,0 +1,7 @@ +import { ObjectType, Field, Int } from '@nestjs/graphql'; + +@ObjectType() +export class Payment { + @Field(() => Int, { description: 'Example field (placeholder)' }) + exampleField: number; +} diff --git a/src/modules/payments/entities/restaurant-payment-method.entity.ts b/src/modules/payments/entities/restaurant-payment-method.entity.ts new file mode 100644 index 0000000..188e3da --- /dev/null +++ b/src/modules/payments/entities/restaurant-payment-method.entity.ts @@ -0,0 +1,26 @@ +import { Entity, ManyToOne, Unique, Property } from '@mikro-orm/core'; +import { BaseEntity } from '../../../common/entities/base.entity'; +import { Restaurant } from '../../restaurants/entities/restaurant.entity'; +import { PaymentMethod } from './payment-method.entity'; +import { ObjectType, Field } from '@nestjs/graphql'; + +@ObjectType() +@Entity({ tableName: 'restaurant_payment_methods' }) +@Unique({ properties: ['restaurant', 'paymentMethod'] }) +export class RestaurantPaymentMethod extends BaseEntity { + @ManyToOne(() => Restaurant) + restaurant!: Restaurant; + + @Field(() => PaymentMethod, { nullable: true }) + @ManyToOne(() => PaymentMethod) + paymentMethod!: PaymentMethod; + + // Add merchant ID for payment gateway providers (e.g., ZarinPal) + @Field({ nullable: true }) + @Property({ nullable: true }) + merchantId?: string; + + @Field({ nullable: true }) + @Property({ type: 'boolean', default: true }) + isActive: boolean = true; +} diff --git a/src/modules/payments/payments.module.ts b/src/modules/payments/payments.module.ts new file mode 100644 index 0000000..2331ae8 --- /dev/null +++ b/src/modules/payments/payments.module.ts @@ -0,0 +1,34 @@ +import { Module } from '@nestjs/common'; +import { PaymentsService } from './services/payments.service'; +import { PaymentsResolver } from './resolvers/payments.resolver'; +import { MikroOrmModule } from '@mikro-orm/nestjs'; +import { PaymentMethod } from './entities/payment-method.entity'; +import { PaymentMethodRepository } from './repositories/payment-method.repository'; +import { PaymentMethodService } from './services/payment-method.service'; +import { PaymentMethodResolver } from './resolvers/payment-method.resolver'; +import { RestaurantPaymentMethod } from './entities/restaurant-payment-method.entity'; +import { RestaurantPaymentMethodRepository } from './repositories/restaurant-payment-method.repository'; +import { RestaurantPaymentMethodService } from './services/restaurant-payment-method.service'; +import { RestaurantPaymentMethodResolver } from './resolvers/restaurant-payment-method.resolver'; +import { Restaurant } from '../restaurants/entities/restaurant.entity'; + +@Module({ + imports: [MikroOrmModule.forFeature([PaymentMethod, RestaurantPaymentMethod, Restaurant])], + providers: [ + PaymentsResolver, + PaymentsService, + PaymentMethodResolver, + PaymentMethodService, + PaymentMethodRepository, + RestaurantPaymentMethodResolver, + RestaurantPaymentMethodService, + RestaurantPaymentMethodRepository, + ], + exports: [ + PaymentMethodRepository, + PaymentMethodService, + RestaurantPaymentMethodRepository, + RestaurantPaymentMethodService, + ], +}) +export class PaymentsModule {} diff --git a/src/modules/payments/repositories/payment-method.repository.ts b/src/modules/payments/repositories/payment-method.repository.ts new file mode 100644 index 0000000..22bf7fa --- /dev/null +++ b/src/modules/payments/repositories/payment-method.repository.ts @@ -0,0 +1,10 @@ +import { Injectable } from '@nestjs/common'; +import { EntityManager, EntityRepository } from '@mikro-orm/postgresql'; +import { PaymentMethod } from '../entities/payment-method.entity'; + +@Injectable() +export class PaymentMethodRepository extends EntityRepository { + constructor(readonly em: EntityManager) { + super(em, PaymentMethod); + } +} diff --git a/src/modules/payments/repositories/restaurant-payment-method.repository.ts b/src/modules/payments/repositories/restaurant-payment-method.repository.ts new file mode 100644 index 0000000..15971b5 --- /dev/null +++ b/src/modules/payments/repositories/restaurant-payment-method.repository.ts @@ -0,0 +1,10 @@ +import { Injectable } from '@nestjs/common'; +import { EntityManager, EntityRepository } from '@mikro-orm/postgresql'; +import { RestaurantPaymentMethod } from '../entities/restaurant-payment-method.entity'; + +@Injectable() +export class RestaurantPaymentMethodRepository extends EntityRepository { + constructor(readonly em: EntityManager) { + super(em, RestaurantPaymentMethod); + } +} diff --git a/src/modules/payments/resolvers/payment-method.resolver.ts b/src/modules/payments/resolvers/payment-method.resolver.ts new file mode 100644 index 0000000..e96ef2e --- /dev/null +++ b/src/modules/payments/resolvers/payment-method.resolver.ts @@ -0,0 +1,35 @@ +import { Resolver, Query, Mutation, Args, ID } from '@nestjs/graphql'; +import { PaymentMethodService } from '../services/payment-method.service'; +import { PaymentMethod } from '../entities/payment-method.entity'; +import { CreatePaymentMethodInput } from '../dto/create-payment-method.input'; +import { UpdatePaymentMethodInput } from '../dto/update-payment-method.input'; + +@Resolver(() => PaymentMethod) +export class PaymentMethodResolver { + constructor(private readonly paymentMethodService: PaymentMethodService) {} + + @Mutation(() => PaymentMethod) + createPaymentMethod(@Args('createPaymentMethodInput') createPaymentMethodInput: CreatePaymentMethodInput) { + return this.paymentMethodService.create(createPaymentMethodInput); + } + + @Query(() => [PaymentMethod], { name: 'paymentMethods' }) + findAll() { + return this.paymentMethodService.findAll(); + } + + @Query(() => PaymentMethod, { name: 'paymentMethod' }) + findOne(@Args('id', { type: () => ID }) id: string) { + return this.paymentMethodService.findOne(id); + } + + @Mutation(() => PaymentMethod) + updatePaymentMethod(@Args('updatePaymentMethodInput') updatePaymentMethodInput: UpdatePaymentMethodInput) { + return this.paymentMethodService.update(updatePaymentMethodInput.id, updatePaymentMethodInput); + } + + @Mutation(() => PaymentMethod) + removePaymentMethod(@Args('id', { type: () => ID }) id: string) { + return this.paymentMethodService.remove(id); + } +} diff --git a/src/modules/payments/resolvers/payments.resolver.ts b/src/modules/payments/resolvers/payments.resolver.ts new file mode 100644 index 0000000..69a70c1 --- /dev/null +++ b/src/modules/payments/resolvers/payments.resolver.ts @@ -0,0 +1,35 @@ +import { Resolver, Query, Mutation, Args, Int } from '@nestjs/graphql'; +import { PaymentsService } from '../services/payments.service'; +import { Payment } from '../entities/payment.entity'; +import { CreatePaymentInput } from '../dto/create-payment.input'; +import { UpdatePaymentInput } from '../dto/update-payment.input'; + +@Resolver(() => Payment) +export class PaymentsResolver { + constructor(private readonly paymentsService: PaymentsService) {} + + @Mutation(() => Payment) + createPayment(@Args('createPaymentInput') createPaymentInput: CreatePaymentInput) { + return this.paymentsService.create(createPaymentInput); + } + + @Query(() => [Payment], { name: 'payments' }) + findAll() { + return this.paymentsService.findAll(); + } + + @Query(() => Payment, { name: 'payment' }) + findOne(@Args('id', { type: () => Int }) id: number) { + return this.paymentsService.findOne(id); + } + + @Mutation(() => Payment) + updatePayment(@Args('updatePaymentInput') updatePaymentInput: UpdatePaymentInput) { + return this.paymentsService.update(updatePaymentInput.id, updatePaymentInput); + } + + @Mutation(() => Payment) + removePayment(@Args('id', { type: () => Int }) id: number) { + return this.paymentsService.remove(id); + } +} diff --git a/src/modules/payments/resolvers/restaurant-payment-method.resolver.ts b/src/modules/payments/resolvers/restaurant-payment-method.resolver.ts new file mode 100644 index 0000000..69fa1dd --- /dev/null +++ b/src/modules/payments/resolvers/restaurant-payment-method.resolver.ts @@ -0,0 +1,52 @@ +import { Resolver, Query, Mutation, Args, ID } from '@nestjs/graphql'; +import { RestaurantPaymentMethodService } from '../services/restaurant-payment-method.service'; +import { RestaurantPaymentMethod } from '../entities/restaurant-payment-method.entity'; +import { CreateRestaurantPaymentMethodInput } from '../dto/create-restaurant-payment-method.input'; +import { UpdateRestaurantPaymentMethodInput } from '../dto/update-restaurant-payment-method.input'; + +@Resolver(() => RestaurantPaymentMethod) +export class RestaurantPaymentMethodResolver { + constructor(private readonly restaurantPaymentMethodService: RestaurantPaymentMethodService) {} + + @Mutation(() => RestaurantPaymentMethod) + createRestaurantPaymentMethod( + @Args('createRestaurantPaymentMethodInput') createRestaurantPaymentMethodInput: CreateRestaurantPaymentMethodInput, + ) { + return this.restaurantPaymentMethodService.create(createRestaurantPaymentMethodInput); + } + + @Query(() => [RestaurantPaymentMethod], { name: 'restaurantPaymentMethods' }) + findAll() { + return this.restaurantPaymentMethodService.findAll(); + } + + @Query(() => [RestaurantPaymentMethod], { name: 'restaurantPaymentMethodsByRestaurant' }) + findByRestaurant(@Args('restaurantId', { type: () => ID }) restaurantId: string) { + return this.restaurantPaymentMethodService.findByRestaurant(restaurantId); + } + + @Query(() => [RestaurantPaymentMethod], { name: 'restaurantPaymentMethodsByPaymentMethod' }) + findByPaymentMethod(@Args('paymentMethodId', { type: () => ID }) paymentMethodId: string) { + return this.restaurantPaymentMethodService.findByPaymentMethod(paymentMethodId); + } + + @Query(() => RestaurantPaymentMethod, { name: 'restaurantPaymentMethod' }) + findOne(@Args('id', { type: () => ID }) id: string) { + return this.restaurantPaymentMethodService.findOne(id); + } + + @Mutation(() => RestaurantPaymentMethod) + updateRestaurantPaymentMethod( + @Args('updateRestaurantPaymentMethodInput') updateRestaurantPaymentMethodInput: UpdateRestaurantPaymentMethodInput, + ) { + return this.restaurantPaymentMethodService.update( + updateRestaurantPaymentMethodInput.id, + updateRestaurantPaymentMethodInput, + ); + } + + @Mutation(() => RestaurantPaymentMethod) + removeRestaurantPaymentMethod(@Args('id', { type: () => ID }) id: string) { + return this.restaurantPaymentMethodService.remove(id); + } +} diff --git a/src/modules/payments/services/payment-method.service.ts b/src/modules/payments/services/payment-method.service.ts new file mode 100644 index 0000000..ecc4b53 --- /dev/null +++ b/src/modules/payments/services/payment-method.service.ts @@ -0,0 +1,48 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { EntityManager } from '@mikro-orm/postgresql'; +import { PaymentMethod } from '../entities/payment-method.entity'; +import { PaymentMethodRepository } from '../repositories/payment-method.repository'; +import { CreatePaymentMethodInput } from '../dto/create-payment-method.input'; +import { UpdatePaymentMethodInput } from '../dto/update-payment-method.input'; +import { RequiredEntityData } from '@mikro-orm/core'; + +@Injectable() +export class PaymentMethodService { + constructor( + private readonly paymentMethodRepository: PaymentMethodRepository, + private readonly em: EntityManager, + ) {} + + async create(createPaymentMethodInput: CreatePaymentMethodInput): Promise { + const paymentMethod = this.paymentMethodRepository.create( + createPaymentMethodInput as unknown as RequiredEntityData, + ); + await this.em.persistAndFlush(paymentMethod); + return paymentMethod; + } + + async findAll(): Promise { + return this.paymentMethodRepository.findAll(); + } + + async findOne(id: string): Promise { + const paymentMethod = await this.paymentMethodRepository.findOne({ id }); + if (!paymentMethod) { + throw new NotFoundException(`PaymentMethod with ID ${id} not found`); + } + return paymentMethod; + } + + async update(id: string, updatePaymentMethodInput: UpdatePaymentMethodInput): Promise { + const paymentMethod = await this.findOne(id); + this.em.assign(paymentMethod, updatePaymentMethodInput); + await this.em.flush(); + return paymentMethod; + } + + async remove(id: string): Promise { + const paymentMethod = await this.findOne(id); + await this.em.removeAndFlush(paymentMethod); + return paymentMethod; + } +} diff --git a/src/modules/payments/services/payments.service.ts b/src/modules/payments/services/payments.service.ts new file mode 100644 index 0000000..8dcf3a9 --- /dev/null +++ b/src/modules/payments/services/payments.service.ts @@ -0,0 +1,26 @@ +import { Injectable } from '@nestjs/common'; +import { CreatePaymentInput } from '../dto/create-payment.input'; +import { UpdatePaymentInput } from '../dto/update-payment.input'; + +@Injectable() +export class PaymentsService { + create(createPaymentInput: CreatePaymentInput) { + return 'This action adds a new payment'; + } + + findAll() { + return `This action returns all payments`; + } + + findOne(id: number) { + return `This action returns a #${id} payment`; + } + + update(id: number, updatePaymentInput: UpdatePaymentInput) { + return `This action updates a #${id} payment`; + } + + remove(id: number) { + return `This action removes a #${id} payment`; + } +} diff --git a/src/modules/payments/services/restaurant-payment-method.service.ts b/src/modules/payments/services/restaurant-payment-method.service.ts new file mode 100644 index 0000000..873d274 --- /dev/null +++ b/src/modules/payments/services/restaurant-payment-method.service.ts @@ -0,0 +1,134 @@ +import { Injectable, NotFoundException, ConflictException } from '@nestjs/common'; +import { EntityManager } from '@mikro-orm/postgresql'; +import { RestaurantPaymentMethod } from '../entities/restaurant-payment-method.entity'; +import { RestaurantPaymentMethodRepository } from '../repositories/restaurant-payment-method.repository'; +import { Restaurant } from '../../restaurants/entities/restaurant.entity'; +import { PaymentMethod } from '../entities/payment-method.entity'; +import { CreateRestaurantPaymentMethodInput } from '../dto/create-restaurant-payment-method.input'; +import { UpdateRestaurantPaymentMethodInput } from '../dto/update-restaurant-payment-method.input'; +import { RequiredEntityData } from '@mikro-orm/core'; + +@Injectable() +export class RestaurantPaymentMethodService { + constructor( + private readonly restaurantPaymentMethodRepository: RestaurantPaymentMethodRepository, + private readonly em: EntityManager, + ) {} + + async create( + createRestaurantPaymentMethodInput: CreateRestaurantPaymentMethodInput, + ): Promise { + // Check if restaurant exists + const restaurant = await this.em.findOne(Restaurant, { id: createRestaurantPaymentMethodInput.restaurantId }); + if (!restaurant) { + throw new NotFoundException(`Restaurant with ID ${createRestaurantPaymentMethodInput.restaurantId} not found`); + } + + // Check if payment method exists + const paymentMethod = await this.em.findOne(PaymentMethod, { id: createRestaurantPaymentMethodInput.paymentMethodId }); + if (!paymentMethod) { + throw new NotFoundException(`PaymentMethod with ID ${createRestaurantPaymentMethodInput.paymentMethodId} not found`); + } + + // Check if the relationship already exists + const existing = await this.restaurantPaymentMethodRepository.findOne({ + restaurant: { id: createRestaurantPaymentMethodInput.restaurantId }, + paymentMethod: { id: createRestaurantPaymentMethodInput.paymentMethodId }, + }); + + if (existing) { + throw new ConflictException('This payment method is already assigned to this restaurant'); + } + + const restaurantPaymentMethod = this.restaurantPaymentMethodRepository.create({ + restaurant, + paymentMethod, + merchantId: createRestaurantPaymentMethodInput.merchantId, + isActive: createRestaurantPaymentMethodInput.isActive ?? true, + } as unknown as RequiredEntityData); + + await this.em.persistAndFlush(restaurantPaymentMethod); + return restaurantPaymentMethod; + } + + async findAll(): Promise { + return this.restaurantPaymentMethodRepository.findAll({ populate: ['restaurant', 'paymentMethod'] }); + } + + async findByRestaurant(restaurantId: string): Promise { + return this.restaurantPaymentMethodRepository.find( + { restaurant: { id: restaurantId } }, + { populate: ['restaurant', 'paymentMethod'] }, + ); + } + + async findByPaymentMethod(paymentMethodId: string): Promise { + return this.restaurantPaymentMethodRepository.find( + { paymentMethod: { id: paymentMethodId } }, + { populate: ['restaurant', 'paymentMethod'] }, + ); + } + + async findOne(id: string): Promise { + const restaurantPaymentMethod = await this.restaurantPaymentMethodRepository.findOne( + { id }, + { populate: ['restaurant', 'paymentMethod'] }, + ); + if (!restaurantPaymentMethod) { + throw new NotFoundException(`RestaurantPaymentMethod with ID ${id} not found`); + } + return restaurantPaymentMethod; + } + + async update( + id: string, + updateRestaurantPaymentMethodInput: UpdateRestaurantPaymentMethodInput, + ): Promise { + const restaurantPaymentMethod = await this.findOne(id); + + if (updateRestaurantPaymentMethodInput.restaurantId) { + const restaurant = await this.em.findOne(Restaurant, { id: updateRestaurantPaymentMethodInput.restaurantId }); + if (!restaurant) { + throw new NotFoundException(`Restaurant with ID ${updateRestaurantPaymentMethodInput.restaurantId} not found`); + } + restaurantPaymentMethod.restaurant = restaurant; + } + + if (updateRestaurantPaymentMethodInput.paymentMethodId) { + const paymentMethod = await this.em.findOne(PaymentMethod, { id: updateRestaurantPaymentMethodInput.paymentMethodId }); + if (!paymentMethod) { + throw new NotFoundException(`PaymentMethod with ID ${updateRestaurantPaymentMethodInput.paymentMethodId} not found`); + } + + // Check if the new relationship already exists + const existing = await this.restaurantPaymentMethodRepository.findOne({ + restaurant: { id: restaurantPaymentMethod.restaurant.id }, + paymentMethod: { id: updateRestaurantPaymentMethodInput.paymentMethodId }, + }); + + if (existing && existing.id !== id) { + throw new ConflictException('This payment method is already assigned to this restaurant'); + } + + restaurantPaymentMethod.paymentMethod = paymentMethod; + } + + // Update merchantId and isActive if provided + if (updateRestaurantPaymentMethodInput.merchantId !== undefined) { + restaurantPaymentMethod.merchantId = updateRestaurantPaymentMethodInput.merchantId; + } + + if (updateRestaurantPaymentMethodInput.isActive !== undefined) { + restaurantPaymentMethod.isActive = updateRestaurantPaymentMethodInput.isActive; + } + + await this.em.flush(); + return restaurantPaymentMethod; + } + + async remove(id: string): Promise { + const restaurantPaymentMethod = await this.findOne(id); + await this.em.removeAndFlush(restaurantPaymentMethod); + return restaurantPaymentMethod; + } +}