It's a quarter past eight in the morning outside Andheri station in Mumbai. Anjali stops at Ramesh's chai stall on her way to the office and asks for a cutting chai and a bun-maska, ₹20 in all. Taped to the side of the counter is a square of laminated card with a QR code on it. She opens a payments app on her phone, points the camera at the code, types 20, enters her four-digit PIN, and taps Pay. Before she's picked up the glass, a small speaker beside the kettle announces in a flat robotic voice that twenty rupees have been received.

From where Anjali stands, that was one tap. Underneath, it's more interesting. Anjali's money is in an account at State Bank of India. Ramesh's is at Bank of Baroda. Those two banks run separate computer systems in separate buildings, with separate databases, and neither can write to the other's. Her app belongs to a third company, and the QR code on the counter was printed by a fourth. Yet ₹20 has to leave one bank and arrive at the other within a couple of seconds, exactly once, and if anything goes wrong in between, somebody has to notice and put it right. On its busiest days now, the system does this more than 800 million times.
The system is India's Unified Payments Interface (UPI), run by the National Payments Corporation of India (NPCI), a not-for-profit company owned by banks and set up under the oversight of the Reserve Bank of India (RBI), the country's central bank. In this case study we'll design it the way an engineer would: start from the obvious design, find exactly where it breaks, and fix it. The question we'll keep coming back to is this: when Anjali taps Pay, how does ₹20 move from her bank to Ramesh's, in two seconds and exactly once, when the two banks share no database and either of them might go quiet halfway through? We'll go from the shape of the network down to the fields of a QR string, the life of one transaction through a timeout, the reconciliation that closes it, and the tricks for surviving the first of the month.
The Stripe case study (chapter 52) already covered card payments, idempotency keys and double-entry ledgers, and chapter 31 covered two-phase commit and sagas. We'll lean on both and point back to them; this chapter is about what's different when the thing in the middle is a national switch between hundreds of banks.
01What we're building, and how big
1.1What it has to do
Moving Anjali's ₹20 comes down to a short list:
- Pay anyone by a name, without knowing their bank or account number: a phone number, a short address like
ramesh.tea@okaxis, or a QR code. - Move money between any two banks in the network, whichever app each person uses.
- Confirm in seconds to both sides, so Ramesh can hand over the chai.
- Ask the payer's permission in a way no app, shop or thief can fake.
- Never lose or duplicate money: if the payment fails anywhere, Anjali gets her ₹20 back, on a known deadline.
- Settle up between banks so that, at the end of the day, SBI has paid Bank of Baroda what its customers owe.
And the qualities it needs while doing that:
- Fast: a couple of seconds end to end, including two banks' systems.
- Open: any bank or app that meets the rules can join, and every one can reach every other.
- Correct where money is concerned: every rupee that leaves one account lands in another or comes back.
- Available at the worst moments: the first of the month, festival days, evening rush, and while some bank somewhere is having a bad day.
This list has something Stripe's didn't. Stripe was one company processing payments for its own customers; it controlled its own databases. UPI is a set of rules and a switch sitting between hundreds of independent banks, each with its own systems, and the switch can't reach into any of them. Most of this case study is about what you can guarantee when the money lives in systems you don't control.
1.2How big is it?
NPCI publishes UPI's numbers every month. In September 2026, UPI carried 24.07 billion transactions worth ₹29.37 lakh crore (about ₹29 trillion), with more than 750 banks live on the network. August 2026 is the record month so far, at 24.51 billion transactions. Across the financial year April 2025 to March 2026 it carried about 241.6 billion transactions worth about ₹314 lakh crore, according to a government release from August 2026. RBI's annual report for 2024-25 put UPI's share of all retail payments in India, by count, at 84%.
It didn't start that way. UPI went live as a pilot with 21 banks in April 2016 and was rolled out to the public that August, when it handled about 90,000 transactions in the month. By December 2019 the monthly count was 1.31 billion; by December 2022, 7.83 billion; by December 2024, 16.73 billion.
| Month | Banks live | Transactions in the month | Value |
|---|---|---|---|
| Aug 2016 | 21 | about 90,000 | ₹3 crore |
| Dec 2019 | 143 | 1.31 billion | ₹2.03 lakh crore |
| Dec 2022 | 382 | 7.83 billion | ₹12.82 lakh crore |
| Dec 2024 | 641 | 16.73 billion | ₹23.25 lakh crore |
| Sep 2026 | 750+ | 24.07 billion | ₹29.37 lakh crore |
(A lakh is 100,000 and a crore is 10 million, so ₹1 lakh crore is ₹1 trillion.)
The daily numbers matter more for the design than the monthly ones. In September 2026 UPI averaged about 802 million transactions a day, the first month above 800 million, and the busiest days run higher than that. An ordinary day in mid-2026 carries roughly 750 to 800 million.

On an ordinary day in September 2026, UPI carried about 800 million transactions. What's the average rate per second across that day? Payments aren't spread evenly: NPCI's own circulars name 10:00 to 13:00 and 17:00 to 21:30 as the hours with the highest transactions per second. What might the rate be in those hours?
02Version 1: every app talks to every bank
2.1The obvious design
Here's the design you might reach for first. Anjali's app needs to take ₹20 from her SBI account and give it to Ramesh's Bank of Baroda account. So the app's company signs an agreement with SBI, gets an API, and asks SBI to debit Anjali. Then it signs an agreement with Bank of Baroda and asks it to credit Ramesh. Do the same for every other bank, and the app can pay anyone.
Three things break. First, the count. With a apps and b banks, there are a × b integrations to build, test and keep working, and every new bank has to integrate with every app before its customers can be paid from them. Second, nobody owns the payment as a whole. If SBI debits Anjali and then Bank of Baroda's API doesn't answer, the only party that knows the payment is half-done is the app, and the app is a private company with no authority over either bank. Third, there's the money between the banks. SBI has taken ₹20 from Anjali and Bank of Baroda has given ₹20 to Ramesh; SBI now owes Bank of Baroda ₹20, and somebody has to keep track of millions of such debts and get them paid.
2.2Put a switch in the middle
All three problems have the same fix: put one system in the middle that every bank and every app connects to once. A system that receives a payment message from one party and routes it to another is called a switch, and UPI's switch is run by NPCI. Each bank and each app now builds one integration, to the switch, so the count is a + b instead of a × b. The switch sees every message of every payment, so it owns the state of each payment from start to finish. And because it sees every debit and credit between every pair of banks, it can add them up and tell each bank what it owes the others.
There's one more piece. The members that connect to the switch are banks (and a few other licensed payment institutions), so an app like PhonePe or Google Pay doesn't connect to NPCI on its own; it connects through a bank that sponsors it. That sponsoring bank is called a payment service provider (PSP), and the app built on top of it is a third-party application provider. PhonePe's @ybl addresses, for example, belong to its PSP relationship with Yes Bank, and Google Pay's @okaxis addresses to its relationship with Axis Bank.
Put together, a UPI payment involves up to four parties around the switch, which NPCI's procedural guidelines describe as "two PSPs … and two banks acting as Remitter & Beneficiary Bank":
| Party | In Anjali's payment | Its job |
|---|---|---|
| Payer PSP | Anjali's app and the bank behind it | Captures her intent and her PIN, sends the request to the switch |
| Remitter bank | SBI | Holds Anjali's account; checks the PIN and debits her |
| NPCI's UPI switch | NPCI | Routes every message, tracks the transaction, computes settlement |
| Payee PSP | The bank behind @okaxis | Knows who ramesh.tea@okaxis is and which account to credit |
| Beneficiary bank | Bank of Baroda | Holds Ramesh's account; credits him |
"Remitter" is the bank that sends money and "beneficiary" is the bank that receives it. Notice that four of the five roles can be four different organisations, and the PSP banks needn't be the banks where Anjali and Ramesh hold their money. That's what makes UPI open: Anjali can use any app with her SBI account, and Ramesh can receive into Bank of Baroda through any app that issued him an address.

