rename to core
This commit is contained in:
162
core/racing/domain/entities/Transaction.ts
Normal file
162
core/racing/domain/entities/Transaction.ts
Normal file
@@ -0,0 +1,162 @@
|
||||
/**
|
||||
* Domain Entity: Transaction
|
||||
*
|
||||
* Represents a financial transaction in the league wallet system.
|
||||
*/
|
||||
|
||||
import { RacingDomainValidationError, RacingDomainInvariantError } from '../errors/RacingDomainError';
|
||||
|
||||
import type { Money } from '../value-objects/Money';
|
||||
import type { IEntity } from '@gridpilot/shared/domain';
|
||||
|
||||
export type TransactionType =
|
||||
| 'sponsorship_payment'
|
||||
| 'membership_payment'
|
||||
| 'prize_payout'
|
||||
| 'withdrawal'
|
||||
| 'refund';
|
||||
|
||||
export type TransactionStatus = 'pending' | 'completed' | 'failed' | 'cancelled';
|
||||
|
||||
export interface TransactionProps {
|
||||
id: string;
|
||||
walletId: string;
|
||||
type: TransactionType;
|
||||
amount: Money;
|
||||
platformFee: Money;
|
||||
netAmount: Money;
|
||||
status: TransactionStatus;
|
||||
createdAt: Date;
|
||||
completedAt: Date | undefined;
|
||||
description: string | undefined;
|
||||
metadata: Record<string, unknown> | undefined;
|
||||
}
|
||||
|
||||
export class Transaction implements IEntity<string> {
|
||||
readonly id: string;
|
||||
readonly walletId: string;
|
||||
readonly type: TransactionType;
|
||||
readonly amount: Money;
|
||||
readonly platformFee: Money;
|
||||
readonly netAmount: Money;
|
||||
readonly status: TransactionStatus;
|
||||
readonly createdAt: Date;
|
||||
readonly completedAt: Date | undefined;
|
||||
readonly description: string | undefined;
|
||||
readonly metadata: Record<string, unknown> | undefined;
|
||||
|
||||
private constructor(props: TransactionProps) {
|
||||
this.id = props.id;
|
||||
this.walletId = props.walletId;
|
||||
this.type = props.type;
|
||||
this.amount = props.amount;
|
||||
this.platformFee = props.platformFee;
|
||||
this.netAmount = props.netAmount;
|
||||
this.status = props.status;
|
||||
this.createdAt = props.createdAt;
|
||||
this.completedAt = props.completedAt;
|
||||
this.description = props.description;
|
||||
this.metadata = props.metadata;
|
||||
}
|
||||
|
||||
static create(props: Omit<TransactionProps, 'createdAt' | 'status' | 'platformFee' | 'netAmount'> & {
|
||||
createdAt?: Date;
|
||||
status?: TransactionStatus;
|
||||
}): Transaction {
|
||||
this.validate(props);
|
||||
|
||||
const platformFee = props.amount.calculatePlatformFee();
|
||||
const netAmount = props.amount.calculateNetAmount();
|
||||
|
||||
return new Transaction({
|
||||
...props,
|
||||
platformFee,
|
||||
netAmount,
|
||||
createdAt: props.createdAt ?? new Date(),
|
||||
status: props.status ?? 'pending',
|
||||
});
|
||||
}
|
||||
|
||||
private static validate(props: Omit<TransactionProps, 'createdAt' | 'status' | 'platformFee' | 'netAmount'>): void {
|
||||
if (!props.id || props.id.trim().length === 0) {
|
||||
throw new RacingDomainValidationError('Transaction ID is required');
|
||||
}
|
||||
|
||||
if (!props.walletId || props.walletId.trim().length === 0) {
|
||||
throw new RacingDomainValidationError('Transaction walletId is required');
|
||||
}
|
||||
|
||||
if (!props.type) {
|
||||
throw new RacingDomainValidationError('Transaction type is required');
|
||||
}
|
||||
|
||||
if (!props.amount) {
|
||||
throw new RacingDomainValidationError('Transaction amount is required');
|
||||
}
|
||||
|
||||
if (props.amount.amount <= 0) {
|
||||
throw new RacingDomainValidationError('Transaction amount must be greater than zero');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark transaction as completed
|
||||
*/
|
||||
complete(): Transaction {
|
||||
if (this.status === 'completed') {
|
||||
throw new RacingDomainInvariantError('Transaction is already completed');
|
||||
}
|
||||
|
||||
if (this.status === 'failed' || this.status === 'cancelled') {
|
||||
throw new RacingDomainInvariantError('Cannot complete a failed or cancelled transaction');
|
||||
}
|
||||
|
||||
return new Transaction({
|
||||
...this,
|
||||
status: 'completed',
|
||||
completedAt: new Date(),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark transaction as failed
|
||||
*/
|
||||
fail(): Transaction {
|
||||
if (this.status === 'completed') {
|
||||
throw new RacingDomainInvariantError('Cannot fail a completed transaction');
|
||||
}
|
||||
|
||||
return new Transaction({
|
||||
...this,
|
||||
status: 'failed',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel transaction
|
||||
*/
|
||||
cancel(): Transaction {
|
||||
if (this.status === 'completed') {
|
||||
throw new RacingDomainInvariantError('Cannot cancel a completed transaction');
|
||||
}
|
||||
|
||||
return new Transaction({
|
||||
...this,
|
||||
status: 'cancelled',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if transaction is completed
|
||||
*/
|
||||
isCompleted(): boolean {
|
||||
return this.status === 'completed';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if transaction is pending
|
||||
*/
|
||||
isPending(): boolean {
|
||||
return this.status === 'pending';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user