When you press "Send $100" on a money transfer app, many people imagine that $100 instantly moves from one bank account to another.
That's not what actually happens.
In most cases, what moves first is information, not money.
The actual settlement between banks may happen minutes or even hours later.
Let's understand the journey.
Step 1: User Initiates Transfer
The mobile application sends a request to the backend.
POST /transfer
{
"sender":"John",
"receiver":"Alex",
"amount":100,
"currency":"USD"
}This request first reaches an API Gateway, which authenticates the user before forwarding it to the Transfer Service.
Mobile App
|
API Gateway
|
Transfer ServiceStep 2: Validation
Before moving any money, several checks happen.
- Is the sender authenticated?
- Is the account active?
- Does the sender have sufficient balance?
- Has the daily transfer limit been exceeded?
- Does this transaction trigger AML or fraud rules?
If any validation fails, the transfer is rejected immediately.
Step 3: Create a Transaction
The system now creates a transaction.
Transfer transfer = new Transfer(
UUID.randomUUID(),
sender,
receiver,
amount,
Status.PENDING
);
transferRepository.save(transfer);Notice that the transaction is PENDING.
No money has actually moved yet.
This allows the system to recover safely if something fails later.
Step 4: Debit the Sender
The sender's balance is reduced.
This operation is usually wrapped inside a database transaction.
@Transactional
public void debit(Account account,double amount){
if(account.getBalance()<amount){
throw new InsufficientFundsException();
}
account.setBalance(
account.getBalance()-amount
);
}Using @Transactional ensures that either everything succeeds or everything rolls back.
Partial money transfers are unacceptable.
Step 5: Publish an Event
Instead of immediately notifying every downstream system, modern applications publish an event.
MoneyDebited EventThe event is pushed to Kafka or RabbitMQ.
Transfer Service
|
Kafka
/ | \
Ledger Notification AuditNow different services can process the transaction independently.
- Ledger Service updates accounting records.
- Notification Service sends SMS and emails.
- Audit Service stores compliance logs.
This keeps services loosely coupled.
Step 6: Settlement
This is where most people get confused.
The receiving bank doesn't instantly receive physical money.
Instead, financial institutions maintain settlement accounts and periodically reconcile transactions.
Sender Bank
↓
Money Transfer Platform
↓
Payment Network (SWIFT / ACH / SEPA / RTP / UPI, etc.)
↓
Receiver Bank
↓
Receiver AccountFrom the user's perspective, the receiver gets paid (confused how? Skip to Step 7).
Behind the scenes, banks later settle the net balances between themselves through established financial networks.
This separation between customer experience and interbank settlement is one reason large payment systems can scale efficiently.
Step 7: How Does the Money Reach the Receiver's Bank?
At this point, our platform has successfully processed the transfer.
But here's an important question.
If John's account was debited in our system, how does Seth's actual bank account receive the money?
The answer is simple.
Our database never updates the bank's database directly.
Banks are independent institutions with their own infrastructure.
Instead, we send a payment instruction through a financial payment network.
John
|
Money Transfer Platform
|
Payment Network
(SWIFT / ACH / SEPA / UPI / RTP)
|
Receiver's Bank
|
Seth's Bank AccountThe payment instruction contains information like:
{
"sender":"John",
"receiver":"Seth",
"receiverBank":"ABC Bank",
"amount":100,
"currency":"USD"
}The receiver's bank validates the instruction and credits Seth's account.
Only the receiver's bank can update Seth's balance.
UPDATE accounts
SET balance = balance + 100
WHERE account_id = 'Seth';This update happens inside the bank's own systems, not ours.
But Where Does The Actual Money Come From?
Another common misconception is that $100 is instantly transferred from one bank to another.
In reality, banks maintain settlement accounts with each other or with central banks.
Imagine these transfers happen throughout the day.
John -> Seth $100
Alice -> Bob $200
Mike -> Emma $50Instead of moving money after every transaction, the payment network keeps track of all obligations.
At the end of a settlement cycle, only the net difference is transferred.
Bank A owes Bank B
$18,450Only this final amount is settled between the banks.
This makes payment systems significantly faster and more efficient.
How Does The Platform Know The Transfer Succeeded?
After crediting Seth's account, the receiving bank sends a response.
POST /payment-status
{
"transactionId":"TXN12345",
"status":"SUCCESS"
}or
{
"transactionId":"TXN12345",
"status":"FAILED"
}Our platform updates the transaction accordingly.
transfer.setStatus(Status.SUCCESS);
transferRepository.save(transfer);If the transfer fails, compensation logic or a refund workflow is triggered depending on the business rules.
Why Event Driven Architecture Helps Here
Notice something interesting.
Our Transfer Service doesn't wait for the receiving bank to finish everything.
Instead, it publishes an event.
MoneyDebited
|
Kafka
|
Settlement Service
|
Payment Network
|
Receiver BankThis allows the user to immediately see:
"Your transfer is being processed."
instead of waiting several seconds for multiple external systems to respond.
This asynchronous architecture is one of the reasons modern payment platforms can process millions of transfers every day while remaining responsive.
Handling Millions of Requests
Imagine thousands of users sending money every second.
Creating a new Java thread for every request would quickly exhaust system resources.
Instead, applications use thread pools.
ExecutorService executor =
Executors.newFixedThreadPool(200);
executor.submit(() ->
transferService.transfer(request));Worker threads are reused, allowing the system to process a massive number of concurrent requests efficiently.
Preventing Duplicate Transfers
Suppose a user's internet connection drops after clicking Send.
They press the button again.
Without protection, the backend might process the payment twice.
Modern payment systems use Idempotency Keys.
Idempotency-Key:
4a12b6d9-f51dIf the same request arrives again with the same key, the server simply returns the previous response instead of processing the transfer again.
This prevents accidental duplicate payments.
Why Event Driven Architecture?
Payment systems involve many independent tasks.
- Updating balances
- Fraud detection
- Sending notifications
- Creating audit logs
- Updating analytics
Doing all of these synchronously would make transfers slow.
Instead, only the critical path executes immediately.
Everything else happens asynchronously using events.
This improves scalability while keeping the user experience fast.
Technologies Commonly Used
- Java & Spring Boot
- REST APIs
- Kafka / RabbitMQ
- SQL databases
- Redis for caching and distributed locking
- Docker & Kubernetes
- Thread Pools (
ExecutorService) - Database Transactions (
@Transactional) - Retry mechanisms
- Circuit Breakers
- Idempotency Keys
Final Thoughts
At first glance, sending money looks like a simple API call.
In reality, every transfer passes through multiple stages involving validation, transactions, messaging systems, fraud detection, notifications, and settlement between financial institutions.
The money itself doesn't instantly fly across the internet. What moves first is trusted information, while reliable distributed systems ensure that every transaction is processed exactly once, remains consistent, and eventually reaches its destination safely.