chore: complete payment system
This commit is contained in:
@@ -1,5 +1,3 @@
|
||||
import { randomUUID } from "node:crypto";
|
||||
|
||||
import { InjectQueue } from "@nestjs/bullmq";
|
||||
import { BadRequestException, HttpException, Injectable, InternalServerErrorException } from "@nestjs/common";
|
||||
import { Queue } from "bullmq";
|
||||
@@ -7,25 +5,31 @@ import { Queue } from "bullmq";
|
||||
import Decimal from "decimal.js";
|
||||
import { DataSource, Not, QueryRunner } from "typeorm";
|
||||
|
||||
import { PaginationDto } from "../../../common/DTO/pagination.dto";
|
||||
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 { RoleEnum } from "../../users/enums/role.enum";
|
||||
import { PaginationUtils } from "../../utils/providers/pagination.utils";
|
||||
import { Wallet } from "../../wallets/entities/wallet.entity";
|
||||
import { WalletsService } from "../../wallets/providers/wallets.service";
|
||||
import { PAYMENT } from "../constants";
|
||||
import { CreateBankAccountDto } from "../DTO/create-bankaccount.dto";
|
||||
import { GatewayDepositDto, TransferDepositDto } from "../DTO/deposit-wallet.dto";
|
||||
import { PaymentTransactionQueryDto } from "../DTO/payment-transaction-query.dto";
|
||||
import { SearchTransactionQueryDto } from "../DTO/search-transaction-query.dto";
|
||||
import { UpdateBankAccountDto } from "../DTO/update-bankaccount.dto";
|
||||
import { RejectDepositRequestDto } from "../DTO/update-deposit-request-status.dto";
|
||||
import { VerifyQueryDto } from "../DTO/verify-payment.dto";
|
||||
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 { DepositRequestStatus } from "../enums/deposit-request-status.enum";
|
||||
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 { DepositRequestsRepository } from "../repositories/deposit-requests.repository";
|
||||
import { PaymentGatewaysRepository } from "../repositories/payment-gateway.repository";
|
||||
import { PaymentsRepository } from "../repositories/payments.repository";
|
||||
import { GatewayType } from "../types/gateway.type";
|
||||
@@ -39,6 +43,7 @@ export class PaymentsService {
|
||||
private readonly paymentsRepository: PaymentsRepository,
|
||||
private readonly bankAccountsRepository: BankAccountsRepository,
|
||||
private readonly walletsService: WalletsService,
|
||||
private readonly depositRequestsRepository: DepositRequestsRepository,
|
||||
private dataSource: DataSource,
|
||||
) {}
|
||||
|
||||
@@ -75,7 +80,6 @@ export class PaymentsService {
|
||||
}
|
||||
|
||||
//*********************************** */
|
||||
//TODO:complete this
|
||||
async chargeWalletWithTransfer(depositDto: TransferDepositDto, userId: string) {
|
||||
const queryRunner = this.dataSource.createQueryRunner();
|
||||
await queryRunner.connect();
|
||||
@@ -87,16 +91,17 @@ export class PaymentsService {
|
||||
|
||||
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;
|
||||
|
||||
// if (method === TransferType.GATEWAY) throw new BadRequestException(PaymentMessage.TRANSFER_METHOD_NOT_ALLOWED);
|
||||
|
||||
//
|
||||
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 });
|
||||
const depositRequest = queryRunner.manager.create(DepositRequest, {
|
||||
bankAccount,
|
||||
user,
|
||||
receiptUrl: transferReceiptUrl,
|
||||
amount,
|
||||
type: method,
|
||||
});
|
||||
|
||||
await queryRunner.manager.save(DepositRequest, depositRequest);
|
||||
|
||||
@@ -134,6 +139,7 @@ export class PaymentsService {
|
||||
relations: ["user"],
|
||||
// lock: { mode: "pessimistic_write" },
|
||||
});
|
||||
|
||||
if (!payment) throw new BadRequestException(PaymentMessage.PAYMENT_NOT_FOUND_WITH_REF);
|
||||
if (payment.status === PaymentStatus.COMPLETED) throw new BadRequestException(PaymentMessage.VALIDATED_BEFORE);
|
||||
|
||||
@@ -184,12 +190,12 @@ export class PaymentsService {
|
||||
async createGatewayPaymentForUser(user: User, amount: number, reference: string, gatewayId: string, queryRunner: QueryRunner) {
|
||||
//
|
||||
const payment = queryRunner.manager.create(Payment, {
|
||||
type: PaymentType.GATEWAY,
|
||||
amount,
|
||||
reference,
|
||||
user,
|
||||
paymentGateway: { id: gatewayId },
|
||||
});
|
||||
//
|
||||
await queryRunner.manager.save(Payment, payment);
|
||||
this.paymentQueue.add(PAYMENT.PAYMENT_START, { payment }, { delay: 1000 });
|
||||
|
||||
@@ -287,4 +293,132 @@ export class PaymentsService {
|
||||
async getTransactions(queryDto: SearchTransactionQueryDto) {
|
||||
return this.walletsService.getTransactionsForAdmin(queryDto);
|
||||
}
|
||||
|
||||
//*********************************** */
|
||||
async getDepositRequests(queryDto: PaymentTransactionQueryDto) {
|
||||
const { limit, skip } = PaginationUtils(queryDto);
|
||||
const { type } = queryDto;
|
||||
|
||||
const queryBuilder = this.depositRequestsRepository
|
||||
.createQueryBuilder("depositRequest")
|
||||
.leftJoinAndSelect("depositRequest.user", "user")
|
||||
.leftJoinAndSelect("depositRequest.bankAccount", "bankAccount")
|
||||
.orderBy("depositRequest.createdAt", "DESC")
|
||||
.skip(skip)
|
||||
.take(limit);
|
||||
|
||||
if (type) {
|
||||
queryBuilder.andWhere("depositRequest.type = :type", { type });
|
||||
}
|
||||
|
||||
const [depositRequests, count] = await queryBuilder.getManyAndCount();
|
||||
|
||||
return {
|
||||
depositRequests,
|
||||
count,
|
||||
paginate: true,
|
||||
};
|
||||
}
|
||||
//*********************************** */
|
||||
|
||||
async getDepositGatewayPayment(queryDto: PaginationDto) {
|
||||
const { limit, skip } = PaginationUtils(queryDto);
|
||||
|
||||
const queryBuilder = this.paymentsRepository
|
||||
.createQueryBuilder("payment")
|
||||
.leftJoinAndSelect("payment.user", "user")
|
||||
.leftJoinAndSelect("payment.paymentGateway", "paymentGateway")
|
||||
.orderBy("payment.createdAt", "DESC")
|
||||
.skip(skip)
|
||||
.take(limit);
|
||||
|
||||
const [payments, count] = await queryBuilder.getManyAndCount();
|
||||
|
||||
return {
|
||||
payments,
|
||||
count,
|
||||
paginate: true,
|
||||
};
|
||||
}
|
||||
|
||||
//*********************************** */
|
||||
|
||||
async getDepositRequestById(depositId: string) {
|
||||
const depositRequest = await this.depositRequestsRepository.findOneBy({ id: depositId });
|
||||
if (!depositRequest) throw new BadRequestException(PaymentMessage.DEPOSIT_NOT_FOUND);
|
||||
|
||||
return {
|
||||
depositRequest,
|
||||
};
|
||||
}
|
||||
|
||||
//*********************************** */
|
||||
async approverDepositRequest(depositId: string) {
|
||||
const queryRunner = this.dataSource.createQueryRunner();
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
try {
|
||||
const depositRequest = await queryRunner.manager.findOne(DepositRequest, {
|
||||
where: { id: depositId },
|
||||
lock: { mode: "pessimistic_write" },
|
||||
});
|
||||
if (!depositRequest) throw new BadRequestException(PaymentMessage.DEPOSIT_NOT_FOUND);
|
||||
|
||||
if (depositRequest.status === DepositRequestStatus.APPROVED) throw new BadRequestException(PaymentMessage.DEPOSIT_ALREADY_APPROVED);
|
||||
|
||||
const wallet = await queryRunner.manager.findOneBy(Wallet, { user: depositRequest.user });
|
||||
if (!wallet) throw new BadRequestException(WalletMessage.WALLET_NOT_FOUND);
|
||||
|
||||
await queryRunner.manager.update(Wallet, { id: wallet.id }, { balance: new Decimal(wallet.balance).add(depositRequest.amount) });
|
||||
|
||||
await queryRunner.manager.update(DepositRequest, { id: depositRequest.id }, { status: DepositRequestStatus.APPROVED });
|
||||
|
||||
await this.walletsService.createDepositTransaction(depositRequest.amount, wallet.id, queryRunner);
|
||||
|
||||
await queryRunner.commitTransaction();
|
||||
return {
|
||||
message: PaymentMessage.DEPOSIT_APPROVED,
|
||||
depositRequest,
|
||||
};
|
||||
} catch (error) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
throw error;
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
}
|
||||
}
|
||||
|
||||
//*********************************** */
|
||||
async rejectDepositRequest(depositId: string, rejectDto: RejectDepositRequestDto) {
|
||||
const queryRunner = this.dataSource.createQueryRunner();
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
try {
|
||||
const depositRequest = await queryRunner.manager.findOne(DepositRequest, {
|
||||
where: { id: depositId },
|
||||
lock: { mode: "pessimistic_write" },
|
||||
});
|
||||
if (!depositRequest) throw new BadRequestException(PaymentMessage.DEPOSIT_NOT_FOUND);
|
||||
|
||||
if (depositRequest.status === DepositRequestStatus.REJECTED) throw new BadRequestException(PaymentMessage.DEPOSIT_ALREADY_REJECTED);
|
||||
|
||||
await queryRunner.manager.update(
|
||||
DepositRequest,
|
||||
{ id: depositRequest.id },
|
||||
{ status: DepositRequestStatus.REJECTED, comment: rejectDto.comment },
|
||||
);
|
||||
|
||||
await queryRunner.commitTransaction();
|
||||
|
||||
return {
|
||||
message: PaymentMessage.DEPOSIT_REJECTED,
|
||||
depositRequest,
|
||||
};
|
||||
} catch (error) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
throw error;
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user