Currency
Minor units, decimal strings (never numbers), ISO 4217, and a few token symbols.
Money is a string. Currency is a code. The platform never accepts a JSON number for a money value, and it never returns one.
Amounts are minor-unit decimal strings
amount is the number of minor units encoded as a positive integer string. Pass "10000" for MYR 100.00, not 10000 and not 100.00.
Why a string:
- JavaScript's
Number.MAX_SAFE_INTEGERis2^53 - 1. A merchant with high-value flows or a low-precision currency exceeds it. - A string crossing the JSON boundary is unambiguous. There is no implicit decimal coercion, no floating-point round trip.
- The platform parses to
bigintserver-side. Your client should treat the wire value the same way.
The accepted shape:
^[1-9]\d*$Rejected: "0", "-1", "01", "100.00", "1e3", the number 100.
Examples
| Currency | Minor unit | amount | Renders as |
|---|---|---|---|
| MYR | sen (1/100) | "10000" | MYR 100.00 |
| USD | cent (1/100) | "5000" | USD 50.00 |
| JPY | yen (whole) | "500" | JPY 500 |
| USDT | wei-style (1e-6) | "1000000" | USDT 1.00 |
If you do not know the minor exponent for a currency, look it up before you POST. The platform stores and reports the value you sent.
Currency codes
currency matches ^[A-Z]{3,6}$. ISO 4217 codes (MYR, USD, JPY) are three letters. Token symbols (USDT, USDC) are typically four. The widened upper bound covers token symbols up to six characters.
Rules:
- The code must be in the merchant's effective currency allowlist. A currency that is not enabled returns
400 invalid_currency. - The code is case-sensitive on the wire — always uppercase.
Country codes
country is ISO 3166-1 alpha-2: MY, SG, TH, JP, US. Two uppercase letters, no exceptions.
A country that is not in the merchant's country allowlist returns 400 invalid_country.
Reading values back
Response bodies return amount as a string for the same reason: precision and unambiguity. Parse to BigInt if you need to do arithmetic; format with the currency-aware code your stack already has.
import { format } from "@my-app/money";
const display = format(BigInt(intent.amount), intent.currency);Never do this:
const wrong = Number(intent.amount) / 100;A high-value MYR amount, a JPY value (no minor unit), and a USDT value (six decimals) all break under that line — precision loss and the wrong divisor.