Wallet Payment Backend

A production-style digital wallet backend focused on transactional correctness, reliability, and auditability.

Built with FastAPI, SQLModel, Redis, and Dramatiq, this project demonstrates how real-world financial APIs handle consistency guarantees, idempotent retries, double-entry ledgers, and async background processing. Every endpoint is crafted to reflect production patterns used in payment platforms.

Why this project exists

Financial backends demand more than CRUD. Every transfer, deposit, and withdrawal must preserve consistency across accounts, maintain an immutable audit trail, and handle failures without losing money. This project implements those production primitives — atomic database transactions, Redis-backed idempotency for safe retries, double-entry accounting with balance snapshots, and async workers that decouple side effects from the request path. It is a reference for how real payment backends are structured.

What it demonstrates

01 JWT authentication with bcrypt
02 Double-entry accounting ledger
03 Redis-backed idempotency with distributed locking
04 Atomic DB transactions for balance updates
05 Async background workers (Dramatiq + Brevo)
06 Database migrations via Alembic
07 Request validation with Pydantic
08 Transaction history & ledger audit trail
09 Rate limiting on auth & sensitive endpoints

Architecture

System Architecture Diagram Architecture showing Client connecting to FastAPI, which branches to SQLite and Redis. Redis connects to Dramatiq Worker which connects to Brevo. Client Browser / Mobile HTTP / JSON FastAPI (Uvicorn) Auth · Routing · Validation SQLite Users · Wallets · Txns Redis Idempotency · Lock · Queue Task push Dramatiq Worker Async email dispatch SMTP / API Brevo Transactional email

Transaction Flow

Transaction Flow (vertical) A top-down flow: Transfer Request → Auth and Validation → DB Transaction → Update Wallets → Record Tx + Ledger → Commit DB → Async Email Receipt Transfer Request Auth & Validation DB Transaction Update Wallets Record Tx + Ledger Commit DB Async Email Receipt

Get started

Explore the full API surface via Swagger UI, browse the ReDoc documentation, or view the source on GitHub.

API Reference

Request–response examples for every endpoint.

Authentication

POST /signup
Request
{
  "first_name": "Jane",
  "last_name": "Doe",
  "email": "jane@example.com",
  "password": "securepass123"
}
Response 201
{
  "id": 1,
  "first_name": "Jane",
  "last_name": "Doe",
  "email": "jane@example.com"
}
POST /token
Request (form-data)
username: jane@example.com
password: securepass123
Response 200
{
  "access_token": "eyJhbGciOiJI...",
  "token_type": "bearer"
}

Wallet

POST /wallet
Headers
Authorization: Bearer <token>
Body
{
  "balance": 500,
  "currency": "USD"
}
Response 201
{
  "id": 1,
  "user_id": 1,
  "balance": 500,
  "created_at": "2026-05-31T12:00:00",
  "currency": "USD"
}
GET /wallet
Headers
Authorization: Bearer <token>
Response 200
{
  "id": 1,
  "user_id": 1,
  "balance": 500,
  "created_at": "2026-05-31T12:00:00",
  "updated_at": "2026-05-31T12:00:00",
  "currency": "USD",
  "full name": "Jane Doe"
}
DELETE /wallet
Headers
Authorization: Bearer <token>
Response 200
{
  "message": "wallet belonging to Jane Doe deleted successfully"
}

Transaction

PATCH /transaction/deposit
Headers
Authorization: Bearer <token>
X-Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
Body
{
  "amount": 200,
  "currency": "USD"
}
Response 200
{
  "wallet_id": 1,
  "amount": 200,
  "current_balance": 700
}
PATCH /transaction/withdraw
Headers
Authorization: Bearer <token>
X-Idempotency-Key: 660e8400-e29b-41d4-a716-446655440001
Body
{
  "amount": 50,
  "currency": "USD"
}
Response 200
{
  "wallet_id": 1,
  "amount": 50,
  "current_balance": 650
}
PATCH /transaction/transfer
Headers
Authorization: Bearer <token>
X-Idempotency-Key: 770e8400-e29b-41d4-a716-446655440002
Body
{
  "to_account_id": 2,
  "amount": 100,
  "currency": "USD"
}
Response 200
{
  "wallet_id": 1,
  "recipient_wallet_id": 2,
  "amount": 100,
  "current_balance": 550
}
GET /transaction/history
Headers
Authorization: Bearer <token>
Query
?limit=10&offset=0
Response 200
{
  "transactions": [
    {
      "id": 3,
      "amount": 100,
      "currency": "USD",
      "type": "transfer",
      "date": "2026-05-31T12:05:00",
      "sender": "Jane Doe",
      "receiver": "John Smith"
    },
    {
      "id": 2,
      "amount": 50,
      "currency": "USD",
      "type": "withdrawal",
      "date": "2026-05-31T12:03:00",
      "sender": "Jane Doe",
      "receiver": "External Destination"
    }
  ],
  "limit": 10,
  "offset": 0
}
GET /transaction/{id}/ledger
Headers
Authorization: Bearer <token>
Path
/transaction/3/ledger
Response 200
[
  {
    "id": 5,
    "transaction_id": 3,
    "wallet_id": 1,
    "entry_type": "debit",
    "amount": 100,
    "currency": "USD",
    "balance_snapshot": 550,
    "created_at": "2026-05-31T12:05:00"
  },
  {
    "id": 6,
    "transaction_id": 3,
    "wallet_id": 2,
    "entry_type": "credit",
    "amount": 100,
    "currency": "USD",
    "balance_snapshot": 300,
    "created_at": "2026-05-31T12:05:00"
  }
]

Users

GET /users/{user_id}
Path
/users/1
Response 200
{
  "id": 1,
  "first_name": "Jane",
  "last_name": "Doe",
  "email": "jane@example.com"
}