A highly resilient, multi-provider payment gateway built with Java 21 and Spring Boot 3. Designed to mirror enterprise fintech architectures, featuring distributed system patterns, API idempotency, and cryptographic webhook security.
This project was designed to demonstrate mastery of concepts required for enterprise payment integration:
- Database-Level Idempotency: Prevents double-charging by utilizing unique constraints on
idempotency_key. If a network drop causes a merchant to retry a request, the system safely returns the original result without re-processing. - Apache Camel Integration: Implements the Enterprise Integration Pattern (EIP) "Content-Based Router". Incoming payments are dynamically routed to different mock providers (Stripe vs. PayPal) based on the transaction currency.
- Webhook Signature Verification: A dedicated endpoint to receive async provider updates. Validates incoming payloads using cryptographic signatures to prevent payload spoofing.
- Containerized DevOps: Built with a multi-stage Dockerfile (optimizing image size) and Docker Compose for seamless local infrastructure provisioning (App + PostgreSQL).
- ⚙️ CI/CD Ready: Includes a declarative CI pipeline configuration for automated build verification.
- Language: Java 21
- Framework: Spring Boot 3.3.4, Spring Data JPA, Spring Validation
- Integration: Apache Camel 4.4.0
- Database: PostgreSQL 15
- Security: HMAC-SHA256 (Javax Crypto)
- Tooling: Lombok, Maven, Docker, Docker Compose, Git
The entire system (Application + Database) can be spun up with a single command:
# Clone the repository
git clone https://github.com/OJCodeCanvas/payment-orchestration-platform.git
cd payment-orchestration-platform
# Start the infrastructure
docker-compose up --build -dNote: The application takes about 15 seconds to fully boot and connect to the database.
curl -X POST http://localhost:8080/api/v1/payments \
-H "Content-Type: application/json" \
-d '{
"merchantId": "merch_001",
"amount": 100.50,
"currency": "EUR",
"idempotencyKey": "unique-order-abc-123"
}'Response:
{
"id": 1,
"status": "COMPLETED",
"transactions": [
{
"providerId": "MOCK_STRIPE",
"providerTransactionId": "ch_8aad3b41...",
"status": "SUCCESS"
}
]
}Running the exact same curl command again will return the identical id: 1 response without creating a new database entry or hitting the mock provider again.
curl -X POST http://localhost:8080/api/v1/payments \
-H "Content-Type: application/json" \
-d '{"merchantId": "merch_001", "amount": 80.00, "currency": "GBP", "idempotencyKey": "unique-order-gbp-456"}'To test the HMAC signature verification:
curl -X POST http://localhost:8080/api/v1/webhooks/stripe \
-H "X-Webhook-Signature: fake_signature" \
-d '{"status": "SUCCESS"}'PAYLOAD='{"providerTransactionId": "ch_123", "status": "SUCCESS"}'
SECRET="wallee_super_secret_webhook_key_12345"
SIGNATURE=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET" -binary | base64)
curl -X POST http://localhost:8080/api/v1/webhooks/stripe \
-H "Content-Type: application/json" \
-H "X-Webhook-Signature: $SIGNATURE" \
-d "$PAYLOAD"src/main/java/com/wallee/mock/payment_gateway/
├── controller/ # REST API endpoints (Payments & Webhooks)
├── dto/ # Data Transfer Objects for incoming requests
├── enums/ # Strict state machines (OrderStatus, TransactionStatus)
├── model/ # JPA/Hibernate Entities with relational mapping
├── repository/ # Spring Data JPA interfaces
├── routing/ # Apache Camel RouteBuilder (Content-Based Router)
└── service/ # Core business logic (Idempotency checks, orchestration)