Developer documentation

Money is a string

Integer micro-USD as a JSON string, and the 2^53 parsing bug that choice prevents.

Sections

Every amount on the wire is integer micro-USD encoded as a JSON string. Never a JSON number, never a decimal, never a float.

What an amount looks likejson
{
  "balance_micro": "10000000",      // $10.00
  "price_micro": "50000",           // $0.05
  "currency": "USD"
}

Why a string#

Reading amounts correctly#

Correct and incorrectjavascript
const { balance_micro } = await response.json();

// CORRECT: BigInt, or keep the string and format it for display.
const micro = BigInt(balance_micro);
const whole = micro / 1000000n;
const cents = String(micro % 1000000n).padStart(6, "0").slice(0, 2);
const display = `$${whole}.${cents}`;

// WRONG: silently lossy above 2^53, and lossy in a way no test
// with small numbers will ever catch.
const broken = Number(balance_micro);

Sending amounts#

The same rule applies in reverse. A top-up amount or a price you publish is a string of integer micro-USD. We refuse an amount with more precision than micro rather than rounding it, because rounding would charge or credit a different number than the one you sent.

Arithmetic#

  • Add and subtract integers. Never convert to a float for intermediate maths.
  • Format only at the edge, for display. Keep the integer everywhere else.
  • Beware your database. A FLOAT or DOUBLE column for money has the same problem one layer down; use a 64-bit integer.