Skip to content

Sagar2inf/virtual-wallet

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

High-Throughput Virtual Wallet System

A robust, multi-currency virtual wallet backend built with Node.js, PostgreSQL, and Redis. This system is engineered to handle high-concurrency transfers while maintaining strict data integrity and financial accuracy.

Why This Tech:

1. PostgreSQL vs. NoSQL (MongoDB/DynamoDB)

While NoSQL databases scale horizontally easily, they often struggle with complex multi-row transactions.

  • The Choice: PostgreSQL.
  • The Reasoning: In a wallet, a "Transfer" is not one event; it is a multi-step process (Check balance -> Debit A -> Credit B -> Log Ledger). PostgreSQL's ACID compliance ensures that if the system fails at step 3, step 1 and 2 are rolled back automatically. Using a Relational DB allows us to use Strict Schema Constraints to prevent "negative balances" at the database level, providing a second layer of defense beyond the application logic.

2. Node.js (Runtime)

  • The Choice: Node.js (Asynchronous I/O).
  • The Reasoning: Financial APIs are "I/O bound," meaning the CPU spends most of its time waiting for the database or cache. Node's non-blocking event loop allows a single instance to handle thousands of concurrent "Balance Check" requests without getting stuck behind a slow "Heavy Transfer" request. It provides the high concurrency needed for a global wallet.

3. Redis: The "Idempotency Shield"

  • The Choice: Redis (In-memory KV Store).
  • The Reasoning: Checking if a transaction is a duplicate by querying a Postgres table with millions of rows is expensive (Disk I/O). By using Redis with a 12-hour TTL (Time-To-Live), we create a high-speed "Shield." 99% of duplicate retries are caught in Redis in <1ms, never even touching the primary database. This preserves the Database's resources for actual money-moving operations.

4. Hybrid Consistency Model (The "Secret Sauce")

Instead of a single architecture, we split the logic based on the Account Type:

  • User-to-User: Strong Consistency. Users expect to see their money gone or arrived immediately. We use row-level locking here.
  • System-to-User: Eventual Consistency. Because the SYSTEM account touches every transaction, locking it would create a "Bottleneck." We use a Ledger-first approach where the system balance is recalculated in the background. This allows the system to scale to 10,000+ transactions per second without the "System Row" becoming a point of contention.

5. Double-Entry Accounting

We do not treat the balance column as the "Source of Truth." It is treated as a Calculated Cache. The true state of the system is the sum of the ledger_entries. This allows us to:

  1. Audit: Detect if a bug ever "created" money out of thin air.
  2. Recover: If the accounts table is corrupted, we can rebuild it perfectly from the ledger history.

🚀 Technical Highlights & Problem Solving

1. Concurrency & Race Conditions

To prevent "Double Spending" while maintaining high performance, the system uses a Hybrid Locking Strategy:

  • For Users: Uses Pessimistic Locking (SELECT ... FOR UPDATE) to ensure that a user's balance cannot be manipulated by two simultaneous requests.
  • For System Accounts: Uses Eventual Consistency. To avoid making the System Account a bottleneck (as it participates in almost every transaction), the system records ledger entries instantly but updates the balance via a background worker.

2. Deadlock Prevention

Deadlocks are avoided by enforcing a Strict Locking Order. In every transfer, the system sorts the Account IDs and locks them in a consistent sequence (e.g., smaller ID first). This ensures that two users transferring to each other simultaneously can never "lock each other out."

3. Idempotency & Duplicate Protection

The system implements a Unique Transaction Guard:

  • Every transfer requires an idempotencyKey.
  • A unique constraint on the ledger_entries table ensures that if a network retry occurs, the system will never process the same transaction twice or "mint" extra money.

4. Data Integrity (The "Double-Entry" Principle)

The system follows a strict ledger-based architecture. Every balance change is backed by a debit and credit entry.

  • 5-Minute Sync: An incremental cron job updates the system balance to keep it "fresh."
  • 24-Hour Audit: A full reconciliation script sums the entire ledger history daily to reset the system balance to the Absolute Truth, fixing any micro-discrepancies.

🏗️ Architecture

  • Runtime: Node.js (ES Modules)
  • Database: PostgreSQL (Relational integrity & MVCC)
  • Cache: Redis (Storing Idempontency Key 12hr, and this avoid hitting db for invalid transections.)
  • Background Tasks: node-cron for scheduled reconciliation

🛠️ Setup & Installation

Prerequisites

  • Docker and Docker Compose

Run with Docker

  1. Clone the repository.
  2. Build and start the containers:
    docker-compose up --build

API Documentation:

  1. Check Balance: GET /wallet/balance/:ownerId

    • Returns all currency accounts (COIN, GEM, etc.) associated with a single owner ID.
  2. Transfer Funds: POST /wallet/transfer

    {
        "fromId": "UUID",
        "toId": "UUID",
        "amount": 50,
        "idempotencyKey": "unique-uuid-123"
    }
    • In this method, there are total 4 ways of transection.
      1. User to User -> (Sending gift cards)
      2. User to System -> (Buying any subscription or spending virtual currency)
      3. System to User -> (offering gifts or rewards to user)
      4. System to User -> (Buying virtual currency with real money)
    • In all of above 4 methods 75% times System account used for transection, this is the reason of not locking system account everytime.
  3. Transection History: GET /wallet/history/:ownerId?limit=20&offset=0 Returns a paginated list of all ledger movements for an owner.

    offset: how many transection rows to skip before start counting limit: how many transection rows to return in single request

[NOTE] I implemented only 2 virtual currencies for now. and a single user will have a separate accout for each type of currency.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors