This commit is contained in:
@@ -0,0 +1,13 @@
|
|||||||
|
import { Migration } from '@mikro-orm/migrations';
|
||||||
|
|
||||||
|
export class Migration20260615140000_add_card_owner_to_payment_methods extends Migration {
|
||||||
|
|
||||||
|
override async up(): Promise<void> {
|
||||||
|
this.addSql(`alter table "payment_methods" add column "card_owner" varchar(255) null;`);
|
||||||
|
}
|
||||||
|
|
||||||
|
override async down(): Promise<void> {
|
||||||
|
this.addSql(`alter table "payment_methods" drop column "card_owner";`);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -745,6 +745,7 @@ export const enum PaymentMessage {
|
|||||||
PAYMENT_METHOD_NOT_CASH = 'روش پرداخت نقدی نیست',
|
PAYMENT_METHOD_NOT_CASH = 'روش پرداخت نقدی نیست',
|
||||||
PAYMENT_METHOD_NOT_CREDIT_CARD = 'روش پرداخت کارت به کارت نیست',
|
PAYMENT_METHOD_NOT_CREDIT_CARD = 'روش پرداخت کارت به کارت نیست',
|
||||||
CARD_NUMBER_REQUIRED = 'شماره کارت برای پرداخت کارت به کارت الزامی است',
|
CARD_NUMBER_REQUIRED = 'شماره کارت برای پرداخت کارت به کارت الزامی است',
|
||||||
|
CARD_OWNER_REQUIRED = 'نام صاحب کارت برای پرداخت کارت به کارت الزامی است',
|
||||||
PAYMENT_ALREADY_PAID = 'پرداخت قبلا انجام شده است',
|
PAYMENT_ALREADY_PAID = 'پرداخت قبلا انجام شده است',
|
||||||
EXISTING_PENDING_PAYMENT_MISMATCH = 'پرداخت در انتظار موجود با روش پرداخت سفارش مطابقت ندارد',
|
EXISTING_PENDING_PAYMENT_MISMATCH = 'پرداخت در انتظار موجود با روش پرداخت سفارش مطابقت ندارد',
|
||||||
ZARINPAL_INVALID_RESPONSE = 'پاسخ نامعتبر از API زرینپال',
|
ZARINPAL_INVALID_RESPONSE = 'پاسخ نامعتبر از API زرینپال',
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { IsString, IsOptional, IsBoolean, IsNumber, IsEnum } from 'class-validator';
|
import { IsString, IsOptional, IsBoolean, IsNumber, IsEnum, ValidateIf, IsNotEmpty } from 'class-validator';
|
||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { PaymentMethodEnum, PaymentGatewayEnum } from '../interface/payment';
|
import { PaymentMethodEnum, PaymentGatewayEnum } from '../interface/payment';
|
||||||
|
|
||||||
@@ -23,6 +23,12 @@ export class CreatePaymentMethodDto {
|
|||||||
@IsString()
|
@IsString()
|
||||||
cardNumber?: string;
|
cardNumber?: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: 'Card owner name for card-to-card payments', required: false })
|
||||||
|
@ValidateIf((dto: CreatePaymentMethodDto) => dto.method === PaymentMethodEnum.CreditCard)
|
||||||
|
@IsNotEmpty()
|
||||||
|
@IsString()
|
||||||
|
cardOwner?: string;
|
||||||
|
|
||||||
@ApiProperty({ description: 'Is payment method enabled', required: false, default: true })
|
@ApiProperty({ description: 'Is payment method enabled', required: false, default: true })
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ export class PaymentMethod extends BaseEntity {
|
|||||||
@Property({ nullable: true })
|
@Property({ nullable: true })
|
||||||
cardNumber?: string;
|
cardNumber?: string;
|
||||||
|
|
||||||
|
@Property({ nullable: true })
|
||||||
|
cardOwner?: string;
|
||||||
|
|
||||||
@Property({ default: true })
|
@Property({ default: true })
|
||||||
enabled: boolean = true;
|
enabled: boolean = true;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Injectable, NotFoundException, ConflictException } from '@nestjs/common';
|
import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';
|
||||||
import { EntityManager } from '@mikro-orm/postgresql';
|
import { EntityManager } from '@mikro-orm/postgresql';
|
||||||
import { PaymentMethod } from '../entities/payment-method.entity';
|
import { PaymentMethod } from '../entities/payment-method.entity';
|
||||||
import { PaymentMethodRepository } from '../repositories/payment-method.repository';
|
import { PaymentMethodRepository } from '../repositories/payment-method.repository';
|
||||||
@@ -7,6 +7,7 @@ import { UpdatePaymentMethodDto } from '../dto/update-payment-method.dto';
|
|||||||
import { RequiredEntityData } from '@mikro-orm/core';
|
import { RequiredEntityData } from '@mikro-orm/core';
|
||||||
import { PaymentMessage } from 'src/common/enums/message.enum';
|
import { PaymentMessage } from 'src/common/enums/message.enum';
|
||||||
import { RestaurantsService } from 'src/modules/restaurants/providers/restaurants.service';
|
import { RestaurantsService } from 'src/modules/restaurants/providers/restaurants.service';
|
||||||
|
import { PaymentMethodEnum } from '../interface/payment';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PaymentMethodService {
|
export class PaymentMethodService {
|
||||||
@@ -18,7 +19,8 @@ export class PaymentMethodService {
|
|||||||
|
|
||||||
async create(restId: string, createPaymentMethodDto: CreatePaymentMethodDto): Promise<PaymentMethod> {
|
async create(restId: string, createPaymentMethodDto: CreatePaymentMethodDto): Promise<PaymentMethod> {
|
||||||
// Check if restaurant exists
|
// Check if restaurant exists
|
||||||
const restaurant = await this.restaurantsService.findOneOrFail(restId)
|
const restaurant = await this.restaurantsService.findOneOrFail(restId);
|
||||||
|
this.assertCreditCardOwner(createPaymentMethodDto.method, createPaymentMethodDto.cardOwner);
|
||||||
|
|
||||||
const paymentMethod = this.paymentMethodRepository.create({
|
const paymentMethod = this.paymentMethodRepository.create({
|
||||||
restaurant,
|
restaurant,
|
||||||
@@ -43,10 +45,17 @@ export class PaymentMethodService {
|
|||||||
async update(id: string, updatePaymentMethodDto: UpdatePaymentMethodDto): Promise<PaymentMethod> {
|
async update(id: string, updatePaymentMethodDto: UpdatePaymentMethodDto): Promise<PaymentMethod> {
|
||||||
const paymentMethod = await this.findOneOrFail(id);
|
const paymentMethod = await this.findOneOrFail(id);
|
||||||
this.em.assign(paymentMethod, updatePaymentMethodDto);
|
this.em.assign(paymentMethod, updatePaymentMethodDto);
|
||||||
|
this.assertCreditCardOwner(paymentMethod.method, paymentMethod.cardOwner);
|
||||||
await this.em.flush();
|
await this.em.flush();
|
||||||
return paymentMethod;
|
return paymentMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private assertCreditCardOwner(method: PaymentMethodEnum, cardOwner?: string): void {
|
||||||
|
if (method === PaymentMethodEnum.CreditCard && !cardOwner?.trim()) {
|
||||||
|
throw new BadRequestException(PaymentMessage.CARD_OWNER_REQUIRED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async remove(id: string): Promise<PaymentMethod> {
|
async remove(id: string): Promise<PaymentMethod> {
|
||||||
const paymentMethod = await this.findOneOrFail(id);
|
const paymentMethod = await this.findOneOrFail(id);
|
||||||
await this.em.removeAndFlush(paymentMethod);
|
await this.em.removeAndFlush(paymentMethod);
|
||||||
|
|||||||
@@ -83,6 +83,10 @@ export class PaymentsService {
|
|||||||
throw new BadRequestException(PaymentMessage.CARD_NUMBER_REQUIRED);
|
throw new BadRequestException(PaymentMessage.CARD_NUMBER_REQUIRED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pm.method === PaymentMethodEnum.CreditCard && !pm.cardOwner?.trim()) {
|
||||||
|
throw new BadRequestException(PaymentMessage.CARD_OWNER_REQUIRED);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
order,
|
order,
|
||||||
amount: order.total,
|
amount: order.total,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ export interface PaymentMethodData {
|
|||||||
order: number;
|
order: number;
|
||||||
gateway: PaymentGatewayEnum | null;
|
gateway: PaymentGatewayEnum | null;
|
||||||
cardNumber?: string;
|
cardNumber?: string;
|
||||||
|
cardOwner?: string;
|
||||||
merchantId?: string;
|
merchantId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,5 +41,6 @@ export const paymentMethodsData: PaymentMethodData[] = [
|
|||||||
order: 4,
|
order: 4,
|
||||||
gateway: null,
|
gateway: null,
|
||||||
cardNumber: '6037991234567890',
|
cardNumber: '6037991234567890',
|
||||||
|
cardOwner: 'صاحب کارت',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ export class PaymentMethodsSeeder {
|
|||||||
order: methodData.order,
|
order: methodData.order,
|
||||||
gateway: methodData.gateway,
|
gateway: methodData.gateway,
|
||||||
cardNumber: methodData.cardNumber,
|
cardNumber: methodData.cardNumber,
|
||||||
|
cardOwner: methodData.cardOwner,
|
||||||
merchantId: methodData.merchantId,
|
merchantId: methodData.merchantId,
|
||||||
});
|
});
|
||||||
em.persist(paymentMethod);
|
em.persist(paymentMethod);
|
||||||
@@ -32,7 +33,8 @@ export class PaymentMethodsSeeder {
|
|||||||
existing.description !== methodData.description ||
|
existing.description !== methodData.description ||
|
||||||
existing.enabled !== methodData.enabled ||
|
existing.enabled !== methodData.enabled ||
|
||||||
existing.order !== methodData.order ||
|
existing.order !== methodData.order ||
|
||||||
existing.cardNumber !== methodData.cardNumber
|
existing.cardNumber !== methodData.cardNumber ||
|
||||||
|
existing.cardOwner !== methodData.cardOwner
|
||||||
) {
|
) {
|
||||||
existing.description = methodData.description;
|
existing.description = methodData.description;
|
||||||
existing.enabled = methodData.enabled;
|
existing.enabled = methodData.enabled;
|
||||||
@@ -43,6 +45,9 @@ export class PaymentMethodsSeeder {
|
|||||||
if (methodData.cardNumber !== undefined) {
|
if (methodData.cardNumber !== undefined) {
|
||||||
existing.cardNumber = methodData.cardNumber;
|
existing.cardNumber = methodData.cardNumber;
|
||||||
}
|
}
|
||||||
|
if (methodData.cardOwner !== undefined) {
|
||||||
|
existing.cardOwner = methodData.cardOwner;
|
||||||
|
}
|
||||||
if (methodData.merchantId !== undefined) {
|
if (methodData.merchantId !== undefined) {
|
||||||
existing.merchantId = methodData.merchantId;
|
existing.merchantId = methodData.merchantId;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user