Money & currency
Every monetary value in Paylera is a string, paired with a currency.
You’ll never see a JSON number for an amount. The reason is simple:
floating-point arithmetic loses precision at scale, and “loses
precision” against a customer’s invoice is a refund and a churn signal.
The shape
{ "amount": "29.00", "currency": "USD" }Or, in fields like invoice totals where the currency is set on the parent object:
{ "subtotal": "29.00", "discount": "0.00", "tax": "2.32", "total": "31.32" }Why strings
JSON numbers are IEEE-754 doubles. 0.1 + 0.2 != 0.3. Doubles can’t
represent 0.1 exactly. Decimal arithmetic is what your accountant
needs; serialising as strings is what keeps the exact value across
JSON parsers in any language.
The internal representation is a 128-bit decimal with 8 fractional digits — enough headroom for milli-cent precision in usage pricing.
Currency
currency is an ISO-4217 alpha-3 code in uppercase. Invalid:
usd, Usd, US$, 840 (numeric ISO). Valid: USD, EUR, GBP,
JPY.
A non-uppercase currency returns 422 validation.failed with
code: currency.invalid.
The full supported list is on Supported currencies.
Minor units
Currencies are rounded at their minor unit — cents for USD, sen for
JPY (which has zero minor units, so the minor unit is 1). Invoice
finalisation rounds each line to the currency’s minor unit using
HalfEven (banker’s rounding).
| Currency | Minor units | Smallest amount |
|---|---|---|
USD, EUR, GBP, CHF, CAD, AUD | 2 | 0.01 |
JPY, KRW | 0 | 1 |
BHD, JOD, KWD, OMR, TND | 3 | 0.001 |
CLF | 4 | 0.0001 |
Rounding modes
HalfEven is the default — it minimises bias over many roundings,
which is what you want for accounting. Some jurisdictions require
HalfUp for tax computation; the tax engine handles that locally.
You don’t pick a mode per call; the system picks based on context
(general accounting → HalfEven, tax → engine-decided).
Per-line rounding
Invoices round per line, not at the total. The engine computes each line, rounds it, then sums. This avoids “off by a cent” disputes where two integrations use different summation orders.
The invariants enforced at finalisation:
total = subtotal − discount + taxamount_paid ≤ total- Every line’s
amount = round(quantity × unit_amount)to the currency’s minor units.
Comparing amounts
In your code, don’t compare amount strings directly. "10.00" and
"10" are equal as numbers but unequal as strings. Use a decimal
library for comparison:
- Python:
decimal.Decimal("10.00") == decimal.Decimal("10") - JavaScript / TypeScript:
BigDecimal(proposed) or decimal.js - Java:
BigDecimal.compareTo - C# / .NET:
decimal.Equalsafter parse - Go:
shopspring/decimal
The official SDKs wrap this; their Money types handle parse,
display, and arithmetic for you.
Negative amounts
Allowed where it makes sense:
- Credit notes carry negative line amounts (they reduce the receivable).
- Refunds carry negative total amounts.
- Wallet debits carry negative deltas.
Forbidden everywhere else — line items, plan component amounts, invoice totals must be ≥ 0.
Rounding in multi-currency
When an invoice carries presentment_currency (what the customer
sees) and settlement_currency (what your bank receives) and they
differ, both are rounded at their respective minor units, and an
explicit FX-gain/loss leg in the ledger absorbs the difference. See
Multi-currency & FX.
Examples
| Input | After round (USD, HalfEven) |
|---|---|
0.005 | 0.00 |
0.015 | 0.02 |
0.025 | 0.02 |
0.035 | 0.04 |
| Input | After round (JPY) |
|---|---|
19.4 | 19 |
19.5 | 20 |
20.5 | 20 |
21.5 | 22 |