The Unified Payments Interface, operated by the National Payments Corporation of India (NPCI) since 2016, lets anyone with a bank account and a phone send money to anyone else in a few seconds, at any hour, using an address like name@bank instead of an account number. It now carries hundreds of millions of payments a day, from tea stalls to tax payments, through dozens of apps and hundreds of banks.

This article treats UPI as a system design case study. It explains the parties and the message flow from first principles, where trust and state live, why a payment can be neither clearly successful nor clearly failed, how money actually settles between banks, and what the 2025 rule changes mean for anyone integrating. The second half is practical: a merchant integration built to survive timeouts, with code, failure modes and a checklist. NPCI's detailed message specifications are shared with member banks rather than published, so this page describes the architecture and avoids asserting field-level details. For a generic marketplace payment design to compare against, see Designing a Payment System.

Advertisement

The parties: four players and a switch

Card networks have a familiar four-party shape: cardholder's bank, merchant's bank, and a network between them. UPI keeps the network in the middle but changes who talks to the user.

  • Payment Service Provider (PSP). A bank that is a UPI member and offers the customer-facing service. The PSP is where a UPI address, the virtual payment address (VPA), is registered. Large consumer apps such as Google Pay and PhonePe are third-party application providers (TPAPs): they are not banks, so they connect through one or more sponsor PSP banks whose handles appear in the address.
  • Remitter bank. The bank holding the payer's account. It verifies the UPI PIN and debits the account. It may be different from the payer's PSP.
  • Beneficiary bank. The bank holding the payee's account, which credits it.
  • NPCI UPI switch. The central hub. Every transaction passes through it: it resolves addresses via the payee's PSP, orchestrates the debit and the credit, enforces limits and timeouts, and records the outcome for settlement and disputes.

The design separates three things that card systems bundle. The app owns the user experience. The PSP owns the address and the customer relationship for that address. The bank owns the money and the credential. One customer can have several apps, several addresses and several linked accounts, and the switch makes them interoperable: any app can pay any address at any bank. That is the core architectural bet: a thin, mandatory central switch buys universal reachability without every bank integrating with every other bank.

A UPI push payment: four parties around one central switchPayer appPSP app or TPAPPayer PSP banksponsor, risk checks1 payNPCI UPI switchroute, orchestrate2 pay requestPayee PSPresolves VPA3 resolveaccountRemitter bankverify PIN, debit4 debitBeneficiary bankcredit payee5 creditdebit first, then credit6 response7 statusDeferred net settlementNPCI nets obligations between member banks per settlement cycle; funds move between their RBI accounts laternet positionsThe payer's PIN is encrypted on the device and only the remitter bank can verify it; PSPs and NPCI relay it.
A push payment. Numbers show the order of messages; the debit always completes before the credit is attempted, and settlement between banks happens later and in bulk.

Identity: addresses, device binding and the PIN

A VPA is a pointer, not an account. The PSP that owns the handle keeps the mapping from address to a linked account, and answers the switch when someone pays that address.

Onboarding binds three things together. The app verifies that the phone number registered with the bank is present on the device, typically by sending an SMS from the device to a number the PSP controls, which proves the SIM is physically there. The PSP then discovers accounts linked to that mobile number at participating banks. Finally the user sets a UPI PIN, which the remitter bank validates using debit card details and an OTP or, increasingly, other bank-approved methods.

The PIN is the second factor and is designed so no intermediary can read it. It is captured inside an NPCI-provided common library embedded in the app, encrypted on the device, and passed through the PSP and the switch to the remitter bank, which alone can decrypt and verify it. The device binding is the first factor: the request comes from a registered device and app instance. This is why most UPI fraud is social engineering, persuading the owner to authorise a payment, rather than credential theft.

Advertisement

Anatomy of a push payment

Take Asha paying a shop 450 rupees by scanning a static QR code. The QR encodes the shop's VPA, name and optionally an amount and reference. The flow, simplified:

  1. Asha's app builds a payment request with a unique transaction id, the payee address, amount and her encrypted PIN, and sends it to her PSP, which applies its own risk checks and limits.
  2. The PSP forwards it to the switch. The switch asks the payee's PSP to resolve the address to an account and the beneficiary bank.
  3. The switch sends a debit request to Asha's remitter bank. The bank verifies the PIN, checks balance and limits, debits 450 and replies.
  4. Only after a successful debit does the switch send a credit request to the shop's beneficiary bank, which credits the account and replies.
  5. The switch returns the final status to Asha's PSP and app, and notifies the payee side, so the shopkeeper's speaker or app announces the payment.

