Skip to content

Ebenezr/paylock

Repository files navigation

Paylock

Java Spring Boot License

A reactive payment and ticketing platform built with Spring Boot WebFlux, designed for handling event reservations, ticket management, and wallet-based payments with high concurrency support.

Table of Contents

Technology Stack

Category Technology
Language Java 17
Framework Spring Boot 3.4.2
Reactive Stack Spring WebFlux, Project Reactor
Database MySQL with R2DBC (Reactive)
Connection Pooling R2DBC Pool
Security Spring Security (WebFlux)
Validation Jakarta Bean Validation
Build Tool Maven
Testing JUnit 5, Testcontainers
Utilities Lombok
API Documentation OpenAPI (springdoc) — not included by default; instructions below to enable

Project Architecture

Paylock follows a layered reactive architecture:

┌─────────────────────────────────────────────────────────┐
│                    Controller Layer                      │
│              (REST API endpoints - WebFlux)              │
├─────────────────────────────────────────────────────────┤
│                    Service Layer                         │
│            (Business logic - Reactive Mono/Flux)         │
├─────────────────────────────────────────────────────────┤
│                   Component Layer                        │
│              (Reusable business components)              │
├─────────────────────────────────────────────────────────┤
│                   Repository Layer                       │
│              (R2DBC reactive data access)                │
├─────────────────────────────────────────────────────────┤
│                     Data Layer                           │
│            (MySQL via R2DBC non-blocking driver)         │
└─────────────────────────────────────────────────────────┘

Key Architectural Patterns

  • Reactive Programming: Fully non-blocking I/O using Project Reactor
  • R2DBC: Reactive database connectivity for MySQL
  • Component-Based Design: Reusable business components for common operations
  • Custom Exception Handling: Global exception handler with specialized exceptions
  • Standardized API Response: Consistent response format with ApiResponse<T> wrapper

Key Features

User Management

  • User registration and profile management
  • User status tracking

Wallet System

  • Digital wallet for each user
  • Credit and debit operations
  • Balance inquiries
  • Transaction history

Event Management

  • Create and manage events
  • Publish and cancel events
  • List published events with pagination

Ticket System

  • Multiple ticket types per event
  • Ticket generation with QR codes
  • Ticket validation and invalidation

Reservation System

  • Create reservations with partial payments
  • Layaway-style payment plans
  • Reservation expiry management
  • Automatic cancellation via scheduler

Payment Processing

  • Internal payment processing
  • Refund capabilities
  • Insufficient funds handling

Audit & Scheduling

  • Audit trail for transactions
  • Scheduled tasks for reservation expiry

Getting Started

Prerequisites

  • Java 17 or higher
  • Maven 3.8+
  • MySQL 8.0+
  • Docker (optional, for Testcontainers)

Installation

  1. Clone the repository

    git clone https://github.com/your-username/paylock.git
    cd paylock
  2. Configure the database

    Create a MySQL database:

    CREATE DATABASE paylock;
  3. Update configuration

    Edit src/main/resources/application.properties:

    spring.r2dbc.url=r2dbc:mysql://localhost:3306/paylock?sslMode=DISABLED
    spring.r2dbc.username=your_username
    spring.r2dbc.password=your_password
  4. Build the project

    ./mvnw clean install
  5. Run the application

    ./mvnw spring-boot:run

    The application will start on http://localhost:8080

Running with Docker (App + Reverse Proxy + API Gateway)

Build the application JAR first:

./mvnw clean package -DskipTests

Start all services:

docker compose up --build -d

Services included:

  • paylock (Spring Boot app)
  • mysql (database)
  • kong (API gateway)
  • nginx (reverse proxy entrypoint)

MySQL schema auto-init:

  • sql/schema.sql is mounted to /docker-entrypoint-initdb.d/01-schema.sql
  • It runs automatically only when MySQL initializes a fresh data directory (first run)
  • To force re-init: docker compose down -v && docker compose up --build -d

Access patterns (Nginx published on port 8088):

  • Reverse proxy path (Nginx -> App): http://localhost:8088/app/api/v1/...
  • API gateway path (Nginx -> Kong -> App): http://localhost:8088/gateway/api/v1/...

Stop services:

docker compose down

Reverse Proxy vs API Gateway

  • Reverse proxy (Nginx): forwards requests to backend services, hides internals, and can handle TLS/headers/basic routing.
  • API gateway (Kong): does proxying plus API-specific controls like rate limiting, auth, quotas, request transforms, and analytics.

