Restates decision 2 of 0036: the money keys stay in
Postgres with the ledger entry. The category is commerce (ADR 0127).
Context
Money is wrong in a payment system when it is stored as a float, rounded differently in two places, or changed in place. The owner’s money answers PP3, PP8, MN1 and P2 (mem L517, L797, L872, 2026-10-20) fix the representation, the rounding and the ledger shape.Decision
- Packages.
@stackra/money(commerce category) represents money in minor units with ISO 4217 currency codes and an@Moneyfield. A ledger package records every movement. FX is snapshotted at the transaction time. Each tenant has a base currency. - Exponent (MN1). The ISO 4217 exponent is used, so KWD and BHD have three digits and JPY has none.
- Rounding (MN1). Half-even rounding by default. Half-up where a tax rule requires it. Allocation of a ratio is exact, with deterministic remainders. FX stores the rate and its source.
- Ledger (P2). A double-entry, immutable ledger: accounts per party and purpose, balances derived from entries, and reversals instead of edits.
- Idempotency. Money idempotency keys stay in Postgres, in the same entry as the movement (restated from 0036).
Consequences
- A balance is always the sum of entries, so a mistake is corrected by a reversal that remains visible.
- Rounding is decided once, so two services cannot disagree on a cent.
Enforcement
planned — property tests on the allocation and the rounding modes; the ledger rejects an update to a posted entry.Supersedes and sources
Supersedes: none. Restates: decision 2 of 0036. Sources: owner money answers PP3, PP8, MN1, P2 (stackra-decisions-2026-10-19.md L517, L797, L872, 2026-10-20).