Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions src/transactions/transaction-fees.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { TransactionFeesService } from './transaction-fees.service';

describe('TransactionFeesService', () => {
let service: TransactionFeesService;

beforeEach(() => {
service = new TransactionFeesService();
});

it('calculates fees correctly for a standard amount', () => {
const result = service.calculateFees(100_000);

expect(result.transactionAmount).toBe(100_000);
expect(result.platformFee).toBe(1_500); // 1.5%
expect(result.platformFeeRate).toBe(0.015);
expect(result.agentCommission).toBe(3_000); // 3%
expect(result.agentCommissionRate).toBe(0.03);
// tax = (100000 + 1500 + 3000) * 0.08 = 8360
expect(result.tax).toBe(8_360);
expect(result.taxRate).toBe(0.08);
expect(result.totalFees).toBe(12_860);
expect(result.totalAmount).toBe(112_860);
});

it('uses custom agent commission rate when provided', () => {
const result = service.calculateFees(100_000, 0.05);

expect(result.agentCommission).toBe(5_000);
expect(result.agentCommissionRate).toBe(0.05);
});

it('returns zero fees for zero amount', () => {
const result = service.calculateFees(0);

expect(result.platformFee).toBe(0);
expect(result.agentCommission).toBe(0);
expect(result.tax).toBe(0);
expect(result.totalFees).toBe(0);
expect(result.totalAmount).toBe(0);
});

it('rounds to 2 decimal places', () => {
const result = service.calculateFees(333.33);

expect(result.platformFee).toBe(5); // 333.33 * 0.015 = 4.99995 → 5
expect(Number.isInteger(result.platformFee * 100)).toBe(true);
});
});
42 changes: 42 additions & 0 deletions src/transactions/transaction-fees.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Injectable } from '@nestjs/common';
import { FeeBreakdown } from './dto/transaction.dto';

/** Default fee rates */
const PLATFORM_FEE_RATE = 0.015; // 1.5%
const DEFAULT_AGENT_COMMISSION_RATE = 0.03; // 3%
const TAX_RATE = 0.08; // 8%

@Injectable()
export class TransactionFeesService {
/**
* Calculate all fees for a given transaction amount.
* @param amount - The base transaction amount
* @param agentCommissionRate - Optional override for agent commission rate (0–1)
*/
calculateFees(amount: number, agentCommissionRate?: number): FeeBreakdown {
const commissionRate = agentCommissionRate ?? DEFAULT_AGENT_COMMISSION_RATE;

const platformFee = this.round(amount * PLATFORM_FEE_RATE);
const agentCommission = this.round(amount * commissionRate);
const taxableAmount = amount + platformFee + agentCommission;
const tax = this.round(taxableAmount * TAX_RATE);
const totalFees = this.round(platformFee + agentCommission + tax);
const totalAmount = this.round(amount + totalFees);

return {
transactionAmount: amount,
platformFee,
platformFeeRate: PLATFORM_FEE_RATE,
agentCommission,
agentCommissionRate: commissionRate,
tax,
taxRate: TAX_RATE,
totalFees,
totalAmount,
};
}

private round(value: number): number {
return Math.round(value * 100) / 100;
}
}