Skip to content

Shubh2-0/PayFlow

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

5 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

PayFlow Banner

πŸ’³ PayFlow

Digital Wallet & Payment System

A production-grade payment backend built with Microservices Architecture

Java Spring Boot MySQL RabbitMQ Docker

Getting Started Β· API Docs Β· Architecture Β· Design Decisions


🎯 What is PayFlow?

PayFlow is a digital wallet system β€” think of it like a simplified version of Paytm, PhonePe, or Google Pay's backend.

What can users do?

  • πŸ“ Sign up & log in securely (passwords are encrypted, sessions use JWT tokens)
  • πŸ’° Create a wallet and add money to it
  • πŸ’Έ Send money to other users instantly
  • πŸ“Š View transaction history and download wallet statements
  • ⭐ Save frequent contacts as beneficiaries for quick transfers
  • πŸ”” Get notified automatically when a transaction happens

What makes this a real engineering project (not a tutorial)?

Challenge How PayFlow Solves It
Two people send money from same wallet at once Pessimistic Locking β€” database locks the wallet row, processes one at a time
Network glitch causes same payment request twice Idempotency Keys β€” duplicate detected, money deducted only once
Two transfers between same wallets cause system freeze Deadlock Prevention β€” wallets always locked in fixed order
Sending email slows down the payment Event-Driven Architecture β€” payment completes instantly, email sent in background via RabbitMQ
One service goes down, others break Microservices β€” each service runs independently with its own database

πŸ— Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                         API Clients                             β”‚
β”‚                   (Mobile App / Web App)                        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
            β”‚                  β”‚                  β”‚
            β–Ό                  β–Ό                  β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  πŸ” Auth        β”‚ β”‚  πŸ’° Wallet       β”‚ β”‚  πŸ”” Notification β”‚
  β”‚  Service        β”‚ β”‚  Service         β”‚ β”‚  Service         β”‚
  β”‚  (:8081)        β”‚ β”‚  (:8082)         β”‚ β”‚  (:8083)         β”‚
  β”‚                 β”‚ β”‚                  β”‚ β”‚                  β”‚
  β”‚ β€’ Register      β”‚ β”‚ β€’ Create Wallet  β”‚ β”‚ β€’ Email Alerts   β”‚
  β”‚ β€’ Login (JWT)   β”‚ β”‚ β€’ Add Money      β”‚ β”‚ β€’ Templates      β”‚
  β”‚ β€’ User Profile  β”‚ β”‚ β€’ Send Money     β”‚ β”‚ β€’ Retry Failed   β”‚
  β”‚ β€’ Role Access   β”‚ β”‚ β€’ Statement      β”‚ β”‚ β€’ Statistics     β”‚
  β”‚                 β”‚ β”‚ β€’ Beneficiaries  β”‚ β”‚                  β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β–²β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚                  β”‚  β”‚                   β”‚
           β”‚                  β”‚  β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
           β”‚                  β”‚  └──►│ 🐰        β”‚β”€β”€β”€β”˜
           β”‚                  β”‚      β”‚ RabbitMQ   β”‚
           β”‚                  β”‚      β”‚ (Messages) β”‚
           β”‚                  β”‚      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  πŸ—„οΈ payflow_   β”‚ β”‚  πŸ—„οΈ payflow_  β”‚ β”‚  πŸ—„οΈ payflow_    β”‚
  β”‚  auth (MySQL)   β”‚ β”‚  wallet (MySQL) β”‚ β”‚  notification    β”‚
  β”‚                 β”‚ β”‚                 β”‚ β”‚  (MySQL)         β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Each service has its own database β€” no shared tables, no tight coupling. Services communicate through REST APIs (synchronous) and RabbitMQ (asynchronous events).


πŸ›  Tech Stack

