La columna llamada amount_cents cobrará 100x de más en Japón
Casi todo esquema de pagos empieza igual. Alguien aprende a no guardar dinero en float — correctamente — y escribe:
amount_cents integer not nullEsto es correcto para dólares, euros, libras y para casi todo lo que facturarás. Es incorrecto la primera vez que cobras a un cliente japonés, y lo es por un factor de cien.
Monedas sin decimales
El yen japonés no tiene subunidad. ¥1.000 son mil yenes, no diez yenes y pico. El won coreano, el dong vietnamita, el peso chileno, el guaraní paraguayo y varios más se comportan igual. Stripe y la mayoría de las APIs de pago lo manejan bien — reciben el importe en la unidad más pequeña de la moneda, y para el JPY la unidad más pequeña ES el yen.
Así que una columna llamada amount_cents con 100000 significa mil dólares, y cien mil yenes. Si tu código multiplica el número humano por 100 a la entrada, acabas de facturar ¥100.000 por un trabajo de ¥1.000.
También existen monedas de tres decimales
El dinar bahreiní, el kuwaití, el jordano y el rial omaní usan tres decimales. La unidad más pequeña es una milésima. Un esquema que asume dos decimales cobra diez veces menos en estas monedas.
La solución es nomenclatura y una función
Guarda el importe en la unidad menor de la propia moneda, y nombra la columna de forma que nadie pueda leerla mal. Luego centraliza la conversión para que exista exactamente un lugar que conozca el exponente:
const ZERO_DECIMAL = new Set(["JPY", "KRW", "VND", "CLP", "PYG", "ISK"]);
const THREE_DECIMAL = new Set(["BHD", "KWD", "JOD", "OMR", "TND"]);
function exponent(currency: string): 0 | 2 | 3 {
const c = currency.toUpperCase();
if (ZERO_DECIMAL.has(c)) return 0;
if (THREE_DECIMAL.has(c)) return 3;
return 2;
}
export function toMinorUnits(amount: number, currency: string): number {
return Math.round(amount * 10 ** exponent(currency));
}Dos reglas lo sostienen: la columna cruda nunca se lee directamente fuera de este módulo, y ningún código de presentación divide por 100. Si alguna plantilla contiene un 100 literal, ahí está el fallo esperando.
Además: rechaza lo que no puedes representar
Si alguien escribe ¥1.000,50, ese importe no existe. Redondear en silencio es una decisión que heredará tu contabilidad. Valida a la entrada y recházalo — un error al capturar sale más barato que una discrepancia en la conciliación.
Nada de esto es difícil. Solo es invisible hasta la primera factura en una moneda que nadie probó, y para entonces el dinero ya se movió.
¿Construyendo algo donde estas decisiones importan?
Iniciar proyecto