Design FASTag toll payments
The interviewer asks
“Design the system behind FASTag electronic toll collection in India: a car with an RFID tag drives through a toll lane, the toll is deducted from the owner's linked wallet or bank account, and the barrier opens without the car stopping for long.”
What a strong answer looks like
A strong answer starts from the hard constraint at the lane: the decision to open the barrier must happen in well under a second, but the money movement crosses four parties (toll plaza, acquirer bank, the NPCI NETC switch and the issuer bank) and cannot be that fast or that reliable. So the lane decides locally using a cached tag status list (valid, blacklisted, low balance), and the debit is processed as an asynchronous, idempotent transaction through acquirer, switch and issuer, with rules for late submissions, duplicate reads and disputes. Key parts: the plaza lane controller and plaza server that keep working when the network is down, the acquirer host that validates and batches transactions, the central switch that routes by tag id to the issuer, the issuer's tag-to-account mapping and balance check, and daily settlement and reconciliation between banks. The deep dives are offline-first lane decisions, keeping status lists fresh, duplicate and wrong-class charges, and settlement.
Concepts to mention
1 Requirements
Functional
- Read the vehicle's RFID tag at the lane and decide to open the barrier or not
- Compute the toll from vehicle class, plaza, direction and return-journey or monthly pass rules
- Debit the toll from the tag holder's linked prepaid wallet or bank account through the issuer bank
- Notify the user by SMS or push with plaza, amount and balance
- Maintain tag status (active, blacklisted, low balance, closed) and distribute it to all plazas
- Settle money daily between issuer banks, acquirer banks and toll operators; handle disputes and refunds
- Support tag issuance, recharge and vehicle registration linking (VRN to tag)
Non-functional
- Lane decision (tag read to barrier open) under ~300 ms, without depending on a remote call
- Lanes keep working for hours with no WAN connectivity
- Every valid read becomes exactly one debit: no missed tolls and no double charges
- Scale: ~1.5 crore (15M) toll transactions/day across ~1,000+ plazas; peaks of 3-4x on holiday weekends
- Tag status changes (blacklist or recharge) reach all plazas within minutes
- Issuer debit and user SMS within a few minutes for most transactions
Out of scope (say it out loud)
- RFID radio hardware and antenna design
- GNSS-based distance tolling and ANPR-only free-flow tolling (mention as future)
- Parking and fuel payments with FASTag
- Tag issuance KYC flow in detail
2 Estimates
3 API
| Endpoint | What it does |
|---|---|
Locallane → plaza server: TagRead | tag_id (EPC/TID), lane, timestamp, AVC vehicle class, camera image ref |
POST/acquirer/v1/toll-txn | Plaza to acquirer: one toll transaction with unique txn_id = plaza + lane + read sequence, amount, tag_id, read_time |
POST/netc/v1/debit | Acquirer to NETC switch to issuer: debit request, routed by tag id to the issuer; response success / decline with reason |
GET/netc/v1/exception-list?since={version} | Delta of tag status changes since the plaza's last version |
POST/issuer/v1/recharge | User recharges the wallet; triggers a status change to good if it was low balance |
POST/disputes | User raises a wrong or double charge with the transaction id |
4 Data model
tag_registry (issuer)Postgres / core bankingtag_id (PK)vrnvehicle_classaccount_idstatusissued_atkyc_statusIssuer is the owner of tag-to-account mapping and balance. Unique vrn per active tag (one vehicle, one tag).
tag_mapper (switch)In-memory + replicated DB at the switchtag_id prefix or tag_id → issuer_idvehicle_classstatusLets the switch route any tag to its issuer and lets plazas confirm the class registered for the tag.
exception_listVersioned log at switch; local SQLite/RocksDB at plazaversiontag_idstatus (blacklist, low_balance, closed)effective_atOnly tags that are NOT good. Plazas apply deltas in order by version; full resync if they fall too far behind.
toll_transactions (plaza)Local durable queue + plaza DBtxn_id (PK)tag_idlaneread_timevehicle_classamountimage_refsync_statusWritten before the barrier opens; forwarded to the acquirer when the network is up.
acquirer_txnsPostgres (partitioned by day)txn_id (PK)plaza_idtag_idamountread_timereceived_atswitch_statusissuer_refUnique txn_id enforces exactly one debit per read; late arrivals flagged by received_at - read_time.
settlement_batchesPostgres + files in S3batch_idcycleparticipantgross_debitsgross_creditsnet_positionfile_refNet positions per bank per settlement cycle, settled between banks; files kept for audit and disputes.
5 Architecture
Hover a box to highlight its connections.
6 Request flows
Car passes a toll lane
- 1The reader reads the tag's ID as the car enters the lane; an automatic vehicle classifier (AVC) and camera capture the vehicle class and number plate image.
- 2The lane controller checks the tag locally against the plaza's exception list: blacklisted or closed tags are refused; good tags pass.
- 3It computes the fare from the plaza fare table, the vehicle class registered for the tag, and any return-journey or pass entitlement held locally.
- 4It writes the transaction to the local durable store with txn_id = plaza_id + lane_id + sequence, then opens the barrier. Elapsed time: a few hundred ms.
- 5The plaza server forwards pending transactions to its acquirer bank continuously (or in small batches) over the network.
- 6The acquirer validates (known plaza, sane amount, not a duplicate txn_id or a repeat read of the same tag in the same lane within minutes), stores it, and sends a debit request to the NETC switch.
- 7The switch routes by tag to the issuer bank; the issuer debits the wallet (or account) and replies success or decline.
- 8The issuer sends an SMS with plaza name, amount and balance; if the balance drops below the threshold, it publishes a 'low balance' status change to the exception list.
User recharges after being blacklisted
- 1User recharges ₹500 via UPI in the issuer's app; the issuer credits the wallet.
- 2The issuer sends a status change 'good' for the tag to the switch's exception list service.
- 3The list service appends it with a new version; every plaza pulls deltas every minute or so and removes the tag from its local list.
- 4On the next trip the tag passes normally. The design must make this fast, because drivers are often recharging while standing in the lane.
7 Deep dives
Offline-first decision at the lane
A lane cannot wait for a four-party round trip over a highway network that may be slow or down. So the system splits the decision from the payment. The decision is local: the plaza holds a list of tags that must not pass (blacklisted, closed, low balance) and the fare rules, and the lane decides in milliseconds. The payment is asynchronous: the read is written to a durable local store first, then forwarded when the network allows. Use an exception list, not a full list of good tags, because most tags are good and the bad list is much smaller and changes less. This creates a risk window: a tag that went bad a few minutes ago may still pass, and its debit may later be declined. The network handles this with rules on time: the status that counts is the one at read time, so a tag blacklisted shortly before the read (inside the list propagation window) is the issuer's risk, while a stale plaza list beyond the allowed lag is the acquirer's risk. NPCI has tightened these rules over time, for example around how long before and after a read a blacklist status applies. In an interview, explaining this 'decide locally, settle later, with clear liability windows' pattern is the main signal.
Exactly one debit per crossing
Double charges are the top FASTag complaint. They come from three sources. First, multiple reads: the antenna reads the same tag many times as the car moves, and a car may stop and reverse. The lane controller collapses reads of the same tag in the same lane within a time window (say 5-10 minutes) into one transaction. Second, network retries: the plaza resends a batch after a timeout. Make every transaction carry a txn_id that is created once at the lane and never changes; the acquirer stores it with a unique constraint and the issuer deduplicates on the acquirer's reference too, so retries are safe. Third, cross-lane or cross-plaza confusion: a tag read by an adjacent lane antenna. Use antenna power tuning and cross-check with the AVC and camera; transactions where the camera plate does not match the tag's registered VRN go to an exception queue rather than being debited blindly. Also guard against missed tolls: transactions stuck at the plaza must be sent within the network's allowed window, after which they may be rejected or need a different process, so the plaza alerts when its local queue age grows. Disputes use the image and read logs to decide.
Distributing tag status to 1,000+ plazas
The exception list service keeps an append-only, versioned log of status changes: (version, tag_id, new_status, effective_at). Issuers write to it when balances cross a threshold, KYC fails or tags close. Each plaza stores the list in a local key-value store (RocksDB or SQLite) and pulls deltas since its last version every 30-60 seconds; a push channel can cut this further. If a plaza has been offline long enough that the log has been compacted past its version, it downloads a full snapshot (compressed, tens of MB) and then applies deltas. To keep it small, the full list holds only non-good tags, and low balance can be expressed as a threshold flag rather than exact balances. Consistency is eventual, so measure and alert on 'list lag per plaza', because a stale list directly costs money. A hot path matters too: when a driver recharges in the lane, the issuer can also publish the change with priority. Integrity: sign the snapshots and deltas so a compromised plaza network cannot inject a list that lets tags pass free.
Settlement and reconciliation between banks
Money does not move per transaction between banks. The switch records every approved debit and computes net positions per settlement cycle: each issuer owes the sum of its customers' tolls, each acquirer is owed the sum for its plazas, minus interchange fees. Banks settle the net amounts through their settlement accounts, and acquirers then pay toll concessionaires. Every participant receives settlement and raw transaction files for each cycle. Reconciliation runs at three levels. Plaza vs acquirer: every txn_id from plaza logs must appear in the acquirer's records (detects lost uploads). Acquirer vs switch: approved, declined and pending counts and amounts must match per cycle. Issuer vs switch: debits in the wallet ledger must match the switch's file. Breaks feed the dispute system, where users can raise wrong-class or double charges; refunds flow back as credit adjustments in a later cycle. Keep everything immutable: corrections are new adjustment records, never edits, which makes audits possible years later.
8 Trade-offs
Local decision vs online authorisation
Online authorisation (wait for the issuer's yes) gives zero credit risk but makes lanes depend on four systems and the network, and adds seconds per vehicle, which creates queues. Local decisions with an exception list keep lanes fast and resilient at the cost of a small, bounded credit risk. Hybrid: use online checks when the network is healthy and the tag is near the threshold.
Exception list vs full tag list at the plaza
A full list (every tag with status) is bigger and changes on every debit. An exception list is small and only changes when a tag goes in or out of a bad state. The trade-off is that an unknown, never-seen tag is treated as good, so newly issued tags work instantly but fake tags must be caught by the switch later.
Per-transaction push vs small batches from plazas
Sending each transaction immediately gives faster debits and SMS but more connections and overhead. Small batches (every few seconds) are efficient and handle bursts. With durable local queues both are safe; choose near-real-time with micro-batching.
9 Bottlenecks
Holiday weekend peaks
At 3-4x traffic, the switch and issuers see bursts after plazas reconnect and flush backlogs. Acquirers should rate-limit flushes and the switch should apply per-issuer backpressure; a queue (Kafka) between acquirer intake and switch calls absorbs bursts without losing transactions.
Low-balance churn
Many tags hover around the threshold, flipping in and out of the list with every debit and recharge. This inflates deltas. Use hysteresis (go bad below ₹X, go good above ₹X + margin) and encourage auto-recharge to reduce churn.
10 Failure handling
Plaza loses network
Lanes keep working using the last list and local fare rules; transactions queue locally on durable storage (with a UPS). On reconnect, the plaza uploads in order, deduped by txn_id. Alerts fire if the list or the queue is older than the allowed window.
Issuer bank down
The switch cannot get debit approvals; transactions stay pending and are retried with the same reference until the issuer returns. Lanes are unaffected. Settlement for that issuer happens in a later cycle.
Tag reader or AVC fails in a lane
The lane falls back to manual handling: the operator scans the tag with a handheld reader or reads the number plate, and the transaction uses the same flow with a flag. Highway toll rules treat a working, funded tag that the plaza fails to read differently from a vehicle with no valid tag (which pays a higher fee), so the system must record why a read failed, for audit and disputes.
11 What impresses
- Separating the barrier decision (local, fast) from the money movement (async, multi-party)
- Using an exception list instead of a full list, and explaining why
- Naming all three causes of double charges and a fix for each
- Talking about liability windows and list lag as a measured metric
- Hysteresis on the low-balance threshold to reduce churn
- Mentioning that the next step is free-flow tolling with ANPR and GNSS, which removes the barrier but keeps the same back end
12 Mistakes
- Making the lane wait for a synchronous issuer debit
- Ignoring offline operation at plazas
- Creating a new transaction id on retry, causing double debits
- Assuming banks move money per transaction instead of net settlement
- Forgetting the vehicle class check and wrong-class charges
- No reconciliation or dispute path
13 Follow-ups
Saved on this device. Create a free account to keep it on every device.
Practise this with Sam
A real mock interview, out loud, with an honest scorecard. Free to start.