The ledger

Strict double-entry accounting, the invariants, and how balances stay provable.

Every payment is real accounting, not a counter. AgentPay keeps a strict double-entry ledger so balances are always provable and reconciliation never surprises you.

One payment, atomically

A single database transaction writes three rows:

  1. A receipts row (status verified, or pending for on-chain settlement).
  2. A ledger row for the agent: -amount_cents.
  3. A ledger row for the merchant: +amount_cents.

Either all three commit, or none do.

Invariants

These hold after every write, and are checked:

  • Agent balance = topups + the sum of that agent’s ledger entries.
  • Merchant balance = the sum of that merchant’s ledger entries.
  • Every receipt’s ledger entries sum to zero.

Integer cents

All values are integer cents end to end. No floats touch money. Dollars exist only at the edges for human ergonomics (wallet.topup(10.00)).

Reconciliation

The internal ledger is reconciled against real money pools (card processor balance, on-chain USDC). A reconciliation pass proves the internal float matches external state and exits non-zero on any drift.

bun run reconcile   # proves ledger == real money, or fails loudly

Treasury backing

Liabilities (agent wallets + withdrawable merchant credits) are tracked against reserves (processor balance + on-chain USDC). The backing ratio shows reserves over liabilities, so the float is always accounted for.