What this is and who needs it

A small but constant source of bugs in payment integrations is rounding. EPD avoids the problem entirely by sending and receiving every amount as an integer in the smallest unit of the given currency. There are no decimals over the wire.

Anyone who shows a price to a user, computes a discount, or reconciles totals needs to read this page once.

Examples

DisplayAPI valueCurrencySmallest unit
$1.00100usdcent
$29.992999usdcent
$297.0029700usdcent
€50.005000eurcent
£12.501250gbppenny
¥1,0001000jpyyen (no decimals)

Currency codes are lowercase. Send usd, eur, gbp — not USD. EPD validates this strictly. Sending "USD" returns validation_error.

Zero-decimal currencies (jpy, krw, vnd, etc.) use the whole-unit value directly — 1000 jpy means ¥1,000, not ¥10. Always check the currency before applying any / 100 conversion in your UI.

Formatting in your UI

Use the platform’s locale-aware formatter rather than manual string concatenation:

function format(amount, currency) {
  return new Intl.NumberFormat("en-US", {
    style: "currency",
    currency: currency.toUpperCase(),
  }).format(amount / (currency.toLowerCase() === "jpy" ? 1 : 100));
}

format(2999, "usd"); // "$29.99"
format(1000, "jpy"); // "¥1,000"
from babel.numbers import format_currency

def format(amount: int, currency: str) -> str:
    divisor = 1 if currency.lower() == "jpy" else 100
    return format_currency(amount / divisor, currency.upper(), locale="en_US")

format(2999, "usd")  # "$29.99"

Common pitfalls