Back to System Design Problems
Q2Question 2 of 13

Design a Digital Wallet System (like Paytm/Google Pay)

Key Concepts
  • Every wallet operation is backed by a double-entry append-only ledger : debit one side, credit the other, sum always zero.
  • Idempotency keys on every operation (top-up, payment, transfer) prevent duplicate money movement on retry.
  • Optimistic locking with a version column prevents lost updates when concurrent operations touch the same balance.
  • P2P transfers wrap the debit and credit in a single database transaction for atomicity.
  • Daily and monthly spending limits are checked before each operation, satisfying regulation and limiting fraud damage.
  • Settlement and reconciliation run as scheduled jobs comparing the wallet ledger against gateway statements.
  • Partial failures (gateway succeeds, local write fails) are repaired by the reconciliation job, never silently losing money.

Definition

Back every wallet balance with an idempotent, double-entry, append-only ledger, protect updates with optimistic locking, enforce limits, detect fraud, and reconcile against the gateway.
  • Double-entry ledger : every operation writes two matching entries (debit + credit) so the sum is always zero.
  • Idempotency : each operation carries a unique key; replays return the same result without moving money twice.
  • Optimistic locking : balance updates succeed only if the row version is unchanged since read, preventing lost updates.
  • Settlement : moving funds between the wallet system and partner banks.
  • Reconciliation : comparing the wallet ledger against external gateway records to detect and repair mismatches.

Rajesh keeps money in his National Bank of India wallet app. He tops up from his bank, pays Priya for groceries, and sometimes gets a refund. Every rupee movement is written twice in the books. If his phone retries, the wallet charges once. Daily limits stop him exceeding a cap. At day's end the wallet checks its books against the bank's records.

Understanding the Concepts

How does double-entry balance management work?

Every operation produces two matching ledger entries. A top-up credits the user's wallet and debits a funding source. A payment debits the payer and credits the payee. The signed sum across both entries is always zero. This is the same accounting principle used by National Bank of India's core banking system.

JAVA
package com.bank.wallet;

import java.math.BigDecimal;

public class WalletLedger {
    public void topUp(String walletId, BigDecimal amount, String fundingSource) {
        ledger.append(new Entry(fundingSource, amount.negate())); // debit source
        ledger.append(new Entry(walletId, amount));               // credit wallet
    }
}

How does optimistic locking protect the balance?

Each wallet row has a version column. A debit succeeds only if the version is unchanged since the read. If two concurrent requests try to debit the same wallet, only one wins. The loser gets zero rows updated and must retry with the fresh version.

JAVA
package com.bank.wallet;

import java.math.BigDecimal;

public class OptimisticLockingDebit {
    public boolean debit(String walletId, BigDecimal amount, long expectedVersion) {
        int rows = jdbc.update(
            "UPDATE wallets SET balance = balance - ?, version = version + 1 " +
            "WHERE id = ? AND version = ? AND balance >= ?",
            amount, walletId, expectedVersion, amount);
        return rows == 1;
    }
}

How does idempotency work?

Each operation carries a client-generated key. The server records outcomes by key, so replays return the same result without moving money twice. If Rajesh's phone retries a top-up because the network timed out, the wallet returns the original result.

JAVA
package com.bank.wallet;

import java.math.BigDecimal;

public class IdempotentWalletService {
    public WalletResult topUp(String idemKey, String walletId, BigDecimal amount) {
        WalletResult prior = idempotencyStore.get(idemKey);
        if (prior != null) return prior;  // safe replay
        WalletResult result = executeTopUp(walletId, amount);
        idempotencyStore.put(idemKey, result);
        return result;
    }
}

How do P2P transfers work atomically?

Debit sender and credit receiver in a single database transaction. If either leg fails, the whole transfer rolls back. The idempotency key prevents duplicate transfers on retry.

JAVA
package com.bank.wallet;

