Free/Libre Open-Source Payments Infrastructure
- Overview
- System Architecture
- Repository Structure
- Prerequisites
- Building from Source
- Configuration
- API Specification
- Database Schema
- Testing
- Security
- Contributing
- License
FlossPay is an enterprise-grade payment processing infrastructure implementing UPI (Unified Payments Interface) rails with support for extensible payment methods. Built on Spring Boot 3.x and Java 21 LTS, it provides:
- Asynchronous Transaction Processing: Redis Streams for reliable queue-based processing
- Idempotency Guarantees: RFC 4122 UUIDv4 with HMAC-SHA256 replay resistance
- Audit-First Architecture: Immutable transaction ledger with SHA-256 checksums
- Horizontal Scalability: Stateless API and worker services deployable on Kubernetes
Current Release: v0.2-alpha (UPI rail hardened)
Target Compliance: PCI-DSS v4.0, SOC 2 Type II, ISO 27001
┌─────────────────────────────────────────────────────────────────────────────┐
│ CLIENT LAYER │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Merchant │ │ Mobile │ │ Web │ │
│ │ App │ │ SDK │ │ Portal │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
└─────────┼────────────────┼────────────────┼──────────────────────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ FLOSSPAY CORE │
│ │
│ ┌─────────────────────────────────────┐ ┌─────────────────────────────┐ │
│ │ API SERVICE │ │ WORKER SERVICE │ │
│ │ ┌────────────┐ ┌──────────────┐ │ │ ┌─────────────────────┐ │ │
│ │ │ REST API │ │ Idempotency │ │ │ │ Stream Consumer │ │ │
│ │ │ Controller │ │ Service │ │ │ │ (XREADGROUP) │ │ │
│ │ └─────┬──────┘ └──────────────┘ │ │ └──────────┬──────────┘ │ │
│ │ │ │ │ │ │ │
│ │ ┌─────▼─────────────────────┐ │ │ ┌──────────▼──────────┐ │ │
│ │ │ TransactionApiProducer │ │ │ │ UPI/NPCI Gateway │ │ │
│ │ │ (Business Logic Layer) │ │ │ │ Client (Mock) │ │ │
│ │ └─────┬─────────────────────┘ │ │ └──────────┬──────────┘ │ │
│ └────────┼───────────────────────────┘ └─────────────┼──────────────┘ │
│ │ │ │
│ │ Redis Streams │ │
│ └────────► transactions.main ────────────────────┘ │
│ transactions.dlq (DLQ) │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ DATA LAYER │
│ ┌─────────────────────────────────────────────────────────────────────────┐│
│ │ PostgreSQL 15+ ││
│ │ ├── transactions (BIGSERIAL PK) ││
│ │ ├── transaction_history (Audit trail) ││
│ │ ├── idempotency_keys (Request deduplication) ││
│ │ ├── webhook_callbacks (Outbound notifications) ││
│ │ ├── service_circuit_breakers (Health monitoring) ││
│ │ └── client_rate_limits (Quota management) ││
│ └─────────────────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────────────────┘
| Component | Technology | Responsibility |
|---|---|---|
| API Service | Spring Boot 3.2, Java 21 | Public REST API, request validation, idempotency enforcement, HMAC authentication, transaction enqueueing |
| Worker Service | Spring Boot 3.2, Java 21 | Async stream consumption, retry logic with exponential backoff, DLQ management, UPI gateway integration |
| Shared Libraries | Java Module System | DTOs, JPA entities, repositories, validation annotations, exception hierarchy |
| Database Layer | PostgreSQL 15+, Flyway | Relational ledger with audit tables, migration management |
| Message Queue | Redis 7+ Streams | Reliable async processing, consumer groups, dead letter queue |
FlossPay/
├── api-service/ # REST API and public interface
│ ├── src/
│ │ ├── main/
│ │ │ ├── java/com/openpay/api/
│ │ │ │ ├── controller/ # REST controllers (Transaction, Health)
│ │ │ │ ├── service/ # Business logic (TransactionApiProducer, Idempotency)
│ │ │ │ ├── security/ # HMAC authentication
│ │ │ │ ├── config/ # Spring configuration (Redis, Web, Logging)
│ │ │ │ └── filter/ # Rate limiting filter
│ │ │ └── resources/
│ │ │ ├── application.properties
│ │ │ └── db/migration/ # Flyway migrations
│ │ └── test/ # Unit and integration tests
│ └── pom.xml
├── worker-service/ # Background processing
│ ├── src/
│ │ ├── main/java/com/openpay/worker/
│ │ │ ├── processor/ # Stream consumer and retry logic
│ │ │ ├── client/ # NPCI/UPI gateway client
│ │ │ └── config/ # Redis worker configuration
│ │ └── resources/
│ └── pom.xml
├── shared-libs/ # Common domain model
│ ├── src/main/java/com/openpay/shared/
│ │ ├── dto/ # Data Transfer Objects
│ │ ├── model/ # JPA Entities
│ │ ├── repository/ # Spring Data Repositories
│ │ ├── exception/ # Exception hierarchy
│ │ └── validation/ # Custom validators
│ └── pom.xml
├── database/ # Database utilities (reserved)
├── scripts/ # Development and deployment scripts
│ ├── gen_hmac.py # HMAC signature generator
│ └── scaffold.sh # Project scaffolding
├── .github/
│ ├── workflows/ # GitHub Actions CI/CD
│ └── ISSUE_TEMPLATE/ # Issue templates
├── pom.xml # Parent Maven POM
├── justfile # Task runner commands
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── SECURITY.md
└── LICENSE
- Java: OpenJDK 21 LTS (tested with Temurin)
- Maven: 3.9.x or later
- PostgreSQL: 15.x or later
- Redis: 7.x or later (with Streams support)
- Docker: 24.x or later (for containerized deployment)
- Docker Compose: 2.x or later (for local development stack)
- Just: Task runner (alternative to Make)
git clone https://github.com/davidgracemann/FlossPay.git
cd FlossPay# PostgreSQL
psql -U postgres -c "CREATE DATABASE flosspay_db;"
psql -U postgres -c "CREATE USER flosspay_user WITH PASSWORD 'secure_random_password';"
psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE flosspay_db TO flosspay_user;"
# Redis
redis-cli ping
# Expected: PONG# Full build with tests
./mvnw clean verify
# Skip tests (development only)
./mvnw clean install -DskipTests# Terminal 1: API Service
java -jar api-service/target/api-service-1.0-SNAPSHOT.jar
# Terminal 2: Worker Service
java -jar worker-service/target/worker-service-1.0-SNAPSHOT.jarOr using Just task runner:
just runa # Run API service
just runw # Run Worker service# Database
spring.datasource.url=jdbc:postgresql://localhost:5432/flosspay_db
spring.datasource.username=flosspay_user
spring.datasource.password=${DB_PASSWORD}
spring.datasource.driver-class-name=org.postgresql.Driver
spring.jpa.hibernate.ddl-auto=validate
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
# Flyway
spring.flyway.enabled=true
spring.flyway.locations=classpath:db/migration
# Redis
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=${REDIS_PASSWORD}
spring.redis.timeout=2000ms
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-idle=8
# Server
server.port=8080
server.servlet.context-path=/api/v1
# Logging
logging.level.root=INFO
logging.level.com.openpay=DEBUG
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n# Database (same as API)
spring.datasource.url=jdbc:postgresql://localhost:5432/flosspay_db
spring.datasource.username=flosspay_user
spring.datasource.password=${DB_PASSWORD}
# Redis
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=${REDIS_PASSWORD}
# Server
server.port=8081| Variable | Required | Description | Example |
|---|---|---|---|
DB_PASSWORD |
Yes | PostgreSQL password | secure_random_password |
REDIS_PASSWORD |
Yes | Redis password | redis_secure_pass |
HMAC_SECRET |
Yes | HMAC signing key (min 32 bytes) | base64_encoded_key |
http://localhost:8080/api/v1
All payment endpoints require HMAC-SHA256 authentication:
- Concatenate request body JSON + idempotency key
- Compute HMAC-SHA256 using shared secret
- Base64 encode result
- Include in
X-HMACheader
POST /pay
Content-Type: application/json
Idempotency-Key: <uuid-v4>
X-HMAC: <base64-hmac-signature>
Request:
{
"senderUpi": "sender@upi",
"receiverUpi": "receiver@upi",
"amount": 100.00
}
Response (200 OK):
{
"id": 12345,
"status": "QUEUED",
"message": "Transaction queued"
}
Response (400 Bad Request):
{
"error": "Sender and receiver UPI must be different"
}
Response (409 Conflict):
{
"error": "Duplicate request"
}POST /collect
Content-Type: application/json
Idempotency-Key: <uuid-v4>
X-HMAC: <base64-hmac-signature>
Request:
{
"senderUpi": "payer@upi",
"receiverUpi": "payee@upi",
"amount": 250.50
}
Response (202 Accepted):
{
"id": 12346,
"status": "REQUESTED",
"message": "Collect request queued"
}GET /transaction/{id}/status
Response (200 OK):
{
"id": 12345,
"status": "COMPLETED",
"senderUpi": "sender@upi",
"receiverUpi": "receiver@upi",
"amount": 100.00,
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-15T10:30:05Z"
}
Response (404 Not Found):
{
"error": "Transaction not found"
}GET /health
Response (200 OK):
{
"status": "UP",
"timestamp": "2024-01-15T10:30:00Z",
"version": "0.2-alpha"
}
GET /health/ready
Response (200 OK):
{
"status": "READY",
"checks": {
"database": "UP",
"redis": "UP"
}
}
Response (503 Service Unavailable):
{
"status": "NOT_READY",
"checks": {
"database": "DOWN",
"redis": "UP"
}
}# Generate HMAC signature
python3 scripts/gen_hmac.py
# Make payment request
curl -X POST http://localhost:8080/api/v1/pay \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-H "X-HMAC: YOUR_HMAC_SIGNATURE" \
-d '{
"senderUpi": "test@upi",
"receiverUpi": "merchant@upi",
"amount": 100.00
}'
# Check status
curl http://localhost:8080/api/v1/transaction/12345/status| Column | Type | Constraints | Description |
|---|---|---|---|
| id | BIGSERIAL | PRIMARY KEY | Auto-generated transaction ID |
| sender_upi | VARCHAR(100) | NOT NULL | Payer UPI ID |
| receiver_upi | VARCHAR(100) | NOT NULL | Payee UPI ID |
| amount | NUMERIC(15,2) | NOT NULL | Transaction amount |
| status | VARCHAR(20) | NOT NULL | queued, processing, completed, failed |
| created_at | TIMESTAMPTZ | DEFAULT NOW() | Creation timestamp |
| updated_at | TIMESTAMPTZ | NULL | Last update timestamp |
Indexes:
CREATE INDEX idx_tx_status_created ON transactions(status, created_at);| Column | Type | Constraints | Description |
|---|---|---|---|
| history_id | BIGSERIAL | PRIMARY KEY | Audit record ID |
| transaction_id | BIGINT | FOREIGN KEY | References transactions.id |
| prev_status | VARCHAR(20) | NOT NULL | Previous status |
| new_status | VARCHAR(20) | NOT NULL | New status |
| changed_at | TIMESTAMPTZ | DEFAULT NOW() | Change timestamp |
| Column | Type | Constraints | Description |
|---|---|---|---|
| idempotency_key | VARCHAR(64) | PRIMARY KEY | Client-provided UUID |
| transaction_id | BIGINT | FOREIGN KEY | Associated transaction |
| created_at | TIMESTAMPTZ | DEFAULT NOW() | Key creation time |
| Column | Type | Constraints | Description |
|---|---|---|---|
| callback_id | BIGSERIAL | PRIMARY KEY | Callback record ID |
| transaction_id | BIGINT | FOREIGN KEY | References transactions.id |
| url | VARCHAR(255) | NOT NULL | Webhook endpoint URL |
| status | VARCHAR(20) | NOT NULL | pending, sent, failed |
| last_attempted_at | TIMESTAMPTZ | NULL | Last attempt timestamp |
| attempts | INTEGER | DEFAULT 0 | Delivery attempts |
| Column | Type | Constraints | Description |
|---|---|---|---|
| service_name | VARCHAR(50) | PRIMARY KEY | External service identifier |
| state | VARCHAR(20) | NOT NULL | CLOSED, OPEN, HALF_OPEN |
| failure_count | INTEGER | DEFAULT 0 | Recent failure count |
| last_failure_at | TIMESTAMPTZ | NULL | Last failure timestamp |
| Column | Type | Constraints | Description |
|---|---|---|---|
| client_id | VARCHAR(64) | PRIMARY KEY | API client identifier |
| tokens | INTEGER | DEFAULT 100 | Available quota tokens |
| last_refill | TIMESTAMPTZ | DEFAULT NOW() | Last quota refill |
./mvnw test./mvnw verify -Pintegration-tests- Line coverage: ≥ 85%
- Branch coverage: ≥ 80%
- Mutation coverage (PIT): ≥ 75%
# Using k6
k6 run scripts/load-test.js
# Using Gatling
gatling.sh -sf src/gatling/scalaDO NOT open public issues for security vulnerabilities.
Email: security@flosspay.dev
Include:
- Detailed vulnerability description
- Affected versions/branches
- Steps to reproduce or PoC
- Impact assessment
24-hour acknowledgment SLA
| Control | Implementation | Standard |
|---|---|---|
| Authentication | HMAC-SHA256 request signing | RFC 2104 |
| Idempotency | UUIDv4 keys with TTL | RFC 4122 |
| Transport | TLS 1.3 mandatory | RFC 8446 |
| Encryption | AES-256-GCM at rest | FIPS 197 |
| Audit Trail | SHA-256 signed logs | FIPS 180-4 |
| Retry Logic | Exponential backoff with jitter | FIPS 140-2 |
- PCI-DSS v4.0: Req 3.4 (PAN protection), Req 10 (logging), Req 11 (testing)
- SOC 2 Type II: CC6.1 (logical access), CC7.2 (system monitoring)
- ISO 27001: A.12.4 (logging), A.14.2.1 (secure development)
- Fork the repository
- Create feature branch:
git checkout -b feature/description - Implement changes with tests
- Run pre-commit checks:
./scripts/pre-commit.sh - Commit with signed-off-by:
git commit -s - Push and open Pull Request
subsystem: Brief description (50 chars)
Detailed explanation of what changed and why.
Can span multiple lines.
Signed-off-by: Developer Name <email@example.com>
- All tests pass
- Code coverage maintained
- Security scan clean
- JavaDoc updated
- No breaking changes (or documented)
- Reviewed by 2+ maintainers
main: Production-ready codefeature/*: New featuresfix/*: Bug fixeshotfix/*: Critical production fixes
See CONTRIBUTING.md for complete guidelines.
| Metric | Target | Notes |
|---|---|---|
| API Latency (p99) | < 100ms | Without external gateway call |
| Worker Throughput | 100+ TPS | Per instance |
| Database Connections | 20 | HikariCP pool size |
| Redis Timeout | 2s | Connection timeout |
| Retry Backoff | 2^n seconds | Exponential, max 5 attempts |
Solution:
# Verify Redis is running
redis-cli ping
# Check configuration in application.properties
spring.redis.host=localhost
spring.redis.port=6379Solution:
# Verify PostgreSQL is running
pg_isready -h localhost -p 5432
# Check database exists
psql -U postgres -c "\l" | grep flosspay
# Verify credentials
psql -U flosspay_user -d flosspay_db -c "SELECT 1"Solution:
- Verify HMAC secret matches between client and server
- Check message format:
JSON body + idempotencyKey - Use provided script:
python3 scripts/gen_hmac.py
MIT License - See LICENSE
Copyright (c) 2024 David Grace
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- Security Issues: security@flosspay.dev
- General Questions: GitHub Discussions
- Bug Reports: GitHub Issues (use templates)
Maintainers:
- David Grace (@davidgracemann) - Project Owner & Chief Architect
- Goutham Rajesh (@gouthamdev) - Product Manager