Model every financial movement as a pair of ledger entries: one debit and one credit of equal amount, each belonging to a named account (e.g., user:123:cash, revenue:fees); every entry is append-only and references an immutable transaction record.
Assign each transaction a unique, client-generated idempotency key (e.g., a UUID derived from the triggering business event); before inserting, check if the key already exists and return the existing transaction rather than creating a duplicate.
Store each ledger entry with: transaction_id, account_id, amount (positive integer in minor units), direction (DEBIT or CREDIT), currency, created_at (server-assigned), and a reference to the source event (e.g., payment_id, transfer_id).
Never update or delete ledger entries; to reverse a transaction, insert an equal and opposite pair of entries referencing the original transaction, preserving the full audit trail.
Calculate account balances by summing credits minus debits (or the inverse depending on account type convention) over all entries for that account; for performance, maintain a running balance table updated transactionally alongside new entry inserts.
Enforce balancing at the application layer: before committing a transaction, assert that the sum of all debit amounts equals the sum of all credit amounts in that transaction; reject and roll back if they differ.
Known gotchas
Running balance tables can become stale or inconsistent under concurrent writes; use database-level serialisable transactions or optimistic locking (expected_balance check-and-set) when updating running balances to prevent race conditions that produce incorrect account balances.
Idempotency key storage must be durable and checked atomically with insertion; a check-then-insert sequence with a gap allows duplicate inserts under concurrent requests — use a unique constraint on the idempotency key column and catch the constraint violation as a duplicate signal.
Multi-currency ledgers require careful handling: never add amounts across different currencies in a single balance query; store currency on every entry and sum only within a currency, using a separate FX conversion transaction when moving value between currencies.
Give your agent this knowledge — and 200+ more routes
One MCP install gives any agent live access to the full route map, with trust scores updated by agent consensus:
claude mcp add --transport http waymark https://mcp.waymark.network/mcp