chore: complete the payment service
This commit is contained in:
@@ -1,27 +1,138 @@
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { QueryRunner } from "typeorm";
|
||||
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 { WalletMessage } from "../../../common/enums/message.enum";
|
||||
import { PaymentMessage, WalletMessage } from "../../../common/enums/message.enum";
|
||||
import { User } from "../../users/entities/user.entity";
|
||||
import { Wallet } from "../../wallets/entities/wallet.entity";
|
||||
import { WalletsService } from "../../wallets/providers/wallets.service";
|
||||
import { PAYMENT } from "../constants";
|
||||
import { GatewayDepositDto } from "../DTO/deposit-wallet.dto";
|
||||
import { VerifyQueryDto } from "../DTO/verify-payment.dto";
|
||||
import { PaymentMethod } from "../entities/payment-method.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 { PaymentsRepository } from "../repositories/payments.repository";
|
||||
import { PaymentsRepository } from "../repositories/payments.repository";
|
||||
import { GatewayType } from "../types/gateway.type";
|
||||
|
||||
@Injectable()
|
||||
export class PaymentsService {
|
||||
constructor(
|
||||
@InjectQueue(PAYMENT.PAYMENT_QUEUE_NAME) private readonly paymentQueue: Queue,
|
||||
private readonly gatewayFactory: PaymentGatewayFactory,
|
||||
// private readonly paymentsRepository: PaymentsRepository,
|
||||
private readonly paymentsRepository: PaymentsRepository,
|
||||
private readonly walletsService: WalletsService,
|
||||
private dataSource: DataSource,
|
||||
) {}
|
||||
|
||||
//*********************************** */
|
||||
async chargeWalletWithGateway(chargeDto: GatewayDepositDto, userId: string) {
|
||||
const queryRunner = this.dataSource.createQueryRunner();
|
||||
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 gatewayData = await this.processPayment(
|
||||
gateway,
|
||||
amount,
|
||||
WalletMessage.DEPOSIT_WALLET_IPG,
|
||||
wallet.user.email,
|
||||
wallet.user.phone,
|
||||
);
|
||||
|
||||
const payment = await this.createPaymentForUser(wallet.user, amount, gatewayData.reference, gateway, queryRunner);
|
||||
|
||||
await queryRunner.commitTransaction();
|
||||
return {
|
||||
payment,
|
||||
...gatewayData,
|
||||
};
|
||||
} catch (error) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
if (error instanceof HttpException) throw error;
|
||||
throw new InternalServerErrorException(WalletMessage.ERROR_IN_CHARGE_WALLET);
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
}
|
||||
}
|
||||
//*********************************** */
|
||||
//*********************************** */
|
||||
async processPayment(provider: GatewayType, amount: number, description: string, email?: string, mobile?: string) {
|
||||
const paymentGateway = this.gatewayFactory.getPaymentGateway(provider);
|
||||
return paymentGateway.processPayment({ amount, description, email, mobile });
|
||||
}
|
||||
|
||||
//*********************************** */
|
||||
//*********************************** */
|
||||
async verifyPayment(gateway: GatewayType, queryDto: VerifyQueryDto) {
|
||||
const queryRunner = this.dataSource.createQueryRunner();
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
|
||||
try {
|
||||
const paymentGateway = this.gatewayFactory.getPaymentGateway(gateway);
|
||||
const payment = await queryRunner.manager.findOne(Payment, {
|
||||
where: { reference: queryDto.Authority },
|
||||
relations: ["user"],
|
||||
// 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 (queryDto.Status !== "OK") {
|
||||
await this.handleFailedPayment(payment.id, queryRunner);
|
||||
await queryRunner.commitTransaction();
|
||||
return { message: "nok" };
|
||||
}
|
||||
const verifyData = await paymentGateway.verifyPayment({ reference: queryDto.Authority, amount: payment.amount });
|
||||
//
|
||||
if (verifyData.code === 100) {
|
||||
const transaction = await this.handleSuccessfulPayment(
|
||||
payment.id,
|
||||
payment.user.id,
|
||||
payment.amount,
|
||||
`${verifyData.ref_id}`,
|
||||
queryRunner,
|
||||
);
|
||||
await queryRunner.commitTransaction();
|
||||
return {
|
||||
message: "success",
|
||||
transaction,
|
||||
};
|
||||
}
|
||||
|
||||
if (verifyData.code === 101) {
|
||||
await queryRunner.commitTransaction();
|
||||
|
||||
return {
|
||||
message: "not valid payment",
|
||||
};
|
||||
}
|
||||
|
||||
await this.handleFailedPayment(payment.id, queryRunner);
|
||||
await queryRunner.commitTransaction();
|
||||
|
||||
return { message: "nok" };
|
||||
} catch (error) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
throw error;
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
}
|
||||
}
|
||||
|
||||
//*********************************** */
|
||||
//*********************************** */
|
||||
async createPaymentForUser(user: User, amount: number, reference: string, gateway: GatewayType, queryRunner: QueryRunner) {
|
||||
@@ -39,8 +150,9 @@ export class PaymentsService {
|
||||
user,
|
||||
paymentMethod,
|
||||
});
|
||||
|
||||
await queryRunner.manager.save(Payment, payment);
|
||||
this.paymentQueue.add(PAYMENT.PAYMENT_START, { payment }, { delay: 1000 });
|
||||
|
||||
return payment;
|
||||
}
|
||||
//*********************************** */
|
||||
@@ -48,4 +160,25 @@ export class PaymentsService {
|
||||
async getAvailableGateways() {
|
||||
return this.gatewayFactory.getAvailablePaymentGateways();
|
||||
}
|
||||
//*********************************** */
|
||||
//*********************************** */
|
||||
async getPaymentWithReference(reference: string) {
|
||||
const payment = await this.paymentsRepository.findOneWithReference(reference);
|
||||
if (!payment) throw new BadRequestException(PaymentMessage.PAYMENT_NOT_FOUND_WITH_REF);
|
||||
|
||||
return { payment };
|
||||
}
|
||||
|
||||
//*********************************** */
|
||||
//*********************************** */
|
||||
async handleFailedPayment(paymentId: string, queryRunner: QueryRunner) {
|
||||
await queryRunner.manager.update(Payment, { id: paymentId }, { status: PaymentStatus.CANCELLED });
|
||||
}
|
||||
|
||||
//*********************************** */
|
||||
async handleSuccessfulPayment(paymentId: string, userId: string, amount: Decimal, transactionId: string, queryRunner: QueryRunner) {
|
||||
await queryRunner.manager.update(Payment, { id: paymentId }, { status: PaymentStatus.COMPLETED, transactionId });
|
||||
const transaction = await this.walletsService.createPaymentTransaction(userId, amount, queryRunner);
|
||||
return transaction;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user