Java
Java 17
Spring
Spring Boot 3
MySQL
MySQL 8
Docker
Docker
RabbitMQ
RabbitMQ
Category Technologies
Language Java 17
Framework Spring Boot 3.2, Spring Security, Spring Data JPA
Authentication JWT (JSON Web Tokens), BCrypt password hashing
ORM & Database Hibernate, MySQL 8 (one database per service)
Messaging RabbitMQ (AMQP) for async event-driven communication
API Docs Swagger / OpenAPI 3 (interactive API playground)
Testing JUnit 5, Mockito, H2 in-memory DB (60+ test cases)
Containerization Docker, Docker Compose (one-command startup)
CI/CD GitHub Actions (automated build + test on every push)

✨ Features

πŸ” Auth Service β€” User Identity & Security

Feature Description
User Registration Sign up with email, username, password (validated)
JWT Login Login returns a signed token β€” no session storage needed
Password Security BCrypt hashing β€” passwords never stored in plain text
Role-Based Access ADMIN and USER roles with different permissions
Protected Routes Profile endpoint only accessible with valid JWT

πŸ’° Wallet Service β€” Core Payment Engine

Feature Description
Wallet Management Create, view, freeze, and unfreeze wallets
Add Money Top-up wallet balance (like adding money to Paytm)
Send Money Transfer to another wallet with real-time balance update
Pessimistic Locking Database-level locks prevent double-spending
Idempotent Transactions Duplicate requests safely return same result
Deadlock Prevention Wallets locked in ascending ID order
Daily Transfer Limits Configurable limit (default: β‚Ή1,00,000/day)
Transaction Reversal Reverse completed transactions (compensating transaction)
Beneficiary Management Save, list, and remove frequent transfer recipients
Wallet Statements Date-filtered history with total credits/debits summary
BigDecimal Precision All money calculations use BigDecimal (no rounding errors)

πŸ”” Notification Service β€” Smart Alerts

Feature Description
Event-Driven Listens to RabbitMQ β€” gets triggered automatically on transactions
Email Notifications Sends transaction receipts and alerts
Templates Create reusable notification templates with {{variables}}
Retry Mechanism Failed notifications auto-retry (configurable max retries)
Statistics Dashboard Track sent, pending, and failed notification counts

πŸ“‘ API Endpoints

πŸ” Authentication (auth-service β€” port 8081)

Method Endpoint Description
POST /api/auth/register Register a new user
POST /api/auth/login Login and receive JWT token
GET /api/auth/profile Get current user profile (requires JWT)

πŸ’° Wallets (wallet-service β€” port 8082)

Method Endpoint Description
POST /api/wallets Create a new wallet
GET /api/wallets/{walletId} Get wallet details
GET /api/wallets/user/{userId} Get wallet by user ID
PUT /api/wallets/{walletId}/freeze Freeze a wallet
PUT /api/wallets/{walletId}/unfreeze Unfreeze a wallet

πŸ’Έ Transactions (wallet-service β€” port 8082)

Method Endpoint Description
POST /api/transactions/wallets/{walletId}/add-money Add money to wallet
POST /api/transactions/wallets/{walletId}/send-money Send money to another wallet
GET /api/transactions/{transactionId} Get transaction details
GET /api/transactions/wallets/{walletId}/history Transaction history (paginated)
GET /api/transactions/wallets/{walletId}/statement Wallet statement (date range)
POST /api/transactions/{transactionId}/reverse Reverse a transaction

⭐ Beneficiaries (wallet-service β€” port 8082)

Method Endpoint Description
POST /api/wallets/{walletId}/beneficiaries Add a beneficiary
GET /api/wallets/{walletId}/beneficiaries List all beneficiaries
DELETE /api/wallets/{walletId}/beneficiaries/{id} Remove a beneficiary

πŸ”” Notifications (notification-service β€” port 8083)

Method Endpoint Description
GET /api/notifications List all notifications
GET /api/notifications/{id} Get notification details
POST /api/notifications/{id}/retry Retry a failed notification
GET /api/notifications/stats Notification statistics
POST /api/notification-templates Create notification template
GET /api/notification-templates List all templates
PUT /api/notification-templates/{id} Update a template
DELETE /api/notification-templates/{id} Delete a template

