The Setup — What Happens When You Hit "Pay"?
Every time you pay via Google Pay, PhonePe, or Paytm, you enter a 4–6 digit UPI PIN. In your head, you probably think the app checks it. It does not. The app is completely blind to your PIN.
Here's the full cast of actors involved in every single UPI transaction:
┌────────────┐ ┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ Your App │────▶│ PSP Bank │────▶│ NPCI │────▶│ Issuer Bank │
│ (GPay/PP) │ │(Axis/HDFC │ │ (the hub) │ │ (your bank) │
│ │ │ for GPay) │ │ │ │ │
└────────────┘ └─────────────┘ └──────────────┘ └─────────────┘
│ │
You enter Decrypts &
PIN here verifies PIN
(app never against CBS
sees it)
PIN Encryption — The HSM Magic
When you tap the UPI PIN keyboard on your phone and enter your digits, those keystrokes never go to Google Pay's servers. Instead, they go through the NPCI Common Library — a tamper-proof SDK embedded in every UPI app by NPCI mandate.
What is a PIN Block?
Your PIN doesn't travel as raw digits. It's packaged into a PIN Block using ISO 9564 Format 0. The format combines your PIN with the transaction details (like your account number) before encryption, so even replaying a captured encrypted PIN to a different account won't work.
// ISO 9564 Format 0 — simplified structure
PIN Block = Encrypt(
PIN_Data XOR Account_Data,
key = Public_Key_of_Issuer_Bank // bank's HSM public key
)
// PIN_Data → "0" + PIN_length + PIN_digits + padding
// Account_Data → "0000" + rightmost 12 digits of PAN
The HSM (Hardware Security Module)
Encryption happens using the Issuer Bank's public key, which is pre-loaded into the NPCI Common Library on your device. The corresponding private key lives only inside the bank's HSM — a tamper-proof hardware device. If someone tries to extract the private key physically, the HSM self-destructs it.
User types PIN
│
▼
┌─────────────────────────────────────┐
│ NPCI Common Library │
│ (runs in isolated secure enclave) │
│ │
│ 1. Accept PIN keystrokes │
│ 2. Build PIN Block (ISO 9564-0) │
│ 3. Encrypt with Bank's Public Key │
│ 4. Clear raw PIN from memory │
└────────────────────┬────────────────┘
│ Encrypted PIN Block
▼
Goes into UPI API call
(GPay server never sees raw PIN)
NPCI Common Library in Practice
Every UPI-enabled app must integrate the NPCI UPI SDK. It renders its own secure PIN pad (the keyboard you type your PIN on), handles encryption, and delivers the opaque encrypted blob back to the app. The app treats it as a black box it cannot read.
NPCI — The Traffic Controller
The encrypted PIN block travels from your app → to the PSP Bank (Google Pay uses Axis Bank, PhonePe uses Yes Bank) → to NPCI.
NPCI is the National Payments Corporation of India. It built and owns UPI. Every UPI transaction must pass through NPCI's switching infrastructure, which does several things:
user@okaxis) to a real bank account. It's essentially a DNS lookup for bank accounts — the VPA maps to an IFSC code + account number stored in NPCI's mapper.Issuer Bank — The Actual Verifier
The Issuer Bank is the only entity that can verify your PIN. It's the only one holding the private key to decrypt the PIN block.
00 (approved) or an error code. This triggers the SAGA pattern for the actual fund movement.The SAGA Pattern — Debit, Credit, Rollback
Once the PIN is verified, the actual fund movement is orchestrated using the SAGA pattern — a distributed transactions pattern designed for systems where you can't use a traditional ACID transaction across multiple databases.
UPI involves at least two banks (payer's and payee's), each with their own databases. A two-phase commit across two separate banks' core banking systems is not feasible at UPI's scale. SAGA solves this.
── HAPPY PATH ──────────────────────────────────────────
NPCI → Payer Bank: "Debit ₹500"
Payer Bank → NPCI: "Debited ✓"
NPCI → Payee Bank: "Credit ₹500"
Payee Bank → NPCI: "Credited ✓"
NPCI → App: "Transaction Successful ✓"
── ROLLBACK PATH ───────────────────────────────────────
NPCI → Payer Bank: "Debit ₹500"
Payer Bank → NPCI: "Debited ✓"
NPCI → Payee Bank: "Credit ₹500"
Payee Bank → NPCI: "FAILED ✗ (account frozen)"
NPCI → Payer Bank: "Reverse debit — Credit ₹500 back"
Payer Bank → NPCI: "Reversed ✓"
NPCI → App: "Transaction Failed — Refund initiated"
No Money Actually Moves — It's All Ledger Entries
Here's the part that surprises most developers: physical money doesn't move between banks in real time. What moves are accounting entries.
How a Bank Account Actually Works
Your bank balance is a number in a database row — your account's ledger entry. When you "send" ₹500 via UPI:
-- Payer's Bank CBS (Core Banking System)
UPDATE accounts
SET balance = balance - 500,
ledger_entry = 'DEBIT | TXN#xyz | ₹500 | NPCI'
WHERE account_id = 'payer_account';
-- Payee's Bank CBS
UPDATE accounts
SET balance = balance + 500,
ledger_entry = 'CREDIT | TXN#xyz | ₹500 | NPCI'
WHERE account_id = 'payee_account';
-- No physical cash moved. Just two database updates.
The Inter-Bank IOU (Nostro/Vostro)
But wait — if both banks just update their own databases, how does the payer's bank actually "pay" the payee's bank? The answer is a Nostro/Vostro account system and RTGS settlement.
After the UPI transaction, the payer's bank owes the payee's bank ₹500. This debt is tracked as a net position. Throughout the day, thousands of such IOUs accumulate in both directions. Settlement (actual inter-bank money movement) happens in batch cycles — not per transaction.
RTGS Settlement — 10 Cycles a Day
At the end of each settlement cycle (there are 10 per business day), NPCI calculates the net position between all participating banks and instructs the Reserve Bank of India (RBI) to move funds between banks' accounts at RBI.
Transactions during Cycle 1 (9am–10:30am):
─────────────────────────────────────────────
HDFC → SBI: ₹1,00,000 (500 transactions)
SBI → HDFC: ₹60,000 (300 transactions)
─────────────────────────────────────────────
Net Position: HDFC owes SBI ₹40,000
NPCI instructs RBI:
→ Debit HDFC's account at RBI: ₹40,000
→ Credit SBI's account at RBI: ₹40,000
This single RTGS entry settles 800 UPI transactions.
Why Net Settlement?
Settling every UPI transaction individually via RTGS would be catastrophically expensive and slow. With 10 billion+ monthly UPI transactions, gross settlement is impossible. Net settlement aggregates thousands of opposing transfers and only settles the difference — massively reducing inter-bank fund movements.
- Your UPI PIN is encrypted on your device using the Issuer Bank's public key, via the NPCI Common Library SDK. No payment app can see it.
- The encrypted PIN block travels: App → PSP Bank → NPCI → Issuer Bank. Only the Issuer Bank's HSM can decrypt and verify it.
- NPCI is the central switch — it resolves VPAs, routes transactions, coordinates the two-leg settlement, and handles rollbacks.
- UPI uses the SAGA pattern: Debit Leg first, then Credit Leg. If the credit fails, a compensating transaction reverses the debit automatically.
- No physical money moves during a UPI transaction — only ledger entries in each bank's Core Banking System are updated.
- Inter-bank settlement happens via RTGS at RBI, in 10 batch cycles per day, using net positions — not per-transaction settlement.
- Your "₹500 sent" is immediate in your ledger but the actual bank-to-bank fund transfer waits for the next RTGS cycle.