1. Requirements & Scope (5 min)
Functional Requirements
- Vehicles with RFID-based FASTag pass through toll plazas without stopping — the system reads the tag, identifies the vehicle, and deducts the toll amount in real time
- Support prepaid wallet accounts linked to FASTag. Users can recharge via UPI, net banking, credit/debit cards, or auto-top-up
- Classify vehicles (car, LCV, bus, truck, multi-axle) automatically using RFID tag metadata and optionally ANPR (Automatic Number Plate Recognition) for verification
- Handle edge cases: insufficient balance (let pass with negative balance up to a threshold, or deny entry), cloned/blacklisted tags, expired tags, tag-less vehicles (fallback to ANPR + manual toll)
- Generate trip receipts, monthly statements, and provide real-time balance/transaction history via mobile app and SMS alerts
Non-Functional Requirements
- Availability: 99.99% — toll plazas operate 24/7. Even 1 minute of downtime causes massive traffic jams at ~800+ plazas nationwide.
- Latency: < 300ms end-to-end from RFID scan to barrier lift. Vehicles pass at 30 km/h through dedicated FASTag lanes — the window for processing is ~2 seconds.
- Consistency: Strong consistency for balance deduction. We cannot deduct the same balance twice or allow a transaction to silently fail and still charge.
- Scale: India has ~8 crore (80M) FASTags issued. ~1 crore (10M) toll transactions/day across 800+ plazas with 4000+ lanes. Peak: ~3x average during festivals/holidays.
- Durability: Every transaction must be recorded. Financial data — zero loss. Full audit trail for regulatory compliance (NHAI, NPCI).
2. Estimation (3 min)
Traffic
- 10M transactions/day = ~115 TPS average
- Peak (festival season, 3x): ~350 TPS
- Each transaction involves: RFID read → tag lookup → balance check → debit → receipt → barrier signal = 6 operations
- Effective peak internal ops: ~2100 ops/sec
- Read-heavy on tag lookup (every vehicle approaching triggers a read), write-heavy on transactions
Storage
- 80M FASTag accounts: ~500 bytes each (tag ID, vehicle info, owner, balance, status) = 40GB
- 10M transactions/day x 365 days x 200 bytes = 730GB/year of transaction logs
- 5 years retention (regulatory): ~3.7TB of historical transactions
- Hot data (accounts + last 30 days txns): ~100GB — fits comfortably in-memory cache + SSD
Bandwidth
- Each RFID read event from plaza: ~200 bytes
- Each transaction response to plaza: ~300 bytes
- Peak: 350 x 500 bytes = ~175KB/s — negligible bandwidth
- The bottleneck is latency (sub-300ms), not throughput
Key Insight
This is a latency-critical financial transaction system with strong consistency requirements. The scale is modest (350 TPS peak is not extreme), but the latency budget is brutal (300ms including network to remote plazas) and the consequences of failure are physical (traffic jams, accidents). The design must prioritize reliability, fast failover, and degraded-mode operation.
3. API Design (3 min)
Toll Transaction API (Plaza → Central System)
POST /api/v1/toll/process
Headers: X-Plaza-ID, X-Lane-ID, X-Timestamp, X-Signature (HMAC)
Body: {
"tag_id": "34161FA820328...", // 24-char RFID EPC
"plaza_id": "PL_MUM_0042",
"lane_id": "L3",
"vehicle_class_detected": "CAR", // from RFID tag + ANPR
"anpr_plate": "MH02AB1234", // optional, for verification
"toll_amount": 125.00, // plaza knows its own rates
"transaction_id": "TXN_20260223_PL42_L3_00145" // idempotency key
}
Response 200: {
"status": "SUCCESS", // SUCCESS | INSUFFICIENT_BALANCE | BLACKLISTED | TAG_EXPIRED
"balance_after": 375.00,
"vehicle_reg": "MH02AB1234",
"vehicle_class": "CAR",
"receipt_id": "RCP_abc123"
}
Response time target: < 150ms server-side (remaining 150ms for network)
Account Management APIs
GET /api/v1/account/{tag_id}/balance
POST /api/v1/account/{tag_id}/recharge
Body: { "amount": 500.00, "payment_method": "UPI", "upi_txn_ref": "..." }
GET /api/v1/account/{tag_id}/transactions?from=2026-02-01&to=2026-02-23
GET /api/v1/account/{tag_id}/statement?month=2026-02
POST /api/v1/tags/blacklist
Body: { "tag_id": "...", "reason": "CLONED" | "FRAUD" | "COURT_ORDER" }
Key Decisions
- Idempotency key (
transaction_id) is critical — network retries from plaza must not double-debit - Plaza sends the toll amount (plaza knows its own toll rates based on vehicle class and distance). The central system validates but trusts the plaza for rate determination.
- HMAC signature prevents tampered requests from compromised plaza hardware
4. Data Model (3 min)
FASTag Accounts (PostgreSQL — primary, with read replicas)
| Field | Type | Notes |
|---|---|---|
| tag_id | VARCHAR(24) PK | RFID EPC code |
| account_id | UUID | Wallet account |
| vehicle_reg | VARCHAR(15) | Registration number |
| vehicle_class | ENUM | CAR, LCV, BUS, TRUCK, MAV, OVERSIZED |
| owner_name | VARCHAR(100) | |
| owner_phone | VARCHAR(15) | For SMS alerts |
| issuer_bank | VARCHAR(10) | Bank code (ICICI, SBI, etc.) |
| balance | DECIMAL(12,2) | Current wallet balance |
| min_balance | DECIMAL(12,2) | Auto-recharge threshold |
| status | ENUM | ACTIVE, BLACKLISTED, EXPIRED, SUSPENDED |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP |
Transactions (PostgreSQL — partitioned by month)
| Field | Type | Notes |
|---|---|---|
| txn_id | VARCHAR(50) PK | Idempotency key from plaza |
| tag_id | VARCHAR(24) FK | |
| plaza_id | VARCHAR(20) | |
| lane_id | VARCHAR(5) | |
| toll_amount | DECIMAL(10,2) | Amount debited |
| balance_before | DECIMAL(12,2) | For audit |
| balance_after | DECIMAL(12,2) | For audit |
| vehicle_class | ENUM | Class at time of transaction |
| status | ENUM | SUCCESS, FAILED, REVERSED, DISPUTED |
| created_at | TIMESTAMP |
Blacklist Table (Redis + PostgreSQL)
| Field | Type | Notes |
|---|---|---|
| tag_id | VARCHAR(24) PK | |
| reason | ENUM | CLONED, FRAUD, COURT_ORDER, REPORTED_LOST |
| blacklisted_at | TIMESTAMP | |
| blacklisted_by | VARCHAR(50) | Issuer bank or NPCI |
Why These DB Choices?
- PostgreSQL for accounts and transactions: ACID compliance is non-negotiable for financial data.
SELECT ... FOR UPDATEon account row provides strong consistency for balance deduction. Partitioned by month for transactions to keep query performance high. - Redis for hot path: Cache tag_id → account mapping and blacklist set. The toll processing path hits Redis first (< 1ms) before touching PostgreSQL. Blacklist is a Redis Set —
SISMEMBERis O(1) and sub-millisecond. - Kafka for async processing: Transaction events stream to Kafka for downstream consumers (SMS alerts, bank reconciliation, analytics, fraud detection).
5. High-Level Design (12 min)
System Architecture
┌─────────────────────────────────────────────────────────────────┐
│ Toll Plaza (x800+) │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────────┐ │
│ │ RFID │ │ ANPR │ │ Plaza │ │ Barrier │ │
│ │ Reader │──▶│ Camera │──▶│ Controller│──▶│ Actuator │ │
│ │ │ │ │ │ (Edge) │ │ │ │
│ └──────────┘ └──────────┘ └─────┬─────┘ └────────────┘ │
│ │ │
└──────────────────────────────────────┼──────────────────────────┘
│ HTTPS (dedicated leased line + 4G backup)
▼
┌─────────────────┐
│ Load Balancer │
│ (Regional, L4) │
└────────┬─────────┘
│
┌───────────────────┼───────────────────┐
│ │ │
┌──────▼──────┐ ┌──────▼──────┐ ┌──────▼──────┐
│ Toll │ │ Toll │ │ Toll │
│ Processing│ │ Processing│ │ Processing│
│ Service │ │ Service │ │ Service │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
┌─────────┼───────────────────┼───────────────────┤
│ │ │ │
┌─────▼────┐ ┌─▼──────────┐ ┌───▼────────┐ ┌─────▼──────┐
│ Redis │ │ PostgreSQL │ │ Kafka │ │ Blacklist │
│ Cache │ │ (Primary │ │ (Events) │ │ Service │
│ (tag → │ │ + Read │ │ │ │ │
│ account)│ │ Replicas) │ │ │ │ │
└──────────┘ └────────────┘ └─────┬──────┘ └────────────┘
│
┌──────────────────┼──────────────────┐
│ │ │
┌──────▼──────┐ ┌──────▼──────┐ ┌──────▼──────┐
│ SMS/Push │ │ Bank │ │ Fraud │
│ Notifier │ │ Reconcile │ │ Detection │
│ Service │ │ Service │ │ Engine │
└─────────────┘ └─────────────┘ └─────────────┘
Toll Transaction Flow (Happy Path)
Vehicle enters FASTag lane at 30 km/h
│
▼
RFID Reader reads tag EPC: "34161FA820328..." (~100ms)
│
▼
ANPR Camera captures plate: "MH02AB1234" (parallel, async)
│
▼
Plaza Controller sends POST /api/v1/toll/process
│
▼
Toll Processing Service:
1. Check Redis blacklist set: SISMEMBER blacklist "34161FA..." → 0 (not blacklisted) [<1ms]
2. Get account from Redis cache: GET tag:34161FA... → {account_id, balance, status} [<1ms]
3. Validate: tag status = ACTIVE, balance >= toll_amount
4. BEGIN TRANSACTION (PostgreSQL)
- SELECT balance FROM accounts WHERE tag_id = '34161FA...' FOR UPDATE
- Verify balance >= 125.00 (double-check, Redis might be stale)
- UPDATE accounts SET balance = balance - 125.00 WHERE tag_id = '34161FA...'
- INSERT INTO transactions (txn_id, ...) VALUES (...)
- ON CONFLICT (txn_id) DO NOTHING // idempotency
COMMIT [~10-20ms]
5. Update Redis cache: SET tag:34161FA... {new_balance} [<1ms]
6. Publish to Kafka: {txn_event} [<5ms]
7. Return response to plaza [total server time: ~30-40ms]
│
▼
Plaza Controller receives SUCCESS → signals barrier to lift
│
▼
Vehicle passes. Total time: ~250ms from RFID read to barrier lift.
Component Responsibilities
- RFID Reader (at plaza): Reads the 96-bit EPC code from the FASTag as the vehicle approaches at 30 km/h. UHF RFID, range 3-10 meters.
- ANPR Camera (at plaza): Captures license plate image. Used for: (a) verifying tag-vehicle match, (b) toll collection from tag-less vehicles, (c) evidence in disputes.
- Plaza Controller (edge device): Local Linux box at each plaza. Orchestrates RFID + ANPR + barrier. Has local fallback logic if the central system is unreachable.
- Toll Processing Service: Stateless. Handles the critical debit path. Horizontally scalable.
- Redis Cache: Hot account data and blacklist. Reduces PostgreSQL load for reads.
- PostgreSQL: Source of truth for all financial data. Single primary with synchronous replication to one standby (zero data loss).
- Kafka: Decouples the critical toll path from downstream consumers. Transaction events feed SMS, reconciliation, fraud, analytics.
- Bank Reconciliation Service: Daily batch job. Matches FASTag transactions with issuer bank settlement files. Flags discrepancies.
- Fraud Detection Engine: Consumes transaction stream. Detects anomalies (same tag at two distant plazas within impossible travel time, rapid transactions, etc.).
6. Deep Dives (15 min)
Deep Dive 1: Low-Latency Transaction Processing & Offline/Degraded Mode
The Latency Budget:
Total budget: 300ms (from RFID read to barrier lift)
RFID read: ~100ms
Network (plaza → DC): ~50ms (dedicated leased line, regional DC)
Server processing: ~40ms
Network (DC → plaza): ~50ms
Barrier actuation: ~50ms
Buffer: ~10ms
The server has only ~40ms to process. This is tight but achievable because:
- Redis lookups: < 1ms
- PostgreSQL
UPDATE ... WHEREwith proper indexing on a single row: 5-15ms - No external service calls in the hot path
Optimizations:
- Connection pooling: Maintain persistent connections from Toll Processing Service to PostgreSQL (PgBouncer, 200 connections per node)
- Prepared statements: Pre-compiled SQL for the debit query — eliminates parse time
- Regional deployment: Deploy Toll Processing Service and PostgreSQL replicas in regional DCs close to plaza clusters. India: Mumbai, Chennai, Delhi, Kolkata.
- Read-through cache: Account data cached in Redis. Cache-aside on write (update DB, then invalidate/update cache).
Offline / Degraded Mode (Critical for Toll Systems):
What happens when the central system is unreachable from a plaza?
Plaza Controller has local fallback logic:
1. CONNECTIVITY LOST detected (3 consecutive failed requests)
2. Switch to OFFLINE MODE:
a. Read RFID tag
b. Check LOCAL blacklist cache (synced every 15 minutes)
c. If tag not blacklisted → ALLOW passage (barrier lifts)
d. Log transaction locally to SQLite on the plaza controller
e. Display "OFFLINE — PASS" on the lane display
3. When connectivity restores:
a. Upload all offline transactions in bulk
b. Central system processes them (may result in negative balances)
c. Accounts with negative balance get flagged for recovery
Trade-off: In offline mode, we allow vehicles through without balance verification. The alternative (blocking all traffic) causes highway jams and is a safety hazard. The financial risk (negative balances) is manageable — most accounts have sufficient balance, and recovery mechanisms exist (auto-debit from linked bank account, toll blacklisting for chronic offenders).
Negative Balance Handling:
- Allow transactions up to -500 INR (configurable per issuer bank)
- Beyond -500: tag is blacklisted centrally, synced to plaza edge caches
- Issuer bank auto-debits the linked bank account to recover negative balance
- If recovery fails after 30 days: escalate to NHAI enforcement
Deep Dive 2: Fraud Detection & Security
Threat Model:
| Threat | Impact | Mitigation |
|---|---|---|
| Cloned RFID tags | Toll charged to wrong account | ANPR cross-verification; velocity checks (same tag at impossible distances) |
| Compromised plaza controller | Fake transactions, stolen tag data | HMAC-signed API requests; TLS mutual auth; tamper-evident hardware |
| Man-in-the-middle | Transaction manipulation | TLS 1.3 with certificate pinning on plaza controller |
| Insider fraud (toll operator) | Manual overrides to skip tolling | Audit logs for all manual overrides; anomaly detection on override frequency |
| Replay attacks | Duplicate transactions | Idempotency key (transaction_id) with uniqueness constraint |
Clone Detection Algorithm:
On each transaction:
1. Get last_transaction for this tag_id from cache/DB
2. Calculate:
distance = haversine(last_plaza.lat_lon, current_plaza.lat_lon)
time_diff = current_txn.timestamp - last_txn.timestamp
max_speed = distance / time_diff
3. If max_speed > 200 km/h:
→ Flag as POTENTIAL_CLONE
→ Alert fraud team
→ Do NOT block transaction (avoid false positives from GPS/clock issues)
4. If 3+ velocity violations in 24 hours:
→ Auto-blacklist tag
→ Notify account holder via SMS: "Your FASTag may be cloned. Contact your bank."
Vehicle Class Mismatch Detection:
- RFID tag encodes the registered vehicle class (written at time of issuance)
- ANPR + ML classifier at the plaza independently classifies the vehicle (car vs truck based on image)
- If mismatch: charge the higher toll amount, flag for investigation
- Common fraud: truck registered as car to pay lower toll. Systematic offenders get blacklisted.
Security Architecture:
- All plaza ↔ DC communication over mutual TLS (client certificates provisioned per plaza)
- RFID tag data is read-only (TID bank is factory-locked; EPC is write-locked after issuance)
- Every 15 minutes: central system pushes updated blacklist + vehicle class corrections to all plazas
- Full transaction audit trail retained for 7 years (regulatory requirement)
Deep Dive 3: Bank Reconciliation Pipeline
FASTag operates as a prepaid wallet, but the money flows through issuer banks (SBI, ICICI, HDFC, etc.). Reconciliation ensures that the central system’s transaction records match each bank’s records.
Daily Reconciliation Flow:
T+0 (End of day, 23:59):
┌──────────────┐
│ Central │ Generate settlement file per issuer bank
│ System │ (all transactions for that bank's tags)
│ │──────────────────────────────────────────┐
└──────────────┘ │
▼
T+1 (Next morning): ┌──────────────────┐
┌──────────────┐ SFTP / Secure API │ Issuer Bank │
│ Reconciliation│◀──────────────────────────────│ Settlement │
│ Service │ Bank sends its own file │ System │
│ │ └──────────────────┘
└──────┬───────┘
│
▼
Three-way match:
1. Central system's debit records
2. Bank's debit records
3. Plaza-level transaction logs
Discrepancy types:
- MISSING_AT_BANK: We debited, bank has no record → re-send transaction
- MISSING_AT_CENTRAL: Bank debited, we have no record → investigate (possible replay?)
- AMOUNT_MISMATCH: Different amounts → flag for manual review
- DUPLICATE: Same transaction billed twice → auto-reverse one
Matched transactions → mark as SETTLED
Unmatched → DISPUTED → manual resolution within 7 days
Settlement:
- NPCI (National Payments Corporation of India) acts as the clearing house
- Net settlement between acquiring bank (plaza’s bank) and issuing bank (tag holder’s bank) happens T+1
- Central system transfers toll collection minus commission to NHAI (toll authority)
- Commission structure: ~1.5% transaction fee split between issuer bank, acquirer bank, and NPCI
7. Extensions (2 min)
- Multi-lane free-flow tolling (no barriers): Instead of one-vehicle-at-a-time lanes, mount overhead RFID readers and cameras on highway gantries. No barriers, no slowdown. Requires high-confidence vehicle identification (multi-antenna RFID + ANPR fusion) and robust post-hoc recovery for missed reads.
- GPS-based distance tolling: Instead of fixed toll plazas, charge based on actual distance traveled. Install OBU (On-Board Unit) with GPS. Challenges: privacy, GPS spoofing, and enormous data volume from continuous location tracking.
- Dynamic toll pricing: Adjust toll rates based on congestion (higher during peak, lower off-peak). Requires real-time traffic data integration and fast rate propagation to all plazas.
- Inter-operable with parking, fuel, EV charging: Use the same FASTag RFID for automated parking payment, fuel station payment, and EV charging. Requires merchant category codes and per-merchant rate cards.
- Fleet management integration: APIs for logistics companies to track toll expenses per vehicle, set per-vehicle spending limits, and get real-time alerts when their trucks pass specific plazas (geo-fencing for route compliance).