Backend Engineering Project
Digital Wallet Backend
Secure transaction workflows, built for correctness under concurrency.
Overview
The Digital Wallet Backend is a Java and Spring Boot service for wallet-based money movement. Users register and verify their account with an OTP, get a wallet, deposit funds, and transfer money to other users either immediately or on a schedule. Identity verification is handled through KYC — users submit a national ID, the backend runs OCR to extract their details, and verified users unlock the full feature set.
The interesting part is not the CRUD around it — it's what has to be true for a balance to be trustworthy: transactions that don't interleave badly, retries that don't duplicate money, actions that can be audited, and permissions that hold at the endpoint boundary. This is a learning-driven project built to work through those concerns properly rather than a deployed financial product.
Key features
- User registration with OTP verification
- Deposits
- Money transfers between wallets
- Scheduled transfers
- Pessimistic locking for concurrent transactions
- Idempotent transaction requests
- WebSocket-based real-time notifications
- Audit logs
- Role-based access control for users and administrators
- KYC verification with OCR-based national ID scanning
Engineering highlights
CONCURRENCY
Concurrency control
Balance changes are the one place where a lost update is unacceptable. Wallet rows are read with a pessimistic write lock inside the transaction that mutates them, so two concurrent transfers touching the same wallet serialise at the database instead of racing on stale balances.
IDEMPOTENCY
Idempotency
Clients retry — after a timeout, a dropped connection, or a double tap. Transaction endpoints accept an idempotency key so a repeated request resolves to the original transaction result rather than creating a second transfer.
SECURITY
Security
Registration is verified through OTP before an account becomes usable. Spring Security handles authentication and guards endpoints.
ACCESS CONTROL
Role-based access control
Two roles — user and admin — govern what each account can do. Users can create wallets, transfer funds, and report suspicious transactions. Admins oversee the platform and can reverse transactions that have been reported, ensuring disputes are resolved without direct database access.
REAL-TIME
Real-time communication
Once a transaction commits, the affected user is notified over a WebSocket connection instead of relying on the client to poll — useful for transfers that arrive while a session is open.
AUDITABILITY
Auditability
Transaction-relevant actions are written to audit logs, so the sequence of events behind a balance can be reconstructed after the fact rather than inferred from the current state.
SCHEDULING
Scheduled transfers
Transfers can be scheduled for later execution and are picked up by a scheduled job, which reuses the same locking and idempotency guarantees as an immediate transfer.
KYC
KYC verification with OCR
Users can submit a national ID for identity verification. The backend uses OCR to extract key details — name, date of birth, document number — from the uploaded image, auto-populates the user's profile fields, and records the verification status so KYC-gated operations can be enforced downstream.
Architecture
Client
HTTP
REST API
JSON
Spring Security
authn / RBAC
Controller
DTO mapping
Service Layer
transactions
Repository
Spring Data JPA
PostgreSQL
source of truth
Redis
supporting cache
WebSockets
notifications
Scheduler
scheduled transfers
OCR Service
KYC / ID scanning
Engineering decisions
Pessimistic over optimistic locking. Wallet contention is expected rather than rare, so retrying failed optimistic writes would add complexity for no benefit. Locking the row for the duration of the transaction keeps the critical section short and the outcome predictable.
Idempotency at the API boundary. Deduplicating at the entry point means retry safety doesn't have to be re-implemented inside every downstream operation.
Layered structure. Controllers stay thin and speak DTOs, services own transactional business rules, repositories own persistence. Testing with JUnit and Mockito is straightforward as a result.
Technology stack
- Java
- Spring Boot
- Spring Security
- Hibernate / JPA
- PostgreSQL
- Supabase
- REST APIs
- Redis
- Docker
- Git
- Maven
- JUnit / Mockito
- Swagger / OpenAPI