How should apps and banks reach each other?
- No central operator to trust
- No single system whose failure stops everything
- apps × banks integrations
- Nobody owns the whole payment
- Interbank debts tracked by nobody
- apps + banks integrations
- One place that owns each payment's state
- One place that computes what banks owe each other
- The switch must never go down
- The switch's operator sets everyone's rules
India chose a switch, as almost every country has for card payments, ATM networks and interbank transfers. NPCI already ran the switch for India's ATM network and for IMPS, an older instant transfer service, so UPI was built as a new layer over the same idea, by an operator owned by the banks and overseen by RBI. The cost of the choice is that the switch becomes the one system that can stop every payment in the country. Much of what follows comes from that.
So now there's a hub, and Anjali's app knows to send her payment to it. But the app still has to say who the money is for, and Anjali doesn't know Ramesh's bank or account number. That's the next problem.
03Paying a name: addresses and the QR code
3.1A name instead of an account number
A bank account in India is identified by an account number and an IFSC, an 11-character code naming the bank branch. Asking Anjali to type sixteen digits and a branch code to pay ₹20 would end the idea at once, and printing them on a card at the stall would hand them to anyone who walked past.
UPI's answer is an address that looks like an email address: ramesh.tea@okaxis. It's called a virtual payment address (VPA), or in the apps, a UPI ID. The part after the @ is the handle, and it names the payee PSP that issued the address. Before the @ comes a name that PSP chose with Ramesh. Only that PSP knows which real account ramesh.tea points to; NPCI's guidelines describe the address being resolved by the "respective PSP", using its own mapping table.
So to find out where Anjali's ₹20 should go, the switch reads the handle, okaxis, looks up which PSP owns it, and asks that PSP: who is ramesh.tea? The PSP answers with the account details and Ramesh's registered name. Nobody but that PSP and the switch ever sees his account number. Anjali's app shows her "Ramesh Tea Stall" before she pays. That's her chance to notice if she's about to pay the wrong person.

?Why does the handle name the PSP, not Ramesh's bank?
Because the PSP is the party that knows the mapping, and the switch needs to know whom to ask. Ramesh's bank is Bank of Baroda, but his address was issued by the PSP behind Google Pay, and if he switches his receiving account to another bank tomorrow, his address and his QR code stay the same; only the PSP's mapping changes. It's the same layering as DNS: a name points to whoever is authoritative for it, and the authority can change the answer without anyone reprinting the name. Phone numbers work too: NPCI runs a central mapper that links a mobile number to a default account, for people who type a number instead of an address.
?Why not one global directory, like Brazil's?
Brazil's instant payment system, Pix (we'll meet it properly later), keeps every key (phone number, email, tax ID or random key) in one directory run by the central bank, called DICT, which held over 920 million keys according to the central bank's 2026 report. That gives one authoritative answer and makes keys portable between banks. UPI spreads the directory across PSPs instead, with each handle owner answering for its own names. The cost is an extra hop to the payee PSP on every payment; the benefit is that the switch holds no customer directory of its own, and each PSP can mint addresses without asking anyone.
3.2What's inside the QR code
Ramesh's QR code doesn't contain an image of anything or a link to a website. Decoded, it's a short line of text, a URI, defined in NPCI's UPI linking specification. A URI with the scheme upi and the host pay is a request to pay, and its parameters are the payment's details:
upi://pay?pa=ramesh.tea@okaxis&pn=Ramesh%20Tea%20Stall&cu=INRThe same format is used when one app opens another to pay, for example a shopping site handing off to your payments app; on a phone that's called an intent, and the specification calls the URI a deep link. Version 1.6 of the specification, dated November 2017, lists these parameters:
| Field | Meaning | Notes from the specification |
|---|---|---|
pa | Payee address | The VPA. Required. |
pn | Payee name | Shown to the payer |
mc | Merchant category code | Four digits saying what kind of business it is |
tid | Transaction ID | Generated by the PSP when the app creates the request |
tr | Transaction reference | The merchant's own order or bill number; "mandatory for merchant transactions" |
tn | Note | Free text, shown to both sides |
am | Amount | "If 'am' is not present then field is editable" by the payer |
mam | Minimum amount | The least the payer may enter |
cu | Currency | "ONLY 'INR'" |
url | Reference URL | A link to the order or bill details |
mode | How the request was started | 01 QR, 02 signed QR, 04 intent, 06 NFC, and others |
orgid | Who generated it | 000000 when the merchant generated it |
mid, msid, mtid | Merchant, store and terminal IDs | For larger merchants |
sign | Signature | Base64-encoded digital signature over the other fields |
A code printed once and taped to the counter, like Ramesh's, is a static QR: it carries the address and name, and Anjali types the amount. A till at a supermarket instead shows a dynamic QR for each bill, which also carries the amount and a reference number. Because am is present, Anjali's app shows the amount and doesn't let her change it, and the reference tr lets the supermarket match the incoming payment to the right bill.
This short program parses both kinds of string the way an app's scanner would. It uses Python's urlsplit to separate the scheme, host and query, and parse_qs to split the query into fields and undo the %20 escapes (a space can't appear raw in a URI, so it's written as %20). Then it checks the two things an app must check before going further: the scheme is upi://pay, and the address has a handle after an @.
from urllib.parse import urlsplit, parse_qs, unquote
# What the phone's camera reads from two QR codes at the stall
static_qr = "upi://pay?pa=ramesh.tea@okaxis&pn=Ramesh%20Tea%20Stall&cu=INR"
dynamic_qr = ("upi://pay?pa=ramesh.tea@okaxis&pn=Ramesh%20Tea%20Stall"
"&mc=5814&tr=ORD20261011A7&am=20.00&cu=INR&tn=2%20cutting%20chai")
FIELDS = {"pa": "payee address (VPA)", "pn": "payee name", "mc": "merchant category code",
"tr": "transaction reference", "am": "amount", "cu": "currency", "tn": "note"}
def parse(uri):
parts = urlsplit(uri)
if parts.scheme != "upi" or parts.netloc != "pay":
raise ValueError("not a UPI payment intent")
q = {k: v[0] for k, v in parse_qs(parts.query).items()}
user, _, handle = q["pa"].partition("@")
if not user or not handle:
raise ValueError("pa must look like name@handle")
return q, handle
for name, uri in [("static", static_qr), ("dynamic", dynamic_qr)]:
q, handle = parse(uri)
print(f"{name} QR, {len(uri)} characters")
for k, v in q.items():
print(f" {k:3} {FIELDS.get(k, '?'):26} {v}")
print(f" -> ask the PSP behind @{handle} who '{q['pa']}' is")
if "am" in q:
print(f" -> amount fixed at Rs {q['am']}; the payer can't edit it")
else:
print(" -> no amount: the payer types it in")
print()static QR, 61 characters
pa payee address (VPA) ramesh.tea@okaxis
pn payee name Ramesh Tea Stall
cu currency INR
-> ask the PSP behind @okaxis who 'ramesh.tea@okaxis' is
-> no amount: the payer types it in
dynamic QR, 117 characters
pa payee address (VPA) ramesh.tea@okaxis
pn payee name Ramesh Tea Stall
mc merchant category code 5814
tr transaction reference ORD20261011A7
am amount 20.00
cu currency INR
tn note 2 cutting chai
-> ask the PSP behind @okaxis who 'ramesh.tea@okaxis' is
-> amount fixed at Rs 20.00; the payer can't edit itLook at what's in the static string: 61 characters and three fields, and nothing secret. That's deliberate. A QR code at a stall can be photographed by anyone, so it carries only what's needed to receive money, the same as writing your address on an envelope. Its dynamic cousin adds the merchant category (5814 is the international code for fast-food restaurants), the order reference and a fixed amount. Notice also the first thing the parser hands on: the handle. Everything the switch does next starts from @okaxis.

