121 lines
3.8 KiB
TypeScript
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);
|
|
}
|
|
} |