Wallet

15/12/2025 · Zascia Hugo · 6 min lees

JavaScriptMySQLREST APIsFinance
Op Hierdie Bladsy

Wallet is a RESTful transaction API designed to handle real financial operations with the seriousness they deserve. It implements two-phase debit authorization, HMAC-SHA256 request signing, multi-currency support, batch processing, and full admin controls - all built with Node.js, Express, Prisma, and MySQL.

This is not a toy CRUD app with a balance field. It is a system where every transaction has a state machine, every request can be cryptographically verified, and every mutation leaves an audit trail.

Why I Built Wallet

Financial APIs are one of the hardest things to get right in software. The domain demands precision, security, and reliability at every layer. A single rounding error, a missed race condition, or a weak auth flow can have real consequences.

I wanted to build something that demonstrates genuine understanding of financial software patterns - not just the happy path, but the edge cases:

  1. Two-phase authorization with authorize → debit/reverse patterns for safe fund holds.
  2. HMAC-SHA256 request signing with replay protection for cryptographic integrity.
  3. Precision arithmetic using monetra v2.3.0 to eliminate floating-point errors.
  4. Idempotent operations with referenceId tracking for safe retries.

System Architecture

Wallet follows a layered architecture with clear separation of concerns. Each layer has a single responsibility and dependencies flow in one direction.

Domain Layer

The domain layer contains the core business logic, completely independent of HTTP or database concerns:

  • wallet.mjs handles wallet state, balance calculations, and freeze/unfreeze logic.
  • transactions.mjs manages the transaction state machine - authorize, debit, credit, and reverse operations with enforced state transitions.

Handler Layer

Request handlers map HTTP requests to domain operations:

  • Auth handlers - registration, login, and current user retrieval with JWT tokens.
  • Wallet handlers - deposit, withdraw, balance checks, and transaction history.
  • Transaction handlers - authorize, debit, credit, and reverse endpoints.
  • Admin handlers - user listing, wallet oversight, freeze/unfreeze, and admin reversals.

Service Layer

Services coordinate between handlers and the database:

  • auth.service.mjs manages user registration, login validation, and JWT issuance.
  • wallet.service.mjs handles wallet creation, balance queries, and state management.
  • transaction.service.mjs orchestrates the full transaction lifecycle.

Middleware Layer

Express middleware handles cross-cutting concerns:

  • auth.mjs - JWT verification and user extraction.
  • rbac.mjs - role-based access control for customer and admin routes.
  • idempotency.mjs - duplicate request detection via referenceId.
  • signature.mjs - HMAC-SHA256 request signing verification.
  • requestLogger.mjs - structured request/response logging.

Infrastructure Layer

  • Prisma ORM with MySQL for type-safe database access.
  • Redis for rate limiting, caching, and idempotency key storage.
  • Structured logging, metrics collection, and alerting infrastructure.

Key Features

Two-Phase Debit Authorization

The core transaction pattern separates fund reservation from fund transfer:

  1. Authorize - places a hold on funds. The amount is reserved but still belongs to the wallet.
  2. Debit - completes the authorization, actually transferring the reserved funds.
  3. Reverse - cancels the authorization, releasing the hold back to available balance.

This is the same pattern used by payment processors like Stripe. It prevents double-spending and gives merchants a window to cancel before funds are captured.

HMAC-SHA256 Request Signing

For high-security environments, every request can be cryptographically signed. The server verifies the signature using a shared secret, ensuring requests have not been tampered with in transit. Combined with nonce-based replay protection, this guards against both modification and duplicate submission attacks.

Multi-Currency Support

The API supports 10 major currencies with automatic conversion. All arithmetic uses monetra for precise decimal operations - no floating-point rounding errors, no silent precision loss.

Batch Transactions

Multiple transactions can be processed atomically in a single request. Either all operations succeed or none do - there is no partial state.

Webhook Notifications

Real-time event notifications for transaction state changes. External systems can subscribe to wallet events and react immediately.

Rate Limiting

Comprehensive rate limiting protects against abuse. Limits are applied per-user and per-endpoint, with Redis-backed sliding window counters.

Transaction States

Every transaction follows a controlled state machine:

StateMeaning
pendingAuthorization created, funds reserved but not captured
completedTransaction finalized, funds transferred
reversedAuthorization cancelled, funds released

Direct operations (deposits and withdrawals) complete immediately. Two-phase operations transition through pending before reaching a terminal state.

API Design

The API follows RESTful conventions with versioned endpoints under /api/v1:

CategoryEndpoints
AuthRegister, login, current user
WalletsBalance, details, transactions, deposit, withdraw
TransactionsAuthorize, debit, credit, reverse
AdminUser management, wallet oversight, freeze/unfreeze, admin reversals

Interactive Swagger documentation is available at /api-docs when the server is running.

Security

Security is foundational, not bolted on:

MeasureImplementation
AuthenticationJWT tokens with configurable expiry
AuthorizationRole-based access control (Customer / Admin)
Request IntegrityHMAC-SHA256 signing with nonce-based replay protection
SQL InjectionPrisma ORM with parameterized queries
IdempotencyreferenceId tracking prevents duplicate processing
Rate LimitingRedis-backed per-user, per-endpoint limits
Precision Mathmonetra library eliminates floating-point errors
Audit TrailFull ledger with balance snapshots for every mutation

Technical Stack

  • Runtime: Node.js 20.x
  • Framework: Express
  • ORM: Prisma
  • Database: MySQL 8.x
  • Cache: Redis 7.x
  • Auth: JWT with bcrypt password hashing
  • Math: monetra v2.3.0 for financial precision
  • Testing: 201 tests (unit + E2E)
  • Docs: Swagger UI at /api-docs

Testing

The test suite covers 201 tests across unit and end-to-end layers:

  • Unit tests verify domain logic, middleware behaviour, services, and repositories in isolation.
  • E2E tests run full API flows against a live server - registration, deposits, withdrawals, two-phase transactions, and admin operations.

What I Learned

Building Wallet reinforced critical lessons about financial software:

  • Precision is non-negotiable. Floating-point arithmetic fails silently in financial contexts. Using a dedicated decimal library from day one prevented an entire class of bugs.
  • State machines prevent impossible states. Modelling transactions as explicit state transitions (pending → completed/reversed) eliminates ambiguity and makes the system auditable.
  • Idempotency is a feature, not an optimization. Network retries happen. Without idempotency keys, a retried deposit becomes a double deposit. The referenceId pattern makes every operation safely repeatable.
  • Security layers compound. JWT alone is not enough. Adding HMAC signing, replay protection, rate limiting, and RBAC creates defence in depth that no single layer could provide.

Where Can I Learn More?

  • Repository: GitHub Repo
  • Documentation: Comprehensive docs in the docs/ folder covering architecture, API reference, authentication, database schema, testing, deployment, monitoring, and scaling.