Goal: To build a simple yet robust digital wallet backend that handles money transactions safely, atomically, and consistently — just like a real e-wallet system (Paytm, PhonePe, etc.), but simplified for learning and demonstration.
The system allows customers to:
- Create a wallet linked to their profile.
- Add money (credit) into their wallet.
- Spend or withdraw (debit) money.
- Transfer money between wallets.
- View all past transactions (ledger/history).
Everything happens within the same database transaction so that no money is lost or duplicated, even if two operations occur at the same time (concurrency-safe).
| Concept | Explanation |
|---|---|
| Transactional Consistency | Using @Transactional ensures debit and credit operations either both happen or both fail. |
| Optimistic Locking | Prevents double spending when multiple users access the same wallet simultaneously. |
| Idempotency | Ensures that reprocessing the same transaction doesn’t duplicate results. |
| Layered Architecture | Clean separation between controller, service, repository, and entity layers. |
| Optional Caching (Redis) | Speeds up balance reads and reduces DB hits. |
| Scalability-Ready Design | Can later evolve into microservices, add Kafka, JWT, etc. |
| Entity | Description |
|---|---|
| Customer | Represents the end user of the wallet system. |
| Wallet | Holds the current balance of each customer. Each customer has exactly one wallet. |
| Transaction | Records each debit, credit, or transfer action — forming an immutable ledger. |
Let’s walk through the main flow of how things work in this project 👇
-
Customer registers using:
POST /api/customers
-
A
Customerrecord is created in MySQL. -
Optionally, an initial
Walletis automatically created for the new customer.
-
Either auto-created during registration or manually via:
POST /api/wallets/{customerId}/create
-
Wallet starts with
balance = 0(or configurable initial balance). -
Walletis linked to theCustomervia one-to-one mapping.
-
When customer adds money:
POST /api/wallets/{walletId}/credit { "amount": 500.00 }
-
System:
- Fetches wallet.
- Adds amount to balance.
- Creates a Transaction of type
CREDIT. - Commits both balance update + transaction record atomically (
@Transactional).
-
When customer makes a purchase or withdrawal:
POST /api/wallets/{walletId}/debit { "amount": 200.00 }
-
System:
- Checks if wallet has sufficient balance.
- Deducts amount.
- Records a Transaction of type
DEBIT. - If insufficient funds, it throws an error → no changes saved (atomic rollback).
-
To send money from one customer to another:
POST /api/wallets/transfer { "fromWalletId": 1, "toWalletId": 2, "amount": 100.00 }
-
System:
- Starts a DB transaction.
- Debits sender’s wallet.
- Credits receiver’s wallet.
- Creates two transaction records (
DEBIT&CREDIT). - Commits all or none — if any step fails, rollback happens.
-
Customer can view past transactions:
GET /api/transactions/wallet/{walletId}
-
Returns list of all transactions — acts as the ledger for audit or debugging.
- Handles HTTP requests and responses.
- Delegates logic to the service layer.
- Example:
WalletController.credit()callsWalletService.credit().
- Contains the business logic.
- Manages atomic operations with
@Transactional. - Example:
transfer()ensures debit & credit happen together.
- Handles database operations via Spring Data JPA.
- Example:
walletRepository.save(wallet).
- Defines entities and their relationships.
- Annotated with
@Entity,@OneToOne,@ManyToOne, etc.
- DTOs → for clean request/response objects.
- Redis Cache → to cache wallet balances.
- Scheduler → for retrying failed transactions.
- Rakesh registers → gets Customer ID = 1
- His wallet is created → Wallet ID = 101, balance = ₹0
- He credits ₹1000 → balance = ₹1000
- He buys something for ₹300 → balance = ₹700
- Transfers ₹200 to his friend’s wallet → balance = ₹500
- Can view all these transactions via
/api/transactions/wallet/101
Everything is atomic, consistent, and fully logged.
Once this works perfectly, you can:
- Add JWT Authentication (using
UserDetailsService). - Add Kafka for async events.
- Split into microservices (WalletService, TransactionService).
- Add API Gateway + Registry.
- Move to eventual consistency (Saga pattern).