πŸš€ Getting Started

Prerequisites

  • β˜• Java 17+
  • πŸ“¦ Maven 3.8+
  • 🐳 Docker & Docker Compose

Quick Start (Docker) β€” One Command Setup

git clone https://github.com/Shubh2-0/PayFlow.git
cd PayFlow
docker-compose up --build

That's it! All services will be running:

Service URL Swagger Docs
πŸ” Auth Service http://localhost:8081 Open
πŸ’° Wallet Service http://localhost:8082 Open
πŸ”” Notification Service http://localhost:8083 Open
🐰 RabbitMQ Dashboard http://localhost:15672 β€”

Manual Setup (without Docker)

  1. Start MySQL and create databases:

    CREATE DATABASE payflow_auth;
    CREATE DATABASE payflow_wallet;
    CREATE DATABASE payflow_notification;
  2. Start RabbitMQ on port 5672

  3. Run each service:

    cd auth-service && mvn spring-boot:run
    cd wallet-service && mvn spring-boot:run
    cd notification-service && mvn spring-boot:run

πŸ“˜ Usage Examples

1. Register a user:

curl -X POST http://localhost:8081/api/auth/register \
  -H "Content-Type: application/json" \
  -d '{
    "username": "john",
    "email": "john@example.com",
    "password": "secret123",
    "fullName": "John Doe"
  }'

2. Login and get JWT token:

curl -X POST http://localhost:8081/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username": "john", "password": "secret123"}'

3. Add β‚Ή5,000 to wallet:

curl -X POST http://localhost:8082/api/transactions/wallets/1/add-money \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 5000.00,
    "description": "Initial top-up",
    "idempotencyKey": "topup-001"
  }'

4. Send β‚Ή1,500 to another wallet:

curl -X POST http://localhost:8082/api/transactions/wallets/1/send-money \
  -H "Content-Type: application/json" \
  -d '{
    "receiverWalletId": 2,
    "amount": 1500.00,
    "description": "Payment for services",
    "idempotencyKey": "txn-001"
  }'

πŸ“ Project Structure

PayFlow/
β”‚
β”œβ”€β”€ 🐳 docker-compose.yml          # One-command setup for all services
β”œβ”€β”€ πŸ—„οΈ init-db.sql                 # Auto-creates all 3 databases
β”œβ”€β”€ πŸ“¦ pom.xml                      # Parent Maven POM
β”‚
β”œβ”€β”€ πŸ” auth-service/                # Authentication & User Management
β”‚   └── src/main/java/
β”‚       β”œβ”€β”€ controller/             # REST API endpoints
β”‚       β”œβ”€β”€ dto/                    # Request/Response objects
β”‚       β”œβ”€β”€ entity/                 # User, Role (JPA entities)
β”‚       β”œβ”€β”€ exception/              # Error handling
β”‚       β”œβ”€β”€ repository/             # Database queries
β”‚       β”œβ”€β”€ security/               # JWT filter, config, token service
β”‚       └── service/                # Business logic
β”‚
β”œβ”€β”€ πŸ’° wallet-service/              # Core Payment Engine
β”‚   └── src/main/java/
β”‚       β”œβ”€β”€ controller/             # Wallet, Transaction, Beneficiary APIs
β”‚       β”œβ”€β”€ dto/                    # 10 DTOs for all operations
β”‚       β”œβ”€β”€ entity/                 # Wallet, Transaction, Beneficiary
β”‚       β”œβ”€β”€ enums/                  # WalletStatus, TransactionType/Status
β”‚       β”œβ”€β”€ event/                  # RabbitMQ event publisher
β”‚       β”œβ”€β”€ exception/              # 6 domain-specific exceptions
β”‚       β”œβ”€β”€ repository/             # Queries with pessimistic locking
β”‚       └── service/                # Transaction logic, daily limits
β”‚
β”œβ”€β”€ πŸ”” notification-service/        # Event-Driven Notifications
β”‚   └── src/main/java/
β”‚       β”œβ”€β”€ config/                 # RabbitMQ queue/exchange setup
β”‚       β”œβ”€β”€ controller/             # Notification & Template APIs
β”‚       β”œβ”€β”€ dto/                    # TransactionEvent, responses
β”‚       β”œβ”€β”€ entity/                 # Notification, Template
β”‚       β”œβ”€β”€ listener/               # RabbitMQ event consumer
β”‚       β”œβ”€β”€ repository/             # Database queries
β”‚       └── service/                # Email & notification logic
β”‚
└── πŸ”„ .github/workflows/
    └── ci.yml                      # GitHub Actions CI pipeline

