Blog 6 — Uber: Rebuilding the Payment Platform

C

Qubits of DPK

March 21, 2026

Core Case Studies
Core Concept: Double-entry bookkeeping in software, ACID vs BASE tradeoffs
Why SDE-2 Critical: Financial systems design — asked at Uber, Ola, Swiggy, Zepto
Status: Draft notes ready

Quick Revision

  • Problem: Payments span multiple services but money must still balance exactly.
  • Core pattern: Double-entry ledger plus idempotent saga-based workflows.
  • Interview one-liner: Financial systems optimize for correctness before availability or convenience.

️ Architecture Overview

javascript
QUBITS OF DPK
1Trip Completed
234Payment Service
56     ├── Ledger Service (double-entry bookkeeping)
7     ├── Payment Gateway (Stripe / Braintree / local)
8     ├── Wallet Service (Uber Cash)
9     └── Invoice Service

Core Concepts

Double-Entry Bookkeeping in Software

  • Every financial transaction has TWO entries: a debit and a credit
  • Sum of all debits = Sum of all credits (always balances)
  • This is how banks have worked for 500+ years — Uber applied it to software
javascript
QUBITS OF DPK
1Trip costs ₹200:
2
3  DEBIT:  Rider's account        -200
4  CREDIT: Uber's revenue account +180
5  CREDIT: Driver's earnings      +20
6
7  Total debits = Total credits → ₹200 =200
Why this matters:
  • If any entry is missing → imbalance detected immediately
  • Audit trail for every rupee — required for financial compliance
  • Bugs in payment code show up as balance mismatches

ACID vs BASE in Payments

javascript
QUBITS OF DPK
1ACID (what payments need):
2  Atomicity  → all steps succeed or all rollback
3  Consistency → money never disappears or duplicates
4  Isolation  → concurrent payments don't interfere
5  Durability → committed payments survive crashes
6
7BASE (what social feeds use):
8  Basically Available
9  Soft state
10  Eventually consistent
11Fine for likes and comments, NEVER for money

The Saga Pattern for Distributed Payments

javascript
QUBITS OF DPK
1Payment involves multiple services:
2  Step 1: Debit rider wallet
3  Step 2: Credit driver wallet
4  Step 3: Record in ledger
5  Step 4: Send receipt
6
7If Step 3 fails:
8  Compensating transaction:
9Reverse Step 2 (debit driver)
10Reverse Step 1 (credit rider back)
11Payment rolled back safely

Idempotency in Payments

javascript
QUBITS OF DPK
1Every payment request has a unique payment_id
2If same payment_id received twice:
3Check DB → already processed → return same result
4Never charge twice

Why the Rebuild Was Needed

5 Interview Questions This Blog Unlocks

Q1. Design a digital wallet system (like Uber Cash / Paytm)

Answer: Ledger table with double-entry. Each credit/debit is a row. Balance = sum of all entries for user. Idempotency keys on all transactions. ACID transactions only — never BASE for money. Event-sourced (store all events, derive balance).

Q2. What is double-entry bookkeeping and why use it in software?

Answer: Every transaction has a matching debit and credit. Total debits always equal total credits. If they don't match, there's a bug. Provides automatic consistency check, full audit trail, and is legally required for financial systems.

Q3. Why must payment systems use ACID, not eventual consistency?

Answer: Eventual consistency means data may be temporarily inconsistent. For social feeds, that's fine — a like appearing 1 second late is acceptable. For money, a 1-second window where ₹500 exists in neither sender's nor receiver's account is a financial bug and regulatory violation.

Q4. How do you handle a payment that partially completes across multiple services?

Answer: Saga pattern. Each step has a compensating transaction. If any step fails, compensating transactions run in reverse order to roll back the entire payment atomically. Unlike 2-phase commit, saga works across microservices without distributed locks.

Q5. How would you prevent race conditions when two concurrent requests debit the same wallet?

Answer: Optimistic locking: add version column to wallet. Read version=5, debit, update WHERE version=5. If another request updated first → version mismatch → retry. Or use database row-level locking: SELECT FOR UPDATE on the wallet row.

Key Engineering Lessons