3.3Can a QR code be forged?
Since a static QR code is a printed card, the obvious attack is to paste your own code over Ramesh's. Customers would then pay a stranger, and the payment would go through perfectly, because nothing in the string is wrong; it's someone else's address. UPI's defences here are on the people, not the protocol: the app shows the payee's registered name ("Ramesh Tea Stall") before Anjali enters her PIN, and the speaker on Ramesh's counter announces every payment he receives, so a customer whose payment didn't make it speak is noticed at once.
For larger merchants the specification offers more: a signed QR, where the sign field holds a digital signature computed with the merchant's private key over the other fields, using RSA with SHA-256. The payer's app checks the signature with the merchant's public key, which PSPs download from NPCI daily, and refuses a string that's been edited. That stops someone changing the amount or the address on a dynamic code. It can't stop someone replacing a whole static card with their own valid one, so the name check still matters.
So the switch now knows whom Anjali wants to pay. Next, the money has to move, and the order in which things happen turns out to be most of the design.
04One payment, leg by leg
4.1The messages behind one tap
NPCI's procedural guidelines describe the push payment flow in five steps, with named messages. Request messages start with Req and responses with Resp, so ReqPay is "please make this payment" and RespPay the answer. Here is Anjali's payment, hop by hop:
ReqPay.The switch does no money-moving of its own. It holds no accounts for Anjali or Ramesh; it's a router with a memory. Each bank does a local transaction in its own database: SBI subtracts ₹20 from a row, Bank of Baroda adds ₹20 to a row. What the switch adds is the record that these two local changes belong to the same payment, under one transaction ID, and the order in which they're asked for.
4.2Why debit first?
The switch could ask Bank of Baroda to credit Ramesh first and SBI to debit Anjali afterwards, or the other way round. Which order is safer, and why?
This is the same reasoning as a saga in chapter 31: run the steps one at a time, each a local transaction, and if a later step fails, undo the earlier ones with a compensating step. In UPI the compensating step for a failed credit has a name, a debit reversal: the switch tells SBI to put the ₹20 back. NPCI even publishes, per bank, how many reversals were needed and what fraction succeeded, which we'll look at in section 8.
4.3Pay and collect
Everything so far is a pay request, also called a push: the payer starts it and pushes money out. UPI also had the opposite, a collect request, or pull: the payee starts it by sending the payer a request for an amount, the payer's app shows "Ramesh is requesting ₹20", and if the payer approves with their PIN, the same debit-then-credit flow runs. Collect is handy for online shops (type your UPI ID at checkout, approve the request in your app) and for splitting a bill with friends. NPCI's guidelines gave collect requests a default expiry of 30 minutes.
The trouble is the screen the payer sees. A collect request asks you to enter your PIN to send money, and fraudsters learned to send requests with notes like "enter PIN to receive your refund". A person used to entering their PIN to make things happen would enter it, and pay. Because the request comes from the payee, the payer is the only check. In July 2025, NPCI's circular OC-220 set the end of it for payments between people: "by 1st October 2025 UPI P2P Collect shall not be allowed to be processed in UPI." Collect requests from merchants continue, under the merchant's PSP's controls.
All of this assumes that the ReqPay came from Anjali. Anyone who can make the switch believe a request came from her can empty her account. That's the next section.
05Who's allowed to say "debit"
5.1Binding the app to a SIM
SBI is about to take ₹20 from Anjali because a message said she wanted it to. Two questions have to be answered before it does: did this message come from Anjali's phone, and did Anjali herself approve this payment? UPI answers them with two factors, something she has and something she knows.
Something she has is her phone, or more precisely, the SIM card with her registered mobile number. When Anjali first sets up the app, the app sends an SMS from her phone to the PSP. NPCI's guidelines call it "an automated outward encrypted SMS (Mobile number to PSP system) which hard binds the Mobile number with the device." Because the SMS travels from her phone through the mobile network, the PSP learns from the network itself that this device holds the SIM for her number. That's probably much harder to fake than a number typed into a form. This is called device binding. From then on, requests from that app installation on that phone carry identifiers tied to the binding, and a request from any other device doesn't.
The bank then finds Anjali's accounts by that mobile number (it's the one registered with SBI), and she sets up her PIN. To prove she's the account holder, she enters the last six digits of her debit card and its expiry date, plus a one-time password (OTP) that SBI sends to her registered number. Since 2021 she can instead use an OTP from Aadhaar, India's national identity system, if her Aadhaar number is linked to the account.
5.2A PIN the app never sees
Something Anjali knows is her UPI PIN, four or six digits. That PIN belongs to her bank, SBI. And here's the problem: she types it into an app written by a different company, running on her phone. If that app could read the PIN, then every PSP in the country would be holding the keys to accounts at every bank, and one leaked database or one dishonest employee would be enough.
UPI's answer is that the PIN screen doesn't belong to the app. NPCI ships a piece of software called the common library (CL), which every UPI app must embed. When it's time to enter the PIN, the app hands control to the CL, which draws the keypad and captures the digits itself. The CL encrypts the PIN together with the transaction's details into a credential block, using RSA with a 2048-bit NPCI public key that the library holds. Anjali's app receives only the encrypted block, and passes it along with the payment.
A hardware security module (HSM) is a tamper-resistant box that holds encryption keys and does operations with them without ever letting the keys out; the procedural guidelines say the issuer bank "has to mandatorily decrypt … using HSM only". So the PIN exists in readable form only on Anjali's keypad and inside security hardware, never in the app's memory or a PSP's logs.
The library also has to know it's talking to a genuine app. On first use it registers itself with NPCI's servers: the app asks for a challenge, fetches a token through a ListKeys request, and registers with a keyed hash (an HMAC) over the device ID, the app ID and the mobile number. Tokens are refreshed periodically; an NPCI circular in 2021 extended its validity from 30 to 90 days.
?Why not let each bank ship its own PIN screen?
Because then every app would need every bank's code inside it: the a × b problem from section 2 again, this time on the phone. One library, one format and one public key at the switch turn it back into a + b: apps embed one library, and banks each give the switch one key.
How should the payer's approval travel from the phone to their bank?
- Simple
- Each app controls its own interface
- Every app and PSP can read every PIN
- One breach exposes customers of every bank
- Apps never see PINs
- One standard library for all apps and banks
- Every app must embed and update NPCI's code
- The switch is in the trust path
UPI's openness depends on this choice. Letting any company build a payments app is only safe if a payments app can't learn your PIN. The cost is that NPCI's code runs inside every UPI app, and NPCI's systems briefly handle the PIN in their own secure hardware when re-encrypting it for the bank.
5.3Small payments without a PIN
Typing a PIN for a ₹20 chai is a bit of friction that slows a queue. In October 2025, an NPCI circular added on-device biometric authentication, a fingerprint or face check by the phone, "in lieu of UPI PIN" for payments up to ₹5,000, along with face authentication through Aadhaar for setting or resetting the PIN. In July 2026 the biometric limit was raised to ₹10,000, from 7 August 2026. Section 9 covers a second way small payments avoid the PIN, UPI Lite, which also takes load off the banks.
So now SBI is sure the request is Anjali's, debits her ₹20, and says so. Next the switch asks Bank of Baroda to credit Ramesh. What happens if Bank of Baroda never answers?
06Two banks, no shared database
6.1Why there's no commit across both banks
Before we deal with Bank of Baroda going silent, it's worth seeing why the problem can't be designed away. If the debit at SBI and the credit at Bank of Baroda were rows in one database, we'd wrap them in one transaction and they'd happen together or not at all. They're in two databases owned by two companies. The textbook tool for making two databases commit together is two-phase commit, from chapter 31: a coordinator asks both to prepare, each locks what it needs and promises it can commit, and then the coordinator tells both to commit.
Applied to UPI, the switch would be the coordinator, and that's where it falls apart. While SBI waits to hear "commit", Anjali's ₹20 is locked in a prepared state; if the switch is slow or the message is lost, SBI can't decide on its own, because the whole point of having prepared is that it promised to wait. Now multiply by 750 banks, thousands of payments a second and any bank's systems pausing at any moment: every bank's ledger would be holding locks on behalf of every other bank's problems. And a bank's core banking system, the decades-old system holding its accounts, is not something any switch operator gets to install a new protocol into.
So UPI does what chapter 31 recommends when you can't hold locks across organisations: each step is a local transaction committed immediately, the steps run in a fixed order, and a failed later step is undone by a compensating step. The switch's job is to remember, for every payment, which steps have happened, so that it always knows what still has to be done or undone.
6.2Ramesh is paid now; the banks settle later
There's a second thing that doesn't move in those two seconds: the bank's money. When Bank of Baroda credits Ramesh ₹20, it hasn't received ₹20 from SBI. It has received a message from the switch saying SBI has debited Anjali. Bank of Baroda pays Ramesh out of its own funds, trusting that SBI will pay it back.
That repayment is called settlement. Banks in India hold settlement accounts at RBI, and the switch adds up, for each bank, everything its customers sent and received through UPI during a window of time. Over a window, SBI's customers pay Bank of Baroda's customers some amount and Bank of Baroda's customers pay SBI's some other amount, and only the difference needs to move. Netting all banks against each other this way is called deferred net settlement: deferred because it happens after the customers' payments, and net because only each bank's overall position moves. Each bank ends a window owing or owed one number, and those numbers are posted to the banks' accounts in RBI's real-time gross settlement system, RTGS.

