A production-inspired Spring Boot payment gateway that processes payment authorizations, captures, voids, and refunds while ensuring safe retries through idempotency. The gateway integrates with a mock bank service, persists payment state in PostgreSQL, and includes integration tests covering the complete payment lifecycle.
Swagger UI
http://18.200.250.3:8080/swagger-ui/index.html
Health Check
http://18.200.250.3:8080/actuator/health
Hosted on AWS EC2 using Docker and PostgreSQL.
- Authorize payments
- Capture authorized payments
- Void authorized payments
- Refund captured payments
- Retrieve payments by payment reference
- Retrieve payment history by order ID
- Retrieve payment history by customer ID
- Idempotent request handling using the
Idempotency-Keyheader - OpenAPI/Swagger documentation
- Flyway database migrations
- Docker support
- Production deployment on AWS EC2
- Integration tests covering successful flows and failure scenarios
- Java 21
- Spring Boot 4
- Spring Data JPA
- PostgreSQL
- Flyway
- MapStruct
- Lombok
- Maven
- Docker & Docker Compose
- JUnit 5
- MockMvc
- AWS EC2
src/main/java
├── api
│ ├── controller
│ ├── dto
│ └── exception
├── application
│ ├── mapper
│ └── service
├── domain
│ ├── entity
│ ├── repository
│ └── enums
└── infrastructure
├── bank
└── configuration
+-----------+
| PENDING |
+-----------+
|
▼
+--------------+
| AUTHORIZED |
+--------------+
| |
| ▼
| +--------+
| | VOIDED |
| +--------+
▼
+--------------+
| CAPTURED |
+--------------+
|
▼
+--------------+
| REFUNDED |
+--------------+
Invalid state transitions are rejected with appropriate error responses.
Every payment operation requires an Idempotency-Key request header.
The gateway:
- Returns the original response when the same request is retried with the same idempotency key.
- Returns 409 Conflict when the same key is reused with a different request body.
- Prevents duplicate payment operations caused by retries or network failures.
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/payments/authorize |
Authorize a payment |
| POST | /api/v1/payments/capture |
Capture an authorized payment |
| POST | /api/v1/payments/void |
Void an authorized payment |
| POST | /api/v1/payments/refund |
Refund a captured payment |
| GET | /api/v1/payments/{paymentReference} |
Retrieve a payment |
| GET | /api/v1/payments/order/{orderId} |
Retrieve payments by order ID |
| GET | /api/v1/payments/customer/{customerId} |
Retrieve payments by customer ID |
- Java 21
- Maven
- PostgreSQL
- Docker (for the mock bank)
git clone https://github.com/Copstud3/PayWall.git
cd PayWallCreate a .env file:
DB_URL=jdbc:postgresql://localhost:5432/payment_gateway
DB_USERNAME=postgres
DB_PASSWORD=your_password
BANK_BASE_URL=http://localhost:8787
Start your local PostgreSQL instance.
mvn flyway:migrateRun the mock bank using its Docker Compose configuration.
mvn spring-boot:runApplication:
http://localhost:8080
Swagger:
http://localhost:8080/swagger-ui/index.html
Health:
http://localhost:8080/actuator/health
- Docker
- Docker Compose
Create a .env file:
PAYMENT_DB_USERNAME=payment_user
PAYMENT_DB_PASSWORD=your_password
BANK_BASE_URL=http://host.docker.internal:8787
docker compose up --buildDetached mode:
docker compose up -d --builddocker compose downRemove volumes:
docker compose down -vApplication:
http://localhost:8080
Clone the repository on your EC2 instance:
git clone https://github.com/Copstud3/PayWall.git
cd PayWallCreate your production .env file.
Deploy using:
./deploy.shThe deployment script:
- Pulls the latest code
- Builds Docker images
- Restarts containers
- Waits for the application health check
- Displays deployment status
Client
│
▼
Port 8080 (EC2)
│
▼
+--------------------+
| Payment Gateway |
+---------+----------+
|
+-------------+-------------+
| |
▼ ▼
+----------------------+ +----------------------+
| Payment PostgreSQL | | Mock Bank API |
+----------------------+ +----------+-----------+
|
▼
+----------------------+
| Mock Bank PostgreSQL |
+----------------------+
Build:
docker compose buildStart:
docker compose upDetached:
docker compose up -dLogs:
docker compose logs -fGateway logs:
docker compose logs -f payment-gatewayStop:
docker compose downLocal:
http://localhost:8080/swagger-ui/index.html
Production:
http://18.200.250.3:8080/swagger-ui/index.html
Local:
http://localhost:8080/actuator/health
Production:
http://18.200.250.3:8080/actuator/health
Run all tests:
mvn testFull verification:
mvn clean verifyThe gateway returns appropriate HTTP status codes for common scenarios, including:
- Request validation failures
- Missing idempotency key
- Payment not found
- Invalid payment state
- Idempotency conflicts
- Bank communication failures
- External bank operation failures
- Layered architecture separating API, application, domain, and infrastructure concerns.
- Payment lifecycle enforced through business rules.
- Payment state stored independently from payment operation history.
- MapStruct used for request and response mapping.
- Flyway manages database schema migrations.
- Integration tests validate the complete payment lifecycle and idempotency behavior.
- Dockerized development and deployment environment.
- Production deployment on AWS EC2.
- Automated deployments using
deploy.sh.
- Optimistic locking for concurrent updates
- Formal payment state machine
- Circuit breaker and retry policies
- Scheduled reconciliation jobs
- Testcontainers for integration testing
- Metrics and distributed tracing
- Authentication and authorization
- PCI-compliant card tokenization
- CI/CD pipeline with GitHub Actions