A payment ledger backend built with Python, FastAPI, SQLAlchemy, and PostgreSQL. LedgerFlow demonstrates backend system design, double-entry accounting logic, concurrency-safe transaction posting, REST API development, Docker-based local setup, and automated testing.
LedgerFlow simulates the core of a payment ledger system used in fintech, wallet, or settlement platforms. Instead of storing only a final balance, it records every payment as a balanced set of debit and credit entries across accounts.
This project allows us to:
- create and manage ledger accounts
- post balanced payment transactions
- store debit and credit entries
- calculate account balances
- validate transaction integrity using double-entry accounting rules
- Python
- FastAPI
- SQLAlchemy
- PostgreSQL
- SQLite
- Pydantic
- Docker
- Pytest
- Locust
- k6
- FastAPI backend with OpenAPI documentation
- SQLAlchemy-based data layer
- PostgreSQL for production-style local development
- SQLite fallback for quick local runs
- double-entry ledger validation
- row-level account locking with PostgreSQL
SELECT ... FOR UPDATE - idempotency-key support for safe client retries
- account management APIs
- transaction posting APIs
- account balance calculation
- automated tests for core ledger workflows
- included benchmark scripts for Locust and k6
app/
api/ # Route handlers
core/ # Configuration and settings
db/ # Database engine and session management
models/ # SQLAlchemy ORM models
schemas/ # Pydantic request and response schemas
services/ # Ledger business logic
benchmarks/ # k6 and Locust load tests
scripts/ # Utility scripts such as benchmark data seeding
tests/ # Automated API tests
Core entities:
AccountLedgerTransactionLedgerEntry
LedgerFlow enforces the following rules:
- every transaction must contain at least two entries
- total debits must equal total credits
- every referenced account must exist
- accounts must be active
- account currency must match transaction currency
These constraints keep the ledger balanced, consistent, and auditable.
git clone https://github.com/Jatin2606/Payment-ledger-.git
cd Payment-ledger-python3 -m venv .venv
source .venv/bin/activatepip install -e ".[dev]"cp .env.example .envExample .env:
DATABASE_URL=postgresql+psycopg://ledger_user:ledger_password@localhost:5432/payment_ledger
APP_NAME=LedgerFlow
API_V1_PREFIX=/api/v1Start PostgreSQL:
docker compose up db -dRun the API:
uvicorn app.main:app --reloadFor quick development, you can run the backend without PostgreSQL. If DATABASE_URL is not set, the app falls back to SQLite.
uvicorn app.main:app --reloadOnce the server is running, open:
- Swagger UI:
http://127.0.0.1:8000/docs - ReDoc:
http://127.0.0.1:8000/redoc - Health Check:
http://127.0.0.1:8000/health
pytestLedgerFlow now protects concurrent posting in two ways:
- PostgreSQL row-level locking on affected accounts using
SELECT ... FOR UPDATE - optional
idempotency_keysupport so duplicate client retries return the original transaction instead of double-posting
That means two requests hitting the same accounts will serialize at the database level in PostgreSQL instead of racing through unprotected writes.
The repo includes both k6 and Locust benchmark scripts:
benchmarks/k6_transactions.jsbenchmarks/locustfile.py
python scripts/seed_benchmark_data.pyThis prints:
WALLET_ACCOUNT_IDSETTLEMENT_ACCOUNT_ID
BASE_URL=http://127.0.0.1:8000 \
WALLET_ACCOUNT_ID=<wallet_id> \
SETTLEMENT_ACCOUNT_ID=<settlement_id> \
VUS=25 \
DURATION=30s \
k6 run benchmarks/k6_transactions.jsWALLET_ACCOUNT_ID=<wallet_id> \
SETTLEMENT_ACCOUNT_ID=<settlement_id> \
locust -f benchmarks/locustfile.py --host http://127.0.0.1:8000The following results were measured on April 20, 2026 against POST /api/v1/transactions using:
- local PostgreSQL 17
- one local Uvicorn process
- Locust headless runs for
20s - benchmark payload with two ledger entries per transaction
- row-level locking and idempotency support enabled
| Concurrent Users | Requests | Throughput (req/s) | Avg Latency | p95 | p99 | Error Rate |
|---|---|---|---|---|---|---|
| 10 | 1,369 | 72.98 | 7 ms | 11 ms | 35 ms | 0.00% |
| 25 | 3,538 | 178.65 | 9 ms | 20 ms | 36 ms | 0.00% |
| 50 | 6,757 | 340.51 | 15 ms | 41 ms | 56 ms | 0.00% |
These are machine-specific local results, but they provide real benchmark data for resume bullets and interview discussion.
GET /health
POST /api/v1/accountsGET /api/v1/accountsGET /api/v1/accounts/{account_id}GET /api/v1/accounts/{account_id}/balance
POST /api/v1/transactionsGET /api/v1/transactionsGET /api/v1/transactions/{transaction_id}
curl -X POST http://127.0.0.1:8000/api/v1/accounts \
-H "Content-Type: application/json" \
-d '{
"name": "User Wallet",
"account_type": "asset",
"currency": "USD"
}'curl -X POST http://127.0.0.1:8000/api/v1/accounts \
-H "Content-Type: application/json" \
-d '{
"name": "Merchant Settlement",
"account_type": "liability",
"currency": "USD"
}'Replace ACCOUNT_ID_1 and ACCOUNT_ID_2 with real IDs from the API.
curl -X POST http://127.0.0.1:8000/api/v1/transactions \
-H "Content-Type: application/json" \
-d '{
"reference": "PAY-1001",
"idempotency_key": "IK-PAY-1001",
"description": "Wallet payment capture",
"currency": "USD",
"entries": [
{
"account_id": "ACCOUNT_ID_1",
"direction": "debit",
"amount": "125.00"
},
{
"account_id": "ACCOUNT_ID_2",
"direction": "credit",
"amount": "125.00"
}
]
}'curl http://127.0.0.1:8000/api/v1/accounts/ACCOUNT_ID_1/balance- JWT authentication and role-based access
- transaction reversal endpoint
- Alembic migrations
- GitHub Actions CI pipeline
- deployment to AWS, Render, or Railway
- audit logs and rate limiting