Skip to main content

Money is exact, never floating

Every amount in Disbursement is an integer. There are no floats anywhere in the config or the math, because the output is a file a bank executes — a rounding error creates or destroys real money.
  • Percentages are basis points (bps). 1% = 100 bps, so 100.00% = 10000 bps. A 90/10 split is 9000 / 1000.
  • Amounts are minor units. Values are stored and computed in the currency’s minor unit (x100 convention). The exporter owns display formatting per the bank spec; storage never changes.
  • Round half up for MDR, service charge, and tax.

The split algorithm

For a gross amount G and a resolved config, the engine computes:
1

Fees

mdr = rhu(G × mdr_bps / 10000), then the service charge (a percentage of gross or a fixed minor-unit amount). fee = mdr + service_charge.
2

Tax

Tax is configurable: a base (fees or gross) × mode (include or exclude) × payer (merchant or klikit). exclude adds tax on top; include backs it out so net_of_tax + tax == base.
3

Distributable pool

Deduct the merchant-borne fee and tax from gross to get the pool P. klikit-borne portions are reported separately as klikit_absorbed_* and never touch the merchant pool. A negative pool (fees exceed gross) is flagged, never clamped — the batch can’t be approved until it’s resolved.
4

Largest-remainder split

Each party gets floor(P × bps / 10000); the leftover minor units are handed out one at a time by descending fractional remainder. This guarantees the shares sum to P exactly — independent per-line rounding is forbidden.
Worked example (IDR). Gross Rp 100,000.00 (10,000,000 minor units), MDR 150 bps, fixed service charge 250,000, 11% tax excluded on fees, merchant bears both. Fee = 400,000, tax = 44,000, pool = 9,556,000. A 9000/1000 split → 8,600,400 + 955,600 = 9,556,000 exactly.

Config validation

A template’s config is rejected unless:
  • Σ split_bps == 10000 (exactly 100%),
  • exactly one party has role merchant, at most one has role klikit,
  • no negative or non-integer values.

Line flags

After compute, each line may carry flags that block approval until resolved:

Permissions

Three distinct permissions gate the workflow (approval is always separate from operation — maker-checker):

Data safety

  • Bank account numbers live only on the instance’s parties, encrypted at rest, masked (last-4) in ordinary reads, full access role-gated and audited.
  • A batch’s config is frozen at compute — later template edits can never change a computed batch. The beneficiary bank details are snapshotted onto each line too, so a post-approval bank edit can’t silently redirect a payment.
  • An exported batch can never be voided (money may have moved; the record is permanent).