Money & paisa
Trile is a payments system, so money handling is strict and non-negotiable.
The rules
Section titled “The rules”- Smallest unit is paisa. 1 NPR = 100 paisa.
- Amounts are integers in paisa, never rupees, never decimals.
- Amounts are serialized as strings, not JSON numbers — to survive values larger than
Number.MAX_SAFE_INTEGERand to forbid float arithmetic. - Currency is
NPRfor v1. The field is always present.
The shape
Section titled “The shape”Most money fields appear either as a flat *_paisa string or as a Money object:
{ "amount_paisa": "49900", "currency": "NPR" }"49900" paisa = NPR 499.00.
Converting for display
Section titled “Converting for display”Do the conversion at the edge (UI), using integer math — never store or compute in rupees.
// paisa string → display rupeesfunction formatNpr(paisa: string): string { const n = BigInt(paisa); // BigInt, not Number const rupees = n / 100n; const sub = (n % 100n).toString().padStart(2, '0'); return `NPR ${rupees}.${sub}`;}
formatNpr("49900"); // "NPR 499.00"formatNpr("100"); // "NPR 1.00"To go the other way for a request body, multiply by 100 with integer math and send a string:
function toPaisa(rupees: number): string { // round to avoid 4.999999 float drift, then to paisa return String(Math.round(rupees * 100));}
toPaisa(499); // "49900"Pitfalls
Section titled “Pitfalls”- Don’t send
amount_paisa: 49900as a JSON number — send the string"49900". - Don’t send rupees (
499or499.00) anywhere. The API expects paisa. - Don’t sum amounts with
+on numbers; useBigInt(or your language’s decimal/bigint type).
Why the backend is this strict
Section titled “Why the backend is this strict”Internally every amount is a PostgreSQL bigint and a typed Money value object with no escape
hatch to number. The wire format mirrors that on purpose — so a careless float can’t enter the
system through the API either.