Files
gridpilot.gg/core/payments/application/use-cases/ProcessWalletTransactionUseCase.ts
2025-12-16 10:50:15 +01:00

121 lines
3.8 KiB
TypeScript

/**
* Application Use Case: ProcessWalletTransactionUseCase
*
* Processes a wallet transaction (deposit or withdrawal).
*/
import type { IWalletRepository, ITransactionRepository } from '../../domain/repositories/IWalletRepository';
import type { Wallet, Transaction, TransactionType, ReferenceType } from '../../domain/entities/Wallet';
import type {
IProcessWalletTransactionPresenter,
ProcessWalletTransactionResultDTO,
ProcessWalletTransactionViewModel,
} from '../presenters/IProcessWalletTransactionPresenter';
import type { UseCase } from '@gridpilot/shared/application/UseCase';
export interface ProcessWalletTransactionInput {
leagueId: string;
type: TransactionType;
amount: number;
description: string;
referenceId?: string;
referenceType?: ReferenceType;
}
export class ProcessWalletTransactionUseCase
implements UseCase<ProcessWalletTransactionInput, ProcessWalletTransactionResultDTO, ProcessWalletTransactionViewModel, IProcessWalletTransactionPresenter>
{
constructor(
private readonly walletRepository: IWalletRepository,
private readonly transactionRepository: ITransactionRepository,
) {}
async execute(
input: ProcessWalletTransactionInput,
presenter: IProcessWalletTransactionPresenter,
): Promise<void> {
presenter.reset();
const { leagueId, type, amount, description, referenceId, referenceType } = input;
if (!leagueId || !type || amount === undefined || !description) {
throw new Error('Missing required fields: leagueId, type, amount, description');
}
if (type !== ('deposit' as TransactionType) && type !== ('withdrawal' as TransactionType)) {
throw new Error('Type must be "deposit" or "withdrawal"');
}
let wallet = await this.walletRepository.findByLeagueId(leagueId);
if (!wallet) {
const id = `wallet-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
const newWallet: Wallet = {
id,
leagueId,
balance: 0,
totalRevenue: 0,
totalPlatformFees: 0,
totalWithdrawn: 0,
currency: 'USD',
createdAt: new Date(),
};
wallet = await this.walletRepository.create(newWallet);
}
if (type === ('withdrawal' as TransactionType)) {
if (amount > wallet.balance) {
throw new Error('Insufficient balance');
}
}
const transactionId = `txn-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
const transaction: Transaction = {
id: transactionId,
walletId: wallet.id,
type,
amount,
description,
referenceId,
referenceType,
createdAt: new Date(),
};
const createdTransaction = await this.transactionRepository.create(transaction);
if (type === ('deposit' as TransactionType)) {
wallet.balance += amount;
wallet.totalRevenue += amount;
} else {
wallet.balance -= amount;
wallet.totalWithdrawn += amount;
}
const updatedWallet = await this.walletRepository.update(wallet);
const dto: ProcessWalletTransactionResultDTO = {
wallet: {
id: updatedWallet.id,
leagueId: updatedWallet.leagueId,
balance: updatedWallet.balance,
totalRevenue: updatedWallet.totalRevenue,
totalPlatformFees: updatedWallet.totalPlatformFees,
totalWithdrawn: updatedWallet.totalWithdrawn,
currency: updatedWallet.currency,
createdAt: updatedWallet.createdAt,
},
transaction: {
id: createdTransaction.id,
walletId: createdTransaction.walletId,
type: createdTransaction.type,
amount: createdTransaction.amount,
description: createdTransaction.description,
referenceId: createdTransaction.referenceId,
referenceType: createdTransaction.referenceType,
createdAt: createdTransaction.createdAt,
},
};
presenter.present(dto);
}
}