mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
reject cents mismatch on CBE bills
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
import { amountsMatchToTheCent } from "./cbe-bill.service";
|
||||
|
||||
describe("amountsMatchToTheCent (CBE payment amount gate)", () => {
|
||||
it("accepts the exact amount", () => {
|
||||
expect(amountsMatchToTheCent(1234.34, 1234.34)).toBe(true);
|
||||
});
|
||||
|
||||
it("rejects a cents-only difference (the 1234.89 vs 1234.34 bug)", () => {
|
||||
expect(amountsMatchToTheCent(1234.89, 1234.34)).toBe(false);
|
||||
expect(amountsMatchToTheCent(1234.35, 1234.34)).toBe(false);
|
||||
});
|
||||
|
||||
it("rejects whole-unit differences", () => {
|
||||
expect(amountsMatchToTheCent(1235.34, 1234.34)).toBe(false);
|
||||
});
|
||||
|
||||
it("absorbs double-precision storage noise", () => {
|
||||
expect(amountsMatchToTheCent(1234.34, 1234.3399999999999)).toBe(true);
|
||||
// classic float artifact: 0.1 + 0.2 !== 0.3
|
||||
expect(amountsMatchToTheCent(0.1 + 0.2, 0.3)).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -38,8 +38,15 @@ import {
|
||||
/** Postgres unique_violation — the DB-level idempotency backstop firing on a concurrent duplicate. */
|
||||
const PG_UNIQUE_VIOLATION = "23505";
|
||||
|
||||
/** Mirrors the short-pay tolerance already applied in handlePaymentEvent. */
|
||||
const AMOUNT_TOLERANCE = 0.01;
|
||||
/**
|
||||
* CBE must pay the bill to the exact cent — compare in integer cents so
|
||||
* double-precision storage noise (1234.34 stored as 1234.33999…) can neither
|
||||
* mask nor fabricate a difference. A relative tolerance is wrong here: 1% of a
|
||||
* 1234.34 bill would wave through anything up to ±12.34.
|
||||
*/
|
||||
export function amountsMatchToTheCent(a: number, b: number): boolean {
|
||||
return Math.round(a * 100) === Math.round(b * 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate an intent's own terminal state into the same reason vocabulary the domain apps
|
||||
@@ -268,8 +275,7 @@ export class CbeBillService {
|
||||
const amount = Number(dto.Amount);
|
||||
if (
|
||||
!Number.isFinite(amount) ||
|
||||
Math.abs(amount - intent.amountMinor) >
|
||||
intent.amountMinor * AMOUNT_TOLERANCE
|
||||
!amountsMatchToTheCent(amount, intent.amountMinor)
|
||||
) {
|
||||
throw new CbeBillError("Amount mismatch", "BUSINESS");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user