NPCI runs these windows, called settlement cycles, many times a day. From 1 August 2024, its circular OC-197 moved UPI to ten settlement cycles a day, and members have to keep enough funds in their RTGS settlement accounts every day of the year. A September 2025 circular kept ten cycles for ordinary payments, from 3 November 2025, and split dispute adjustments into two separate cycles of their own.
?What if SBI couldn't pay at the end of the cycle?
Then Bank of Baroda would have paid Ramesh with money it never receives. That's settlement risk, and every deferred net system carries it. NPCI's rule that banks must keep funds in their settlement accounts every day of the year is one guard against it. What happens when a member does default is a matter for NPCI's settlement rules, which we won't go into here. The design point is that banks trust each other for a few hours at a time so their customers don't have to wait.
How should money move between the banks?
- No settlement risk: the payee's bank is paid before it credits
- Nothing to reconcile at the end of the day
- Every payment needs the central bank's system in its path
- Each bank must keep enough funds at every moment for every payment
- The central bank's system handles a few postings per bank per cycle, not billions
- Banks need far less money parked in settlement accounts
- Banks carry each other's credit risk for hours
- Every cycle has to be reconciled
UPI settles net, in cycles. Brazil's Pix made the other choice: the central bank's own instant payment system, SPI, settles each Pix payment individually and immediately between the two institutions' accounts at the central bank, around the clock. The US FedNow service, launched in July 2023, also settles each payment individually in the banks' accounts at the Federal Reserve. Both remove settlement risk at the price of putting the central bank's system in the path of every payment. India put a switch in the path instead, and kept RBI's system for a few large postings a day.
So each payment touches the switch and two bank databases, and the banks square up later. Now we can face the case we've been putting off.
07When the credit leg goes silent
7.1Three answers, and no answer
SBI has debited Anjali ₹20. The switch has sent Bank of Baroda the credit request. Bank of Baroda can come back with one of three things, or nothing:
- Success. Ramesh is credited. The switch tells both sides and the payment is done.
- A clear refusal. Ramesh's account is frozen, say. Nothing was credited. The switch sends SBI a debit reversal, SBI gives Anjali back her ₹20, and her app says the payment failed. Annoying, but nothing is unknown.
- Silence. The request timed out. Perhaps Bank of Baroda never got it; perhaps it got it, credited Ramesh, and the reply was lost; perhaps it's still working on it. From the outside, the switch can't tell which.
The third case is the one Stripe's Maya faced in her tunnel (chapter 52), but with a difference that makes it harder. Stripe could make retries safe with an idempotency key, because Stripe controlled both the key store and the charge. Here, the switch can ask Bank of Baroda again, but it can't look inside Bank of Baroda's database, and it can't move the money itself. Whatever it decides, two independent ledgers have to end up agreeing.
?Why not just treat silence as failure and refund Anjali?
Because in some of those silent cases Bank of Baroda did credit Ramesh. Refund Anjali as well, and ₹20 has been created out of nothing: Anjali has her money back and Ramesh has his. Treat silence as success instead, and in the cases where the credit never happened, Anjali has lost ₹20 that went nowhere. Either guess is wrong some of the time, and with hundreds of millions of payments a day, "some of the time" is thousands of people.
Here's a simulation of 100,000 payments of ₹20. One percent are clearly refused by the payee bank. Half a percent time out, and of those, 60% were in fact credited before the reply was lost (the exact share is made up; what matters is that it's neither 0% nor 100%). The program applies three policies to the timeouts: refund at once, assume success, or mark the payment as unknown and settle it later against the payee bank's own records. It then adds up the money: everything debited must have been either credited or refunded, so the gap, debited minus credited minus refunded, should be zero.
import random
random.seed(7)
N = 100_000 # payments of Rs 20, each debited from the payer first
P_DECLINE = 0.01 # payee bank answers "no" (account frozen, say)
P_TIMEOUT = 0.005 # payee bank goes silent: did the credit happen or not?
P_SILENT_OK = 0.6 # of those silent ones, how many were in fact credited
def run(policy):
payer_out = payee_in = refunded = timeouts = 0
for _ in range(N):
payer_out += 20 # debit leg succeeded
r = random.random()
if r < P_DECLINE:
refunded += 20 # clear "no": reverse at once
elif r < P_DECLINE + P_TIMEOUT:
timeouts += 1
credited = random.random() < P_SILENT_OK
payee_in += 20 if credited else 0
if policy == "timeout = failed":
refunded += 20 # reverse blindly
elif policy == "timeout = done":
pass # assume it went through
else: # "deemed, then reconcile"
if not credited: # the bank's records say no credit
refunded += 20
else:
payee_in += 20 # normal success
gap = payer_out - payee_in - refunded # money that left and arrived nowhere
return payer_out, payee_in, refunded, gap, timeouts
print(f"{'policy':24}{'debited':>11}{'credited':>11}{'refunded':>10}{'gap':>8}")
for policy in ["timeout = failed", "timeout = done", "deemed, then reconcile"]:
random.seed(7)
out, inn, ref, gap, t = run(policy)
print(f"{policy:24}{out:>11,}{inn:>11,}{ref:>10,}{gap:>8,}")
print(f"\n{t} of {N:,} payments timed out on the credit leg")policy debited credited refunded gap
timeout = failed 2,000,000 1,975,400 30,880 -6,280
timeout = done 2,000,000 1,975,400 20,400 4,200
deemed, then reconcile 2,000,000 1,975,400 24,600 0
524 of 100,000 payments timed out on the credit legRead the gap column. With "timeout = failed", it's −₹6,280: the system refunded 314 payments that had in fact reached the payee, so ₹6,280 was paid out twice. With "timeout = done", it's +₹4,200: 210 people lost ₹20 each to payments that never arrived. Only the third policy balances, and it can only do so because it waits for an answer that comes from the payee bank's own books instead of guessing. Notice too how small the problem is in count, 524 payments out of 100,000, and how much machinery it's going to need.
7.2Pending, deemed, and checking the status
UPI's rules changed their mind on exactly this point. NPCI's original procedural guidelines from 2016 treated a timeout as a decline and promised that "there will not be any transactions with pending status." That's the first policy in the simulation, and it pays some money out twice. Circulars in early 2017 introduced deemed approval, the third policy.
A deemed-approved payment is one where, in NPCI's words, "credit confirmations are not received online from the beneficiary banks". The debit has happened; the credit's outcome is unknown; and the responsibility for finding out moves to the beneficiary bank, which must check its own records and either confirm the credit or return the money. NPCI publishes each beneficiary bank's deemed-approved percentage every month; in August 2026, Yes Bank, the largest receiving bank, had 0.01% of its 10 billion incoming transactions deemed.
Meanwhile Anjali is standing at the stall looking at a spinner, and then a screen that says the payment is pending. Her app can ask the switch what happened with a check transaction status request (ChkTxn), and the switch asks the bank. Done carefully, this resolves most unknown payments within a minute or two: Bank of Baroda's systems catch up, find the credit or don't, and the switch marks the payment as a success or reverses the debit.
ReqPay and gives it a transaction ID. Nothing has moved yet.Notice that "Credit unknown" is a real state the system records, with rules for leaving it, and not a gap in the design. A payment can sit there for minutes, or, if a bank's systems are badly broken, until the end of the day.
7.3Deadlines, compensation, and the end-of-day files
What if a check-status doesn't settle it? The backstop is a deadline set by the regulator. In September 2019, RBI's circular on harmonising turnaround times for failed transactions set, for UPI transfers between people: "If unable to credit the beneficiary account, auto reversal (R) by the Beneficiary bank latest on T + 1 day. ₹100/- per day if delay is beyond T + 1 day." Here T is the calendar day of the payment, so a failed payment from Monday must be reversed by the end of Tuesday. For payments to merchants the limit is T + 5 days. And the compensation is to be paid "suo moto", on the bank's own initiative, "without waiting for a complaint". That turns a stuck payment from the customer's problem into the bank's cost. It's a design choice too: a bank that leaves payments stuck pays for it every day.
The tool that makes "by T + 1" possible is reconciliation: comparing two independent records of the same events and resolving every difference. Every settlement cycle, the switch produces files listing every transaction and how it ended according to the switch. Every bank compares those files line by line against its own ledger. A payment the switch shows as deemed but Bank of Baroda's ledger shows as credited is confirmed; one the ledger doesn't show is returned, and the reversal flows back to SBI and to Anjali. NPCI's back office for this is called URCS, and its dispute system, UDIR (Unified Dispute and Issue Resolution), lets banks and apps raise and track individual cases.
08When a bank is the weak link
8.1Business declines and technical declines
Every payment in section 7 depended on two banks' systems answering promptly, and the switch can't make them. Some banks run modern systems; others run core banking systems designed long before anyone imagined 800 million payments a day. So a large part of UPI's operation is measuring the banks and making the measurements public.
NPCI splits failed payments into two kinds. A business decline is a refusal for a reason about the customer: in NPCI's glossary, "a customer entering an invalid pin, incorrect beneficiary account etc." A technical decline is a refusal because something broke: "unavailability of systems and network issues on bank or NPCI side." Business declines are the system working correctly. Technical declines are the ones a bank can fix.
Every month NPCI publishes a table of the top 50 remitter banks and the top 50 beneficiary banks with those rates. Here is part of the remitter table for August 2026:
| Remitter bank | Transactions | Approved | Business declines | Technical declines | Debit reversal success |
|---|---|---|---|---|---|
| State Bank of India | 6,846 million | 90.78% | 9.11% | 0.10% | 96.60% |
| HDFC Bank | 1,786 million | 93.07% | 6.93% | 0.00% | 100.00% |
The beneficiary table has the same columns, plus the deemed-approved percentage from section 7. NPCI also publishes a monthly list of bank downtimes, with each incident's length.
?Why publish each bank's failure rate?
Because the switch has no other lever over a bank's systems, and the tables give it one. An app can see which banks are failing its users and route its own users' attention accordingly; a bank's management can see where it stands against its peers; and the regulator can see who needs pushing. It's a bit like a status page inside a company: a number that everyone can see gets fixed faster.
8.2When checking makes things worse
Section 7 introduced check-status as the polite way to learn what happened to an unknown payment. In April 2025 it turned out to be the problem. On 12 April 2025, NPCI said it was facing "intermittent technical issues, leading to partial UPI transaction declines," and news reports at the time described hours of failures. Two weeks later, on 26 April, NPCI's circular OC-215 named the cause it was guarding against: a "high number of 'Check Transaction Status' APIs by PSP banks at a very high Transactions Per Second (TPS) rate in repetitive manner".
You can see how that happens with a little arithmetic. When a bank slows down, more payments become unknown, so apps send more status checks, which land on the same struggling systems and slow them further. That loop is a retry storm, the same feedback chapter 40 describes for retries in general. This program estimates how much extra traffic status checks add when one bank holding a fifth of payers' accounts loses 30% of its replies during a 15,000-a-second evening peak (all three numbers are illustrative), under three checking policies.
# One slow bank at 7 pm: how much extra traffic do status checks add?
PAYMENTS_PER_SEC = 15_000 # an illustrative evening peak
SLOW_BANK_SHARE = 0.20 # this bank holds 20% of payers' accounts
UNKNOWN_RATE = 0.30 # while it's struggling, 30% of its replies go missing
unknown_per_sec = PAYMENTS_PER_SEC * SLOW_BANK_SHARE * UNKNOWN_RATE
policies = {
"app polls every 5 s for 2 min": 120 // 5,
"app polls every 15 s for 2 min": 120 // 15,
"at most 3 checks, first at 90 s": 3,
}
print(f"payments in doubt: {unknown_per_sec:,.0f} per second\n")
print(f"{'policy':34}{'checks/s':>10}{'vs payments':>13}")
for name, checks_each in policies.items():
checks = unknown_per_sec * checks_each
print(f"{name:34}{checks:>10,.0f}{checks / PAYMENTS_PER_SEC:>12.0%}")payments in doubt: 900 per second
policy checks/s vs payments
app polls every 5 s for 2 min 21,600 144%
app polls every 15 s for 2 min 7,200 48%
at most 3 checks, first at 90 s 2,700 18%With eager polling, status checks outnumber the payments themselves, 21,600 a second against 15,000, and every one of them goes to the bank that's already in trouble. Capping each unknown payment at three checks cuts that roughly eightfold. Waiting 90 seconds before the first check helps in a way the program doesn't show: it gives the bank time to recover before anyone asks.
That's what OC-215 imposed. The first check should come "after 90 seconds" (45 to 60 seconds once new timers came in), with a "maximum of 3 check transaction status APIs, preferably within 2 hours". After that, apps should wait for the settlement files or make one check through UDIR. No check at all is allowed after errors listed in the circular, such as a connection timeout or "no route to host", which already mean the other side can't be reached; the payment is treated as failed.
8.3Shorter timeouts and rationed API calls
NPCI followed with two more changes in 2025. Circular OC-214, issued the same day as OC-215, said that from 16 June 2025 the time allowed for a pay request and its response was cut from 30 seconds to 15, and for check-status and reversal from 30 seconds to 10. A shorter timeout sounds harsher, but it means a stuck payment is declared unknown and sent down the recovery path sooner, instead of holding Anjali at a spinner and holding threads at every bank along the way.
The second change rationed the calls that don't move money at all. Circular OC-215A, from May 2025, limited balance enquiries to 50 per app per customer a day, and only when the customer asks, with banks required to return the balance with every successful payment instead. It limited listing a customer's accounts to 25 a day per app, allowed address validation only when the customer is about to pay, and pushed the execution of recurring mandates, such as subscriptions, out of peak hours, which it defined as "10:00 hrs to 13:00 hrs and from 17:00 hrs to 21:30 hrs."
These rules protect the network from its own clients on an ordinary bad day. The predictable bad days, when everyone in the country pays at once, need something more.
09Festivals, the first of the month, and UPI Lite
9.1When the bursts come
UPI's daily statistics show two kinds of burst. The first is the turn of the month. The first days of a month, when salaries land and rent, bills and subscriptions go out, run well above an ordinary day, and value spikes even harder than count: an ordinary day in September 2026 carried about ₹0.98 lakh crore.
The second is festivals. In October 2025, the days before Diwali (20 October) ran high, with 754 million payments on 18 October, a record at the time. Festival shopping, gifts and travel push the days before up; the holiday itself is quieter.