🧠 Design Decisions

These are the engineering choices that make PayFlow production-grade, not just another CRUD project.

1. Why Pessimistic Locking for Balance Updates?

Problem: If two requests read wallet balance (β‚Ή1000) at the same time, both deduct β‚Ή500, and both write β‚Ή500 β€” the user loses β‚Ή500.

Solution: SELECT ... FOR UPDATE locks the wallet row. Second request waits until first completes. No money is lost.

Why not Optimistic Locking? In payments, retrying a failed transaction is risky and expensive. Better to wait 10ms for a lock than risk incorrect balances.

2. Why Idempotency Keys?

Problem: User clicks "Pay" β†’ network timeout β†’ user clicks again β†’ money deducted twice.

Solution: Every transaction has a unique idempotencyKey. If the same key comes again, we return the existing result instead of creating a new transaction. This is how Stripe, Razorpay, and every production payment API works.

3. Why Ordered Locking for Deadlock Prevention?

Problem: Transfer A→B locks wallet A, then tries to lock B. Simultaneously, transfer B→A locks wallet B, then tries to lock A. Both wait forever = deadlock.

Solution: Always lock the wallet with the smaller ID first. Both transfers lock A first, then B. No circular wait = no deadlock. Ever.

4. Why Event-Driven Notifications?

Problem: Sending email inside the payment transaction β€” if email server is slow (3 seconds), payment is slow. If email fails, does the payment rollback?

Solution: Payment completes β†’ event published to RabbitMQ β†’ notification service picks it up independently. Payment is fast, email failures don't affect payments, and we can add new consumers (SMS, push notifications) without changing wallet-service.

5. Why Database Per Service?

Problem: Shared database = one service changes a table, other service breaks. Tight coupling defeats the purpose of microservices.

Solution: payflow_auth, payflow_wallet, payflow_notification β€” three separate databases. Each service owns its data. No cross-database joins.


πŸ§ͺ Testing

60+ test cases across 12 test classes in all three services:

Type What it Tests Tools
Unit Tests Service layer logic in isolation JUnit 5, Mockito
Controller Tests API endpoints, request validation, error responses @WebMvcTest, MockMvc
Repository Tests Database queries and JPA mappings @DataJpaTest, H2
# Run all tests
cd auth-service && mvn test
cd wallet-service && mvn test
cd notification-service && mvn test

πŸ”„ CI/CD

GitHub Actions automatically runs on every push:

  1. β˜• Sets up JDK 17 (Temurin) with Maven caching
  2. πŸ”¨ Builds all three services (mvn clean verify)
  3. πŸ§ͺ Runs complete test suite

πŸ“„ License

This project is licensed under the MIT License.


Built with ❀️ by Shubham Bhati

⭐ Star this repo if you found it useful!

About

Production-grade Digital Wallet & Payment System built with Microservices Architecture. Java 17 | Spring Boot 3 | Spring Security + JWT | Hibernate | MySQL | RabbitMQ | Docker | JUnit 5. Features: pessimistic locking, idempotent transactions, deadlock prevention, event-driven notifications, daily transfer limits.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors