Production-grade P2P payment infrastructure with distributed fraud detection and event-driven architecture
Explore the code Β»
Read the Build Journal
Β·
Report Bug
Β·
Request Feature
Table of Contents
A distributed payment processing system built with Spring Boot microservices β modeled after the infrastructure layer that powers platforms like Razorpay or PayPal. Handles peer-to-peer money movement with fraud detection, event-driven architecture, and production-grade reliability patterns.
Built to answer one question: what actually happens when you tap "Pay"?
Key capabilities:
- Never processes the same payment twice β Redis idempotency
- Prevents double spending β Pessimistic DB locking
- Zero message loss on crash β Outbox Pattern
- Automatic fraud reversal β Saga Pattern
- Real-time fraud detection β Rule-based engine with admin review queue
Client
β
ββββββββΌββββββ
β API Gatewayβ JWT Auth Β· Rate Limiting
β Port 8080 β Role-based Routing
βββββββ¬βββββββ
β
βββββββββββββββββΌββββββββββββββββ
β β β
βββββββΌβββββββ βββββββΌβββββββ ββββββΌββββββββ
β Auth β β Payment β β Fraud β
β Service β β Service β β Service β
β Port 8081 β β Port 8082 β β Port 8083 β
βββββββ¬βββββββ βββββββ¬βββββββ ββββββ¬ββββββββ
β β β
Auth DB Payment DB Fraud DB
(Postgres) (Postgres) (Postgres)
β
Redis Cache
(Idempotency +
Rate Limiting)
ββββββ Kafka Event Bus ββββββ
Auth Service β user-registered-topic β Payment Service
Payment Service β payment-topic β Fraud Service
Payment Service β payment-topic β Notification Service
Fraud Service β fraud-alert-topic β Payment Service
Fraud Service β fraud-review-topic β Payment Service
ββββββββββββββββ
β Notification β
β Service β
β Port 8084 β
ββββββββββββββββ
βββββββββββββββββββββββββββ
β Grafana LGTM Stack β
β Traces Β· Logs Β· Metricsβ
βββββββββββββββββββββββββββ
ββββββββββββββββββββββββββββββββββββββ
β Docker Internal Network β
β Only port 8080 exposed externally β
ββββββββββββββββββββββββββββββββββββββ
- JWT validation on every request β no token, no entry
- Role-based access control β
/fraud/**restricted to ADMIN - Rate limiting per IP via Redis β 5 req/min on login, 10 req/min on payments
- Strips untrusted
X-User-Idheaders β injects verified identity downstream
- User registration and login
- JWT issuance with userId as subject and role as claim
- BCrypt password hashing
- Publishes
user-registeredevent on registration
- Double-entry ledger β every transaction creates DEBIT and CREDIT entries
- Idempotency via Redis β duplicate requests return same response without reprocessing
- Pessimistic DB locking β prevents double spending on concurrent requests
- Outbox Pattern β guaranteed Kafka delivery even on service crash
- Account ownership validation β users can only move money from their own accounts
- Account number abstraction β UUIDs stay internal, users interact via account numbers
- Consumes
user-registeredβ auto-creates account on registration - Consumes
fraud-alertβ reverses transaction and freezes account (Saga) - Consumes
fraud-reviewβ unfreezes account on admin approval
- Consumes payment events from Kafka
- Async payment confirmations β never blocks payment processing
- Extensible to email/SMS providers
- Rule-based fraud engine β evaluates every transaction
- Rules: high value (β₯ βΉ10,000), round numbers, self-transfer, rapid successive payments
- Saves alerts to dedicated fraud DB with risk levels (LOW, MEDIUM, HIGH)
- Admin review queue β approve (unfreeze) or reject (keep frozen) via outbox pattern
- Publishes fraud alerts to trigger Saga compensation in Payment Service
Outbox Pattern β Direct Kafka publishing risks message loss if service crashes mid-transaction. Events are written to an outbox table in the same DB transaction. A scheduler reads and publishes. Atomicity guaranteed.
Idempotency with Redis β Every payment request carries a client-generated Idempotency-Key header. Redis stores the key with TTL. Duplicate requests return the original response without reprocessing β prevents double charges on network retries.
Pessimistic Locking β Concurrent payments to the same account can cause double spending. @Lock(PESSIMISTIC_WRITE) on account fetches ensures one transaction processes at a time per account.
Saga Pattern β Fraud detection is async. Payment processes first, fraud analysis follows. When fraud is confirmed, a compensation event reverses the transaction and freezes the account. No records deleted β full audit trail preserved.
Kafka over RabbitMQ β Multiple services consume every payment event independently. Kafka's message retention means no events are lost if a service goes down β consumer catches up on restart.
Database per Service β Each service owns its data. No cross-service JPA relationships. Services communicate via events. Payment DB, Fraud DB, and Auth DB are completely independent.
Account Number Abstraction β Users share account numbers (e.g. ABC123DEF456). Internal UUIDs never surface in the API. Account number to UUID resolution happens server-side.
JWT at Gateway Only β Auth Service issues JWT. Gateway validates it using a shared secret β no Auth Service call needed per request. Downstream services trust the X-User-Id header injected by Gateway.
| Pattern | Implementation |
|---|---|
| API Gateway | Spring Cloud Gateway β single entry point |
| Database per Service | Auth DB, Payment DB, Fraud DB |
| Event-Driven Architecture | Kafka backbone across all services |
| Outbox Pattern | Payment Service + Fraud Service |
| Idempotent Consumer | Redis idempotency keys |
| Saga Pattern | Fraud reversal compensation flow |
| Pessimistic Locking | Account fetches during payment processing |
- JWT validation at Gateway β no unauthenticated request reaches any service
- Header stripping β client-supplied
X-User-Idremoved, Gateway injects verified identity - Account ownership validation β users can only deposit/transfer from their own accounts
- Role-based access β ADMIN role required for fraud endpoints
- Rate limiting per IP β brute force and spam protection on auth and payment routes
- BCrypt password hashing
- Internal network isolation β only port 8080 exposed externally via Docker
- No UUIDs in API surface β account numbers only
- IDOR prevention β account ownership checked on every write operation
- Docker and Docker Compose
- Java 21
- Maven
-
Clone the repository
git clone https://github.com/abubakkar-siddhiq/payment-system.git cd payment-system -
Start infrastructure
docker-compose up -d
Starts: PostgreSQL (x3), Redis, Kafka (KRaft), Grafana LGTM
-
Build each service
cd payment-service && mvn clean package -DskipTests && cd .. cd authservice && mvn clean package -DskipTests && cd .. cd frauddetectionsystem && mvn clean package -DskipTests && cd .. cd notificationservice && mvn clean package -DskipTests && cd .. cd gateway && mvn clean package -DskipTests && cd ..
-
Start services in order
1. Auth Service β port 8081 2. Payment Service β port 8082 3. Fraud Service β port 8083 4. Notification Service β port 8084 5. API Gateway β port 8080 -
Create admin user
UPDATE users SET role = 'ADMIN' WHERE email = 'admin@yourdomain.com';
POST /api/v1/auth/register β Register, returns JWT + userId
POST /api/v1/auth/login β Login, returns JWT + userId
GET /api/v1/accounts/me β My account info
GET /api/v1/accounts/{accountNumber}/balance β My balance
POST /api/v1/accounts/{accountNumber}/deposit β Deposit funds
GET /api/v1/accounts β List all (ADMIN only)
POST /api/v1/payments β Process payment between accounts
Required headers:
Authorization: Bearer <jwt>
Idempotency-Key: <uuid>
Body:
{
"senderAccountNumber": "ABC123DEF456",
"receiverAccountNumber": "XYZ789GHI012",
"amount": 500.00
}GET /api/v1/fraud/alerts β List all fraud alerts
GET /api/v1/fraud/alerts/{id} β Get specific alert
PUT /api/v1/fraud/alerts/{id}/approve β Legitimate β unfreeze account
PUT /api/v1/fraud/alerts/{id}/reject β Confirmed fraud β keep frozen
1. Register POST /api/v1/auth/register
β Account auto-created via Kafka event
2. Login POST /api/v1/auth/login
β Receive JWT + account number
3. Deposit POST /api/v1/accounts/{number}/deposit
β Fund your account
4. Pay POST /api/v1/payments
β Money moves, ledger updated, Kafka event fired
5. Notification β Notification Service logs confirmation
6. Fraud Check β Fraud Service analyzes transaction
β If HIGH risk: account frozen, payment reversed
7. Admin Review β PUT /api/v1/fraud/alerts/{id}/approve
β Account unfrozen
- Payment Service with idempotency and pessimistic locking
- Outbox Pattern for guaranteed Kafka delivery
- Fraud Detection with rule-based engine
- Saga Pattern for fraud reversal
- API Gateway with JWT and rate limiting
- Distributed tracing with Grafana LGTM
- Docker network isolation
- Service discovery via Eureka/Consul
- ML-based fraud scoring
- Kubernetes orchestration
- CI/CD pipeline
- Load testing with k6
Abubakkar Siddhiq - LinkedIn
Project Link: https://github.com/abubakkar-siddhiq/payment-system
Build Journal: medium blog
- Microservices.io β Pattern reference
- Baeldung β Spring Boot guides
- Stripe Engineering Blog β Idempotency
- ByteByteGo β Payment system design
- Confluent β Kafka fundamentals
- Monzo Engineering Blog β Ledger design