chore: add new route for get single transaction for user

This commit is contained in:
mahyargdz
2025-03-03 15:28:57 +03:30
parent 1b722d0e79
commit e234f98998
8 changed files with 71 additions and 19 deletions
+1
View File
@@ -268,6 +268,7 @@ export const enum WalletMessage {
INSUFFICIENT_BALANCE = "موجودی کیف پول کافی نیست",
SUBSCRIPTION_WALLET_TRANSFER = "پرداخت اشتراک از طریق کیف پول",
INVOICE_WALLET_TRANSFER = "پرداخت صورت حساب از طریق کیف پول",
TRANSACTION_NOT_FOUND = "تراکنش پیدا نشد",
}
export const enum PaymentMessage {
@@ -1,10 +1,11 @@
import Decimal from "decimal.js";
import { Column, Entity, ManyToOne } from "typeorm";
import { Column, Entity, ManyToOne, OneToMany } from "typeorm";
import { BankAccount } from "./bank-account.entity";
import { BaseEntity } from "../../../common/entities/base.entity";
import { DecimalTransformer } from "../../../common/transformers/decimal.transformer";
import { User } from "../../users/entities/user.entity";
import { WalletTransaction } from "../../wallets/entities/transaction.entity";
import { DepositRequestStatus } from "../enums/deposit-request-status.enum";
import { TransferType } from "../enums/payment-type.enum";
@@ -30,4 +31,7 @@ export class DepositRequest extends BaseEntity {
@Column({ type: "enum", enum: DepositRequestStatus, default: DepositRequestStatus.PENDING })
status: DepositRequestStatus;
@OneToMany(() => WalletTransaction, (transaction) => transaction.depositRequest)
transactions: WalletTransaction[];
}
@@ -5,7 +5,7 @@ import { GatewayEnum } from "../enums/gateway.enum";
@Entity()
export class PaymentGateway extends BaseEntity {
@Column({ type: "enum", enum: GatewayEnum, nullable: false })
@Column({ type: "enum", enum: GatewayEnum, nullable: false, unique: true })
name: GatewayEnum;
@Column({ type: "varchar", length: 150, nullable: false })
@@ -1,11 +1,12 @@
// eslint-disable-next-line import/no-named-as-default
import Decimal from "decimal.js";
import { Column, Entity, ManyToOne } from "typeorm";
import { Column, Entity, ManyToOne, OneToMany } from "typeorm";
import { PaymentGateway } from "./payment-gateway.entity";
import { BaseEntity } from "../../../common/entities/base.entity";
import { DecimalTransformer } from "../../../common/transformers/decimal.transformer";
import { User } from "../../users/entities/user.entity";
import { WalletTransaction } from "../../wallets/entities/transaction.entity";
import { PaymentStatus } from "../enums/payment-status.enum";
@Entity()
@@ -27,4 +28,7 @@ export class Payment extends BaseEntity {
@ManyToOne(() => PaymentGateway, { nullable: false, onDelete: "RESTRICT" })
paymentGateway: PaymentGateway;
@OneToMany(() => WalletTransaction, (transaction) => transaction.payment)
transactions: WalletTransaction[];
}
@@ -139,10 +139,17 @@ export class PaymentsService {
try {
const paymentGateway = this.gatewayFactory.getPaymentGateway(gateway);
const payment = await queryRunner.manager.findOne(Payment, {
where: { reference: queryDto.Authority },
relations: ["user"],
});
// const payment = await queryRunner.manager.findOne(Payment, {
// where: { reference: queryDto.Authority },
// relations: ["user"],
// });
const payment = await queryRunner.manager
.createQueryBuilder(Payment, "payment")
.innerJoinAndSelect("payment.user", "user")
.where("payment.reference = :reference", { reference: queryDto.Authority })
.setLock("pessimistic_write")
.getOne();
if (!payment) throw new BadRequestException(PaymentMessage.PAYMENT_NOT_FOUND_WITH_REF);
if (payment.status === PaymentStatus.COMPLETED) throw new BadRequestException(PaymentMessage.VALIDATED_BEFORE);
@@ -162,7 +169,7 @@ export class PaymentsService {
if (verifyData.code === 100) {
const { wallet } = await this.handleSuccessfulPayment(
payment.id,
payment,
payment.user.id,
payment.amount,
`${verifyData.ref_id}`,
@@ -248,9 +255,9 @@ export class PaymentsService {
}
//*********************************** */
async handleSuccessfulPayment(paymentId: string, userId: string, amount: Decimal, transactionId: string, queryRunner: QueryRunner) {
await queryRunner.manager.update(Payment, { id: paymentId }, { status: PaymentStatus.COMPLETED, transactionId });
return this.walletsService.createPaymentTransaction(userId, amount, queryRunner);
async handleSuccessfulPayment(payment: Payment, userId: string, amount: Decimal, transactionId: string, queryRunner: QueryRunner) {
await queryRunner.manager.update(Payment, { id: payment.id }, { status: PaymentStatus.COMPLETED, transactionId });
return this.walletsService.createPaymentTransaction(userId, amount, payment, queryRunner);
}
//*********************************** */
@@ -399,7 +406,7 @@ export class PaymentsService {
await queryRunner.manager.update(DepositRequest, { id: depositRequest.id }, { status: DepositRequestStatus.APPROVED });
await this.walletsService.createDepositTransaction(depositRequest.amount, depositRequest.user.id, queryRunner);
await this.walletsService.createDepositTransaction(depositRequest.user.id, depositRequest.amount, depositRequest, queryRunner);
await queryRunner.commitTransaction();
return {
@@ -5,6 +5,8 @@ import { Column, Entity, ManyToOne } from "typeorm";
import { Wallet } from "./wallet.entity";
import { BaseEntity } from "../../../common/entities/base.entity";
import { DecimalTransformer } from "../../../common/transformers/decimal.transformer";
import { DepositRequest } from "../../payments/entities/deposit-request.entity";
import { Payment } from "../../payments/entities/payment.entity";
import { TransactionType } from "../enums/transaction-type.enum";
@Entity()
@@ -23,4 +25,10 @@ export class WalletTransaction extends BaseEntity {
@Column({ type: "text", nullable: false })
description: string;
@ManyToOne(() => Payment, (payment) => payment.transactions, { onDelete: "CASCADE", nullable: true })
payment: Payment | null;
@ManyToOne(() => DepositRequest, (depositRequest) => depositRequest.transactions, { onDelete: "CASCADE", nullable: true })
depositRequest: DepositRequest | null;
}
@@ -7,6 +7,8 @@ import { PaginationDto } from "../../../common/DTO/pagination.dto";
import { WalletMessage } from "../../../common/enums/message.enum";
import { decimal } from "../../../common/transformers/decimal.transformer";
import { SearchTransactionQueryDto } from "../../payments/DTO/search-transaction-query.dto";
import { DepositRequest } from "../../payments/entities/deposit-request.entity";
import { Payment } from "../../payments/entities/payment.entity";
import { PaginationUtils } from "../../utils/providers/pagination.utils";
import { WalletTransaction } from "../entities/transaction.entity";
import { Wallet } from "../entities/wallet.entity";
@@ -39,7 +41,7 @@ export class WalletsService {
return wallet;
}
//*********************************** */
async createPaymentTransaction(userId: string, amount: Decimal, queryRunner: QueryRunner) {
async createPaymentTransaction(userId: string, amount: Decimal, payment: Payment, queryRunner: QueryRunner) {
const wallet = await queryRunner.manager.findOne(Wallet, { where: { user: { id: userId } }, lock: { mode: "pessimistic_write" } });
if (!wallet) throw new BadRequestException(WalletMessage.WALLET_NOT_FOUND);
@@ -48,6 +50,7 @@ export class WalletsService {
wallet: { id: wallet.id },
type: TransactionType.DEPOSIT,
description: WalletMessage.DEPOSIT_WALLET_IPG,
payment,
});
wallet.balance = new Decimal(wallet.balance).add(transaction.amount);
@@ -59,7 +62,7 @@ export class WalletsService {
}
//*********************************** */
async createDepositTransaction(amount: Decimal, userId: string, queryRunner: QueryRunner) {
async createDepositTransaction(userId: string, amount: Decimal, depositRequest: DepositRequest, queryRunner: QueryRunner) {
const wallet = await queryRunner.manager.findOne(Wallet, { where: { user: { id: userId } }, lock: { mode: "pessimistic_write" } });
if (!wallet) throw new BadRequestException(WalletMessage.WALLET_NOT_FOUND);
@@ -68,6 +71,7 @@ export class WalletsService {
wallet: { id: wallet.id },
type: TransactionType.DEPOSIT,
description: WalletMessage.DEPOSIT_WALLET_TRANSFER,
depositRequest,
});
wallet.balance = new Decimal(wallet.balance).add(transaction.amount);
@@ -149,13 +153,29 @@ export class WalletsService {
count,
totalDeposits: decimal(totals.totalDeposits) || 0,
totalWithdrawals: decimal(totals.totalWithdrawals) || 0,
//TODO:fix this
balancePercentChange: 5,
totalDepositsPercentChange: 4,
totalWithdrawalsPercentChange: 10,
paginate: true,
};
}
//*********************************** */
async getTransactionById(transactionId: string, userId: string) {
const transaction = await this.walletsTransactionRepo
.createQueryBuilder("transaction")
.leftJoinAndSelect("transaction.wallet", "wallet")
.leftJoin("wallet.user", "user")
.addSelect(["user.id", "user.firstName", "user.lastName"])
.leftJoinAndSelect("transaction.payment", "payment", "payment.id IS NOT NULL")
.leftJoinAndSelect("payment.paymentGateway", "paymentGateway", "payment.id IS NOT NULL")
.leftJoinAndSelect("transaction.depositRequest", "depositRequest", "depositRequest.id IS NOT NULL")
.leftJoinAndSelect("depositRequest.bankAccount", "bankAccount", "depositRequest.id IS NOT NULL")
.where("transaction.id = :transactionId", { transactionId })
.andWhere("wallet.userId = :userId", { userId })
.getOne();
if (!transaction) throw new BadRequestException(WalletMessage.TRANSACTION_NOT_FOUND);
return { transaction };
}
//*********************************** */
+9 -1
View File
@@ -1,4 +1,4 @@
import { Controller, Get, Query } from "@nestjs/common";
import { Controller, Get, Param, Query } from "@nestjs/common";
import { ApiOperation, ApiTags } from "@nestjs/swagger";
import { WalletsService } from "./providers/wallets.service";
@@ -6,6 +6,7 @@ import { AuthGuards } from "../../common/decorators/auth-guard.decorator";
import { Pagination } from "../../common/decorators/pagination.decorator";
import { UserDec } from "../../common/decorators/user.decorator";
import { PaginationDto } from "../../common/DTO/pagination.dto";
import { ParamDto } from "../../common/DTO/param.dto";
@Controller("wallets")
@ApiTags("wallets")
@@ -26,4 +27,11 @@ export class WalletsController {
getTransactions(@UserDec("id") userId: string, @Query() paginationDto: PaginationDto) {
return this.walletsService.getTransaction(userId, paginationDto);
}
@ApiOperation({ summary: "get transaction by id" })
@AuthGuards()
@Get("transactions/:id")
getTransactionById(@Param() paramDto: ParamDto, @UserDec("id") userId: string) {
return this.walletsService.getTransactionById(paramDto.id, userId);
}
}