Skip to main content

Money Service — Overview

The Money Service is the financial core of the platform. It provides safe wallet management, balance operations, and transaction processing for all modules. No module handles money arithmetic directly — modules call kernel.money() and the service guarantees correctness, atomicity, and a full audit trail.

The single most important rule: All amounts are stored and transmitted as integer cents. Never use floating-point for money. $12.50 = 1250. $0.01 = 1.


Technical Stack

ComponentTechnologyRole
Go runtimepgxpool + pgx/v5 + sqlcDatabase access, connection pool
DatabasePostgreSQL (ACID)Wallets, transactions, holds
Amount typeshopspring/decimalDecimal arithmetic without float rounding
Storage typeBIGINTInteger cents; max ≈ $92 quadrillion
IdempotencyUUID per operationDuplicate-safe at the database level
Limits cacheValkey (5-minute TTL)Billing plan limits (max tx amount, max balance)

Design Principles

Integer Cents — No Float

All amounts are BIGINT cents. Float arithmetic is banned because IEEE 754 rounding causes errors in financial calculations:

0.1 + 0.2 = 0.30000000000000004 ← float error
100 + 200 = 300 ← integer, exact

Every SDK method accepts and returns integer cents. Formatting for display ($12.50) is the responsibility of the UI layer — the Money Service never formats amounts as strings.

ACID Transactions

All operations (credit, debit, transfer, hold, confirm, cancel, reversal) run inside a single PostgreSQL ACID transaction. Partial failure is structurally impossible — either all steps commit or all steps roll back.

The Saga Pattern is not needed for internal Money operations. All wallets live in the same database. Saga applies only when coordinating Money with external payment gateways (Stripe, PayPal), which is handled by the hold/confirm/cancel pattern.

Double-Entry Bookkeeping

Every transaction produces two ledger entries: a debit on one account and a credit on another. This ensures the sum of all balances across the system is always zero (conservation of value).

Idempotency

Every balance-modifying operation requires an idempotencyKey (UUID). A duplicate request with the same idempotencyKey returns the original result without re-executing the operation. This makes retry logic safe for all callers.


Wallet Balance Fields

A wallet balance has three independent fields:

FieldMeaning
availableSpendable balance. Reduced by debit and hold. Increased by credit and cancel.
pendingReserved for async deposit confirmations (e.g. payment gateway callback pending)
frozenBalance locked by a hold. Not spendable. Released by confirm (→ debit) or cancel (→ available)
Total wallet value = available + pending + frozen

REST API

MethodEndpointDescription
POST/api/v1/walletsCreate wallet
GET/api/v1/walletsList wallets (paginated, filter by userId, currency)
GET/api/v1/wallets/:idGet wallet details
GET/api/v1/wallets/:id/balanceGet current balance (available, pending, frozen)
POST/api/v1/wallets/:id/creditCredit (add funds)
POST/api/v1/wallets/:id/debitDebit (withdraw funds)
POST/api/v1/wallets/:id/holdFreeze funds (hold)
POST/api/v1/wallets/:id/confirmConfirm hold → debit
POST/api/v1/wallets/:id/cancelCancel hold → return to available
POST/api/v1/wallets/:id/reversalReverse a completed transaction
POST/api/v1/wallets/transferTransfer between two wallets
GET/api/v1/wallets/:id/transactionsTransaction history (paginated)
GET/api/v1/transactions/:idGet transaction details

Transaction Types

TypeDescriptionBalance effect
creditAdd funds to a walletavailable += amount
debitWithdraw funds from a walletavailable -= amount
transferMove funds between two walletsavailable -= amount (from), available += amount (to)
holdFreeze funds pending confirmationavailable -= amount, frozen += amount
confirmFinalize a hold as a debitfrozen -= amount
cancelRelease a hold back to availablefrozen -= amount, available += amount
reversalCounter-transaction to a completed txOpposite effect of original

Transaction Status Lifecycle

┌─────────┐
│ created │
└────┬────┘
│ Processing
┌─────────┴─────────┐
│ │
┌────▼─────┐ ┌──────▼──────┐
│ completed│ │ pending │
└────┬─────┘ └──────┬──────┘
│ │ hold
┌────▼──────┐ ┌──────▼──────┐
│ reversed │ │ held │
└───────────┘ └──┬───────┬──┘
confirm│ │cancel / TTL expiry
┌────▼──┐ ┌──▼──────┐
│confirm│ │canceled │
└───────┘ └─────────┘

  • Wallets — create, list, balance fields, multi-currency
  • Transactions — credit, debit, transfer, idempotency
  • Hold / Confirm / Cancel — two-phase debit pattern, TTL, auto-cancel worker
  • Reversals — counter-transactions, 365-day window
  • Guarantees — ACID, concurrent protection, deadlock handling
  • Limits — max amounts, Billing plan, fail-closed behavior