Skip to content

Jaskirat-s7/distributed_payment

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

1 Commit
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Distributed Payment Ledger Service

A production-grade fintech payment ledger system built with Java 17 and Spring Boot 3.x, featuring strict ACID compliance, double-entry accounting, and distributed transaction support.

πŸ—οΈ Architecture

Hexagonal Architecture (Ports and Adapters) with clean service-layer separation

Tech Stack

  • Java 17
  • Spring Boot 3.2.0 (Web, Data JPA, Validation, Kafka, Redis)
  • PostgreSQL 15 - Relational database with strict integrity
  • Apache Kafka - Event streaming for asynchronous reconciliation
  • Redis 7 - Distributed caching and idempotency
  • Docker Compose - Local infrastructure orchestration

🎯 Key Features

1. Double-Entry Bookkeeping

Every transaction records two ledger entries (DEBIT and CREDIT) in a single atomic database transaction. The sum of debits must equal the sum of credits.

2. Optimistic Locking with Retry

  • JPA @Version annotation on Account entity
  • REPEATABLE_READ transaction isolation level
  • Exponential backoff retry (max 3 attempts) on ObjectOptimisticLockingFailureException
  • Prevents "Lost Update" anomalies during concurrent balance updates

3. Idempotency

  • Redis-backed cache with 24-hour TTL
  • Checks X-Idempotency-Key header before processing
  • Returns cached response for duplicate requests

4. Transaction Lifecycle

State machine: PENDING β†’ AUTHORIZED β†’ CAPTURED (or FAILED)

5. Transactional Outbox Pattern

  • Uses @TransactionalEventListener(phase = AFTER_COMMIT)
  • Publishes TransactionSettledEvent to Kafka only after successful DB commit
  • Prevents message loss or duplicate processing

πŸ“Š Data Model

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Account   β”‚         β”‚   Transaction    β”‚         β”‚ LedgerEntry  β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€         β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€         β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ id (PK)     │────┐    β”‚ id (PK)          β”‚    β”Œβ”€β”€β”€β”‚ id (PK)      β”‚
β”‚ user_id     β”‚    β”‚    β”‚ reference_id (UK)β”‚    β”‚   β”‚ transaction  β”‚
β”‚ currency    β”‚    β”‚    β”‚ amount           β”‚    β”‚   β”‚ account      β”‚
β”‚ balance     β”‚    β”‚    β”‚ currency         β”‚    β”‚   β”‚ direction    β”‚
β”‚ version     β”‚    └───<β”‚ source_account   β”‚    β”‚   β”‚ amount       β”‚
β”‚ created_at  β”‚         β”‚ dest_account     β”‚    β”‚   β”‚ created_at   β”‚
β”‚ updated_at  β”‚      β”Œβ”€<β”‚ status           β”‚>β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β”‚  β”‚ idempotency_key  β”‚
                     β”‚  β”‚ created_at       β”‚
                     β”‚  β”‚ updated_at       β”‚
                     β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                     β”‚
                     └── Foreign Key

πŸš€ Getting Started

Prerequisites

  • Java 17+
  • Maven 3.8+
  • Docker & Docker Compose

1. Start Infrastructure

cd payment-ledger
docker-compose up -d

This starts:

  • PostgreSQL on port 5432
  • Redis on port 6379
  • Kafka on port 9092
  • Zookeeper on port 2181

2. Build Application

./mvnw clean install

3. Run Application

./mvnw spring-boot:run

Application will start on http://localhost:8080

πŸ“‘ API Endpoints

Transfer Funds

POST /api/v1/transfers
Headers:
  Content-Type: application/json
  X-Idempotency-Key: unique-key-123

Body:
{
  "sourceAccountId": 1,
  "destinationAccountId": 2,
  "amount": 100.50,
  "currency": "USD",
  "description": "Payment for invoice #12345"
}

Response (200 OK):
{
  "transactionId": 1,
  "referenceId": "a1b2c3d4-e5f6-...",
  "amount": 100.50,
  "currency": "USD",
  "status": "CAPTURED",
  "sourceAccountId": 1,
  "destinationAccountId": 2,
  "description": "Payment for invoice #12345",
  "createdAt": "2025-11-27T00:15:00",
  "updatedAt": "2025-11-27T00:15:00"
}

Get Transaction by ID

GET /api/v1/transfers/{transactionId}

Get Transaction by Reference ID

GET /api/v1/transfers/reference/{referenceId}

πŸ”’ Bank-Grade Safety Features

Race Condition Handling

@Transactional(isolation = Isolation.REPEATABLE_READ)
public TransactionResponse executeTransfer(TransferRequest request, String idempotencyKey) {
    // Optimistic locking prevents concurrent modification
    Account sourceAccount = accountRepository.findById(sourceAccountId).orElseThrow();
    Account destinationAccount = accountRepository.findById(destinationAccountId).orElseThrow();
    
    // Version field automatically checked by JPA
    sourceAccount.debit(amount);
    destinationAccount.credit(amount);
    
    // If version mismatch: ObjectOptimisticLockingFailureException β†’ retry
}

Double-Entry Validation

BigDecimal totalDebits = ledgerEntryRepository.sumByTransactionIdAndDirection(
    transactionId, EntryDirection.DEBIT);
BigDecimal totalCredits = ledgerEntryRepository.sumByTransactionIdAndDirection(
    transactionId, EntryDirection.CREDIT);

if (totalDebits.compareTo(totalCredits) != 0) {
    throw new DoubleEntryViolationException("Debits must equal credits");
}

πŸŽ›οΈ Configuration

Edit src/main/resources/application.yml:

idempotency:
  cache-ttl-hours: 24
  retry:
    max-attempts: 3
    initial-delay-ms: 100
    multiplier: 2.0

kafka:
  topics:
    ledger-events: ledger.events

πŸ§ͺ Error Handling

Error HTTP Status Description
InsufficientBalanceException 422 Source account lacks funds
AccountNotFoundException 404 Account ID not found
DuplicateTransactionException 409 Idempotency key already exists
OptimisticLockingFailureException 409 Concurrent modification after retries
DoubleEntryViolationException 500 CRITICAL: Accounting rules violated
MissingRequestHeaderException 400 X-Idempotency-Key header missing

πŸ“ˆ Monitoring

Health check endpoint:

curl http://localhost:8080/actuator/health

πŸ› οΈ Development

Database Schema

Auto-created by Hibernate on startup (spring.jpa.hibernate.ddl-auto=update)

Kafka Topics

Auto-created by KafkaConfig:

  • ledger.events (3 partitions, replication factor 1)

Redis Keys

Format: idempotency:{key} with 24-hour TTL

πŸ“ License

This is a demonstration project for educational purposes.

🀝 Contributing

This is a reference implementation for fintech payment systems. Feel free to adapt for your use case.


Built with ❀️ for bank-grade reliability

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors