A production-grade, RAG-powered personal finance system built with Spring Boot and React.
Track expenses, set budgets, manage savings goals, and get AI-driven financial insights through a natural language chat assistant — all grounded in your real transaction data.
This project is designed as a senior-level full-stack portfolio system with:
- Enterprise-style backend architecture (layered services, validation, exception handling, schedulers, security, observability)
- Modern frontend architecture (feature modules, typed API layer, route guards, state + server-cache separation)
- Production deployment practices (Docker, CI/CD, cloud VM rollout)
- Applied AI engineering (RAG pipeline over user-scoped financial data)
- JWT authentication with refresh-token flow and role-based access (
USER,ADMIN) - Transaction tracking (income/expense), filtering, pagination, CSV export
- Category and payment mode management (defaults + custom)
- Monthly budgets with threshold and limit breach tracking
- Savings goals with contribution workflow and automatic status transitions
- Recurring transactions with forecast engine (1 to 365 days)
- Real-time notifications (REST + WebSocket STOMP/SockJS)
- Dashboard analytics (summary, category breakdown, trends, top expenses)
- Subscription workflow (Razorpay order + signature verification)
- AI chat assistant powered by RAG over user transactions, budget, goals, and recent chat context
| Area | Stack | Version | Why this was chosen |
|---|---|---|---|
| Language | Java | 21 | LTS performance, records/sealed types, mature ecosystem |
| Framework | Spring Boot | 3.5.11 | Production-ready web/security/data/scheduling stack |
| Security | Spring Security + JWT (JJWT) | Spring Security via Boot, JJWT 0.12.6 | Stateless auth, token-based API security, rotation support |
| API Docs | SpringDoc OpenAPI | 2.8.14 | Auto-generated contract docs for interview/demo usage |
| DB | PostgreSQL | 16 (pgvector image) | Reliable relational store + SQL analytics |
| ORM | Spring Data JPA / Hibernate | Boot-managed | Clean repository + domain mapping model |
| Migrations | Flyway | Boot-managed | Versioned schema migration for reproducible environments |
| Cache | Redis | 7.2-alpine | Low-latency caching for dashboard and static-ish data |
| AI Integration | Spring AI | 1.1.2 | Unified LLM + embedding + vector-store abstraction |
| Vector Store | PgVector | via Spring AI starter | Native vector similarity search near transactional data |
| Spring Mail + Thymeleaf | Boot-managed | Templated transactional emails | |
| CSV | Apache Commons CSV | 1.10.0 | Stable CSV generation with encoding control |
| Payments | Razorpay Java SDK | 1.4.8 | Indian payment gateway integration for subscription monetization |
| Container Build | Google Jib Maven Plugin | 3.4.2 | Daemonless, reproducible image builds in CI |
| Area | Stack | Version | Why this was chosen |
|---|---|---|---|
| Framework | React | 19.2.4 | Component model + ecosystem maturity |
| Language | TypeScript | 5.9.3 | Strong typing for API/domain correctness |
| Router | React Router DOM | 7.13.1 | Declarative route layout and guards |
| Server State | TanStack React Query | 5.90.21 | Fetch lifecycle, cache, retries, invalidation |
| Client State | Zustand | 5.0.12 | Lightweight auth/session state management |
| HTTP | Axios | 1.13.6 | Interceptors for JWT injection and silent refresh |
| Charts | Recharts | 3.8.0 | Dashboard visualizations |
| Motion | Framer Motion | 12.38.0 | UX polish and transitions |
| UI Notifications | Sonner | 2.0.7 | Simple toast system |
| Styling | Tailwind CSS | 3.4.3 | Utility-first, scalable UI implementation |
| Build Tool | Vite | 8.0.0 | Fast local dev + optimized production bundles |
| Area | Stack | Why this was chosen |
|---|---|---|
| Local Orchestration | Docker Compose | Quick reproducible local infra (Postgres + Redis + optional backend) |
| Backend Container Runtime | Docker | Standard deployment artifact/runtime |
| CI/CD | GitHub Actions | Automated build/push/deploy on main |
| Registry | Docker Hub | Central image distribution for VM pull deployments |
| Cloud VM | Google Compute Engine (GCP) | Direct control of runtime host and Docker operations |
| Frontend Hosting | Vercel | Fast static hosting + easy React deployment |
| Production DB | Neon PostgreSQL | Managed Postgres with SSL and developer-friendly workflow |
| Production Redis | Upstash Redis | Managed Redis with TLS URL support |
flowchart LR
U[User Browser] --> FE[React Frontend on Vercel]
FE -->|REST /api/v1| BE[Spring Boot Backend]
FE -->|WebSocket /ws SockJS STOMP| BE
BE --> PG[(PostgreSQL + pgvector)]
BE --> RD[(Redis Cache)]
BE --> AI[OpenAI via Spring AI]
BE --> M[SMTP Gmail]
BE --> RZ[Razorpay]
GH[GitHub Actions] --> DH[(Docker Hub)]
GH --> GCE[GCP Compute Engine VM]
DH --> GCE
GCE --> BE
- Every user question is processed inside a chat session.
- System checks active subscription before AI access.
- Semantic retrieval is done from vector store with user-level filter.
- Additional financial context is assembled from budgets and goals.
- Recent chat history is included (last 10 messages).
- LLM response is sanitized before returning to UI.
flowchart TD
Q[User question] --> S{Active subscription?}
S -- No --> E[Return SUBSCRIPTION_REQUIRED]
S -- Yes --> V[Vector similarity search topK=8]
V --> F[Filter by metadata userId]
F --> C1[Budget context builder]
F --> C2[Savings goal context builder]
F --> C3[Relevant transaction chunks]
H[Last up to 10 chat messages] --> P
C1 --> P
C2 --> P
C3 --> P
P[Prompt assembly with system prompt] --> LLM[Chat model call]
LLM --> X[Response sanitization]
X --> R[Persist assistant message + return response]
- System prompt file:
backend/src/main/resources/prompts/walletiq-system-prompt.txt - Retrieval config in service:
TOP_K = 8,MAX_HISTORY = 10 - Filter expression:
userId == '<currentUserId>'
File: .github/workflows/deploy.yaml
- Trigger: push to
main(only when backend/workflow files change) or manual dispatch - Build: Maven package + Jib image build
- Push: Docker Hub image
walletiq-backend:latest - Deploy: SSH into GCP VM and run pull + compose restart
flowchart LR
A[Push to main] --> B[GitHub Actions]
B --> C[Setup JDK 21 + Maven cache]
C --> D[mvn clean package jib:build -DskipTests]
D --> E[Push image to Docker Hub]
E --> F[SSH to GCP Compute Engine VM]
F --> G[docker pull latest image]
G --> H[docker-compose down --remove-orphans]
H --> I[docker-compose up -d]
I --> J[docker image prune -f]
wallet-iq/
├── backend/ # Spring Boot backend
├── frontend/ # React + TypeScript frontend
├── docker/ # Docker compose setups (local/prod/combined)
├── public/ # diagrams, docs, screenshots assets
├── .github/workflows/ # CI/CD pipeline definitions
├── README.md
└── SCREENSHOTS.md
backend/
├── pom.xml
├── src/main/java/online/walletiq/
│ ├── controller/ # REST APIs
│ ├── service/ + service/impl # business logic
│ ├── repository/ # persistence access
│ ├── entity/ # JPA domain models
│ ├── dto/ # request/response contracts
│ ├── config/ # security, mail, ai, redis, websocket, openapi
│ ├── security/ # JWT filters/services/handlers
│ ├── schedular/ # cron jobs
│ ├── mapper/ # entity-dto mappers
│ ├── exception/ # domain exceptions + global handler
│ └── util/ # helpers (response, security, cache, etc.)
└── src/main/resources/
├── application*.yml
├── db/migration/ # Flyway SQL migrations
├── prompts/ # AI system prompts
├── templates/mail/ # email templates
└── keys/ # RSA key resources
frontend/
├── package.json
├── src/
│ ├── features/
│ │ ├── auth/ dashboard/ transactions/ recurring/
│ │ ├── budgets/ savings/ categories/ payment-modes/
│ │ ├── chat/ notifications/ profile/ subscription/
│ │ └── admin/ about/ home/
│ ├── lib/axios.ts # HTTP client + token refresh queue
│ ├── store/authStore.ts # persisted auth state
│ ├── routes/ # route constants + route tree
│ ├── shared/ # reusable UI/hooks/utils
│ └── types/ # generic API wrapper/error types
└── vercel.json
docker/
└── docker-compose/
├── local/ # Postgres + Redis for local IDE backend runs
├── compose/ # full local stack compose (db+redis+backend)
└── prod/ # production backend container compose
Base URL (local): http://localhost:8080/api/v1
Swagger (non-prod):
- UI:
/api/v1/swagger-ui.html - OpenAPI JSON:
/api/v1/api-docs
All protected APIs require:
- Header:
Authorization: Bearer <access_token>
- Description: Register a user.
- Request body:
{
"fullName": "Ripan Baidya",
"email": "ripan@gmail.com",
"password": "MySecure123"
}- Description: Login and issue access + refresh tokens.
- Request body:
{
"email": "ripanbaidya@gmail.com",
"password": "MySecurePassword123!"
}- Description: Revoke refresh token.
- Request body:
{
"refreshToken": "<refresh_token>"
}- Description: Rotate refresh token and issue new token pair.
- Request body:
{
"refreshToken": "<refresh_token>"
}- Description: Utility endpoint for hashing passwords.
- Request body:
{
"password": "MySecurePassword123!"
}- Description: Send 6-digit OTP for email verification.
- Request body:
{
"email": "ripan@gmail.com"
}- Description: Verify OTP and mark email verified.
- Request body:
{
"email": "ripan@gmail.com",
"otp": "123456"
}- Description: Get current user profile.
- Request body: none.
- Description: Update profile name.
- Request body:
{
"fullName": "John Doe"
}- Description: Paginated filtered transactions.
- Query params:
type,categoryId,dateFrom,dateTo, pageable params (page,size,sort). - Request body: none.
- Description: Fetch transaction by ID.
- Request body: none.
- Description: Create transaction.
- Request body:
{
"amount": 1250.5,
"type": "EXPENSE",
"date": "2026-03-16",
"note": "Dinner with friends",
"categoryId": "9c5c0f4c-9a3d-4b22-9a11-8f8c9b0c1234",
"paymentModeId": "2d9a93df-8d2e-4b32-9f7e-1d7c7a12c567"
}- Description: Update transaction.
- Request body:
{
"amount": 950.75,
"type": "EXPENSE",
"date": "2026-03-16",
"note": "Dinner updated",
"categoryId": "9c5c0f4c-9a3d-4b22-9a11-8f8c9b0c1234",
"paymentModeId": "7b1fce20-8e9a-4a45-9b73-3a2dfecb8a21"
}- Description: Delete transaction.
- Request body: none.
- Description: Export all transactions to UTF-8 CSV (with BOM for Excel compatibility).
- Request body: none.
- Description: Fetch categories by type.
- Request body: none.
- Description: Create category.
- Request body:
{
"name": "Food",
"categoryType": "EXPENSE"
}- Description: Update category.
- Request body:
{
"name": "Groceries",
"categoryType": "EXPENSE"
}- Description: Delete category.
- Request body: none.
- Description: Fetch payment modes.
- Request body: none.
- Description: Create payment mode.
- Request body:
{
"name": "UPI"
}- Description: Update payment mode.
- Request body:
{
"name": "Credit Card"
}- Description: Delete payment mode.
- Request body: none.
- Description: Create budget for category + month.
- Request body:
{
"categoryId": "b3c4d5e6-7f8a-4b2c-9d1e-0a2b3c4d5e6f",
"month": "2026-04",
"limitAmount": 5000,
"alertThreshold": 80
}- Description: Fetch month budgets.
- Request body: none.
- Description: Get spent/remaining/usage status for one budget.
- Request body: none.
- Description: Create savings goal.
- Request body:
{
"title": "Buy MacBook Pro",
"targetAmount": 150000,
"deadline": "2026-12-31",
"note": "Saving monthly"
}- Description: List all goals.
- Request body: none.
- Description: Contribute to goal.
- Request body:
{
"amount": 500
}- Description: Goal progress details.
- Request body: none.
- Description: Create recurring rule.
- Request body:
{
"title": "Wifi Bill",
"amount": 500,
"type": "EXPENSE",
"frequency": "MONTHLY",
"startDate": "2026-04-01",
"endDate": "2027-04-01",
"note": "Monthly wifi bill",
"categoryId": "c7d8e9f1-6b2a-4c5d-8f3e-2a1b0c9d8e7f",
"paymentModeId": "b3c4d5e6-7f8a-4b2c-9d1e-0a2b3c4d5e6f"
}- Description: List active recurring transactions.
- Request body: none.
- Description: Get recurring transaction by ID.
- Request body: none.
- Description: Partial update recurring transaction.
- Request body:
{
"title": "Updated Wifi Bill",
"amount": 650,
"frequency": "MONTHLY",
"endDate": "2027-05-01",
"note": "Updated note",
"categoryId": "c7d8e9f1-6b2a-4c5d-8f3e-2a1b0c9d8e7f",
"paymentModeId": "b3c4d5e6-7f8a-4b2c-9d1e-0a2b3c4d5e6f"
}- Description: Deactivate recurring transaction.
- Request body: none.
- Description: Forecast recurring cashflow for 1 to 365 days.
- Request body: none.
- Description: Dashboard analytics for selected/current month.
- Request body: none.
- Description: List user notifications.
- Request body: none.
- Description: Delete one notification.
- Request body: none.
- Description: Delete all notifications for current user.
- Request body: none.
- Description: Fetch all chat sessions.
- Request body: none.
- Description: Create chat session.
- Request body:
{
"title": "New Chat"
}- Description: Delete session + messages.
- Request body: none.
- Description: Fetch ordered session messages.
- Request body: none.
- Description: Ask AI assistant inside session.
- Request body:
{
"question": "How much did I spend on food this month?"
}- Description: Create Razorpay order for subscription.
- Request body: none.
- Description: Verify Razorpay signature and activate subscription.
- Request body:
{
"razorpayOrderId": "order_Ma8J9x7k2YpQz1",
"razorpayPaymentId": "pay_Ma8K3l9ZxYpQw2",
"razorpaySignature": "<signature>"
}- Description: Get current subscription state/expiry.
- Request body: none.
- Description: Paginated user list.
- Request body: none.
- Description: Get user by ID.
- Request body: none.
- Description: Count users by role and active status.
- Request body: none.
- Description: Application metadata for About page.
- Request body: none.
- Endpoint:
/api/v1/ws(SockJS) - Broker topic format:
/topic/notifications/{userId} - Use case: push budget alerts, recurring execution notices, savings goal updates
- Recurring execution scheduler: daily at
08:00server time - Goal expiry scheduler: daily at
00:30 - Refresh token cleanup scheduler: daily at
02:00 - Daily summary mail scheduler exists but cron annotation is currently commented out
dev: local DB/Redis and OpenAI model settingsdocker: container-network DB/Redis profileprod: managed services (Neon/Upstash), hardened logs, actuator-safe exposure
# AI
OPENAI_API_KEY=
OPENAI_CHAT_MODEL=gpt-4o-mini
OPENAI_EMBEDDING_MODEL=text-embedding-3-small
PGVECTOR_DIMENSIONS=1536
# Mail
MAIL_USERNAME=
MAIL_PASSWORD=
MAIL_SSL_TRUST=smtp.gmail.com
# Razorpay
RAZORPAY_KEY_ID=
RAZORPAY_KEY_SECRET=
SUBSCRIPTION_AMOUNT=19900
SUBSCRIPTION_DAYS=30
# Production DB
DB_HOST=
DB_PORT=
DB_NAME=
DB_USERNAME=
DB_PASSWORD=
# Redis
REDIS_URL=
REDIS_SSL_ENABLED=true
# CORS
CORS_ORIGIN_LOCAL=
CORS_ORIGIN_VERCEL=
CORS_ORIGIN_PRIMARY=
# JWT expiry
JWT_ACCESS_TOKEN_EXPIRY=3600000
JWT_REFRESH_TOKEN_EXPIRY=604800000- Java 21
- Maven 3.9+
- Node.js 20+
- Docker Desktop
cd /Users/ripanbaidya/Documents/projects/wallet-iq/docker/docker-compose/local
docker compose up -dcd /Users/ripanbaidya/Documents/projects/wallet-iq/backend
./mvnw spring-boot:runBackend URL: http://localhost:8080/api/v1
cd /Users/ripanbaidya/Documents/projects/wallet-iq/frontend
npm install
npm run devFrontend URL: http://localhost:5173
- GCP Compute Engine VM hosts Docker runtime
- Workflow SSHs into VM path
/opt/walletiq - VM pulls latest Docker image and restarts compose services
- File:
docker/docker-compose/prod/docker-compose.yaml - Runtime env from
.env - Backend healthcheck:
/api/v1/actuator/health
- Schema migrations:
backend/src/main/resources/db/migration/V1__init_schema.sqlV2__insert_default_category_and_payment_mode.sqlV3__create_system_admin.sql
- Domain-specific exception classes + global exception handler
- DTO-level validation (
jakarta.validation) with clear constraints - Stateless security with JWT filter chain and role authorization
- Reusable response envelope pattern (
ResponseWrapper) - Cache key generation strategy for expensive query shapes
- Service layer ownership checks to prevent cross-user data access
- OpenAPI annotations for maintainable API contracts
- Scheduler responsibilities separated from service logic
- Live Link: https://www.walletiq.online
- Github Repository: https://github.com/ripanbaidya/wallet-iq
This repository includes LICENSE at root.