143 lines
4.4 KiB
TypeScript
143 lines
4.4 KiB
TypeScript
import { Result } from '@core/shared/application/Result';
|
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
|
import type { Logger, UseCaseOutputPort } from '@core/shared/application';
|
|
import type { ILeagueRepository } from '../../domain/repositories/ILeagueRepository';
|
|
import type { ILeagueWalletRepository } from '../../domain/repositories/ILeagueWalletRepository';
|
|
import type { ITransactionRepository } from '../../domain/repositories/ITransactionRepository';
|
|
import { Money } from '../../domain/value-objects/Money';
|
|
import { Transaction } from '../../domain/entities/league-wallet/Transaction';
|
|
import { TransactionId } from '../../domain/entities/league-wallet/TransactionId';
|
|
import { LeagueWalletId } from '../../domain/entities/league-wallet/LeagueWalletId';
|
|
|
|
export type WithdrawFromLeagueWalletInput = {
|
|
leagueId: string;
|
|
requestedById: string;
|
|
amount: number;
|
|
currency: 'USD' | 'EUR' | 'GBP';
|
|
reason?: string;
|
|
};
|
|
|
|
export type WithdrawFromLeagueWalletResult = {
|
|
leagueId: string;
|
|
amount: Money;
|
|
transactionId: string;
|
|
walletBalanceAfter: Money;
|
|
};
|
|
|
|
export type WithdrawFromLeagueWalletErrorCode =
|
|
| 'LEAGUE_NOT_FOUND'
|
|
| 'WALLET_NOT_FOUND'
|
|
| 'INSUFFICIENT_FUNDS'
|
|
| 'UNAUTHORIZED_WITHDRAWAL'
|
|
| 'REPOSITORY_ERROR';
|
|
|
|
/**
|
|
* Use Case for withdrawing from league wallet.
|
|
*/
|
|
export class WithdrawFromLeagueWalletUseCase {
|
|
constructor(
|
|
private readonly leagueRepository: ILeagueRepository,
|
|
private readonly walletRepository: ILeagueWalletRepository,
|
|
private readonly transactionRepository: ITransactionRepository,
|
|
private readonly logger: Logger,
|
|
private readonly output: UseCaseOutputPort<WithdrawFromLeagueWalletResult>,
|
|
) {}
|
|
|
|
async execute(
|
|
input: WithdrawFromLeagueWalletInput,
|
|
): Promise<
|
|
Result<
|
|
void,
|
|
ApplicationErrorCode<
|
|
WithdrawFromLeagueWalletErrorCode,
|
|
{
|
|
message: string;
|
|
}
|
|
>
|
|
>
|
|
> {
|
|
try {
|
|
const league = await this.leagueRepository.findById(input.leagueId);
|
|
if (!league) {
|
|
return Result.err({
|
|
code: 'LEAGUE_NOT_FOUND',
|
|
details: { message: `League with id ${input.leagueId} not found` },
|
|
});
|
|
}
|
|
|
|
const wallet = await this.walletRepository.findByLeagueId(input.leagueId);
|
|
if (!wallet) {
|
|
return Result.err({
|
|
code: 'WALLET_NOT_FOUND',
|
|
details: { message: `Wallet for league ${input.leagueId} not found` },
|
|
});
|
|
}
|
|
|
|
if (league.ownerId.toString() !== input.requestedById) {
|
|
return Result.err({
|
|
code: 'UNAUTHORIZED_WITHDRAWAL',
|
|
details: { message: 'Only the league owner can withdraw from the league wallet' },
|
|
});
|
|
}
|
|
|
|
const withdrawalAmount = Money.create(input.amount, input.currency);
|
|
|
|
if (!wallet.canWithdraw(withdrawalAmount)) {
|
|
return Result.err({
|
|
code: 'INSUFFICIENT_FUNDS',
|
|
details: { message: 'Insufficient balance for withdrawal' },
|
|
});
|
|
}
|
|
|
|
const transactionId = TransactionId.create(`txn-${Date.now()}`);
|
|
const transaction = Transaction.create({
|
|
id: transactionId,
|
|
walletId: LeagueWalletId.create(wallet.id.toString()),
|
|
type: 'withdrawal',
|
|
amount: withdrawalAmount,
|
|
description: input.reason ?? 'League wallet withdrawal',
|
|
metadata: {
|
|
reason: input.reason,
|
|
requestedById: input.requestedById,
|
|
},
|
|
completedAt: undefined,
|
|
});
|
|
|
|
const updatedWallet = wallet.withdrawFunds(withdrawalAmount, transactionId.toString());
|
|
|
|
await this.transactionRepository.create(transaction);
|
|
await this.walletRepository.update(updatedWallet);
|
|
|
|
const result: WithdrawFromLeagueWalletResult = {
|
|
leagueId: input.leagueId,
|
|
amount: withdrawalAmount,
|
|
transactionId: transactionId.toString(),
|
|
walletBalanceAfter: updatedWallet.balance,
|
|
};
|
|
|
|
this.output.present(result);
|
|
|
|
return Result.ok(undefined);
|
|
} catch (error) {
|
|
this.logger.error(
|
|
'Failed to withdraw from league wallet',
|
|
error instanceof Error ? error : undefined,
|
|
{
|
|
leagueId: input.leagueId,
|
|
requestedById: input.requestedById,
|
|
},
|
|
);
|
|
|
|
return Result.err({
|
|
code: 'REPOSITORY_ERROR',
|
|
details: {
|
|
message:
|
|
error instanceof Error
|
|
? error.message
|
|
: 'Failed to withdraw from league wallet',
|
|
},
|
|
});
|
|
}
|
|
}
|
|
}
|