Debit-then-credit is a deliberate choice: never credit money that has not been taken. The cost is a window where Asha has been debited and the shop not yet credited. If the credit leg fails, the switch instructs a reversal of the debit. If the credit leg times out, nobody yet knows whether the shop was credited. That window is the source of almost every hard problem in UPI engineering.

Collect requests, mandates and the end of P2P collect

UPI also supports pull payments. A collect request is initiated by the payee; the payer's app shows it and the payer approves with the PIN. It was also a favourite fraud tool, with requests disguised as refunds or prizes. NPCI reduced the risk in steps and, by a circular dated 29 July 2025, directed members not to initiate, route or process peer-to-peer collect transactions from 1 October 2025. Merchant collect continues for verified merchants. If you design a feature around asking another person for money, use a link or QR code the payer pays from, not a collect.

AutoPay mandates let a customer authorise recurring debits once, with a PIN, for subscriptions, bills and loan instalments; later executions happen without a PIN within the mandate's limits. Under NPCI's API usage guidelines enforced from 1 August 2025, mandate executions are scheduled outside peak windows and limited in retries. If you depend on recurring collections, design for execution at a time you do not fully control and for a bounded number of attempts.

The state problem: pending, deemed and reversed

From the payer's side, a transaction can end in four ways: success, failure before debit, failure after debit with reversal, or unknown. Unknown is not rare at this scale. A bank's core system is slow, a network hop drops a response, the switch's timer fires. NPCI's guidelines set response time limits, reported as 15 seconds for payment requests and 10 seconds for status checks and reversals, and a leg that does not answer in time leaves the transaction pending.

A pending or deemed-approved transaction must be resolved by asking, not by guessing. The resolution paths are status-check calls through the PSP, final status pushed later, end-of-day reconciliation between banks and the switch, and, if the payer was debited but the payee never credited, an automatic reversal. The RBI's turnaround-time rules require such failed transactions to be reversed within a defined time, with compensation to the customer if the bank misses it, and NPCI's dispute system handles what automation cannot.

Status checks are rate-limited. The 2025 guidelines, as reported, allow a check transaction status call only a small number of times per transaction, three, spaced 90 seconds apart, and cap balance enquiries at 50 per app per user per day. A merchant backend that polls every two seconds is now non-compliant as well as wasteful. Confirm the current numbers with your PSP before coding them, because NPCI revises them.

Worked example: a merchant checkout that survives timeouts

Suppose an online shop integrates through a PSP's merchant API: it creates a payment intent for an order, shows a dynamic QR or an intent link that opens the customer's UPI app, receives a callback, and can query status. The rules for a robust design follow from the state problem.

  • One live payment per order. Never create a second UPI transaction for an order while the first is pending. A customer who retries on a spinner can otherwise be debited twice.
  • Callbacks are hints, not truth. They can be late, duplicated or missing. Process them idempotently, verify their signature, and confirm with a status call or the settlement file before shipping high-value goods.
  • Pending is a real state. Show the customer that the payment is being confirmed, not that it failed. Failing the order while money may have moved creates refunds and support tickets.
STATUS_GAP_S = 90          # spacing in NPCI's 2025 API guidelines, as reported
MAX_STATUS_CHECKS = 3      # confirm current limits with your PSP

def create_payment(order_id, amount_paise):
    with db.transaction():
        order = db.orders.get_for_update(order_id)
        live = db.payments.find_one(order_id=order_id, state__in=("CREATED", "PENDING"))
        if live:
            return live                     # reuse; never open a second debit
        txn = psp.create_intent(order_ref=order_id, amount=amount_paise)
        return db.payments.insert(txn_id=txn.id, order_id=order_id, amount=amount_paise,
                                  state="PENDING", checks=0, next_check=now() + STATUS_GAP_S)

def on_callback(payload, signature):
    if not psp.verify_signature(payload, signature):
        raise Forbidden()
    apply_status(payload["txn_id"], payload["status"], payload.get("rrn"))

def apply_status(txn_id, status, rrn):
    with db.transaction():
        p = db.payments.get_for_update(txn_id)
        if p.state in ("SUCCESS", "FAILED"):
            return                          # duplicate or late message: no-op
        if status == "SUCCESS":
            p.state, p.rrn = "SUCCESS", rrn
            outbox.emit("order.paid", p.order_id)
        elif status == "FAILED":
            p.state = "FAILED"
            outbox.emit("order.payment_failed", p.order_id)
        # anything else stays PENDING

def sweeper():                              # runs every minute
    for p in db.payments.due(state="PENDING", next_check__lte=now()):
        if p.checks >= MAX_STATUS_CHECKS:
            p.state = "AWAITING_RECON"      # do not fail: the debit may have happened
            continue
        p.checks += 1
        p.next_check = now() + STATUS_GAP_S
        apply_status(p.txn_id, psp.check_status(p.txn_id), None)

The outbox emits order events in the same database transaction as the state change, so a crash cannot mark a payment successful without telling fulfilment; see the outbox pattern and idempotency for the underlying techniques. Reconciliation then joins the PSP's settlement report against the payments table by transaction id and sorts every row into a bucket: matched; amount mismatch; success in the report but pending or failed internally, a late success that must be fulfilled or refunded; and success internally but absent from the report, which must be investigated before the goods ship. Every unmatched bucket should open a ticket automatically.

Settlement: real-time credit, deferred money movement

The customer sees the credit in seconds, but banks do not move money between themselves per transaction. The switch records every successful debit and credit and computes, for each settlement cycle, each bank's net position: what its customers paid out minus what they received. Several cycles run each day, and the net amounts are settled across the banks' accounts at the Reserve Bank of India.

This is the classic split between clearing and settlement. Real-time clearing gives the user instant finality in practice, while deferred net settlement keeps interbank transfers small and few. The price is credit exposure: between cycles, a beneficiary bank has credited customers with money it has not yet received, which the settlement framework and member obligations are designed to cover. For engineers, the consequence is that the transaction id and the bank reference number are the keys that tie the user-visible event to the settlement record, and both must be stored.

Capacity and resilience

UPI's load is spiky: salary days, festivals, sale events and evening shopping hours drive peaks many times the average. Each participant must hold its latency budget at peak, because a slow remitter bank does not just fail its own payments; its timeouts turn into status checks and reversals that load everyone else. NPCI's 2025 guidelines respond to that directly: they cap non-financial API calls such as balance enquiry and account listing per app, move AutoPay execution out of peak hours, and require PSPs to rate-limit system-initiated calls, not only user-initiated ones.

For an app or PSP the toolkit is standard: per-bank circuit breakers, backpressure instead of retry storms, and priority for financial calls, covered in load shedding. The UPI-specific rule is that retries of a payment are not yours to make: the transaction id is fixed, and a new attempt is a new payment.

Failure modes

  • Double debit. The app or merchant creates a fresh transaction when the first is merely pending.
  • Premature failure. An order is cancelled on timeout; the debit completes and the customer is charged for nothing until reconciliation.
  • Poll storms. Aggressive status polling breaches API limits and adds load exactly when a bank is struggling.
  • Trusting unsigned callbacks. A forged success notification ships goods without payment.
  • Collect-based designs. A product feature built on P2P collect, which is no longer processed.
  • Social-engineering fraud. The system works as designed but the user is tricked into approving; mitigations are UX friction, payee name display and risk scoring, not cryptography.

Trade-offs

A mandatory central switch gives universal interoperability, consistent rules and one place to see fraud and load, but concentrates operational risk and makes NPCI a dependency for every payment. Debit-before-credit protects against crediting unpaid money, at the cost of a debited-but-not-credited window. Deferred net settlement keeps interbank movement efficient but creates intraday exposure. Strict API limits stabilise the network but push complexity into merchant backends.

What to do next

  1. Draw your integration's state machine with pending, success, failed, reversed and awaiting-reconciliation states before writing code.
  2. Enforce one live UPI transaction per order in the database, and store both the transaction id and the bank reference number.
  3. Verify callback signatures, apply them idempotently, and emit downstream events through an outbox.
  4. Implement status checks within the current NPCI limits your PSP confirms, and stop at the cap instead of failing the order.
  5. Build daily reconciliation against the settlement report with automatic alerts for every unmatched bucket.
  6. Remove any dependence on P2P collect; use QR codes, intent links or merchant collect where eligible.
Key takeaway: UPI is a central switch connecting apps, address-owning PSPs and account-holding banks, with device binding plus an issuer-verified PIN for authentication, debit-then-credit for safety and deferred net settlement for efficiency. The hard part for anyone building on it is the pending transaction: never retry a payment as a new one, never fail an order on a timeout, ask for status within NPCI's limits, and let reconciliation decide.