chore: normalize the payment and payment transaction
This commit is contained in:
@@ -1,22 +1,32 @@
|
||||
import { randomUUID } from "node:crypto";
|
||||
|
||||
import { InjectQueue } from "@nestjs/bullmq";
|
||||
import { BadRequestException, HttpException, Injectable, InternalServerErrorException } from "@nestjs/common";
|
||||
import { Queue } from "bullmq";
|
||||
// eslint-disable-next-line import/no-named-as-default
|
||||
import Decimal from "decimal.js";
|
||||
import { DataSource, QueryRunner } from "typeorm";
|
||||
import { DataSource, Not, QueryRunner } from "typeorm";
|
||||
|
||||
import { PaymentMessage, WalletMessage } from "../../../common/enums/message.enum";
|
||||
import { CommonMessage, PaymentMessage, UserMessage, WalletMessage } from "../../../common/enums/message.enum";
|
||||
import { Role } from "../../users/entities/role.entity";
|
||||
import { User } from "../../users/entities/user.entity";
|
||||
import { Wallet } from "../../wallets/entities/wallet.entity";
|
||||
import { RoleEnum } from "../../users/enums/role.enum";
|
||||
import { WalletsService } from "../../wallets/providers/wallets.service";
|
||||
import { PAYMENT } from "../constants";
|
||||
import { GatewayDepositDto } from "../DTO/deposit-wallet.dto";
|
||||
import { CreateBankAccountDto } from "../DTO/create-bankaccount.dto";
|
||||
import { GatewayDepositDto, TransferDepositDto } from "../DTO/deposit-wallet.dto";
|
||||
import { SearchTransactionQueryDto } from "../DTO/search-transaction-query.dto";
|
||||
import { UpdateBankAccountDto } from "../DTO/update-bankaccount.dto";
|
||||
import { VerifyQueryDto } from "../DTO/verify-payment.dto";
|
||||
import { PaymentMethod } from "../entities/payment-method.entity";
|
||||
import { BankAccount } from "../entities/bank-account.entity";
|
||||
import { DepositRequest } from "../entities/deposit-request.entity";
|
||||
import { PaymentGateway } from "../entities/payment-gateway.entity";
|
||||
import { Payment } from "../entities/payment.entity";
|
||||
import { PaymentStatus } from "../enums/payment-status.enum";
|
||||
import { PaymentType } from "../enums/payment-type.enum";
|
||||
import { PaymentGatewayFactory } from "../factories/payment.factory";
|
||||
import { BankAccountsRepository } from "../repositories/bank-accounts.repository";
|
||||
import { PaymentGatewaysRepository } from "../repositories/payment-gateway.repository";
|
||||
import { PaymentsRepository } from "../repositories/payments.repository";
|
||||
import { GatewayType } from "../types/gateway.type";
|
||||
|
||||
@@ -25,7 +35,9 @@ export class PaymentsService {
|
||||
constructor(
|
||||
@InjectQueue(PAYMENT.PAYMENT_QUEUE_NAME) private readonly paymentQueue: Queue,
|
||||
private readonly gatewayFactory: PaymentGatewayFactory,
|
||||
private readonly paymentGatewaysRepository: PaymentGatewaysRepository,
|
||||
private readonly paymentsRepository: PaymentsRepository,
|
||||
private readonly bankAccountsRepository: BankAccountsRepository,
|
||||
private readonly walletsService: WalletsService,
|
||||
private dataSource: DataSource,
|
||||
) {}
|
||||
@@ -36,19 +48,17 @@ export class PaymentsService {
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
try {
|
||||
const { amount, gateway } = chargeDto;
|
||||
const wallet = await queryRunner.manager.findOne(Wallet, { where: { user: { id: userId } }, relations: ["user"] });
|
||||
if (!wallet) throw new BadRequestException(WalletMessage.WALLET_NOT_FOUND);
|
||||
const { amount, gatewayId } = chargeDto;
|
||||
|
||||
const gatewayData = await this.processPayment(
|
||||
gateway,
|
||||
amount,
|
||||
WalletMessage.DEPOSIT_WALLET_IPG,
|
||||
wallet.user.email,
|
||||
wallet.user.phone,
|
||||
);
|
||||
const user = await queryRunner.manager.findOneBy(User, { id: userId });
|
||||
if (!user) throw new BadRequestException(UserMessage.USER_NOT_FOUND);
|
||||
|
||||
const payment = await this.createPaymentForUser(wallet.user, amount, gatewayData.reference, gateway, queryRunner);
|
||||
const paymentGateway = await queryRunner.manager.findOneBy(PaymentGateway, { id: gatewayId });
|
||||
if (!paymentGateway) throw new BadRequestException(PaymentMessage.PAYMENT_GATEWAY_NOT_FOUND);
|
||||
|
||||
const gatewayData = await this.processPayment(paymentGateway.name, amount, WalletMessage.DEPOSIT_WALLET_IPG, user.email, user.phone);
|
||||
|
||||
const payment = await this.createGatewayPaymentForUser(user, amount, gatewayData.reference, paymentGateway.id, queryRunner);
|
||||
|
||||
await queryRunner.commitTransaction();
|
||||
return {
|
||||
@@ -63,6 +73,46 @@ export class PaymentsService {
|
||||
await queryRunner.release();
|
||||
}
|
||||
}
|
||||
|
||||
//*********************************** */
|
||||
//TODO:complete this
|
||||
async chargeWalletWithTransfer(depositDto: TransferDepositDto, userId: string) {
|
||||
const queryRunner = this.dataSource.createQueryRunner();
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
const { amount, bankAccountId, method, transferReceiptUrl } = depositDto;
|
||||
try {
|
||||
const bankAccount = await queryRunner.manager.findOneBy(BankAccount, { id: bankAccountId });
|
||||
if (!bankAccount) throw new BadRequestException(PaymentMessage.BANK_ACCOUNT_NOT_FOUND);
|
||||
|
||||
const user = await queryRunner.manager.findOneBy(User, { id: userId });
|
||||
if (!user) throw new BadRequestException(UserMessage.USER_NOT_FOUND);
|
||||
//
|
||||
const reference = randomUUID();
|
||||
const paymentType = method as unknown as PaymentType;
|
||||
|
||||
//
|
||||
const payment = queryRunner.manager.create(Payment, { amount, reference, user, type: paymentType, bankAccount });
|
||||
|
||||
await queryRunner.manager.save(Payment, payment);
|
||||
//
|
||||
const depositRequest = queryRunner.manager.create(DepositRequest, { bankAccount, user, receiptUrl: transferReceiptUrl });
|
||||
|
||||
await queryRunner.manager.save(DepositRequest, depositRequest);
|
||||
|
||||
await queryRunner.commitTransaction();
|
||||
return {
|
||||
message: PaymentMessage.DEPOSIT_CREATED,
|
||||
depositId: depositRequest.id,
|
||||
};
|
||||
} catch (error) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
|
||||
throw error;
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
}
|
||||
}
|
||||
//*********************************** */
|
||||
//*********************************** */
|
||||
async processPayment(provider: GatewayType, amount: number, description: string, email?: string, mobile?: string) {
|
||||
@@ -85,11 +135,7 @@ export class PaymentsService {
|
||||
// lock: { mode: "pessimistic_write" },
|
||||
});
|
||||
if (!payment) throw new BadRequestException(PaymentMessage.PAYMENT_NOT_FOUND_WITH_REF);
|
||||
if (payment.status === PaymentStatus.COMPLETED) {
|
||||
return {
|
||||
message: "completed before",
|
||||
};
|
||||
}
|
||||
if (payment.status === PaymentStatus.COMPLETED) throw new BadRequestException(PaymentMessage.VALIDATED_BEFORE);
|
||||
|
||||
if (queryDto.Status !== "OK") {
|
||||
await this.handleFailedPayment(payment.id, queryRunner);
|
||||
@@ -135,20 +181,14 @@ export class PaymentsService {
|
||||
|
||||
//*********************************** */
|
||||
//*********************************** */
|
||||
async createPaymentForUser(user: User, amount: number, reference: string, gateway: GatewayType, queryRunner: QueryRunner) {
|
||||
const paymentMethod = queryRunner.manager.create(PaymentMethod, {
|
||||
gatewayName: gateway,
|
||||
description: WalletMessage.DEPOSIT_WALLET_IPG,
|
||||
});
|
||||
|
||||
await queryRunner.manager.save(PaymentMethod, paymentMethod);
|
||||
|
||||
async createGatewayPaymentForUser(user: User, amount: number, reference: string, gatewayId: string, queryRunner: QueryRunner) {
|
||||
//
|
||||
const payment = queryRunner.manager.create(Payment, {
|
||||
type: PaymentType.GATEWAY,
|
||||
amount,
|
||||
reference,
|
||||
user,
|
||||
paymentMethod,
|
||||
paymentGateway: { id: gatewayId },
|
||||
});
|
||||
await queryRunner.manager.save(Payment, payment);
|
||||
this.paymentQueue.add(PAYMENT.PAYMENT_START, { payment }, { delay: 1000 });
|
||||
@@ -158,7 +198,8 @@ export class PaymentsService {
|
||||
//*********************************** */
|
||||
//*********************************** */
|
||||
async getAvailableGateways() {
|
||||
return this.gatewayFactory.getAvailablePaymentGateways();
|
||||
const paymentGateways = await this.paymentGatewaysRepository.find({ where: { isActive: true } });
|
||||
return { paymentGateways };
|
||||
}
|
||||
//*********************************** */
|
||||
//*********************************** */
|
||||
@@ -181,4 +222,69 @@ export class PaymentsService {
|
||||
const transaction = await this.walletsService.createPaymentTransaction(userId, amount, queryRunner);
|
||||
return transaction;
|
||||
}
|
||||
|
||||
//*********************************** */
|
||||
async addBankAccount(createDto: CreateBankAccountDto) {
|
||||
const existCardNumber = await this.bankAccountsRepository.findOneBy({ cardNumber: createDto.cardNumber });
|
||||
if (existCardNumber) throw new BadRequestException(PaymentMessage.CARD_NUMBER_EXIST);
|
||||
//
|
||||
const existIBan = await this.bankAccountsRepository.findOneBy({ IBan: createDto.IBan });
|
||||
if (existIBan) throw new BadRequestException(PaymentMessage.IBAN_EXIST);
|
||||
//
|
||||
const bankAccount = this.bankAccountsRepository.create({ ...createDto });
|
||||
await this.bankAccountsRepository.save(bankAccount);
|
||||
|
||||
return {
|
||||
message: CommonMessage.CREATED,
|
||||
bankAccount,
|
||||
};
|
||||
}
|
||||
//*********************************** */
|
||||
async updateBankAccount(updateDto: UpdateBankAccountDto, bankAccountId: string) {
|
||||
const bankAccount = await this.bankAccountsRepository.findOneBy({ id: bankAccountId });
|
||||
if (!bankAccount) throw new BadRequestException(PaymentMessage.BANK_ACCOUNT_NOT_FOUND);
|
||||
|
||||
if (updateDto.cardNumber) {
|
||||
const existCardNumber = await this.bankAccountsRepository.findOneBy({ cardNumber: updateDto.cardNumber, id: Not(bankAccountId) });
|
||||
if (existCardNumber) throw new BadRequestException(PaymentMessage.CARD_NUMBER_EXIST);
|
||||
}
|
||||
//
|
||||
if (updateDto.IBan) {
|
||||
const existIBan = await this.bankAccountsRepository.findOneBy({ IBan: updateDto.IBan, id: Not(bankAccountId) });
|
||||
if (existIBan) throw new BadRequestException(PaymentMessage.IBAN_EXIST);
|
||||
}
|
||||
|
||||
await this.bankAccountsRepository.save({ ...bankAccount, ...updateDto });
|
||||
}
|
||||
//*********************************** */
|
||||
async getBankAccounts(role: Role) {
|
||||
const userRole = role as unknown as RoleEnum;
|
||||
let bankAccounts: BankAccount[];
|
||||
|
||||
if (userRole === RoleEnum.USER) {
|
||||
bankAccounts = await this.bankAccountsRepository.find({ where: { isActive: true } });
|
||||
} else {
|
||||
bankAccounts = await this.bankAccountsRepository.find();
|
||||
}
|
||||
|
||||
return {
|
||||
bankAccounts,
|
||||
};
|
||||
}
|
||||
|
||||
//*********************************** */
|
||||
async getBankAccountById(bankAccountId: string) {
|
||||
const bankAccount = await this.bankAccountsRepository.findOneBy({ id: bankAccountId });
|
||||
if (!bankAccount) throw new BadRequestException(PaymentMessage.BANK_ACCOUNT_NOT_FOUND);
|
||||
|
||||
return {
|
||||
bankAccount,
|
||||
};
|
||||
}
|
||||
|
||||
// async deleteBankAccount()
|
||||
//*********************************** */
|
||||
async getTransactions(queryDto: SearchTransactionQueryDto) {
|
||||
return this.walletsService.getTransactionsForAdmin(queryDto);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user