45 lines
1.3 KiB
TypeScript
45 lines
1.3 KiB
TypeScript
import { AppError } from '../common/errors/app-error';
|
|
import type { CapturedPayment } from './gateway.contract';
|
|
export interface ExpectedPayment {
|
|
orderId: string;
|
|
paymentId: string;
|
|
amountMinor: string;
|
|
currency: string;
|
|
expiresAt: Date;
|
|
cancelled: boolean;
|
|
alreadyCaptured: boolean;
|
|
}
|
|
export function captureDecision(
|
|
expected: ExpectedPayment,
|
|
event: CapturedPayment,
|
|
now: Date,
|
|
) {
|
|
if (
|
|
event.orderId !== expected.orderId ||
|
|
event.paymentId !== expected.paymentId
|
|
)
|
|
throw new AppError('PAYMENT_REFERENCE_MISMATCH');
|
|
if (event.currency !== expected.currency)
|
|
throw new AppError('PAYMENT_CURRENCY_MISMATCH');
|
|
if (event.amountMinor !== expected.amountMinor)
|
|
throw new AppError('PAYMENT_AMOUNT_MISMATCH');
|
|
if (expected.alreadyCaptured) return 'DUPLICATE' as const;
|
|
if (expected.cancelled || expected.expiresAt <= now)
|
|
return 'REVIEW_AND_REFUND' as const;
|
|
return 'COMMIT_RESERVED_STOCK' as const;
|
|
}
|
|
export function refundAmount(
|
|
captured: bigint,
|
|
completed: bigint,
|
|
pending: bigint,
|
|
requested: bigint,
|
|
) {
|
|
if (
|
|
[captured, completed, pending].some((value) => value < 0n) ||
|
|
requested <= 0n ||
|
|
completed + pending + requested > captured
|
|
)
|
|
throw new AppError('REFUND_AMOUNT_INVALID');
|
|
return requested.toString();
|
|
}
|