import java.math.BigDecimal;
import org.springframework.transaction.annotation.Transactional;

public class P2PTransferService {
    @Transactional
    public void transfer(String from, String to, BigDecimal amount, String idemKey) {
        if (idempotency.seen(idemKey)) return;
        ledger.post(from, to, amount);
        walletRepo.debit(from, amount);
        walletRepo.credit(to, amount);
        idempotency.record(idemKey);
    }
}

How do daily and monthly limits work?

Limits are checked before each spend operation. An over-limit operation is blocked before any money moves. This satisfies RBI regulations and limits the damage from a compromised account. The limit check queries the sum of recent transactions within the window.

JAVA
package com.bank.wallet;

import java.math.BigDecimal;

public class LimitChecker {
    private static final BigDecimal DAILY_LIMIT = new BigDecimal("100000.00");

    public void checkDailyLimit(String walletId, BigDecimal amount) {
        BigDecimal spentToday = ledger.sumDebitsToday(walletId);
        if (spentToday.add(amount).compareTo(DAILY_LIMIT) > 0)
            throw new DailyLimitExceededException(walletId, DAILY_LIMIT);
    }
}

How does fraud detection work?

The system scores each transaction on velocity (how many transactions in the last hour), amount (unusually large), and device signals (new device, new location). Suspicious transactions are blocked or challenged with an OTP. Rajesh transferring 50,000 rupees from a new device at 3 AM triggers a challenge.

How does refund work?

A refund is a new pair of ledger entries: credit the original payer, debit the merchant. Never edit the original payment entries. Both the payment and the refund are visible in the ledger, linked by a reference id, preserving the full audit trail.

How does reconciliation handle partial failures?

If a top-up succeeds at the gateway but the local write fails, the reconciliation job detects the mismatch by comparing the gateway's settlement file against the wallet ledger. It creates correcting entries and alerts the operations team. No money is ever silently lost or gained.

What should a senior engineer know?

Designing idempotency, optimistic locking, atomic transfers, and reconciliation together is what makes a wallet trustworthy enough to hold real money. A senior engineer also designs for multi-currency support, plans the settlement cycle with partner banks, and monitors reconciliation mismatches as a key operational health metric.

Visual Overview

DIAGRAM
  Digital Wallet Architecture

  ┌──────────┐  top-up/pay  ┌──────────────────┐
  │  Client  │─────────────►│  Wallet Service  │
  │  (idem   │              │                  │
  │   key)   │              │ 1. Idempotency   │
  └──────────┘              │ 2. Limit check   │
                            │ 3. Fraud score   │
                            │ 4. Debit/credit  │
                            │ 5. Ledger entry  │
                            └────────┬─────────┘
                                     │
                   ┌─────────────────┼─────────────────┐
                   │                 │                 │
            ┌──────▼──────┐  ┌──────▼──────┐  ┌──────▼──────┐
            │  Wallets    │  │  Ledger     │  │  Gateway    │
            │  (balance,  │  │  (append-   │  │  (bank      │
            │   version)  │  │   only,     │  │   funding)  │
            └─────────────┘  │   double-   │  └─────────────┘
                             │   entry)    │
                             └─────────────┘

  Reconciliation job (nightly):
  Ledger entries <-> Gateway statements
  Mismatch -> auto-repair or alert

Key Takeaway

A digital wallet system maintains user balances backed by a double-entry append-only ledger. Idempotency keys prevent duplicate money movement. Optimistic locking prevents lost updates. P2P transfers wrap debit and credit in one transaction. Limits and fraud detection protect the system, and reconciliation repairs partial failures.

At National Bank of India, this combination made the wallet trustworthy enough to hold real money, with the reconciliation job as the safety net that never lets a mismatch go undetected.

Remember one sentence: back every balance with a double-entry ledger, make every operation idempotent, protect with optimistic locking, and reconcile nightly to catch partial failures.

---

Share
Use ← → arrow keys to navigate