Loading...

12-Hour Money-Back Guarantee

Design (LLD) Paytm/Phone Wallet System - Machine Coding

Design (LLD) Paytm/Phone Wallet System - Machine Coding

Design (LLD) Paytm/Phone Wallet System - Machine Coding

4 Sept 20254 min read

Asked in Phonepe interview.

Design and implement a Paytm/PhonePe Wallet System that supports multiple concurrent users performing transactions at the same time. Your system should:

✅ Features Required

  1. User & Wallet Management

    • Each user has a unique wallet with balance.

    • Support wallet creation and balance inquiry.

  2. Concurrent Transactions

    • Add money to wallet (via UPI, Card, NetBanking).

    • Send money from one wallet to another.

    • Handle concurrent deposits and transfers without race conditions.

  3. Transaction Lifecycle

    • Each transaction can be PENDING → SUCCESS / FAILED / REVERSED.

    • Ensure atomic debit/credit (no money disappears or duplicates even under concurrency).

  4. Transaction Reliability

    • Idempotency (retries should not cause double debit).

    • Refund logic (if debit succeeds but credit fails, rollback debit).

  5. Transaction History

    • Maintain mini-statement per user.

    • Show in descending order of time (latest first).

Solution approaches

🔑 Design Patterns to Use

  1. Strategy Pattern → Different funding sources (UPI/Card/NetBanking).

    1. Why Strategy?

      • Each payment source (UPI, Card, NetBanking) has the same high-level goal (fund wallet) but different implementation.

      • We want to swap/add new methods without modifying existing code.

    2. Why not Adapter?

      • Adapter is for integrating third-party APIs with different interfaces.

      • Here, we control the implementation; we don’t need to adapt legacy/external code.

    3. Why not Bridge?

      • Bridge separates abstraction from implementation. Overkill here because abstraction (payment) and implementation (different sources) are already cleanly separated.
  2. Template Method Pattern → Standard transaction workflow (Validate → Debit → Credit → Notify).

    1. Why Template?

      • All transactions (add money, transfer, refund) follow the same skeleton: validate → debit/credit → update status → notify.

      • Only some steps differ (refund logic vs transfer logic).

    2. Why not Chain of Responsibility?

      • Chain is for passing requests along handlers dynamically. But our transaction flow is strict and sequential, not optional.
    3. Why not Command Pattern?

      • Command encapsulates actions as objects (undo/redo). We need structured workflow, not undo commands.
  3. Factory Pattern → Create the right Transaction object.

    1. Why Factory?

      • We need to create AddMoneyTransaction, SendMoneyTransaction, or RefundTransaction dynamically.

      • Centralized creation avoids if-else scattered everywhere.

    2. Why not Builder?

      • Builder is for stepwise object construction (useful for complex objects). Our transaction objects are straightforward.
    3. Why not Abstract Factory?

      • Abstract Factory creates families of related objects. Here, only one family exists (transactions).
  4. Observer Pattern → Notify users on transaction completion.

    1. Why Observer?

      • After a transaction, multiple services (SMS, Email, Push) need notification.

      • Observer decouples transaction logic from notification system.

    2. Why not Mediator?

      • Mediator centralizes communication between objects, but here we don’t want a central coordinator, we want one-to-many updates.
    3. Why not Publisher-Subscriber with Message Queue?

      • That would be a distributed solution. For in-process design, Observer is sufficient.
  5. State Pattern → Handle transaction states (PENDING, SUCCESS, etc.).

    1. Why State?

      • A transaction can move from PENDING → SUCCESS, or PENDING → FAILED, etc.

      • Each state has different rules (e.g., SUCCESS cannot go back to PENDING).

    2. Why not Strategy?

      • Strategy is for choosing different algorithms. Here, we need to model object behavior change over time.
    3. Why not Enum with Switch Case?

      • Enums are fine for simple state. But extensibility suffers when adding new states or behaviors. State Pattern is more scalable.
  6. Singleton Pattern → TransactionManager (to avoid inconsistent transaction coordination).

    1. Why Singleton?

      • TransactionManager should coordinate transactions globally. Multiple instances would risk inconsistent tracking.
    2. Why not Static Class?

      • Static class prevents dependency injection/testing. Singleton provides controlled access + lazy initialization.
    3. Why not Monostate?

      • Monostate shares state across instances, but still allows multiple objects. For a central coordinator, true Singleton is cleaner.

🔥 Concurrency Challenges to Handle

  • Race Condition: Two concurrent debits must not allow balance to go negative.

  • Deadlock Avoidance: If two users send money to each other simultaneously, system should not deadlock.

  • Atomicity: Debit and credit should be all-or-nothing.

  • Thread-Safe Wallets: Ensure balance updates are synchronized correctly.

⚡ Concurrency Choices

  1. Fine-Grained Locking per Wallet

    • Avoids blocking all transactions globally.

    • Why not Global Lock? → Poor scalability; one user blocks all.

  2. Consistent Lock Ordering (by UserId)

    • Prevents deadlock when two users transfer to each other.

    • Why not Random Locking? → Leads to deadlocks.

  3. Optimistic Concurrency (Version Check)

    • Not chosen here because wallet balances are critical → safer with pessimistic locks.

⚡ Algorithms Involved

  1. Concurrency Control Algorithm

    • Use fine-grained locks per wallet instead of global locks for scalability.

    • Always acquire locks in a consistent order (lower userId first) to avoid deadlocks.

  2. Idempotency Algorithm

    • Maintain a transactionId → status map.

    • If the same request is retried, return previous result.

  3. Rollback Algorithm

    • If debit succeeds but credit fails, perform rollback.
  4. Mini-Statement Algorithm

    • Use a priority queue / sorted list for recent transactions.

Code