102 lines
3.4 KiB
TypeScript
102 lines
3.4 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 } from '../../domain/entities/Wallet';
|
|
import { TransactionType, ReferenceType } from '../../domain/entities/Wallet';
|
|
import type { UseCase } from '@core/shared/application/UseCase';
|
|
import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort';
|
|
import { Result } from '@core/shared/application/Result';
|
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
|
|
|
export interface ProcessWalletTransactionInput {
|
|
leagueId: string;
|
|
type: TransactionType;
|
|
amount: number;
|
|
description: string;
|
|
referenceId?: string;
|
|
referenceType?: ReferenceType;
|
|
}
|
|
|
|
export interface ProcessWalletTransactionResult {
|
|
wallet: Wallet;
|
|
transaction: Transaction;
|
|
}
|
|
|
|
export type ProcessWalletTransactionErrorCode = 'MISSING_REQUIRED_FIELDS' | 'INVALID_TYPE' | 'INSUFFICIENT_BALANCE';
|
|
|
|
export class ProcessWalletTransactionUseCase
|
|
implements UseCase<ProcessWalletTransactionInput, void, ProcessWalletTransactionErrorCode>
|
|
{
|
|
constructor(
|
|
private readonly walletRepository: IWalletRepository,
|
|
private readonly transactionRepository: ITransactionRepository,
|
|
private readonly output: UseCaseOutputPort<ProcessWalletTransactionResult>,
|
|
) {}
|
|
|
|
async execute(input: ProcessWalletTransactionInput): Promise<Result<void, ApplicationErrorCode<ProcessWalletTransactionErrorCode>>> {
|
|
const { leagueId, type, amount, description, referenceId, referenceType } = input;
|
|
|
|
if (!leagueId || !type || amount === undefined || !description) {
|
|
return Result.err({ code: 'MISSING_REQUIRED_FIELDS' as const });
|
|
}
|
|
|
|
if (type !== TransactionType.DEPOSIT && type !== TransactionType.WITHDRAWAL) {
|
|
return Result.err({ code: 'INVALID_TYPE' as const });
|
|
}
|
|
|
|
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 === TransactionType.WITHDRAWAL) {
|
|
if (amount > wallet.balance) {
|
|
return Result.err({ code: 'INSUFFICIENT_BALANCE' as const });
|
|
}
|
|
}
|
|
|
|
const transactionId = `txn-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
const transaction: Transaction = {
|
|
id: transactionId,
|
|
walletId: wallet.id,
|
|
type,
|
|
amount,
|
|
description,
|
|
createdAt: new Date(),
|
|
...(referenceId !== undefined ? { referenceId } : {}),
|
|
...(referenceType !== undefined ? { referenceType } : {}),
|
|
};
|
|
|
|
const createdTransaction = await this.transactionRepository.create(transaction);
|
|
|
|
if (type === TransactionType.DEPOSIT) {
|
|
wallet.balance += amount;
|
|
wallet.totalRevenue += amount;
|
|
} else {
|
|
wallet.balance -= amount;
|
|
wallet.totalWithdrawn += amount;
|
|
}
|
|
|
|
const updatedWallet = await this.walletRepository.update(wallet);
|
|
|
|
this.output.present({ wallet: updatedWallet, transaction: createdTransaction });
|
|
|
|
return Result.ok(undefined);
|
|
}
|
|
} |