Both kinds are predictable, and that shapes the defences. Capacity can be added ahead of a known date, as the JioHotstar case study (chapter 69) does before a final. Background work, such as recurring mandates, can be moved away from peaks, as OC-215A did. And, most interesting for the design, the smallest payments can be taken off the busiest path altogether.
?What does NPCI's switch run on?
Very little of it is published. NPCI announced two Tier IV data centres in 2020, in Chennai and Hyderabad. Its peak transactions per second, how payments are spread across sites, and how a site failure is handled are unpublished. What the design probably needs is clear enough from this chapter: the switch must keep each transaction's state durably (because section 7 depends on it), must keep running if a whole site is lost, and must have spare capacity well beyond the first-of-the-month peak, because the banks behind it can't absorb a sudden backlog either.
9.2UPI Lite: small payments off the core banking system
Look at Anjali's ₹20 from SBI's side. To approve it, SBI's core banking system had to verify the PIN, lock her account row, check the balance, write a debit, and later write the reversal if needed, all within a few seconds, for a sum a bit bigger than a bus fare. Most UPI payments are small, and every one of them is a write to the busiest database a bank owns.
UPI Lite, launched in September 2022, takes those payments off that path. A customer moves a small amount from their bank account into a Lite balance, a top-up that goes through the normal UPI flow with the PIN. Payments from the Lite balance then need no PIN, and, in NPCI's description, they work by "reducing load on core banking systems in real-time"; the remaining balance is shown in the app by NPCI's common library. Since a February 2025 circular, the limits are ₹1,000 per payment and ₹5,000 in the Lite balance. UPI Lite X, a variant, goes further: small payments between two phones over NFC with no network at all, for either side.
How each bank stores and posts Lite balances internally isn't published; the diagram shows the idea, a small separate ledger in front of the core system. It's a clear trade: a capped balance and no PIN, so a lost phone can lose at most ₹5,000, in exchange for taking the most numerous payments off the most loaded system. It's the same move as a cache in front of a database (chapter 25) or a prepaid transit card: absorb many small operations in a cheap place and settle with the expensive one rarely.
How do you stop tiny payments from overloading the banks?
- One path for every payment
- No separate balance to explain to customers
- Hundreds of banks, many on old systems, must all scale together
- Every ₹10 payment costs a full core-system write
- Far fewer core-system writes
- Faster and PIN-free at the counter
- Customers must top up
- Money at risk on a lost phone, up to the cap
NPCI did both, in a sense: the API limits and response-time rules of section 8 push banks to keep up, and UPI Lite gives them a way to keep the smallest payments out of the queue. Lite's limits have been raised as confidence grew, to ₹1,000 per payment and ₹5,000 in balance in 2025.
9.3Phones without apps, and credit
Two more extensions reach people and money the original design didn't. UPI 123PAY, launched in March 2022, lets feature phones, the keypad phones without app stores that many Indians still use, pay over UPI by calling an interactive voice response (IVR) number, giving a missed call, using an app built into the phone, or through sound-based payments.

