A production-grade RESTful API for digital wallet and fixed deposit management with enterprise-level security, concurrency handling, and transactional integrity.
- JWT-based Authentication with secure token generation
- Password Encryption using BCrypt
- Role-based Access Control (USER/ADMIN)
- Session management with token expiration
- Deposit Money - Add funds to your wallet
- Withdraw Money - Withdraw with balance validation
- Money Transfer - P2P transfers with double-spending prevention
- Transaction History - Complete audit trail with pagination
- Optimistic Locking - Prevents race conditions
- Create FD - Lock funds for fixed tenure with interest
- Auto Interest Calculation - Simple interest formula
- Break FD - Premature withdrawal with penalty (2%)
- Maturity Tracking - Track FD status and maturity dates
- Pro-rata Interest - Calculate interest for partial tenure
- ACID Transactions - @Transactional for data consistency
- Pessimistic Locking - Prevents concurrent modifications
- Global Exception Handling - Consistent error responses
- Input Validation - Jakarta Validation annotations
- DTO Pattern - Clean separation of concerns
- Layered Architecture - Controller β Service β Repository
βββββββββββββββ ββββββββββββββββ βββββββββββββββββββ
β users β1ββββ1 β wallets β1ββββM β transactions β
βββββββββββββββ€ ββββββββββββββββ€ βββββββββββββββββββ€
β id (PK) β β id (PK) β β id (PK) β
β email β β user_id (FK) β β wallet_id (FK) β
β password β β balance β β type β
β full_name β β version β β amount β
β phone β β created_at β β balance_before β
β role β β updated_at β β balance_after β
β is_active β ββββββββββββββββ β description β
β created_at β β reference_id β
βββββββββββββββ β related_user_id β
β β created_at β
β βββββββββββββββββββ
β
β1
β
βM
βΌ
ββββββββββββββββββββ
β fixed_deposits β
ββββββββββββββββββββ€
β id (PK) β
β user_id (FK) β
β principal_amount β
β interest_rate β
β tenure_months β
β maturity_amount β
β maturity_date β
β status β
β penalty_amount β
β amount_received β
β closed_at β
β created_at β
ββββββββββββββββββββ
Key Relationships:
- User 1:1 Wallet (One user has exactly one wallet)
- Wallet 1:M Transactions (One wallet has many transactions)
- User 1:M FixedDeposits (One user can have multiple FDs)
- Java 17 or higher
- Maven 3.6+
- MySQL 8.0+
git clone <repository-url>
cd wallet-apiEdit src/main/resources/application.properties:
spring.datasource.url=jdbc:mysql://localhost:3306/wallet_db?createDatabaseIfNotExist=true
spring.datasource.username=your_username
spring.datasource.password=your_password# Build the project
mvn clean install
# Run the application
mvn spring-boot:runApplication runs on: http://localhost:8080
POST /api/auth/signup
Content-Type: application/json
{
"email": "john@example.com",
"password": "password123",
"fullName": "John Doe",
"phoneNumber": "9876543210"
}Response:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"type": "Bearer",
"userId": 1,
"email": "john@example.com",
"fullName": "John Doe",
"role": "USER"
}POST /api/auth/login
Content-Type: application/json
{
"email": "john@example.com",
"password": "password123"
}All wallet endpoints require JWT token:
Authorization: Bearer <your-jwt-token>GET /api/wallet/{userId}Response:
{
"walletId": 1,
"userId": 1,
"userEmail": "john@example.com",
"balance": 5000.00,
"updatedAt": "2024-02-12T10:30:00"
}POST /api/wallet/{userId}/deposit
Content-Type: application/json
{
"amount": 1000.00,
"description": "Salary credit"
}POST /api/wallet/{userId}/withdraw
Content-Type: application/json
{
"amount": 500.00,
"description": "ATM withdrawal"
}POST /api/wallet/{userId}/transfer
Content-Type: application/json
{
"recipientEmail": "jane@example.com",
"amount": 200.00,
"description": "Payment for dinner"
}Response:
{
"id": 10,
"type": "TRANSFER_SENT",
"amount": 200.00,
"balanceBefore": 5000.00,
"balanceAfter": 4800.00,
"description": "Transfer to jane@example.com",
"referenceId": "TXN-A1B2C3D4",
"relatedUserEmail": "jane@example.com",
"createdAt": "2024-02-12T11:00:00"
}GET /api/wallet/{userId}/transactions?page=0&size=10Response:
{
"content": [
{
"id": 10,
"type": "TRANSFER_SENT",
"amount": 200.00,
"balanceBefore": 5000.00,
"balanceAfter": 4800.00,
"description": "Transfer to jane@example.com",
"referenceId": "TXN-A1B2C3D4",
"createdAt": "2024-02-12T11:00:00"
}
],
"totalElements": 25,
"totalPages": 3,
"size": 10,
"number": 0
}POST /api/fixed-deposits/{userId}
Content-Type: application/json
{
"amount": 10000.00,
"tenureInMonths": 12,
"interestRate": 7.5
}Response:
{
"fdId": 1,
"userId": 1,
"userEmail": "john@example.com",
"principalAmount": 10000.00,
"interestRate": 7.5,
"tenureInMonths": 12,
"maturityAmount": 10750.00,
"maturityDate": "2025-02-12T10:30:00",
"status": "ACTIVE",
"createdAt": "2024-02-12T10:30:00"
}Interest Calculation:
- Formula: Interest = (Principal Γ Rate Γ Tenure) / 1200
- Example: (10000 Γ 7.5 Γ 12) / 1200 = βΉ750
- Maturity Amount = Principal + Interest = βΉ10,750
POST /api/fixed-deposits/{userId}/{fdId}/breakResponse:
{
"fdId": 1,
"principalAmount": 10000.00,
"interestEarned": 375.00,
"penaltyAmount": 200.00,
"amountReturned": 10175.00,
"message": "FD broken successfully. Amount credited to wallet after penalty deduction."
}Penalty Logic:
- Penalty Rate: 2% of principal amount
- Pro-rata Interest: Calculated based on months elapsed
- Amount Returned = Principal + Interest Earned - Penalty
GET /api/fixed-deposits/{userId}GET /api/fixed-deposits/{userId}/{fdId}- Token-based stateless authentication
- Token expiration: 24 hours (configurable)
- Secure password storage with BCrypt
Pessimistic Locking:
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT w FROM Wallet w WHERE w.user.id = :userId")
Optional<Wallet> findByUserIdWithLock(Long userId);Optimistic Locking:
@Version
private Long version; // In Wallet entity@Transactional // Ensures ACID properties
public TransactionDto transfer(Long senderId, WalletDto.TransferRequest request) {
// All operations succeed or rollback together
}β
Implemented pessimistic locking to prevent race conditions
β
Optimistic locking with @Version for wallet balance
β
Deadlock prevention with ordered locking
β
ACID compliance with Spring @Transactional
β
Atomic money transfers - both debit and credit happen together
β
Rollback on failures
Controller (API Layer)
β
Service (Business Logic)
β
Repository (Data Access)
β
Database (MySQL)
β
DTO pattern for clean separation
β
Global exception handling
β
Input validation with Jakarta Validation
β
Proper HTTP status codes
β
Pagination for large datasets
1. Signup:
curl -X POST http://localhost:8080/api/auth/signup \
-H "Content-Type: application/json" \
-d '{
"email": "test@example.com",
"password": "test123",
"fullName": "Test User",
"phoneNumber": "9876543210"
}'2. Login & Get Token:
curl -X POST http://localhost:8080/api/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "test@example.com",
"password": "test123"
}'3. Deposit Money:
curl -X POST http://localhost:8080/api/wallet/1/deposit \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"amount": 5000.00,
"description": "Initial deposit"
}'4. Create FD:
curl -X POST http://localhost:8080/api/fixed-deposits/1 \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"amount": 2000.00,
"tenureInMonths": 6,
"interestRate": 6.5
}'-
Database Indexing:
- Index on
emailin users table - Index on
user_idin wallets and fixed_deposits - Composite index on
wallet_idandcreated_atin transactions
- Index on
-
Query Optimization:
- Used pagination for transaction history
- Lazy loading for relationships
- Selective fetch strategies
-
Connection Pooling:
- HikariCP for efficient database connections
The API provides consistent error responses:
{
"timestamp": "2024-02-12T10:30:00",
"status": 400,
"error": "Insufficient Balance",
"message": "Insufficient balance. Available: 1000.00",
"path": "/api/wallet/1/withdraw"
}Common Error Codes:
400- Bad Request (validation errors, insufficient balance)401- Unauthorized (invalid/missing token)404- Not Found (resource doesn't exist)409- Conflict (duplicate email/phone)500- Internal Server Error
# Server Configuration
server.port=8080
# Database
spring.datasource.url=jdbc:mysql://localhost:3306/wallet_db
spring.datasource.username=root
spring.datasource.password=root
# JPA
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
# JWT
jwt.secret=your-secret-key-here
jwt.expiration=86400000wallet-api/
βββ src/main/java/com/fintech/wallet/
β βββ controller/ # REST Controllers
β β βββ AuthController.java
β β βββ WalletController.java
β β βββ FixedDepositController.java
β βββ service/ # Business Logic
β β βββ AuthService.java
β β βββ WalletService.java
β β βββ FixedDepositService.java
β βββ repository/ # Data Access
β β βββ UserRepository.java
β β βββ WalletRepository.java
β β βββ TransactionRepository.java
β β βββ FixedDepositRepository.java
β βββ entity/ # JPA Entities
β β βββ User.java
β β βββ Wallet.java
β β βββ Transaction.java
β β βββ FixedDeposit.java
β βββ dto/ # Data Transfer Objects
β β βββ AuthDto.java
β β βββ WalletDto.java
β β βββ FixedDepositDto.java
β β βββ TransactionDto.java
β βββ security/ # Security Configuration
β β βββ SecurityConfig.java
β β βββ JwtUtil.java
β β βββ JwtAuthenticationFilter.java
β β βββ CustomUserDetailsService.java
β βββ exception/ # Exception Handling
β β βββ GlobalExceptionHandler.java
β β βββ ResourceNotFoundException.java
β β βββ InsufficientBalanceException.java
β β βββ DuplicateResourceException.java
β β βββ InvalidOperationException.java
β βββ WalletApplication.java
βββ src/main/resources/
βββ application.properties
For questions or issues:
- Create an issue in the repository
- Contact:
hitmeup.taral@example.com
This project is licensed under the MIT License.
Built with β€οΈ by Taral Shah