In this project:

  • Nginx exposes one public endpoint on port 80.
  • /app/* routes directly to Paylock (reverse proxy behavior).
  • /gateway/* routes through Kong first (gateway behavior).

Project Structure

src/main/java/com/blind/paylock/
├── PaylockApplication.java       # Application entry point
├── component/                    # Reusable business components
│   ├── UserComponent.java
│   └── UserComponentImpl.java
├── config/                       # Configuration classes
│   ├── HeaderProperties.java     # Custom header configuration
│   ├── R2dbcConfig.java          # R2DBC database configuration
│   └── SecurityConfig.java       # Security configuration
├── controller/                   # REST API controllers
│   └── UserController.java
├── datalayer/
│   ├── dto/                      # Data Transfer Objects
│   │   ├── request/              # Request DTOs
│   │   └── response/             # Response DTOs
│   └── model/                    # Domain entities
│       ├── Event.java
│       ├── Payment.java
│       ├── Reservation.java
│       ├── Ticket.java
│       ├── TicketType.java
│       ├── User.java
│       ├── Wallet.java
│       └── WalletTransaction.java
├── exception/                    # Custom exceptions
│   ├── BusinessException.java
│   ├── GlobalExceptionHandler.java
│   ├── InsufficientFundsException.java
│   ├── InvalidStateException.java
│   ├── NotFoundException.java
│   └── ValidationException.java
├── repository/                   # R2DBC repositories
│   ├── UserRepository.java
│   └── WalletRepository.java
├── service/                      # Business logic services
│   ├── AuditService.java
│   ├── EventService.java
│   ├── PaymentService.java
│   ├── RefundService.java
│   ├── ReservationService.java
│   ├── SchedulerService.java
│   ├── TicketService.java
│   ├── TicketTypeService.java
│   ├── UserService.java
│   ├── WalletService.java
│   └── impl/                     # Service implementations
├── utils/
│   ├── apis/                     # API utilities
│   └── enums/                    # Enumerations
└── web/
    └── filter/                   # WebFlux filters

API Endpoints

User Management

Method Endpoint Description
POST /api/v1/users Register a new user
GET /api/v1/users/me Get current user profile

Custom Headers

The API uses custom headers for request tracking:

Header Description
X-Request-Ref-Id Unique request reference ID
X-Organization Organization identifier
X-Channel Channel identifier
X-User-Id User identifier

OpenAPI / API documentation

This repository does not include an OpenAPI (springdoc) dependency by default. Below are simple, recommended ways to enable and view interactive API documentation for the WebFlux application.

  1. Enable runtime Swagger UI (recommended for development)
  • Add the Springdoc starter for WebFlux to pom.xml (use the latest stable version). Example dependency:
<dependency>
  <groupId>org.springdoc</groupId>
  <artifactId>springdoc-openapi-starter-webflux-ui</artifactId>
  <version>2.1.0</version> <!-- use latest available -->
</dependency>
  1. Generate static OpenAPI artifacts at build time (CI-friendly)

You can also generate an OpenAPI JSON/YAML during your Maven build using the springdoc-openapi-maven-plugin. Example plugin configuration:

<plugin>
  <groupId>org.springdoc</groupId>
  <artifactId>springdoc-openapi-maven-plugin</artifactId>
  <version>1.6.13</version> <!-- use latest available -->
  <executions>
    <execution>
      <goals>
        <goal>generate</goal>
      </goals>
    </execution>
  </executions>
  <configuration>
    <!-- optional: output directory, format, groups -->
    <outputFileName>openapi.json</outputFileName>
  </configuration>
</plugin>

After running ./mvnw package with the plugin enabled, the generated openapi.json can be published to your docs site or included in releases.

  1. Common configuration notes
  • If your app uses a context path or is routed through Nginx/Kong, set the appropriate server config so links in the Swagger UI are correct. Example properties you may set in application.properties:
# If your app is served under a context path
server.servlet.context-path=/app
# springdoc property to tweak the swagger-ui path (if needed)
springdoc.swagger-ui.path=/swagger-ui.html
  • For secured endpoints (Spring Security), you may need to permit the swagger UI and API docs endpoints in SecurityConfig so the UI can load resources. Example (conceptual): allow /v3/api-docs/** and /swagger-ui/** in your WebFlux security configuration.
  1. Quick checks
  • Fetch raw OpenAPI JSON:
curl http://localhost:8080/v3/api-docs | jq .
  • When using Docker/Nginx (default compose in this repo):
curl http://localhost:8088/app/v3/api-docs | jq .

If you want, I can open a PR that (a) adds the springdoc-openapi-starter-webflux-ui dependency to pom.xml, (b) configures a minimal OpenApiConfig class if needed, and (c) adds a small CI step to publish openapi.json. Ask and I'll prepare the changes and run the test build.

Configuration

Application Properties

# Application
spring.application.name=paylock
server.port=8080

# R2DBC MySQL Configuration
spring.r2dbc.url=r2dbc:mysql://localhost:3306/paylock?sslMode=DISABLED
spring.r2dbc.username=root
spring.r2dbc.password=password

# Connection Pool
spring.r2dbc.pool.enabled=true
spring.r2dbc.pool.initial-size=5
spring.r2dbc.pool.max-size=20
spring.r2dbc.pool.max-idle-time=30m

# Logging
logging.level.org.springframework.r2dbc=INFO
logging.level.reactor.netty.http.server=INFO

Testing

The project uses JUnit 5 and Testcontainers for testing:

# Run all tests
./mvnw test

# Run with coverage
./mvnw test jacoco:report

Test Stack

  • JUnit Jupiter: Unit and integration testing
  • Testcontainers: MySQL container for integration tests
  • H2 Database: In-memory database for unit tests

Contributing

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Coding Standards

  • Follow Java naming conventions
  • Use Lombok annotations to reduce boilerplate
  • Write reactive code using Mono and Flux
  • Include validation annotations on DTOs
  • Add comprehensive JavaDoc for public methods
  • Use meaningful commit messages

License

This project is licensed under the MIT License - see the LICENSE file for details.


Additional Resources


Built with ❤️ using Spring Boot WebFlux

About

Paylock is a micro-service for an installment-based event ticketing system. Users reserve tickets early, pay in parts using an internal wallet, and receive tickets only after full payment. Supports expiry handling, refunds, and event cancellation using Spring Boot, WebFlux, R2DBC, and MySQL.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors