The money engine inside a digital wallet — an immutable, double-entry payment ledger with an event-driven core.
Like the part of Venmo or a neobank that actually moves money: people hold balances, get paid, send money to each other, and pay subscriptions. LedgerFlow is the backend that does that correctly — every cent accounted for, no double-charges, nothing lost, and a mathematical guarantee that the books always balance.
🔗 Live demo: add your Render URL here after deploying · log in with the admin credentials you set, or click Load demo data.
Most CRUD apps store a balance column and update it. That silently corrupts the moment two requests race, a retry fires twice, or a multi-step payment fails halfway. LedgerFlow is built the way real payment systems are, around a few guarantees:
| Guarantee | How |
|---|---|
| 🧮 Always balanced | Money is integer cents recorded as immutable, append-only double-entry rows. Every transaction's debits and credits sum to exactly zero. A balance is derived (SUM(entries)), never stored. |
| 🛡️ No double-charge | Every money movement carries an idempotency key with a unique DB constraint. Retried requests return the original transaction instead of moving money twice. |
| 🔁 Safe multi-step transfers | Multi-step flows run as a SAGA: reserve → capture, with compensation that releases funds back if a later step fails. No partial states. |
| 📡 No lost events | Each movement writes a domain event to a transactional outbox in the same DB transaction, then a relay publishes it to Redis Streams for at-least-once delivery. A Redis outage delays events; it never loses them. |
| ✅ Provably no drift | A reconciliation job re-proves that the entire ledger nets to exactly $0.00 — surfaced live on the Operations dashboard. |
There's also a recurring-payments scheduler (standing orders that execute on their own) and role-based access (customers see only their own wallets; operators see system-wide dashboards).
┌─────────────────────────────────────────────┐
React SPA ──HTTPS──▶ │ FastAPI │
(wallets, │ • /ledger double-entry posting │
ops & events │ • idempotency keys, row locks (SELECT…FOR │
dashboards) │ UPDATE), SAGA + compensation │
│ │
│ one DB transaction writes BOTH: │
│ ledger entries ──┐ ┌── outbox event │
└────────────────────┼──────┼───────────────────┘
▼ ▼
┌──────────────────────────┐
│ PostgreSQL │
│ account · ledger_entry · │
│ ledger_transaction · │
│ outbox_event · recurring │
└──────────┬────────────────┘
│ relay polls pending events
▼
┌──────────────────────────┐ ┌──────────────┐
│ Redis Stream │────▶ │ Event worker │
│ "ledger.events" │ XACK │ consumer grp │
└──────────────────────────┘ └──────────────┘
The outbox relay also drives the recurring-payment scheduler. See docs/PROJECT_GUIDE.md for the full, beginner-to-advanced walkthrough.
Backend: Python 3.14 · FastAPI · SQLModel / SQLAlchemy · PostgreSQL · Redis Streams · Alembic · uv
Frontend: React 19 · Vite · TanStack Router · Tailwind · shadcn/Radix
Infra: Docker Compose · GitHub Actions CI · Prometheus metrics · deploy on Render
# 1. create your local env from the templates (placeholders work out of the box)
cp .env.example .env
cp frontend/.env.example frontend/.env
# 2. bring up Postgres, Redis, backend, relay, worker, frontend
docker compose up -d
# 3. seed the superuser (first run)
docker compose exec backend python app/initial_data.py- Frontend: http://localhost:5173 — log in as
admin@example.com/changethis, then click Load demo data. - API docs: http://localhost:8000/docs
- Metrics: http://localhost:8000/api/v1/ledger/metrics
docker compose exec backend python -m pytest tests/ -qIncludes a concurrency test that fires simultaneous transfers against one account and asserts it can never overdraft.
- Ledger — create wallets, deposit, transfer, set up a recurring payment, watch the live activity feed.
- Operations (operator) — total volume, the drift gauge held at $0.00, and Redis stream/consumer health.
- Events (operator) — a flow diagram of the event-driven pipeline plus a live event log where you watch each event go
pending → published.
One-click-ish via the Render blueprint — see DEPLOY.md.
docs/PROJECT_GUIDE.md— full explanation from fundamentals to advanced, with examples, and how to explain it in an interview.DEPLOY.md— deploying to Render and inspecting the production database.
Built on the full-stack-fastapi-template foundation; the ledger domain, event-driven core, recurring payments, dashboards, and access model are bespoke.