Concepts

Amounts and time

Money is exact and time is simple: decimal strings and Unix seconds, everywhere.

Money

Every amount in a response is a plain decimal string: no exponent, no trailing zeros, "0" for zero, null when absent. Parse it with a decimal type, and never compare amounts as strings or as floats. Amounts you send use the same format, and may not have more decimals than the currency's theoretical precision.

Parsing amounts

A float turns 0.1 + 0.2 into 0.30000000000000004, which is how a fully paid order reads as short. Compare amounts as decimals, or as whole units of the currency's smallest denomination.

Compare
from decimal import Decimal


def paid_in_full(payment: dict) -> bool:
    return Decimal(payment["paid"]) >= Decimal(payment["topay"])
// Whole minor units: "0.00021" with 8 decimals is 21000n.
function toUnits(amount, decimals) {
  const [whole, fraction = ""] = amount.split(".");
  return BigInt(whole + fraction.padEnd(decimals, "0").slice(0, decimals));
}

function paidInFull(payment, decimals) {
  return toUnits(payment.paid, decimals) >= toUnits(payment.topay, decimals);
}
// bcmath compares decimal strings exactly.
function paidInFull(array $payment, int $decimals): bool
{
    return bccomp($payment["paid"], $payment["topay"], $decimals) >= 0;
}

Each currency's precision is on Payment methods and in the currency registry.

Time

Every timestamp is an integer number of seconds since the Unix epoch, in UTC.