Exact money
SAPA represents runtime monetary amounts as bigint minor units with an
explicit currency. It never uses floating-point arithmetic for money.
Version 0.1.0 defines exact semantics for IDR and USD; other three-letter
codes are rejected until their minor-unit policy is registered centrally.
import { Billing } from "@byfungsi/sapa";
const billing = yield * Billing.oneTime({ price: { currency: "IDR", amount: 149_000n }, });149_000n IDR means 149,000 rupiah. Amounts in current checkout, price, and
payment definitions are positive. Signed adjustments use distinct constrained
types.
The same representation supports currencies with fractional minor units:
const usdPrice = { currency: "USD", amount: 14_900n } as const;14_900n USD means USD 149.00 because Polar and other USD payment rails express
amounts in cents. SAPA stores the integer and currency together; it never stores
149.00 as a floating-point number and never converts currencies implicitly.
JSON uses canonical strings
Section titled “JSON uses canonical strings”JSON has no bigint value. Use the facade-owned codecs instead of applying a
global BigInt.prototype.toJSON patch.
import { ProductCatalog, ProductCatalogJson } from "@byfungsi/sapa";import { Schema } from "effect";
declare const catalog: ProductCatalog;
const wireCatalog = Schema.encodeSync(ProductCatalogJson)(catalog);const json = JSON.stringify(wireCatalog);const decoded = Schema.decodeUnknownSync(ProductCatalogJson)(JSON.parse(json));Encoded amounts are canonical decimal strings such as "100000". The codecs
reject JSON numbers, signs, decimal points, exponent notation, leading zeroes,
and values longer than 78 digits.
Metered rates
Section titled “Metered rates”A metered rate is rational:
const rate = { currency: "IDR", amount: 25n, perUnits: 10n, rounding: "trunc",} as const;SAPA aggregates the period quantity and applies the rate once. It does not round each usage event independently.