payment
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { Entity, ManyToOne, OneToMany, Property, Collection, Cascade, Enum } from '@mikro-orm/core';
|
||||
import { BaseEntity } from '../../../common/entities/base.entity';
|
||||
import { PaymentStatus } from '../../payments/interface/payment-status';
|
||||
import { PaymentStatusEnum } from '../../payments/interface/payment';
|
||||
import { OrderStatus } from '../interface/order-status';
|
||||
import { User } from '../../users/entities/user.entity';
|
||||
import { Restaurant } from '../../restaurants/entities/restaurant.entity';
|
||||
@@ -55,6 +55,6 @@ export class Order extends BaseEntity {
|
||||
@Enum(() => OrderStatus)
|
||||
status!: OrderStatus;
|
||||
|
||||
@Enum(() => PaymentStatus)
|
||||
paymentStatus!: PaymentStatus;
|
||||
@Enum(() => PaymentStatusEnum)
|
||||
paymentStatus!: PaymentStatusEnum;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import { UserAddress } from '../users/entities/user-address.entity';
|
||||
import { RestaurantPaymentMethod } from '../payments/entities/restaurant-payment-method.entity';
|
||||
import { CartService } from '../cart/providers/cart.service';
|
||||
import { OrderStatus } from './interface/order-status';
|
||||
import { PaymentStatus } from '../payments/interface/payment-status';
|
||||
import { PaymentStatusEnum } from '../payments/interface/payment';
|
||||
import { Cart } from '../cart/interfaces/cart.interface';
|
||||
import { RestaurantPaymentMethodRepository } from '../payments/repositories/restaurant-payment-method.repository';
|
||||
// import { PaymentGatewayService } from '../payments/services/payment-gateway.service.tss';
|
||||
@@ -59,7 +59,7 @@ export class OrdersService {
|
||||
total: cart.total || 0,
|
||||
totalItems: cart.totalItems || 0,
|
||||
status: OrderStatus.Pending,
|
||||
paymentStatus: PaymentStatus.Pending,
|
||||
paymentStatus: PaymentStatusEnum.Pending,
|
||||
});
|
||||
|
||||
em.persist(order);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Controller, Get, Post, Body, Patch, Param, Delete, UseGuards } from '@nestjs/common';
|
||||
import { PaymentsService } from '../services/payments.service';
|
||||
import { CreatePaymentDto } from '../dto/create-payment.dto';
|
||||
import { UpdatePaymentDto } from '../dto/update-payment.dto';
|
||||
import {
|
||||
ApiTags,
|
||||
@@ -14,9 +13,8 @@ import {
|
||||
} from '@nestjs/swagger';
|
||||
import { AdminAuthGuard } from 'src/modules/auth/guards/adminAuth.guard';
|
||||
import { PaymentMethodService } from '../services/payment-method.service';
|
||||
import { RestaurantPaymentMethodService } from '../services/restaurant-payment-method.service';
|
||||
import { CreateRestaurantPaymentMethodDto } from '../dto/create-restaurant-payment-method.dto';
|
||||
import { UpdateRestaurantPaymentMethodDto } from '../dto/update-restaurant-payment-method.dto';
|
||||
import { CreatePaymentMethodDto } from '../dto/create-payment-method.dto';
|
||||
import { UpdatePaymentMethodDto } from '../dto/update-payment-method.dto';
|
||||
import { RestId } from 'src/common/decorators';
|
||||
import { AuthGuard } from 'src/modules/auth/guards/auth.guard';
|
||||
|
||||
@@ -26,7 +24,6 @@ export class PaymentsController {
|
||||
constructor(
|
||||
private readonly paymentsService: PaymentsService,
|
||||
private readonly paymentMethodService: PaymentMethodService,
|
||||
private readonly restaurantPaymentMethodService: RestaurantPaymentMethodService,
|
||||
) {}
|
||||
|
||||
@UseGuards(AuthGuard)
|
||||
@@ -35,19 +32,9 @@ export class PaymentsController {
|
||||
@ApiOperation({ summary: 'Get the restaurant payment methods' })
|
||||
@ApiNotFoundResponse({ description: 'Restaurant payment methods not found' })
|
||||
getTheRestaurantPaymentMethods(@RestId() restId: string) {
|
||||
return this.restaurantPaymentMethodService.findByRestaurant(restId);
|
||||
return this.paymentMethodService.findByRestaurant(restId);
|
||||
}
|
||||
|
||||
// @UseGuards(AdminAuthGuard)
|
||||
// @ApiBearerAuth()
|
||||
// @Post('admin/payments')
|
||||
// @ApiOperation({ summary: 'Create a new payment' })
|
||||
// @ApiBody({ type: CreatePaymentDto })
|
||||
// @ApiCreatedResponse({ description: 'Payment created successfully' })
|
||||
// createPayment(@Body() createPaymentDto: CreatePaymentDto) {
|
||||
// return this.paymentsService.create(createPaymentDto);
|
||||
// }
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@Get('admin/payments')
|
||||
@@ -93,78 +80,57 @@ export class PaymentsController {
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@Get('admin/payments/methods')
|
||||
@ApiOperation({ summary: 'Get all payment methods' })
|
||||
@ApiOkResponse({ description: 'List of all payment methods' })
|
||||
findAll() {
|
||||
return this.paymentMethodService.findAll();
|
||||
@ApiOperation({ summary: 'Get restaurant all payment methods' })
|
||||
@ApiOkResponse({ description: 'List of restaurant payment methods' })
|
||||
@ApiNotFoundResponse({ description: 'Restaurant payment methods not found' })
|
||||
findByRestaurant(@RestId() restId: string) {
|
||||
return this.paymentMethodService.findByRestaurant(restId);
|
||||
}
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@Get('admin/payments/methods/:id')
|
||||
@ApiOperation({ summary: 'Get a payment method by ID' })
|
||||
@ApiParam({ name: 'id', type: 'string', description: 'Payment method ID' })
|
||||
@Post('admin/payments/methods')
|
||||
@ApiOperation({ summary: 'Create a new restaurant payment method' })
|
||||
@ApiBody({ type: CreatePaymentMethodDto })
|
||||
@ApiCreatedResponse({ description: 'Restaurant payment method created successfully' })
|
||||
createRestaurantPaymentMethod(@RestId() restId: string, @Body() createPaymentMethodDto: CreatePaymentMethodDto) {
|
||||
return this.paymentMethodService.create(restId, createPaymentMethodDto);
|
||||
}
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@Get('admin/payments/methods/:paymentMethodId')
|
||||
@ApiOperation({ summary: 'Get restaurant payment method by ID' })
|
||||
@ApiParam({ name: 'paymentMethodId', type: 'string', description: 'Payment method ID' })
|
||||
@ApiOkResponse({ description: 'Payment method found' })
|
||||
@ApiNotFoundResponse({ description: 'Payment method not found' })
|
||||
findOne(@Param('id') id: string) {
|
||||
return this.paymentMethodService.findOne(id);
|
||||
}
|
||||
|
||||
/** restaurant payment methods */
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@Post('admin/payments/methods/restaurant')
|
||||
@ApiOperation({ summary: 'Create a new restaurant payment method' })
|
||||
@ApiBody({ type: CreateRestaurantPaymentMethodDto })
|
||||
@ApiCreatedResponse({ description: 'Restaurant payment method created successfully' })
|
||||
create(@RestId() restId: string, @Body() createRestaurantPaymentMethodDto: CreateRestaurantPaymentMethodDto) {
|
||||
return this.restaurantPaymentMethodService.create(restId, createRestaurantPaymentMethodDto);
|
||||
findOneRestaurantPaymentMethod(@Param('paymentMethodId') paymentMethodId: string) {
|
||||
return this.paymentMethodService.findOne(paymentMethodId);
|
||||
}
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@Get('admin/payments/methods/restaurant/:restaurantPaymentMethodId')
|
||||
@ApiOperation({ summary: 'Get restaurant payment method by payment method ID' })
|
||||
@ApiParam({ name: 'restaurantPaymentMethodId', type: 'string', description: 'restaurantPaymentMethodId method ID' })
|
||||
@ApiOkResponse({ description: 'List of restaurant payment methods for the payment method' })
|
||||
findByPaymentMethod(@Param('restaurantPaymentMethodId') restaurantPaymentMethodId: string) {
|
||||
return this.restaurantPaymentMethodService.findByPaymentMethod(restaurantPaymentMethodId);
|
||||
}
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@Get('admin/payments/methods/restaurant')
|
||||
@ApiOperation({ summary: 'Get restaurant all payment methods ' })
|
||||
@ApiOkResponse({ description: 'Restaurant payment method found' })
|
||||
@ApiNotFoundResponse({ description: 'Restaurant payment method not found' })
|
||||
findByRestaurant(@RestId() restId: string) {
|
||||
return this.restaurantPaymentMethodService.findByRestaurant(restId);
|
||||
}
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@Patch('admin/payments/methods/restaurant/:restaurantPaymentMethodId')
|
||||
@Patch('admin/payments/methods/:paymentMethodId')
|
||||
@ApiOperation({ summary: 'Update a restaurant payment method' })
|
||||
@ApiParam({ name: 'restaurantPaymentMethodId', type: 'string', description: 'Restaurant payment method ID' })
|
||||
@ApiBody({ type: UpdateRestaurantPaymentMethodDto })
|
||||
@ApiParam({ name: 'paymentMethodId', type: 'string', description: 'Payment method ID' })
|
||||
@ApiBody({ type: UpdatePaymentMethodDto })
|
||||
@ApiOkResponse({ description: 'Restaurant payment method updated successfully' })
|
||||
@ApiNotFoundResponse({ description: 'Restaurant payment method not found' })
|
||||
update(
|
||||
@Param('restaurantPaymentMethodId') restaurantPaymentMethodId: string,
|
||||
@Body() updateRestaurantPaymentMethodDto: UpdateRestaurantPaymentMethodDto,
|
||||
updateRestaurantPaymentMethod(
|
||||
@Param('paymentMethodId') paymentMethodId: string,
|
||||
@Body() updatePaymentMethodDto: UpdatePaymentMethodDto,
|
||||
) {
|
||||
return this.restaurantPaymentMethodService.update(restaurantPaymentMethodId, updateRestaurantPaymentMethodDto);
|
||||
return this.paymentMethodService.update(paymentMethodId, updatePaymentMethodDto);
|
||||
}
|
||||
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@Delete('admin/payments/methods/restaurant/:restaurantPaymentMethodId')
|
||||
@Delete('admin/payments/methods/:paymentMethodId')
|
||||
@ApiOperation({ summary: 'Delete a restaurant payment method' })
|
||||
@ApiParam({ name: 'restaurantPaymentMethodId', type: 'string', description: 'Restaurant payment method ID' })
|
||||
@ApiParam({ name: 'paymentMethodId', type: 'string', description: 'Payment method ID' })
|
||||
@ApiOkResponse({ description: 'Restaurant payment method deleted successfully' })
|
||||
@ApiNotFoundResponse({ description: 'Restaurant payment method not found' })
|
||||
remove(@Param('restaurantPaymentMethodId') restaurantPaymentMethodId: string) {
|
||||
return this.restaurantPaymentMethodService.remove(restaurantPaymentMethodId);
|
||||
removeRestaurantPaymentMethod(@Param('paymentMethodId') paymentMethodId: string) {
|
||||
return this.paymentMethodService.remove(paymentMethodId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,42 +1,38 @@
|
||||
import { IsString, IsOptional, IsBoolean, IsNumber } from 'class-validator';
|
||||
import { IsString, IsOptional, IsBoolean, IsNumber, IsEnum } from 'class-validator';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { PaymentMethodEnum, PaymentGatewayEnum } from '../interface/payment';
|
||||
|
||||
export class CreatePaymentMethodDto {
|
||||
@ApiProperty({ description: 'Payment method name' })
|
||||
@ApiProperty({ description: 'Payment method title' })
|
||||
@IsString()
|
||||
name!: string;
|
||||
title!: string;
|
||||
|
||||
@ApiProperty({ description: 'English Payment method name' })
|
||||
@IsString()
|
||||
keyName!: string;
|
||||
@ApiProperty({ description: 'Payment method', enum: PaymentMethodEnum })
|
||||
@IsEnum(PaymentMethodEnum)
|
||||
method!: PaymentMethodEnum;
|
||||
|
||||
@ApiProperty({ description: 'Payment gateway', enum: PaymentGatewayEnum, required: false })
|
||||
@IsOptional()
|
||||
@IsEnum(PaymentGatewayEnum)
|
||||
gateway?: PaymentGatewayEnum | null;
|
||||
|
||||
@ApiProperty({ description: 'Payment method description', required: false })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
description?: string;
|
||||
|
||||
@ApiProperty({ description: 'Payment method icon', required: false })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
icon?: string;
|
||||
|
||||
@ApiProperty({ description: 'Is payment method active', required: false, default: true })
|
||||
@ApiProperty({ description: 'Is payment method enabled', required: false, default: true })
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
isActive?: boolean;
|
||||
enabled?: boolean;
|
||||
|
||||
@ApiProperty({ description: 'Is payment method online', required: false, default: true })
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
isOnline?: boolean;
|
||||
|
||||
@ApiProperty({ description: 'Display order', required: false })
|
||||
@ApiProperty({ description: 'Display order', required: false, default: 0 })
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
order?: number;
|
||||
|
||||
@ApiProperty({ description: 'Callback URL for payment method', required: false })
|
||||
@ApiProperty({ description: 'Merchant ID', required: false })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
paymentUrl?: string;
|
||||
merchantId?: string;
|
||||
}
|
||||
|
||||
@@ -1,16 +1,27 @@
|
||||
import { IsNumber, IsString } from 'class-validator';
|
||||
import { IsEnum, IsNumber, IsOptional, IsString } from 'class-validator';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { PaymentGatewayEnum, PaymentMethodEnum } from '../interface/payment';
|
||||
|
||||
export class CreatePaymentDto {
|
||||
@ApiProperty({ description: 'Order ID' })
|
||||
@IsString()
|
||||
orderId: string;
|
||||
|
||||
@ApiProperty({ description: 'Payment amount' })
|
||||
@IsNumber()
|
||||
amount: number;
|
||||
|
||||
@ApiProperty({ description: 'Payment method' })
|
||||
@IsEnum(PaymentMethodEnum)
|
||||
paymentMethod: PaymentMethodEnum;
|
||||
|
||||
@ApiProperty({ description: 'Payment authority' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
restaurantPaymentId: string;
|
||||
merchantId?: string | null;
|
||||
|
||||
@ApiProperty({ description: 'Payment gateway' })
|
||||
@IsString()
|
||||
gateway: string;
|
||||
@IsOptional()
|
||||
@IsEnum(PaymentGatewayEnum)
|
||||
gateway?: PaymentGatewayEnum | null;
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
import { IsString, IsOptional, IsBoolean } from 'class-validator';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class CreateRestaurantPaymentMethodDto {
|
||||
@ApiProperty({ description: 'Payment method ID' })
|
||||
@IsString()
|
||||
paymentMethodId!: string;
|
||||
|
||||
@ApiProperty({ description: 'Merchant ID', required: false })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
merchantId?: string;
|
||||
|
||||
@ApiProperty({ description: 'Is active', required: false, default: true })
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
isActive?: boolean;
|
||||
}
|
||||
@@ -1,11 +1,5 @@
|
||||
import { PartialType } from '@nestjs/mapped-types';
|
||||
import { CreatePaymentMethodDto } from './create-payment-method.dto';
|
||||
import { IsString } from 'class-validator';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class UpdatePaymentMethodDto extends PartialType(CreatePaymentMethodDto) {
|
||||
@ApiProperty({ description: 'Payment method ID' })
|
||||
@IsString()
|
||||
id!: string;
|
||||
}
|
||||
export class UpdatePaymentMethodDto extends PartialType(CreatePaymentMethodDto) {}
|
||||
|
||||
|
||||
@@ -8,4 +8,3 @@ export class UpdatePaymentDto extends PartialType(CreatePaymentDto) {
|
||||
@IsNumber()
|
||||
id: number;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import { PartialType } from '@nestjs/mapped-types';
|
||||
import { CreateRestaurantPaymentMethodDto } from './create-restaurant-payment-method.dto';
|
||||
|
||||
|
||||
export class UpdateRestaurantPaymentMethodDto extends PartialType(CreateRestaurantPaymentMethodDto) {}
|
||||
@@ -1,30 +1,31 @@
|
||||
import { Entity, Property } from '@mikro-orm/core';
|
||||
import { Entity, Enum, ManyToOne, Property } from '@mikro-orm/core';
|
||||
import { BaseEntity } from '../../../common/entities/base.entity';
|
||||
import { Restaurant } from 'src/modules/restaurants/entities/restaurant.entity';
|
||||
import { PaymentGatewayEnum, PaymentMethodEnum } from '../interface/payment';
|
||||
|
||||
@Entity({ tableName: 'payment_methods' })
|
||||
export class PaymentMethod extends BaseEntity {
|
||||
@Property()
|
||||
name!: string;
|
||||
@ManyToOne(() => Restaurant)
|
||||
restaurant!: Restaurant;
|
||||
|
||||
@Property()
|
||||
keyName!: string;
|
||||
title!: string;
|
||||
|
||||
@Enum(() => PaymentMethodEnum)
|
||||
method!: PaymentMethodEnum;
|
||||
|
||||
@Enum(() => PaymentGatewayEnum)
|
||||
gateway: PaymentGatewayEnum | null = null;
|
||||
|
||||
@Property({ nullable: true })
|
||||
description?: string;
|
||||
|
||||
@Property({ nullable: true })
|
||||
icon?: string;
|
||||
|
||||
@Property({ default: true })
|
||||
isActive: boolean = true;
|
||||
|
||||
@Property({ default: true })
|
||||
isOnline: boolean = true;
|
||||
|
||||
@Property({ type: 'int', nullable: true })
|
||||
order?: number;
|
||||
enabled: boolean = true;
|
||||
|
||||
@Property({ type: 'integer', default: 0 })
|
||||
order: number = 0;
|
||||
|
||||
@Property({ nullable: true })
|
||||
paymentUrl?: string;
|
||||
merchantId?: string;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Entity, ManyToOne, Property, Enum } from '@mikro-orm/core';
|
||||
import { BaseEntity } from '../../../common/entities/base.entity';
|
||||
import { Order } from '../../orders/entities/order.entity';
|
||||
import { PaymentStatus } from '../interface/payment-status';
|
||||
import { PaymentGatewayEnum, PaymentStatusEnum } from '../interface/payment';
|
||||
|
||||
@Entity({ tableName: 'payments' })
|
||||
export class Payment extends BaseEntity {
|
||||
@@ -14,17 +14,17 @@ export class Payment extends BaseEntity {
|
||||
@Property({ unique: true })
|
||||
authority!: string;
|
||||
|
||||
@Property()
|
||||
gateway!: string;
|
||||
@Enum(() => PaymentGatewayEnum)
|
||||
gateway?: PaymentGatewayEnum | null = null;
|
||||
|
||||
@Property({ nullable: true })
|
||||
refId?: string;
|
||||
refId?: string | null = null;
|
||||
|
||||
@Enum(() => PaymentStatus)
|
||||
status!: PaymentStatus;
|
||||
@Enum(() => PaymentStatusEnum)
|
||||
status!: PaymentStatusEnum;
|
||||
|
||||
@Property({ nullable: true })
|
||||
cardPan?: string;
|
||||
cardPan?: string | null = null;
|
||||
|
||||
@Property({ type: 'json' })
|
||||
verifyResponse?: Record<string, any>;
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
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';
|
||||
|
||||
@Entity({ tableName: 'restaurant_payment_methods' })
|
||||
@Unique({ properties: ['restaurant', 'paymentMethod'] })
|
||||
export class RestaurantPaymentMethod extends BaseEntity {
|
||||
@ManyToOne(() => Restaurant)
|
||||
restaurant!: Restaurant;
|
||||
|
||||
@ManyToOne(() => PaymentMethod)
|
||||
paymentMethod!: PaymentMethod;
|
||||
|
||||
// Add merchant ID for payment gateway providers (e.g., ZarinPal)
|
||||
@Property({ nullable: true })
|
||||
merchantId?: string;
|
||||
|
||||
@Property({ nullable: true })
|
||||
callbackUrl?: string;
|
||||
|
||||
@Property({ type: 'boolean', default: true })
|
||||
isActive: boolean = true;
|
||||
}
|
||||
+13
-1
@@ -1,14 +1,26 @@
|
||||
export enum PaymentStatus {
|
||||
export enum PaymentMethodEnum {
|
||||
Online = 'Online',
|
||||
Cash = 'Cash',
|
||||
CardOnDelivery = 'CardOnDelivery',
|
||||
Wallet = 'Wallet',
|
||||
}
|
||||
export enum PaymentStatusEnum {
|
||||
Pending = 'pending',
|
||||
Paid = 'paid',
|
||||
Failed = 'failed',
|
||||
}
|
||||
export enum PaymentGatewayEnum {
|
||||
ZarinPal = 'zarinpal',
|
||||
}
|
||||
|
||||
export interface IPaymentRequest {
|
||||
amount: number;
|
||||
callbackUrl: string;
|
||||
merchantId: string;
|
||||
description: string;
|
||||
metadata: {
|
||||
orderId: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface IPaymentResponse {
|
||||
@@ -4,9 +4,6 @@ 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 { RestaurantPaymentMethod } from './entities/restaurant-payment-method.entity';
|
||||
import { RestaurantPaymentMethodRepository } from './repositories/restaurant-payment-method.repository';
|
||||
import { RestaurantPaymentMethodService } from './services/restaurant-payment-method.service';
|
||||
// import { PaymentGatewayService } from './services/payment-gateway.service.tss';
|
||||
import { Restaurant } from '../restaurants/entities/restaurant.entity';
|
||||
import { PaymentsController } from './controllers/payments.controller';
|
||||
@@ -14,21 +11,17 @@ import { AuthModule } from '../auth/auth.module';
|
||||
import { JwtModule } from '@nestjs/jwt';
|
||||
|
||||
@Module({
|
||||
imports: [MikroOrmModule.forFeature([PaymentMethod, RestaurantPaymentMethod, Restaurant]), AuthModule, JwtModule],
|
||||
imports: [MikroOrmModule.forFeature([PaymentMethod, Restaurant]), AuthModule, JwtModule],
|
||||
controllers: [PaymentsController],
|
||||
providers: [
|
||||
PaymentsService,
|
||||
PaymentMethodService,
|
||||
PaymentMethodRepository,
|
||||
RestaurantPaymentMethodService,
|
||||
RestaurantPaymentMethodRepository,
|
||||
// PaymentGatewayService,
|
||||
],
|
||||
exports: [
|
||||
PaymentMethodRepository,
|
||||
PaymentMethodService,
|
||||
RestaurantPaymentMethodRepository,
|
||||
RestaurantPaymentMethodService,
|
||||
PaymentsService,
|
||||
// PaymentGatewayService,
|
||||
],
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { EntityManager, EntityRepository } from '@mikro-orm/postgresql';
|
||||
import { RestaurantPaymentMethod } from '../entities/restaurant-payment-method.entity';
|
||||
|
||||
@Injectable()
|
||||
export class RestaurantPaymentMethodRepository extends EntityRepository<RestaurantPaymentMethod> {
|
||||
constructor(readonly em: EntityManager) {
|
||||
super(em, RestaurantPaymentMethod);
|
||||
}
|
||||
async findOneWithPopulate(id: string): Promise<RestaurantPaymentMethod> {
|
||||
const restaurantPaymentMethod = await this.findOne({ id }, { populate: ['restaurant', 'paymentMethod'] });
|
||||
if (!restaurantPaymentMethod) {
|
||||
throw new NotFoundException('Restaurant payment method not found');
|
||||
}
|
||||
// await restaurantPaymentMethod.restaurant.load();
|
||||
// await restaurantPaymentMethod.paymentMethod.load();
|
||||
return restaurantPaymentMethod;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { Injectable, NotFoundException, ConflictException } from '@nestjs/common';
|
||||
import { EntityManager } from '@mikro-orm/postgresql';
|
||||
import { PaymentMethod } from '../entities/payment-method.entity';
|
||||
import { PaymentMethodRepository } from '../repositories/payment-method.repository';
|
||||
import { CreatePaymentMethodDto } from '../dto/create-payment-method.dto';
|
||||
import { UpdatePaymentMethodDto } from '../dto/update-payment-method.dto';
|
||||
import { RequiredEntityData } from '@mikro-orm/core';
|
||||
import { Restaurant } from '../../restaurants/entities/restaurant.entity';
|
||||
|
||||
@Injectable()
|
||||
export class PaymentMethodService {
|
||||
@@ -13,26 +14,40 @@ export class PaymentMethodService {
|
||||
private readonly em: EntityManager,
|
||||
) {}
|
||||
|
||||
async create(createPaymentMethodDto: CreatePaymentMethodDto): Promise<PaymentMethod> {
|
||||
const paymentMethod = this.paymentMethodRepository.create(
|
||||
createPaymentMethodDto as unknown as RequiredEntityData<PaymentMethod>,
|
||||
);
|
||||
async create(restId: string, createPaymentMethodDto: CreatePaymentMethodDto): Promise<PaymentMethod> {
|
||||
// Check if restaurant exists
|
||||
const restaurant = await this.em.findOne(Restaurant, { id: restId });
|
||||
if (!restaurant) {
|
||||
throw new NotFoundException(`Restaurant with ID ${restId} not found`);
|
||||
}
|
||||
|
||||
const paymentMethod = this.paymentMethodRepository.create({
|
||||
restaurant,
|
||||
...createPaymentMethodDto,
|
||||
} as unknown as RequiredEntityData<PaymentMethod>);
|
||||
await this.em.persistAndFlush(paymentMethod);
|
||||
return paymentMethod;
|
||||
}
|
||||
|
||||
async findAll(): Promise<PaymentMethod[]> {
|
||||
return this.paymentMethodRepository.findAll();
|
||||
return this.paymentMethodRepository.findAll({ populate: ['restaurant'] });
|
||||
}
|
||||
|
||||
async findOne(id: string): Promise<PaymentMethod> {
|
||||
const paymentMethod = await this.paymentMethodRepository.findOne({ id });
|
||||
const paymentMethod = await this.paymentMethodRepository.findOne({ id }, { populate: ['restaurant'] });
|
||||
if (!paymentMethod) {
|
||||
throw new NotFoundException(`PaymentMethod with ID ${id} not found`);
|
||||
}
|
||||
return paymentMethod;
|
||||
}
|
||||
|
||||
async findByRestaurant(restaurantId: string): Promise<PaymentMethod[]> {
|
||||
return this.paymentMethodRepository.find(
|
||||
{ restaurant: { id: restaurantId } },
|
||||
{ populate: ['restaurant'] },
|
||||
);
|
||||
}
|
||||
|
||||
async update(id: string, updatePaymentMethodDto: UpdatePaymentMethodDto): Promise<PaymentMethod> {
|
||||
const paymentMethod = await this.findOne(id);
|
||||
this.em.assign(paymentMethod, updatePaymentMethodDto);
|
||||
|
||||
@@ -1,17 +1,24 @@
|
||||
import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { RestaurantPaymentMethod } from '../entities/restaurant-payment-method.entity';
|
||||
import axios from 'axios';
|
||||
import { IPaymentRequest, IPaymentResponse, PaymentStatus } from '../interface/payment-status';
|
||||
import {
|
||||
IPaymentRequest,
|
||||
IPaymentResponse,
|
||||
PaymentGatewayEnum,
|
||||
PaymentMethodEnum,
|
||||
PaymentStatusEnum,
|
||||
} from '../interface/payment';
|
||||
import { Payment } from '../entities/payment.entity';
|
||||
import { EntityManager, RequiredEntityData } from '@mikro-orm/core';
|
||||
import { Order } from '../../orders/entities/order.entity';
|
||||
import { PaymentMethod } from '../entities/payment-method.entity';
|
||||
import { CreatePaymentDto } from '../dto/create-payment.dto';
|
||||
|
||||
@Injectable()
|
||||
export class PaymentsService {
|
||||
constructor(private readonly em: EntityManager) {}
|
||||
|
||||
async initializePayment(
|
||||
restaurantPaymentMethodId: string,
|
||||
paymentMethodId: string,
|
||||
amount: number,
|
||||
orderId: string,
|
||||
): Promise<{ paymentUrl: string | null }> {
|
||||
@@ -27,78 +34,97 @@ export class PaymentsService {
|
||||
}
|
||||
|
||||
// Load restaurant payment method with payment method relationship
|
||||
const restaurantPaymentMethod = await this.em.findOne(
|
||||
RestaurantPaymentMethod,
|
||||
{ id: restaurantPaymentMethodId },
|
||||
{ populate: ['paymentMethod'] },
|
||||
);
|
||||
if (!restaurantPaymentMethod) {
|
||||
throw new NotFoundException('Restaurant payment method not found');
|
||||
const paymentMethod = await this.em.findOne(PaymentMethod, { id: paymentMethodId }, { populate: ['restaurant'] });
|
||||
if (!paymentMethod) {
|
||||
throw new NotFoundException('Payment method not found');
|
||||
}
|
||||
|
||||
if (!restaurantPaymentMethod.paymentMethod?.isOnline) {
|
||||
return { paymentUrl: null };
|
||||
}
|
||||
|
||||
if (!restaurantPaymentMethod.callbackUrl) {
|
||||
throw new BadRequestException('Callback URL is not configured');
|
||||
}
|
||||
|
||||
if (!restaurantPaymentMethod.merchantId) {
|
||||
if (paymentMethod.method !== PaymentMethodEnum.Online && !paymentMethod.merchantId) {
|
||||
throw new BadRequestException('Merchant ID is not provided');
|
||||
}
|
||||
|
||||
if (!restaurantPaymentMethod.paymentMethod?.paymentUrl) {
|
||||
throw new BadRequestException('Payment URL is not configured');
|
||||
}
|
||||
|
||||
// Request to payment gateway with error handling
|
||||
let gatewayResponse: IPaymentResponse;
|
||||
try {
|
||||
gatewayResponse = await this.requestToPaymentGateway(restaurantPaymentMethod.paymentMethod.paymentUrl, {
|
||||
amount,
|
||||
callbackUrl: restaurantPaymentMethod.callbackUrl,
|
||||
merchantId: restaurantPaymentMethod.merchantId,
|
||||
description: `Payment for order #${orderId}`,
|
||||
});
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
||||
throw new BadRequestException(`Failed to connect to payment gateway: ${errorMessage}`);
|
||||
}
|
||||
|
||||
// Check gateway response code (typically 100 means success for Iranian gateways)
|
||||
if (gatewayResponse.code !== 100 && gatewayResponse.code !== 0) {
|
||||
throw new BadRequestException(`Payment gateway error: ${gatewayResponse.message || 'Unknown error'}`);
|
||||
}
|
||||
|
||||
if (!gatewayResponse.authority) {
|
||||
throw new BadRequestException('Payment gateway did not return an authority token');
|
||||
}
|
||||
|
||||
// Create payment record and save authority
|
||||
const payment = this.em.create(Payment, {
|
||||
const restaurantDomain = paymentMethod.restaurant.domain;
|
||||
const { authority } = await this.createPayment(restaurantDomain, {
|
||||
amount,
|
||||
authority: gatewayResponse.authority,
|
||||
order: this.em.getReference(Order, orderId),
|
||||
gateway: restaurantPaymentMethod.paymentMethod.name,
|
||||
status: PaymentStatus.Pending,
|
||||
} as RequiredEntityData<Payment>);
|
||||
orderId,
|
||||
paymentMethod: paymentMethod.method,
|
||||
merchantId: paymentMethod.merchantId ?? null,
|
||||
gateway: paymentMethod.gateway as PaymentGatewayEnum | null,
|
||||
});
|
||||
|
||||
await this.em.persistAndFlush(payment);
|
||||
const paymentUrl = this.zarinpalPaymentUrl(paymentMethod.gateway, authority);
|
||||
|
||||
// Return payment URL
|
||||
const paymentUrl = `${restaurantPaymentMethod.paymentMethod.paymentUrl}/${gatewayResponse.authority}`;
|
||||
return { paymentUrl };
|
||||
}
|
||||
|
||||
private async requestToPaymentGateway(
|
||||
gatewayPaymentUrl: string,
|
||||
requestPayment: IPaymentRequest,
|
||||
): Promise<IPaymentResponse> {
|
||||
async createPayment(domain: string, dto: CreatePaymentDto) {
|
||||
const { amount, orderId, merchantId, gateway, paymentMethod } = dto;
|
||||
|
||||
const { authority } = await this.requestToGateway(paymentMethod, amount, orderId, merchantId, domain, gateway);
|
||||
|
||||
const payment = this.em.create(Payment, {
|
||||
amount,
|
||||
authority: authority,
|
||||
order: this.em.getReference(Order, orderId),
|
||||
gateway,
|
||||
status: PaymentStatusEnum.Pending,
|
||||
} as RequiredEntityData<Payment>);
|
||||
|
||||
await this.em.persistAndFlush(payment);
|
||||
return { authority, payment };
|
||||
}
|
||||
|
||||
async requestToGateway(
|
||||
paymentMethod: PaymentMethodEnum,
|
||||
amount: number,
|
||||
orderId: string,
|
||||
merchantId: string | null | undefined,
|
||||
domain: string,
|
||||
gateway: PaymentGatewayEnum | null | undefined,
|
||||
): Promise<{ authority: string | null }> {
|
||||
if (paymentMethod === PaymentMethodEnum.Online && merchantId) {
|
||||
const callbackUrl = `${domain}/payments/callback`;
|
||||
if (gateway === PaymentGatewayEnum.ZarinPal) {
|
||||
try {
|
||||
const gatewayResponse = await this.requestToZarinPalGateway({
|
||||
amount,
|
||||
merchantId,
|
||||
description: `Payment for order #${orderId}`,
|
||||
callbackUrl,
|
||||
metadata: {
|
||||
orderId,
|
||||
},
|
||||
});
|
||||
// Check gateway response code (typically 100 means success for Iranian gateways)
|
||||
if (gatewayResponse.code !== 100 && gatewayResponse.code !== 0) {
|
||||
throw new BadRequestException(`Payment gateway error: ${gatewayResponse.message || 'Unknown error'}`);
|
||||
}
|
||||
|
||||
if (!gatewayResponse.authority) {
|
||||
throw new BadRequestException('Payment gateway did not return an authority token');
|
||||
}
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
||||
throw new BadRequestException(`Failed to connect to payment gateway: ${errorMessage}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
return { authority: null };
|
||||
}
|
||||
|
||||
private async requestToZarinPalGateway(requestPayment: IPaymentRequest): Promise<IPaymentResponse> {
|
||||
const gatewayPaymentUrl = 'https://payment.zarinpal.com/pg/v4/payment/request.json';
|
||||
const response = await axios.post<IPaymentResponse>(gatewayPaymentUrl, requestPayment);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
zarinpalPaymentUrl(gateway: PaymentGatewayEnum | null, authority: string | null) {
|
||||
if (gateway === PaymentGatewayEnum.ZarinPal) {
|
||||
return `https://payment.zarinpal.com/pg/StartPay/${authority}`;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
update(_id: number, _updatePaymentDto: unknown) {
|
||||
return `This action updates a #${_id} payment`;
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
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 { CreateRestaurantPaymentMethodDto } from '../dto/create-restaurant-payment-method.dto';
|
||||
import { UpdateRestaurantPaymentMethodDto } from '../dto/update-restaurant-payment-method.dto';
|
||||
import { RequiredEntityData } from '@mikro-orm/core';
|
||||
|
||||
@Injectable()
|
||||
export class RestaurantPaymentMethodService {
|
||||
constructor(
|
||||
private readonly restaurantPaymentMethodRepository: RestaurantPaymentMethodRepository,
|
||||
private readonly em: EntityManager,
|
||||
) {}
|
||||
|
||||
async create(
|
||||
restId: string,
|
||||
createRestaurantPaymentMethodDto: CreateRestaurantPaymentMethodDto,
|
||||
): Promise<RestaurantPaymentMethod> {
|
||||
// Check if restaurant exists
|
||||
const restaurant = await this.em.findOne(Restaurant, { id: restId });
|
||||
if (!restaurant) {
|
||||
throw new NotFoundException(`Restaurant with ID ${restId} not found`);
|
||||
}
|
||||
|
||||
// Check if payment method exists
|
||||
const paymentMethod = await this.em.findOne(PaymentMethod, {
|
||||
id: createRestaurantPaymentMethodDto.paymentMethodId,
|
||||
});
|
||||
if (!paymentMethod) {
|
||||
throw new NotFoundException(
|
||||
`PaymentMethod with ID ${createRestaurantPaymentMethodDto.paymentMethodId} not found`,
|
||||
);
|
||||
}
|
||||
|
||||
// Check if the relationship already exists
|
||||
const existing = await this.restaurantPaymentMethodRepository.findOne({
|
||||
restaurant: { id: restId },
|
||||
paymentMethod: { id: createRestaurantPaymentMethodDto.paymentMethodId },
|
||||
});
|
||||
|
||||
if (existing) {
|
||||
throw new ConflictException('This payment method is already assigned to this restaurant');
|
||||
}
|
||||
|
||||
const restaurantPaymentMethod = this.restaurantPaymentMethodRepository.create({
|
||||
restaurant,
|
||||
paymentMethod,
|
||||
merchantId: createRestaurantPaymentMethodDto.merchantId,
|
||||
isActive: createRestaurantPaymentMethodDto.isActive ?? true,
|
||||
} as unknown as RequiredEntityData<RestaurantPaymentMethod>);
|
||||
|
||||
await this.em.persistAndFlush(restaurantPaymentMethod);
|
||||
return restaurantPaymentMethod;
|
||||
}
|
||||
|
||||
async findAll(): Promise<RestaurantPaymentMethod[]> {
|
||||
return this.restaurantPaymentMethodRepository.findAll({ populate: ['restaurant', 'paymentMethod'] });
|
||||
}
|
||||
|
||||
async findByRestaurant(restaurantId: string): Promise<RestaurantPaymentMethod[]> {
|
||||
return this.restaurantPaymentMethodRepository.find(
|
||||
{ restaurant: { id: restaurantId } },
|
||||
{ populate: ['restaurant', 'paymentMethod'] },
|
||||
);
|
||||
}
|
||||
|
||||
async findByPaymentMethod(restPaymentMethodId: string): Promise<RestaurantPaymentMethod | null> {
|
||||
return this.restaurantPaymentMethodRepository.findOneOrFail(
|
||||
{ id: restPaymentMethodId },
|
||||
{ populate: ['restaurant', 'paymentMethod'] },
|
||||
);
|
||||
}
|
||||
|
||||
async findOne(id: string): Promise<RestaurantPaymentMethod> {
|
||||
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,
|
||||
updateRestaurantPaymentMethodDto: UpdateRestaurantPaymentMethodDto,
|
||||
): Promise<RestaurantPaymentMethod> {
|
||||
const restaurantPaymentMethod = await this.findOne(id);
|
||||
|
||||
if (updateRestaurantPaymentMethodDto.paymentMethodId) {
|
||||
const paymentMethod = await this.em.findOne(PaymentMethod, {
|
||||
id: updateRestaurantPaymentMethodDto.paymentMethodId,
|
||||
});
|
||||
if (!paymentMethod) {
|
||||
throw new NotFoundException(
|
||||
`PaymentMethod with ID ${updateRestaurantPaymentMethodDto.paymentMethodId} not found`,
|
||||
);
|
||||
}
|
||||
|
||||
// Check if the new relationship already exists
|
||||
const existing = await this.restaurantPaymentMethodRepository.findOne({
|
||||
restaurant: { id: restaurantPaymentMethod.restaurant.id },
|
||||
paymentMethod: { id: updateRestaurantPaymentMethodDto.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 (updateRestaurantPaymentMethodDto.merchantId !== undefined) {
|
||||
restaurantPaymentMethod.merchantId = updateRestaurantPaymentMethodDto.merchantId;
|
||||
}
|
||||
|
||||
if (updateRestaurantPaymentMethodDto.isActive !== undefined) {
|
||||
restaurantPaymentMethod.isActive = updateRestaurantPaymentMethodDto.isActive;
|
||||
}
|
||||
|
||||
await this.em.flush();
|
||||
return restaurantPaymentMethod;
|
||||
}
|
||||
|
||||
async remove(id: string): Promise<RestaurantPaymentMethod> {
|
||||
const restaurantPaymentMethod = await this.findOne(id);
|
||||
await this.em.removeAndFlush(restaurantPaymentMethod);
|
||||
return restaurantPaymentMethod;
|
||||
}
|
||||
}
|
||||
@@ -77,6 +77,9 @@ export class Restaurant extends BaseEntity {
|
||||
@Property({ type: 'decimal', default: 0 })
|
||||
vat?: number = 0;
|
||||
|
||||
@Property()
|
||||
domain!: string;
|
||||
|
||||
// --- روشهای ارسال ---
|
||||
@OneToMany(() => Delivery, delivery => delivery.restaurant)
|
||||
deliveries = new Collection<Delivery>(this);
|
||||
|
||||
Reference in New Issue
Block a user