A production-grade digital wallet built with Java Spring Boot (backend) + React (frontend).
Designed with the same architectural principles as Paytm, PhonePe, and Razorpay.
| Layer | Technology |
|---|---|
| Frontend | React 18, Vite, React Router v6, Axios, React Hot Toast |
| Backend | Spring Boot 3.3, Java 21 |
| Database | MySQL 8 + Spring Data JPA |
| Cache | Redis 7 (idempotency + balance cache) |
| Auth | JWT (JJWT 0.12) + Spring Security |
| API Docs | SpringDoc OpenAPI 3 / Swagger UI |
| Testing | JUnit 5, Mockito, H2 in-memory |
| Containers | Docker (multi-stage), Docker Compose |
| CI | GitHub Actions |
ewallet/
├── src/ ← Spring Boot backend (Java)
│ └── main/java/com/ewallet/
│ ├── controller/ ← REST endpoints (AuthController, WalletController)
│ ├── service/ ← Business logic
│ ├── repository/ ← JPA repositories
│ ├── entity/ ← User, Wallet, TransactionLedger
│ ├── dto/ ← Request/Response DTOs
│ ├── config/ ← Security, CORS, Redis, OpenAPI
│ ├── filter/ ← JWT auth filter
│ └── exception/ ← Global exception handler
│
├── frontend/ ← React frontend (Vite)
│ └── src/
│ ├── api/client.js ← Axios client with JWT interceptor
│ ├── context/ ← Auth + wallet state (React Context)
│ ├── pages/ ← Login, Register, Dashboard, Transfer, AddMoney, History
│ └── components/ ← Layout + sidebar
│
├── docker-compose.yml ← MySQL + Redis + App
├── Dockerfile ← Multi-stage build
└── schema.sql ← DB schema (JPA auto-creates too)
git clone <your-repo-url>
cd ewallet
cp .env.example .env
docker-compose up --build
# App: http://localhost:8080
# Swagger UI: http://localhost:8080/swagger-ui.htmlThen in another terminal:
cd frontend
npm install
npm run dev
# Frontend: http://localhost:5173# Start MySQL and Redis via Docker (just for the DBs)
docker run -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=ewallet_db mysql:8.0
docker run -d -p 6379:6379 redis:7-alpine
# Start Spring Boot backend
./mvnw spring-boot:run
# Start React frontend (new terminal)
cd frontend && npm install && npm run devThe Vite proxy forwards /api/* to http://localhost:8080 — no CORS setup needed in dev.
| Page | Route | Description |
|---|---|---|
| Login | /login |
JWT sign in |
| Register | /register |
Create account (wallet auto-created) |
| Dashboard | / |
Balance card, quick actions, recent activity |
| Add Money | /add-money |
Top-up with quick amount presets + receipt |
| Transfer | /transfer |
Send by phone number + receipt |
| History | /history |
Paginated ledger, search, type filter |
POST /api/auth/register { fullName, email, phone, password }
POST /api/auth/login { email, password }
→ returns { token, userId, email, fullName, ... }
POST /api/wallet/create
GET /api/wallet/balance
POST /api/wallet/add-money + header: Idempotency-Key: <uuid>
POST /api/wallet/transfer + header: Idempotency-Key: <uuid>
GET /api/wallet/transactions?page=0&size=20
- Idempotency —
Idempotency-Keyheader prevents double charges on retries (Redis fast-path + DB fallback) - Ledger Accounting — Append-only double-entry ledger; DEBIT + CREDIT written before balance update
- Concurrency Safety —
SELECT ... FOR UPDATEpessimistic locking; consistent lock order prevents deadlocks - Stateless Auth — JWT signed with HMAC-SHA256; userId injected via
@AuthenticationPrincipal
./mvnw test # Uses H2 in-memory — no MySQL or Redis neededcd frontend && npm run build # → frontend/dist/
# Copy dist/ to src/main/resources/static/ to serve from Spring Boot