Skip to content

About

TypeScript money library for minor units, rounding, allocation, and serialized money payloads.

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

money-safe

A small TypeScript money library for amount boundaries, minor units, rounding, allocation, formatting, and serialized money payloads.

Use decimal strings at product and API boundaries, store integer minor units in infrastructure, and move JSON payloads between services without passing money through JavaScript floating point numbers.

money-safe focuses on amount shape. It does not own exchange rates, accounting ledgers, tax policy, provider orchestration, or product pricing rules.

Install

pnpm add @crapthings/money-safe

Quick Start

import { amount } from '@crapthings/money-safe';

const subtotal = amount('19.99', 'CNY').times(3);
const total = subtotal.minus('5.00').plusMinor(1200);

total.toDecimal();
// '66.97'

total.toJSON();
// { amountMinor: '6697', currency: 'CNY', minorUnit: 2 }

Boundary Guide

Use decimal strings for user input and public APIs:

amount('19.99', 'CNY');
amount.decimal('19.99', 'CNY');

Use minor units for databases, payment providers, and ledgers:

amount.minor(1999, 'CNY');

Use serialized payloads across services and audit logs:

const payload = amount('19.99', 'CNY').toJSON();
const value = amount.fromJSON(payload);

amountMinor is serialized as a string so JSON does not force large values through JavaScript Number.

Use fromNumber only when integrating with existing code that already passes decimal numbers. The rounding option is required so the conversion is visible in code review:

amount.fromNumber(19.995, 'USD', { rounding: 'half-up' });

Use bigint for values that may exceed JavaScript safe integers:

amount.bigint('900719925474099.93', 'USD').toMinor();
// 90071992547409993n

Checkout Payload

import { amount } from '@crapthings/money-safe';

const subtotal = amount('39.90', 'CNY').times(2).plus('29.00');
const discount = subtotal.times('0.10', { rounding: 'half-up' });
const shipping = subtotal.toMinor() >= 9900 ? amount('0.00', 'CNY') : amount('12.00', 'CNY');
const total = subtotal.minus(discount).plus(shipping);

const paymentPayload = total.toJSON();

See the checked checkout example.

Examples

import { allocate, amount, money, multiply, parseMoneyBigInt } from '@crapthings/money-safe';

const price = money(1999, 'USD');
const discounted = multiply(price, '0.875', { rounding: 'half-even' });

const shares = allocate(money(100, 'CNY'), [1, 1, 1]);
// [{ amountMinor: 34, currency: 'CNY', minorUnit: 2 }, ...]

const largeBalance = parseMoneyBigInt('900719925474099.93', 'USD');

amount('1.00', 'CNY').split(3).map((part) => part.toDecimal());
// ['0.34', '0.33', '0.33']

Core Ideas

  • Store and calculate in minor units.
  • Keep currency and minor-unit metadata with the amount.
  • Add or subtract only matching money values.
  • Use decimal strings at text/API boundaries.
  • Use minor units at database/payment boundaries.
  • Format only at display boundaries.
  • Choose a rounding mode when converting rates into minor units.

Not For

money-safe intentionally leaves business policy in your application:

  • Foreign-exchange rates.
  • Tax and invoice policy.
  • Ledger account movement.
  • Payment-provider orchestration.
  • Product pricing rules.

API

See:

Recipes:

About

TypeScript money library for minor units, rounding, allocation, and serialized money payloads.

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages