order
This commit is contained in:
@@ -1,302 +1,301 @@
|
||||
import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { PaymentGatewayEnum, PaymentMethodEnum, PaymentStatusEnum } from '../interface/payment';
|
||||
import { EntityManager } from '@mikro-orm/postgresql';
|
||||
import { Order } from '../../order/entities/order.entity';
|
||||
import { Logger } from '@nestjs/common';
|
||||
import { GatewayManager } from '../gateways/gateway.manager';
|
||||
import { OrderPaymentContext } from '../interface/payment';
|
||||
import { PaymentMessage, OrderMessage } from 'src/common/enums/message.enum';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { onlinePaymentSucceedEvent, paymentSucceedEvent } from '../events/payment.events';
|
||||
import { OrderService } from 'src/modules/order/providers/order.service';
|
||||
import { PayOrderDto } from '../dto/pay-order.dto';
|
||||
import { PaymentRepository } from '../repositories/payment.repository';
|
||||
import { CreditRepository } from 'src/modules/user/repositories/credit.repository';
|
||||
import { CreditService } from 'src/modules/user/providers/credit.service';
|
||||
import { CreditTransactionType } from 'src/modules/user/interface/user';
|
||||
import { Payment } from '../entities/payment.entity';
|
||||
import { FindPaymentsDto } from '../dto/find-payments.dto';
|
||||
import { Admin } from 'src/modules/admin/entities/admin.entity';
|
||||
import { AdminRepository } from 'src/modules/admin/repositories/admin.repository';
|
||||
import { CreditTransaction } from 'src/modules/user/entities/credit-transaction.entity';
|
||||
// import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';
|
||||
// import { PaymentGatewayEnum, PaymentMethodEnum, PaymentStatusEnum } from '../interface/payment';
|
||||
// import { EntityManager } from '@mikro-orm/postgresql';
|
||||
// import { Logger } from '@nestjs/common';
|
||||
// import { GatewayManager } from '../gateways/gateway.manager';
|
||||
// import { OrderPaymentContext } from '../interface/payment';
|
||||
// import { PaymentMessage, OrderMessage } from 'src/common/enums/message.enum';
|
||||
// import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
// import { onlinePaymentSucceedEvent, paymentSucceedEvent } from '../events/payment.events';
|
||||
// import { OrderService } from 'src/modules/order/providers/order.service';
|
||||
// import { PayOrderDto } from '../dto/pay-order.dto';
|
||||
// import { PaymentRepository } from '../repositories/payment.repository';
|
||||
// import { CreditRepository } from 'src/modules/user/repositories/credit.repository';
|
||||
// import { CreditService } from 'src/modules/user/providers/credit.service';
|
||||
// import { CreditTransactionType } from 'src/modules/user/interface/user';
|
||||
// import { Payment } from '../entities/payment.entity';
|
||||
// import { FindPaymentsDto } from '../dto/find-payments.dto';
|
||||
// import { Admin } from 'src/modules/admin/entities/admin.entity';
|
||||
// import { AdminRepository } from 'src/modules/admin/repositories/admin.repository';
|
||||
// import { CreditTransaction } from 'src/modules/user/entities/credit-transaction.entity';
|
||||
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class PaymentService {
|
||||
private readonly logger = new Logger(PaymentService.name);
|
||||
// @Injectable()
|
||||
// export class PaymentService {
|
||||
// private readonly logger = new Logger(PaymentService.name);
|
||||
|
||||
constructor(
|
||||
private readonly em: EntityManager,
|
||||
private readonly orderService: OrderService,
|
||||
private readonly paymentRepository: PaymentRepository,
|
||||
private readonly gatewayManager: GatewayManager,
|
||||
private readonly eventEmitter: EventEmitter2,
|
||||
private readonly creditService: CreditService,
|
||||
private readonly creditRepository: CreditRepository,
|
||||
private readonly adminRepository: AdminRepository,
|
||||
) { }
|
||||
// constructor(
|
||||
// private readonly em: EntityManager,
|
||||
// private readonly orderService: OrderService,
|
||||
// private readonly paymentRepository: PaymentRepository,
|
||||
// private readonly gatewayManager: GatewayManager,
|
||||
// private readonly eventEmitter: EventEmitter2,
|
||||
// private readonly creditService: CreditService,
|
||||
// private readonly creditRepository: CreditRepository,
|
||||
// private readonly adminRepository: AdminRepository,
|
||||
// ) { }
|
||||
|
||||
async payOrder(orderId: string, dto: PayOrderDto, adminId?: string): Promise<{ paymentUrl: string | null }> {
|
||||
// async payOrder(orderId: string, dto: PayOrderDto, adminId?: string): Promise<{ paymentUrl: string | null }> {
|
||||
|
||||
const ctx = await this.loadAndValidateOrder(orderId, dto, adminId)
|
||||
// const ctx = await this.loadAndValidateOrder(orderId, dto, adminId)
|
||||
|
||||
switch (dto.method) {
|
||||
case PaymentMethodEnum.Cash:
|
||||
await this.handleCashPayment(ctx);
|
||||
return { paymentUrl: null };
|
||||
// switch (dto.method) {
|
||||
// case PaymentMethodEnum.Cash:
|
||||
// await this.handleCashPayment(ctx);
|
||||
// return { paymentUrl: null };
|
||||
|
||||
case PaymentMethodEnum.Credit:
|
||||
await this.handleCreditPayment(ctx);
|
||||
return { paymentUrl: null };
|
||||
// case PaymentMethodEnum.Credit:
|
||||
// await this.handleCreditPayment(ctx);
|
||||
// return { paymentUrl: null };
|
||||
|
||||
case PaymentMethodEnum.Online:
|
||||
return this.handleOnlinePayment(ctx);
|
||||
// case PaymentMethodEnum.Online:
|
||||
// return this.handleOnlinePayment(ctx);
|
||||
|
||||
default:
|
||||
throw new BadRequestException(PaymentMessage.UNSUPPORTED_PAYMENT_METHOD);
|
||||
}
|
||||
}
|
||||
// default:
|
||||
// throw new BadRequestException(PaymentMessage.UNSUPPORTED_PAYMENT_METHOD);
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
private async loadAndValidateOrder(orderId: string, dto: PayOrderDto, adminId?: string): Promise<OrderPaymentContext> {
|
||||
const { amount, method, attachments, description, gateway } = dto
|
||||
// private async loadAndValidateOrder(orderId: string, dto: PayOrderDto, adminId?: string): Promise<OrderPaymentContext> {
|
||||
// const { amount, method, attachments, description, gateway } = dto
|
||||
|
||||
const order = await this.orderService.findOrderOrFail(orderId)
|
||||
// const order = await this.orderService.findOrderOrFail(orderId)
|
||||
|
||||
if (!order.balance) {
|
||||
throw new NotFoundException('Balance is not set');
|
||||
}
|
||||
// if (!order.balance) {
|
||||
// throw new NotFoundException('Balance is not set');
|
||||
// }
|
||||
|
||||
let admin: null | Admin = null
|
||||
if (adminId) {
|
||||
admin = await this.adminRepository.findOne({ id: adminId })
|
||||
if (!admin) {
|
||||
throw new BadRequestException("Admin not found")
|
||||
}
|
||||
}
|
||||
// let admin: null | Admin = null
|
||||
// if (adminId) {
|
||||
// admin = await this.adminRepository.findOne({ id: adminId })
|
||||
// if (!admin) {
|
||||
// throw new BadRequestException("Admin not found")
|
||||
// }
|
||||
// }
|
||||
|
||||
if (amount <= 0) {
|
||||
throw new BadRequestException(PaymentMessage.AMOUNT_MUST_BE_GREATER_THAN_ZERO);
|
||||
}
|
||||
// if (amount <= 0) {
|
||||
// throw new BadRequestException(PaymentMessage.AMOUNT_MUST_BE_GREATER_THAN_ZERO);
|
||||
// }
|
||||
|
||||
if (amount > order.balance) {
|
||||
throw new BadRequestException(`You cant pay more than ${order.balance}`);
|
||||
}
|
||||
// if (amount > order.balance) {
|
||||
// throw new BadRequestException(`You cant pay more than ${order.balance}`);
|
||||
// }
|
||||
|
||||
return {
|
||||
admin,
|
||||
user: order.user,
|
||||
order,
|
||||
amount,
|
||||
attachments,
|
||||
description,
|
||||
gateway,
|
||||
method
|
||||
};
|
||||
}
|
||||
// return {
|
||||
// admin,
|
||||
// user: order.user,
|
||||
// order,
|
||||
// amount,
|
||||
// attachments,
|
||||
// description,
|
||||
// gateway,
|
||||
// method
|
||||
// };
|
||||
// }
|
||||
|
||||
private async handleCashPayment(ctx: OrderPaymentContext): Promise<void> {
|
||||
const { order, amount, method, attachments, description, admin } = ctx
|
||||
const payment = this.paymentRepository.create({
|
||||
creator: admin,
|
||||
order,
|
||||
amount,
|
||||
method,
|
||||
status: PaymentStatusEnum.Pending,
|
||||
attachments,
|
||||
description
|
||||
})
|
||||
await this.em.persistAndFlush(payment)
|
||||
return
|
||||
}
|
||||
// private async handleCashPayment(ctx: OrderPaymentContext): Promise<void> {
|
||||
// const { order, amount, method, attachments, description, admin } = ctx
|
||||
// const payment = this.paymentRepository.create({
|
||||
// creator: admin,
|
||||
// order,
|
||||
// amount,
|
||||
// method,
|
||||
// status: PaymentStatusEnum.Pending,
|
||||
// attachments,
|
||||
// description
|
||||
// })
|
||||
// await this.em.persistAndFlush(payment)
|
||||
// return
|
||||
// }
|
||||
|
||||
|
||||
private async handleCreditPayment(ctx: OrderPaymentContext): Promise<void> {
|
||||
const { order, amount, user, admin } = ctx
|
||||
// private async handleCreditPayment(ctx: OrderPaymentContext): Promise<void> {
|
||||
// const { order, amount, user, admin } = ctx
|
||||
|
||||
await this.em.transactional(async em => {
|
||||
// 1. get User remained Credit
|
||||
const remainedCredit = await this.creditService.getCurrentCredit(user.id)
|
||||
// await this.em.transactional(async em => {
|
||||
// // 1. get User remained Credit
|
||||
// const remainedCredit = await this.creditService.getCurrentCredit(user.id)
|
||||
|
||||
if (remainedCredit < amount) {
|
||||
throw new BadRequestException("You Dont have enough Credit")
|
||||
}
|
||||
// if (remainedCredit < amount) {
|
||||
// throw new BadRequestException("You Dont have enough Credit")
|
||||
// }
|
||||
|
||||
const newBalance = remainedCredit - amount;
|
||||
// 2. Create payment record
|
||||
const payment = em.create(Payment, {
|
||||
creator: admin,
|
||||
amount: ctx.amount,
|
||||
method: PaymentMethodEnum.Credit,
|
||||
gateway: null,
|
||||
order,
|
||||
status: PaymentStatusEnum.Paid,
|
||||
paidAt: new Date(),
|
||||
});
|
||||
// 3. Create Credit transaction record
|
||||
const newCreditTransaction = em.create(CreditTransaction, {
|
||||
user: order.user,
|
||||
amount,
|
||||
type: CreditTransactionType.WITHDRAW,
|
||||
balance: newBalance,
|
||||
orderId: order.id,
|
||||
});
|
||||
// const newBalance = remainedCredit - amount;
|
||||
// // 2. Create payment record
|
||||
// const payment = em.create(Payment, {
|
||||
// creator: admin,
|
||||
// amount: ctx.amount,
|
||||
// method: PaymentMethodEnum.Credit,
|
||||
// gateway: null,
|
||||
// order,
|
||||
// status: PaymentStatusEnum.Paid,
|
||||
// paidAt: new Date(),
|
||||
// });
|
||||
// // 3. Create Credit transaction record
|
||||
// const newCreditTransaction = em.create(CreditTransaction, {
|
||||
// user: order.user,
|
||||
// amount,
|
||||
// type: CreditTransactionType.WITHDRAW,
|
||||
// balance: newBalance,
|
||||
// orderId: order.id,
|
||||
// });
|
||||
|
||||
await em.persistAndFlush([payment, newCreditTransaction]);
|
||||
});
|
||||
// await em.persistAndFlush([payment, newCreditTransaction]);
|
||||
// });
|
||||
|
||||
this.eventEmitter.emit(
|
||||
paymentSucceedEvent.name,
|
||||
new paymentSucceedEvent(ctx.order.id, String(ctx.order?.orderNumber) || '', ctx.method, ctx.amount),
|
||||
);
|
||||
}
|
||||
// this.eventEmitter.emit(
|
||||
// paymentSucceedEvent.name,
|
||||
// new paymentSucceedEvent(ctx.order.id, String(ctx.order?.orderNumber) || '', ctx.method, ctx.amount),
|
||||
// );
|
||||
// }
|
||||
|
||||
private async handleOnlinePayment(ctx: OrderPaymentContext): Promise<{ paymentUrl: string }> {
|
||||
const { amount, order, gateway: requestedGateway, admin } = ctx
|
||||
// private async handleOnlinePayment(ctx: OrderPaymentContext): Promise<{ paymentUrl: string }> {
|
||||
// const { amount, order, gateway: requestedGateway, admin } = ctx
|
||||
|
||||
const gateway = this.gatewayManager.get(requestedGateway!);
|
||||
// const gateway = this.gatewayManager.get(requestedGateway!);
|
||||
|
||||
const { token, paymentUrl } = await gateway.requestPayment({
|
||||
amount: ctx.amount,
|
||||
orderId: ctx.order.id,
|
||||
});
|
||||
// const { token, paymentUrl } = await gateway.requestPayment({
|
||||
// amount: ctx.amount,
|
||||
// orderId: ctx.order.id,
|
||||
// });
|
||||
|
||||
const payment = this.paymentRepository.create({
|
||||
creator: admin,
|
||||
order,
|
||||
amount,
|
||||
method: PaymentMethodEnum.Online,
|
||||
status: PaymentStatusEnum.Pending,
|
||||
gateway: requestedGateway,
|
||||
token,
|
||||
})
|
||||
// const payment = this.paymentRepository.create({
|
||||
// creator: admin,
|
||||
// order,
|
||||
// amount,
|
||||
// method: PaymentMethodEnum.Online,
|
||||
// status: PaymentStatusEnum.Pending,
|
||||
// gateway: requestedGateway,
|
||||
// token,
|
||||
// })
|
||||
|
||||
await this.em.persistAndFlush(payment);
|
||||
// await this.em.persistAndFlush(payment);
|
||||
|
||||
return {
|
||||
paymentUrl,
|
||||
};
|
||||
}
|
||||
// return {
|
||||
// paymentUrl,
|
||||
// };
|
||||
// }
|
||||
|
||||
async verifyOnlinePayment(token: string): Promise<Payment> {
|
||||
const payment = await this.em.transactional(async em => {
|
||||
const payment = await this.paymentRepository.findOne(
|
||||
{ token },
|
||||
{ populate: ['order'] },
|
||||
);
|
||||
// async verifyOnlinePayment(token: string): Promise<Payment> {
|
||||
// const payment = await this.em.transactional(async em => {
|
||||
// const payment = await this.paymentRepository.findOne(
|
||||
// { token },
|
||||
// { populate: ['order'] },
|
||||
// );
|
||||
|
||||
if (!payment) {
|
||||
throw new NotFoundException(PaymentMessage.PAYMENT_NOT_FOUND);
|
||||
}
|
||||
// if (!payment) {
|
||||
// throw new NotFoundException(PaymentMessage.PAYMENT_NOT_FOUND);
|
||||
// }
|
||||
|
||||
if (payment.status === PaymentStatusEnum.Paid) {
|
||||
return payment;
|
||||
}
|
||||
// if (payment.status === PaymentStatusEnum.Paid) {
|
||||
// return payment;
|
||||
// }
|
||||
|
||||
if (!payment.gateway) {
|
||||
throw new NotFoundException("Payment gateway not found");
|
||||
}
|
||||
// if (!payment.gateway) {
|
||||
// throw new NotFoundException("Payment gateway not found");
|
||||
// }
|
||||
|
||||
const gateway = this.gatewayManager.get(payment.gateway);
|
||||
// const gateway = this.gatewayManager.get(payment.gateway);
|
||||
|
||||
const { raw, referenceId, success } = await gateway.verifyPayment({
|
||||
amount: payment.amount,
|
||||
token: payment.token!,
|
||||
});
|
||||
// const { raw, referenceId, success } = await gateway.verifyPayment({
|
||||
// amount: payment.amount,
|
||||
// token: payment.token!,
|
||||
// });
|
||||
|
||||
payment.verifyResponse = raw;
|
||||
// payment.verifyResponse = raw;
|
||||
|
||||
if (!success) {
|
||||
payment.status = PaymentStatusEnum.Failed;
|
||||
payment.failedAt = new Date();
|
||||
return payment;
|
||||
}
|
||||
// if (!success) {
|
||||
// payment.status = PaymentStatusEnum.Failed;
|
||||
// payment.failedAt = new Date();
|
||||
// return payment;
|
||||
// }
|
||||
|
||||
payment.status = PaymentStatusEnum.Paid;
|
||||
payment.paidAt = new Date();
|
||||
payment.transactionId = referenceId;
|
||||
// TODO : use decimal js
|
||||
// does need persist or not
|
||||
// payment.order.paidAmount! += payment.amount
|
||||
// payment.order.balance! -= payment.amount
|
||||
// payment.status = PaymentStatusEnum.Paid;
|
||||
// payment.paidAt = new Date();
|
||||
// payment.transactionId = referenceId;
|
||||
// // TODO : use decimal js
|
||||
// // does need persist or not
|
||||
// // payment.order.paidAmount! += payment.amount
|
||||
// // payment.order.balance! -= payment.amount
|
||||
|
||||
await em.flush();
|
||||
return payment;
|
||||
});
|
||||
this.eventEmitter.emit(
|
||||
onlinePaymentSucceedEvent.name,
|
||||
new onlinePaymentSucceedEvent(payment.id, payment.order.id, String(payment.order?.orderNumber) || '', payment.amount),
|
||||
);
|
||||
return payment;
|
||||
}
|
||||
// await em.flush();
|
||||
// return payment;
|
||||
// });
|
||||
// this.eventEmitter.emit(
|
||||
// onlinePaymentSucceedEvent.name,
|
||||
// new onlinePaymentSucceedEvent(payment.id, payment.order.id, String(payment.order?.orderNumber) || '', payment.amount),
|
||||
// );
|
||||
// return payment;
|
||||
// }
|
||||
|
||||
findAll(dto: FindPaymentsDto) {
|
||||
return this.paymentRepository.findAllPaginated(dto)
|
||||
}
|
||||
// findAll(dto: FindPaymentsDto) {
|
||||
// return this.paymentRepository.findAllPaginated(dto)
|
||||
// }
|
||||
|
||||
findAllByUser(dto: FindPaymentsDto, userId: string) {
|
||||
return this.paymentRepository.findAllPaginated({ ...dto, userId })
|
||||
}
|
||||
// findAllByUser(dto: FindPaymentsDto, userId: string) {
|
||||
// return this.paymentRepository.findAllPaginated({ ...dto, userId })
|
||||
// }
|
||||
|
||||
async confirmCashPayment(paymentId: string): Promise<Payment> {
|
||||
const payment = await this.em.transactional(async em => {
|
||||
const payment = await em.findOne(Payment, paymentId, { populate: ['order'] });
|
||||
if (!payment) {
|
||||
throw new NotFoundException(PaymentMessage.PAYMENT_NOT_FOUND);
|
||||
}
|
||||
if (payment.method !== PaymentMethodEnum.Cash) {
|
||||
throw new BadRequestException(PaymentMessage.PAYMENT_METHOD_NOT_CASH);
|
||||
}
|
||||
if (payment.status === PaymentStatusEnum.Paid) {
|
||||
throw new BadRequestException(PaymentMessage.PAYMENT_ALREADY_PAID);
|
||||
}
|
||||
// async confirmCashPayment(paymentId: string): Promise<Payment> {
|
||||
// const payment = await this.em.transactional(async em => {
|
||||
// const payment = await em.findOne(Payment, paymentId, { populate: ['order'] });
|
||||
// if (!payment) {
|
||||
// throw new NotFoundException(PaymentMessage.PAYMENT_NOT_FOUND);
|
||||
// }
|
||||
// if (payment.method !== PaymentMethodEnum.Cash) {
|
||||
// throw new BadRequestException(PaymentMessage.PAYMENT_METHOD_NOT_CASH);
|
||||
// }
|
||||
// if (payment.status === PaymentStatusEnum.Paid) {
|
||||
// throw new BadRequestException(PaymentMessage.PAYMENT_ALREADY_PAID);
|
||||
// }
|
||||
|
||||
payment.status = PaymentStatusEnum.Paid;
|
||||
payment.paidAt = new Date();
|
||||
payment.failedAt = null;
|
||||
// payment.status = PaymentStatusEnum.Paid;
|
||||
// payment.paidAt = new Date();
|
||||
// payment.failedAt = null;
|
||||
|
||||
// TODO : use decimal js
|
||||
// payment.order.paidAmount! += payment.amount
|
||||
// payment.order.balance! -= payment.amount
|
||||
// // TODO : use decimal js
|
||||
// // payment.order.paidAmount! += payment.amount
|
||||
// // payment.order.balance! -= payment.amount
|
||||
|
||||
em.persist(payment);
|
||||
em.persist(payment.order);
|
||||
await em.flush();
|
||||
return payment;
|
||||
});
|
||||
return payment;
|
||||
}
|
||||
// em.persist(payment);
|
||||
// em.persist(payment.order);
|
||||
// await em.flush();
|
||||
// return payment;
|
||||
// });
|
||||
// return payment;
|
||||
// }
|
||||
|
||||
async denyCashPayment(paymentId: string): Promise<Payment> {
|
||||
const payment = await this.em.transactional(async em => {
|
||||
// async denyCashPayment(paymentId: string): Promise<Payment> {
|
||||
// const payment = await this.em.transactional(async em => {
|
||||
|
||||
const payment = await em.findOne(Payment, paymentId, { populate: ['order'] });
|
||||
// const payment = await em.findOne(Payment, paymentId, { populate: ['order'] });
|
||||
|
||||
if (!payment) {
|
||||
throw new NotFoundException(PaymentMessage.PAYMENT_NOT_FOUND);
|
||||
}
|
||||
// if (!payment) {
|
||||
// throw new NotFoundException(PaymentMessage.PAYMENT_NOT_FOUND);
|
||||
// }
|
||||
|
||||
if (payment.method !== PaymentMethodEnum.Cash) {
|
||||
throw new BadRequestException(PaymentMessage.PAYMENT_METHOD_NOT_CASH);
|
||||
}
|
||||
// if (payment.method !== PaymentMethodEnum.Cash) {
|
||||
// throw new BadRequestException(PaymentMessage.PAYMENT_METHOD_NOT_CASH);
|
||||
// }
|
||||
|
||||
if (payment.status == PaymentStatusEnum.Paid) {
|
||||
// payment.order.paidAmount! -= payment.amount
|
||||
// payment.order.balance! += payment.amount
|
||||
// if (payment.status == PaymentStatusEnum.Paid) {
|
||||
// // payment.order.paidAmount! -= payment.amount
|
||||
// // payment.order.balance! += payment.amount
|
||||
|
||||
em.persistAndFlush(payment.order)
|
||||
}
|
||||
// em.persistAndFlush(payment.order)
|
||||
// }
|
||||
|
||||
payment.status = PaymentStatusEnum.Failed;
|
||||
payment.failedAt = new Date();
|
||||
payment.paidAt = null
|
||||
// payment.status = PaymentStatusEnum.Failed;
|
||||
// payment.failedAt = new Date();
|
||||
// payment.paidAt = null
|
||||
|
||||
em.persistAndFlush(payment);
|
||||
// em.persistAndFlush(payment);
|
||||
|
||||
return payment;
|
||||
});
|
||||
return payment;
|
||||
}
|
||||
// return payment;
|
||||
// });
|
||||
// return payment;
|
||||
// }
|
||||
|
||||
}
|
||||
// }
|
||||
|
||||
Reference in New Issue
Block a user