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.
- Technology Stack
- Project Architecture
- Key Features
- Getting Started
- Project Structure
- API Endpoints
- OpenAPI / API documentation
- Configuration
- Testing
- Contributing
- License
| 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 |
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) │
└─────────────────────────────────────────────────────────┘
- 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
- User registration and profile management
- User status tracking
- Digital wallet for each user
- Credit and debit operations
- Balance inquiries
- Transaction history
- Create and manage events
- Publish and cancel events
- List published events with pagination
- Multiple ticket types per event
- Ticket generation with QR codes
- Ticket validation and invalidation
- Create reservations with partial payments
- Layaway-style payment plans
- Reservation expiry management
- Automatic cancellation via scheduler
- Internal payment processing
- Refund capabilities
- Insufficient funds handling
- Audit trail for transactions
- Scheduled tasks for reservation expiry
- Java 17 or higher
- Maven 3.8+
- MySQL 8.0+
- Docker (optional, for Testcontainers)
-
Clone the repository
git clone https://github.com/your-username/paylock.git cd paylock -
Configure the database
Create a MySQL database:
CREATE DATABASE paylock;
-
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
-
Build the project
./mvnw clean install
-
Run the application
./mvnw spring-boot:run
The application will start on
http://localhost:8080
Build the application JAR first:
./mvnw clean package -DskipTestsStart all services:
docker compose up --build -dServices included:
paylock(Spring Boot app)mysql(database)kong(API gateway)nginx(reverse proxy entrypoint)
MySQL schema auto-init:
sql/schema.sqlis 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 (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).
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
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/v1/users |
Register a new user |
GET |
/api/v1/users/me |
Get current user profile |
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 |
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.
- 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>-
Once the application runs, the OpenAPI JSON is exposed at:
-
The interactive Swagger UI will be available at:
-
If you're running behind the included Nginx reverse proxy (default compose in this repo), use the proxied paths e.g.:
- 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.
- 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
SecurityConfigso the UI can load resources. Example (conceptual): allow/v3/api-docs/**and/swagger-ui/**in your WebFlux security configuration.
- 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.
# 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=INFOThe project uses JUnit 5 and Testcontainers for testing:
# Run all tests
./mvnw test
# Run with coverage
./mvnw test jacoco:report- JUnit Jupiter: Unit and integration testing
- Testcontainers: MySQL container for integration tests
- H2 Database: In-memory database for unit tests
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Follow Java naming conventions
- Use Lombok annotations to reduce boilerplate
- Write reactive code using
MonoandFlux - Include validation annotations on DTOs
- Add comprehensive JavaDoc for public methods
- Use meaningful commit messages
This project is licensed under the MIT License - see the LICENSE file for details.
Built with ❤️ using Spring Boot WebFlux