Every payment you send is a conversation between strangers who don’t trust each other.
You open your UPI app.
You type an amount.
You enter your PIN.
Two seconds later, a green checkmark appears. ₹500 sent successfully.
It feels instant. It feels like money just travelled from your account to someone else’s.
It didn’t.
Your money hasn’t actually moved yet.
What travelled in those two seconds was a message — passed between systems that don’t trust each other, don’t share a database, and belong to completely different organizations. That checkmark is the final sentence of a conversation involving API gateways, core banking systems, a national payment switch, fraud engines, reconciliation jobs, and settlement systems.
As users, we only ever see the ending.
Let’s rewind the conversation from the beginning.

Money Doesn’t Travel. Messages Do.
Imagine two completely separate databases.
One belongs to the sender’s bank. The other belongs to the receiver’s bank.
There’s no cable connecting them. No shared transaction. No shared table. No distributed query updating both at once.
Instead, one database decreases a number. A separate database, on separate infrastructure, increases a different number.
The hard part was never updating a balance. The hard part is getting two independent systems that share nothing to agree that both updates should happen, without either one lying, stalling, or going quiet halfway through.
A payment is a protocol. The rupees are just what the two sides are negotiating over.
Every Payment Starts at the API Gateway
Your app never talks directly to Core Banking. It first reaches an API Gateway think of it as the bank’s receptionist.
Before your request is allowed inside, it’s asked:
- Is this request authenticated?
- Is the session still valid?
- Does this look like a normal payment, or a script hammering an endpoint?
For backend engineers, this layer looks familiar: authentication, authorization, rate limiting, request validation, logging, routing. It’s the part of the stack most people mistake for “the whole system.”
It isn’t. It’s the front door. Not the vault.
What looks like one tap is really a relay of messages across six or more independent systems each with its own database, its own failure modes, and its own opinion about whether your payment should go through.
Core Banking Makes the First Decision
The request reaches the Core Banking System the actual ledger that owns the sender’s balance. It checks:
- Does this account exist, and is it active?
- Is there enough available balance — not just displayed balance?
- Can we reserve this amount right now, so it can’t be spent twice while the rest of the conversation plays out?
Notice the wording on that third one.
Authorization is not the same as settlement.
The sender’s bank has agreed to participate. It still has no idea whether the receiving bank will.
Even after the sender’s bank approves the payment, the receiving bank can still reject it.
Same payment. Two banks. Two completely independent decisions — and one bank saying yes guarantees nothing about what the other side says.
For engineers: this is a hold-then-commit pattern the same shape as a two-phase commit, except nobody trusts the coordinator, and the “commit” message can get lost on the way back. Keep that in mind it resurfaces in a few sections.

Why Banks Don’t Call Each Other Directly
Suppose there were 100 banks.
If every bank connected directly to every other bank, you’d need thousands of secure integrations each one needing its own security posture, version compatibility, monitoring, and failover.
Nobody survives that math.
Seven banks talking directly need 21 separate relationships. The same seven banks routed through one switch need seven and all seven speak one language.
So banks don’t talk to each other at all. Everyone connects once, to one shared switch. Integrate once. Let the switch handle the rest.
Why Not One Big Database?
If two banks disagreeing is such a headache, here’s the obvious question.
Why not skip all of this? Why not put every account, at every bank, in one shared database?
Technically, trivial. One ledger. No reconciliation. No ACK, no NACK, no timeouts.
Here’s why it never happens.
The problem was never the technology. It’s the governance.

One shared database means every bank and every one of their customers has to trust a single company with everyone’s money. One outage takes down the entire country. One breach exposes every account at once. One company decides who gets to move money and who doesn’t.
Banks need independence. Customers need the ability to move between banks without asking permission. Regulators need many independent institutions they can hold individually accountable, not one company too large to fail.
A single shared database solves one engineering problem by creating about ten legal, political, and systemic-risk problems.
So instead, every bank keeps its own ledger, its own rules, its own accountability and they talk to each other through a switch instead of merging into one.
How NPCI Finds the Right Bank
NPCI doesn’t own any account. It doesn’t store any balance.
Picture it instead as the operator of the country’s busiest railway junction.
Thousands of trains arrive every minute. The junction owns none of them doesn’t know the passengers’ names, doesn’t care where anyone’s headed for dinner.
It does exactly one thing: reads the destination on the ticket, and decides which platform the train leaves from.
Here’s the part that makes this feel less like bureaucracy and more like magic.
When you type in a payment identifier, NPCI doesn’t go searching through every bank in the country to find out where it belongs.
It already knows. That identifier was registered against a specific bank the moment it was created — long before you ever typed it. The destination was known before the first byte of your payment even left your bank’s servers.
The switch isn’t discovering anything. It’s routing something it already had the answer to.
Underneath that, three unglamorous jobs are happening:

If you’ve ever built an integration layer between two systems that don’t trust each other’s data models message queues, retries, dead-letter handling, circuit breakers for the partner that’s timing out — you’ve built a small, low-stakes cousin of what a national switch runs at the scale of billions of messages.
The Receiver Bank Decides Too
The sender’s bank approving the payment isn’t enough. The receiving bank independently verifies:
- Does the beneficiary account actually exist, and does the name match?
- Is it active, frozen, dormant, or flagged?
- Is this account even allowed to receive this amount?
Only after all of that clears does it post the credit actually increase the payee’s balance.
Notice the asymmetry: one bank held money. The other posted money. Two different operations, in two different systems, forced into agreement despite neither one seeing what the other is doing in real time.
ACK, NACK, and the Silence That Terrifies Engineers
Once the receiving bank decides, it has to tell the rest of the chain what happened. There are only three real outcomes.
ACK — Acknowledgement,
NACK — Negative Acknowledgement

ACK and NACK are easy one side says yes or no, the other reacts. Silence is where it gets genuinely hard.
Why Payments Stay Pending
- A payment travels through multiple independent systems before an ACK or NACK is returned.
- If the confirmation is delayed or lost, the sender cannot safely determine whether the payment succeeded or failed — even if the receiver has already been credited.
- This uncertainty is a classic distributed systems problem known as the Two Generals Problem.
- Instead of guessing, the systems query the transaction’s current state using its Transaction ID.
- That’s why a payment may temporarily show “Pending” — not because it’s stuck, but because the system refuses to guess.
Nobody Trusts Anybody
- Every participant in the payment chain independently verifies every message.
- Every request is cryptographically signed to prove it came from a trusted source.
- Every request carries a timestamp to prevent replay attacks.
- Every payment has a unique Transaction ID to prevent duplicate processing.
- Payment systems have followed the “Trust nothing until it’s verified” principle long before Zero Trust became a popular security concept.
The Transaction ID That Saves Millions
Go back to the timeout scenario for a second.
Here’s what actually stops it: every payment carries a transaction ID, generated once, before the very first attempt.
- If an ACK is lost, the payment may be retried.
- Every payment carries a unique Transaction ID.
- Retries reuse the same Transaction ID, not a new one.
- Duplicate requests are detected and ignored.
- Retry many times. Get charged only once. (Idempotency)
Why Retrying Doesn’t Mean Paying Twice
Imagine ordering food online.
You place an order, but your internet disconnects before you see the confirmation.
Now you have a question:
- Did the restaurant receive my order?
- Or should I place it again?
If you order again without checking, you might receive two meals.
Payments face the same problem.
- Networks can lose requests or responses.
- Systems can’t always know whether a payment was already processed.
- So every retry uses the same Transaction ID.
- If the payment already exists, it’s ignored instead of processed again.
Retry as many times as you want. Pay only once.
Money Still Hasn’t Moved
Here’s the part that surprises almost everyone.
When the screen says “Payment Successful,” the money between banks generally hasn’t actually moved yet.
What has happened: both banks’ ledgers now agree with each other. One bank knows it owes the network. The other knows the network owes it. The real transfer — actual reserves shifting between institutions — happens separately, in batches.
Picture ten friends who eat together every night for a month. Nobody settles the bill after every meal — everyone keeps a running tally, and once a month, only the net difference actually changes hands.
That’s exactly what a payment switch does with netting:

Thousands of individual payments between two banks collapse into a single net figure. Only that difference actually moves. Everything else cancels out on paper. This single decision is the entire reason a network processing billions of transactions doesn’t also require billions of individual bank-to-bank transfers.
The green checkmark and the real movement of money are two different events, running on two entirely different clocks.
Clearing vs Settlement
Even engineers who’ve built payment features tend to use “clearing” and “settlement” interchangeably. They’re not the same step, and the difference is the whole point of this article
Payment Initiated
│
▼
Authorization
"Can this payment happen at all?"
│
▼
Clearing
"Both banks now agree on who owes whom, and how much."
│
▼
Settlement
"Actual money moves between the banks' accounts at the central bank."
Authorization happens in milliseconds — it’s the hold, the ACK, the checkmark on your screen.
Clearing happens shortly after — this is the bookkeeping step, where the switch and both banks agree on the exact obligation this transaction created, and file it alongside thousands of others in the same cycle.
Settlement happens last, often much later — this is the netting step from the previous section, where real reserves actually move.
Three separate steps. Three separate systems. Three separate points of failure. And only the first one is something you, the customer, ever actually see.
Why Reconciliation Exists
Here’s a scenario that happens more often than any bank would like to admit.
Sender's Bank → SUCCESS
NPCI → SUCCESS
Receiver's Bank → PENDING
Three systems. Three opinions. About the exact same transaction.
Imagine three friends splitting a restaurant bill.
- One says “I paid.”
- Another says “I received it.”
- The third says “I’m still waiting.”
Before anyone leaves, they compare their records until everyone agrees on what actually happened.
Banks do the same thing.
- The Sender Bank, NPCI, and Receiver Bank compare the Transaction ID, Amount, Status, and Timestamp.
- If something doesn’t match, they keep checking until all three agree.
- This process is called Reconciliation, and it runs continuously in the background — even after you see “Payment Successful.”
Notifications Are the Last to Know
The SMS is not part of the payment. Neither is the push notification, nor the transaction history update.
These are downstream systems reacting to an event that has already happened — not participants in deciding whether it happened.
That’s why you can occasionally see: money debited, no SMS, payment still successful. The notification system is independent of the payment system, and it’s allowed to fail entirely on its own.
One Payment, Twenty-Plus Backend Events
Zoom out for a second and look at everything this article has actually described.
Most people picture a payment like this:
Tap -> Done
Here’s what it actually looks like, end to end:

One tap. One green checkmark. Comfortably past twenty distinct backend events, spread across systems owned by different teams, different companies, and in several cases, different countries’ regulators.
This is what modern financial infrastructure actually is: not one system doing one job, but twenty-plus systems, each doing one small job well, none of them aware of the whole picture — and somehow, almost every single time, it works.
When a Payment Gets Stuck
Here’s how one of these actually ends, on a bad day.
A payment is made. One bank debits the sender. The other credits the receiver.
The acknowledgement, somewhere on the way back, disappears.
The app shows “Pending.” Just a spinner. Nothing more.
Behind that spinner: two reconciliation jobs wake up on a schedule nobody watching the spinner will ever see. Six databases compare notes. Twenty log entries get pulled and diffed, transaction ID by transaction ID, until every system agrees on the same version of events.
Four hours later, the phone quietly changes one word. Pending becomes Success.
To the person waiting, it looked like nothing happened at all.
To the systems underneath, it was one of the hardest problems in distributed computing solved silently, without asking anyone to notice.
That’s the trick. The whole system is built so that the moment two computers that don’t trust each other manage to agree on the truth, it feels, to the person waiting, exactly like nothing happened at all.
One Last Thought
The next time your screen shows “Payment Successful,” remember:
- It wasn’t one system making one decision.
- It was many systems reaching the same decision.
- Every message was verified.
- Every retry was handled safely.
- Every transaction remained traceable.
- And all of that happened in just a few seconds.
That’s what modern payment engineering looks like.
This is why Pending sometimes quietly becomes Success minutes later.
#Fintech #Java #SystemDesignConcepts #BankingTechnology #Database