Credit reached UPI too. Since 2022, RuPay credit cards can be linked to UPI addresses, and since 2023 banks can offer pre-approved credit lines over UPI. In all three, the switch and its flows are unchanged; what changes is how the payer's request is captured, or what kind of account sits behind the debit.
9.4Too much of the network in two apps
One kind of failure the switch can't route around is the failure of an app most people use. In August 2026, by NPCI's app-wise statistics, PhonePe carried about 45.6% of UPI transactions, Google Pay about 32.3% and Paytm about 8.0%. In November 2020 NPCI announced a cap of 30% of UPI volume for any single third-party app, measured over a rolling three months, with time to comply. NPCI has extended the deadline more than once; the latest extension, at the end of December 2024, moved it to 31 December 2026. Whatever happens to the cap, the system design concern behind it is concentration: if one app's outage can stop nearly half the country's payments, the network's redundancy at the switch matters less than it looks.
10The same problem in other countries
10.1Pix, FedNow and the card networks
Other countries have built the same thing with different answers to this chapter's decisions, and comparing them shows which answers were forced and which were choices.

| UPI (India) | Pix (Brazil) | FedNow (US) | Card networks | |
|---|---|---|---|---|
| Launched | 2016 | 16 Nov 2020 | 20 Jul 2023 | 1950s onward |
| Run by | NPCI, owned by banks | Banco Central do Brasil | Federal Reserve | Visa, Mastercard and others |
| Addresses | VPAs, resolved by each PSP | Keys in one central directory (DICT) | Account details; directories left to banks | Card numbers |
| Who starts it | Payer (push) | Payer (push) | Payer (push) | Merchant pulls from the card |
| Bank-to-bank settlement | Net, in 10 cycles a day | Gross, each payment, in central bank money | Gross, each payment, in Fed accounts | Net, usually next day |
| Scale | 24.07 billion in Sep 2026 | 7.8 billion in Dec 2025 | about 5 million in Q2 2026 |
Pix, the closest relative, made a record of 313 million payments on 5 December 2025 and 7.8 billion in that month, according to the central bank's 2026 report on it; its DICT directory held over 920 million keys. FedNow went live with 35 early-adopting banks and credit unions; in the second quarter of 2026 it carried just under 5 million payments, about 55,000 a day. That suggests the hard part of an instant payment system is probably getting banks and customers to use it, more than building the switch. ACI Worldwide's 2024 report on real-time payments put India at 49% of the world's real-time payments by count in 2023, and Brazil at 14%.
The card networks are the oldest relatives. A card payment is a pull: the shop's bank asks the card network to take money from the cardholder's bank, and the network settles between the banks later, net. UPI's pay flow is a push, started by the payer and approved with a PIN on the payer's own device, so there's no card number for a shop to store or leak. Chapter 52 follows a card payment end to end. One more difference is the fee: UPI payments have carried no merchant discount rate, the fee a shop pays per payment, since January 2020. That changes on 15 October 2026: an NPCI circular of 15 September 2026 introduced a fee of up to 0.4%, capped at ₹300, on payments to merchants above ₹2,000, with lower flat fees for some sectors such as fuel and railways. Payments up to ₹2,000, which NPCI says are more than 95% of merchant payments, stay free, and so do small merchants who receive up to ₹1 lakh a month straight into their own accounts. Ramesh's ₹20 costs him nothing.
11The whole system
11.1Every box, and why it's there
upi://pay?pa=ramesh.tea@okaxis…, types 20 and enters her PIN in NPCI's library, which encrypts it. Her app's PSP sends ReqPay to the switch.| Component | What it does | Added because |
|---|---|---|
| UPI switch | Routes every message, records each payment's state | apps × banks integrations, nobody owning the payment (§2) |
| VPAs and PSP directories | Turn name@handle into an account | Account numbers are unusable and unsafe to share (§3) |
QR / upi://pay URI | Carries the payee's address, and sometimes amount and reference | Paying a shop must take one scan (§3) |
| Debit-then-credit with reversal | Moves money as two local transactions | No transaction can span two banks (§4, §6) |
| Device binding and common library | Proves the phone and the PIN without the app seeing it | Any company may build an app (§5) |
| Deferred net settlement | Banks square up in ten cycles a day | Customers can't wait for interbank transfers (§6) |
| Deemed state, check status, T + 1 | Gives unknown payments a state, a deadline and an owner | Timeouts leave the credit unknown (§7) |
| Reconciliation and UDIR | Compare independent records; resolve disputes | Only the payee bank's own ledger knows the truth (§7) |
| Decline statistics and API rules | Measure banks; ration retries and polling | Banks are the weak link; retries make it worse (§8) |
| UPI Lite | Small payments from a capped side balance | Tiny payments overload core banking systems (§9) |
11.2From top to bottom
| Level | The choice | Data structure or algorithm |
|---|---|---|
| Network | One switch between all banks and apps | Hub routing: a + b connections instead of a × b |
| Addressing | Federated directory by handle | name@handle; handle → PSP table at the switch; name → account map at each PSP |
| Payment request | A URI with named fields | upi://pay?pa=…&pn=…&am=…&tr=…&sign=…, optionally RSA-signed |
| One payment | Ordered local transactions | Debit, then credit, then a compensating reversal on failure (a saga) |
| Authorisation | Encrypt at the keypad, decrypt in hardware | RSA-2048 credential block; re-encryption at the switch; bank HSM |
| Unknown outcomes | A named in-between state | State machine: initiated → debited → deemed → success or reversed |
| Bank settlement | Net, in cycles | Per-bank sums over a window, posted to RTGS |
| Recovery | Compare records | Line-by-line reconciliation of settlement files against bank ledgers |
| Load | Bound the retries | At most 3 status checks, first after 45–90 s; per-customer API quotas |
12What goes wrong, and what it cost
12.1Failures this design has to survive
| What happens | What Anjali or Ramesh sees | What the design does |
|---|---|---|
| Wrong PIN or low balance | "Payment failed" at once | Debit refused first, so nothing moved (business decline) |
| Payee bank refuses the credit | "Payment failed", money back in seconds | Debit reversal to SBI |
| Payee bank times out | "Pending" | Deemed state; limited status checks; reversal by T + 1 or ₹100 a day |
| A bank's systems are down | Payments from that bank fail | Technical decline, counted against that bank in NPCI's public tables |
| Apps hammer a slow bank with status checks | Failures spread to everyone | Caps on status checks, timers, and API quotas since 2025 |
| A fake QR pasted over the real one | Money goes to a stranger | The app shows the payee's registered name; speakers announce receipts |
| A fraudster sends a collect request | A request to "enter PIN to receive" | Collect between people switched off from October 2025 |
| First of the month | Slower payments, if anything | Capacity ahead of known peaks, mandates moved off-peak, UPI Lite |
12.2The tradeoffs, in one table
| Decision | Chosen | Given up | Why it was worth it |
|---|---|---|---|
| Topology | A central switch | Freedom from one critical system | a + b integrations; one owner per payment |
| Directory | Federated by PSP handle | One authoritative directory | No customer directory at the switch; PSPs mint addresses freely |
| Ordering | Debit, then credit | Money briefly out of the payer's account on a failed credit | Never pay out money that wasn't collected |
| Atomicity | Local steps plus reversal | An atomic commit across banks | No cross-bank locks; banks keep their own systems |
| PIN | NPCI library, end-to-end encrypted | App freedom over the PIN screen | Open app market without exposing PINs |
| Settlement | Deferred net, ten cycles a day | Zero settlement risk | RBI's system isn't in every payment's path |
| Timeouts | Deemed, then reconcile | An immediate answer for every payment | No money created or lost (§7's TryIt) |
| Small payments | UPI Lite with caps | PIN on every payment | Far fewer core-banking writes |
13Summary
- A national payment network needs a switch: with hundreds of banks and apps, point-to-point integration is a × b, and nobody would own the payment or the debts between banks.
- A VPA is a federated name: the handle names the PSP that knows the account, so the switch asks it, and Ramesh's QR never shows an account number.
- A UPI QR code is a URI with named fields; a static one says only who to pay, a dynamic one fixes the amount and a reference, and a signed one can't be edited.
- A payment is two local transactions in order, debit then credit, with a debit reversal when the credit fails, because no transaction can span two banks.
- The app never sees the PIN: NPCI's common library encrypts it at the keypad, and only the bank's HSM decrypts it, while device binding ties the app to the SIM.
- Customers are paid in seconds, banks later: deferred net settlement in ten cycles a day at RBI, where Pix and FedNow settle each payment gross.
- A timeout leaves the credit unknown, and both guesses are wrong; UPI records it as deemed, checks the payee bank, and reconciles against its ledger.
- Unknown payments have a deadline and an owner: reversal by T + 1 for transfers between people, or ₹100 a day to the customer.
- Banks are the weak link, and retries make it worse: NPCI publishes per-bank decline rates and, since April 2025, caps status checks and API calls.
- Bursts are predictable, so capacity is added ahead of the 1st of the month and festivals, and UPI Lite takes the smallest payments off core banking systems.
14Build this
A toy UPI switch.
- Write three small services: two "banks", each a SQLite database of accounts with
debit,credit,reverseandstatusendpoints, and a "switch" that keeps a table of transactions with a state column. - Make the switch run debit then credit, store each state change before acting on it, and reverse the debit when the credit is refused.
- Make the payee bank occasionally sleep past the switch's timeout, sometimes after committing the credit and sometimes before. Mark those payments deemed.
- Add a status checker that asks at most three times with growing gaps, and an end-of-day job that compares the switch's table with each bank's ledger and reverses every deemed payment the payee bank can't find.
- Run 10,000 payments and check the invariant from section 7: total debited equals total credited plus total reversed. Then break the rules (refund on timeout; poll every 100 ms) and watch the invariant or the load go wrong.
15Interview questions
beginnerWhat happens, step by step, when you scan a UPI QR code and pay?›
The app reads a upi://pay URI from the code, which holds the payee's address and perhaps an amount. You enter your PIN on a keypad drawn by NPCI's library, which encrypts it. Your app's PSP sends the request to NPCI's switch. The switch asks the PSP named by the address's handle which account it belongs to, asks your bank to verify the PIN and debit you, then asks the payee's bank to credit the payee, and confirms to both sides. Banks settle the money between them later, in net, several times a day.
beginnerWhy does UPI debit the payer before crediting the payee?›
Because most failures happen at the debit, wrong PIN, low balance, a blocked account, so doing it first means most failed payments fail before any money moves. And if the credit fails after a successful debit, the fix is to give the payer their money back, which is safe; the other order could leave the payee's bank having paid out money nobody collected.
intermediateThe payee bank times out after the payer was debited. What should the switch do?›
Neither refund nor confirm blindly: refunding pays twice when the credit did happen, and confirming loses money when it didn't. Record the payment in a distinct unknown state (UPI's deemed approval), tell the payer it's pending, ask the payee bank for the status a few times with spaced-out checks, and if that doesn't resolve it, settle it against the payee bank's own ledger through the settlement files. Put a deadline and an owner on it: in UPI, the beneficiary bank must reverse an uncredited transfer by T + 1, or pay compensation daily.
intermediateHow does UPI let third-party apps take payments without them seeing your PIN?›
The PIN is captured by NPCI's common library, embedded in every UPI app, which encrypts it with NPCI's public key into a credential block the app can't open. The switch re-encrypts it for the issuing bank, which decrypts it only inside a hardware security module. Separately, device binding by an SMS sent from the phone ties the app installation to the registered mobile number, so a request has to come from that phone and carry the PIN.
deepWhy doesn't UPI use two-phase commit between banks, and what does it use instead?›
Two-phase commit would have each bank lock the funds in a prepared state while it waits for the switch, so a slow switch or a lost message would hold locks in every bank's core system, across organisations that don't share infrastructure or trust. Instead each leg is a local transaction committed at once, in a fixed order, with a compensating reversal when a later leg fails: a saga. The switch records every leg; the unknown outcomes are closed by status checks and, finally, by reconciling settlement files with each bank's ledger. The banks' money moves later still, by deferred net settlement.
deepHow would you keep a slow bank from taking down a national payment network?›
Measure and publish each bank's technical decline rate so there's pressure to fix it. Keep timeouts short so payments to a slow bank fail fast and enter recovery instead of holding resources. Bound the recovery traffic: cap status checks per payment and delay the first one, forbid checks after overload errors, and ration non-payment calls such as balance enquiries, as NPCI did after its April 2025 incidents. Move background work like recurring mandates off peak hours, and take the smallest payments off core banking systems with a side balance like UPI Lite.
16Go deeper
A QR code says upi://pay?pa=shop@okicici&pn=Shop&cu=INR. Which party does the switch ask about the address, and can the payer change the amount?›
The PSP that owns the handle okicici, which maps "shop" to an account. There's no am field, so the payer types the amount.
The credit leg fails with a clear refusal. Is the payment deemed?›
No. A clear refusal means nothing was credited, so the switch reverses the debit at once. Deemed is only for the case where the payee bank didn't answer and the credit's outcome is unknown.
A payment between two people on Monday couldn't be credited. By when must the money be back, and what if it isn't?›
By the end of Tuesday (T + 1). After that the bank owes the customer ₹100 for every day of delay, paid without waiting for a complaint.
Monthly and daily volumes and values, app-wise shares, and the top-50 remitter and beneficiary bank tables with business and technical decline rates.
The four-party model, device binding, PIN encryption, and every field of the upi://pay URI.
Ten settlement cycles a day; limits on status checks and API calls after the April 2025 incidents; the end of collect requests between people.
T + 1 reversal and ₹100-a-day compensation for UPI and other payment systems.
The other national instant payment system: central directory, gross settlement in central bank money, and its growth since 2020.
The US instant payment service's volumes and participants since July 2023.
17Related chapters
Card payments, idempotency keys and double-entry ledgers: the single-company version of moving money exactly once. Chapter 52.
Two-phase commit, and the sagas and compensations UPI uses instead. Chapter 31.
Timeouts, retries, retry storms and load shedding. Chapter 40.
Why a busy system's latency explodes near saturation, and how to plan for known peaks. Chapter 42.
Another Indian system built around predictable national bursts. Chapter 69.
