Skip to content
saifcoresPublic

About

Multi-service payment backend for African markets: mobile money, cards (via PSPs), bank transfers, wallets, double-entry ledger, webhooks, subscriptions, and payouts. PSP integrations use an adapter layer (Flutterwave, Paystack, CinetPay) with routing, failover, and Resilience4j circuit breakers.

Resources

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Repository files navigation

AfriPay Platform

Multi-service payment backend for African markets: mobile money, cards (via PSPs), bank transfers, wallets, double-entry ledger, webhooks, subscriptions, and payouts. PSP integrations use an adapter layer (Flutterwave, Paystack, CinetPay) with routing, failover, and Resilience4j circuit breakers.

Stack

Layer Technology
Runtime Java 17 (Spring Boot parent); use JDK 21 in production if desired
Framework Spring Boot 3.4, Spring Cloud 2024
API edge Spring Cloud Gateway (rate limiting via Redis)
Data PostgreSQL (per-service schemas + Flyway), Redis
Messaging Kafka
Observability Actuator, Micrometer, Prometheus, Grafana (see Monitoring)
API docs springdoc OpenAPI (Swagger UI per service)

Documentation

  • Architecture — sequence-style flows, Kafka topics, idempotency, orchestrator routing, failure modes
  • Operations — environment variables, dependencies, scaling, backups, CI, runbook prompts
  • Contributing — Maven modules, adding a PSP adapter, database migrations
  • Monitoring — Prometheus, Grafana, metrics endpoints

Architecture

Services (ports are local defaults):

Service Port Role
api-gateway 8080 Routes, JWT validation, Redis rate limits
auth-service 8081 Users, merchants, roles, JWT issuance (/auth context path)
payment-service 8082 Payment intents, idempotency, Kafka events, orchestrator client
gateway-orchestrator-service 8083 PaymentGateway adapters, smart routing, circuit breaker (/orchestrator)
wallet-service 8084 Balances, freeze/unfreeze
ledger-service 8085 Double-entry journal posting
webhook-service 8086 HMAC verification, dedupe, Kafka
subscription-billing-service 8087 Plans, subscriptions, billing run
payout-service 8088 Payout batches

Payment flow (happy path)

  1. Client POST /payments (through gateway) with Authorization + optional Idempotency-Key.
  2. payment-service persists intent (PostgreSQL payment schema), calls orchestrator POST /orchestrator/v1/charge with the same JWT.
  3. Orchestrator selects a gateway (currency/country + hint), returns authorization URL (sandbox/mock in default profile).
  4. PSP sends webhook → webhook-service verifies signature (when secrets set), dedupes, emits Kafka payments.webhooks.
  5. Downstream consumers (your workers) call wallet + ledger to settle funds and post accounting entries.

PCI-DSS posture

  • Never persist raw card numbers or CVV; use hosted fields / PSP tokens only.
  • AesEncryptionService (AES-256-GCM) in common-lib is for non-PCI secrets (e.g. API tokens at rest), not cards.

Database model (ER overview)

PostgreSQL database afripay with one schema per bounded context (each Flyway runs independently).

erDiagram
  AUTH_MERCHANTS ||--o{ AUTH_USERS : "merchant_id"
  AUTH_USERS ||--|{ AUTH_USER_ROLES : "roles"

  PAYMENT_INTENTS {
    uuid id PK
    bigint amount_minor
    varchar currency
    uuid customer_id
    varchar status
    varchar idempotency_key UK
  }

  WALLET_WALLETS {
    uuid id PK
    uuid owner_id
    varchar currency
    bigint balance_minor
    bigint frozen_minor
  }

  LEDGER_ACCOUNTS {
    uuid id PK
    varchar code UK
    varchar type
    varchar currency
  }

  LEDGER_JOURNAL_TRANSACTIONS {
    uuid id PK
    varchar idempotency_key UK
    varchar currency
  }

  LEDGER_JOURNAL_TRANSACTIONS ||--|{ LEDGER_LEDGER_ENTRIES : "contains"
  LEDGER_ACCOUNTS ||--|{ LEDGER_LEDGER_ENTRIES : "posted_to"

  BILLING_PLANS ||--o{ BILLING_SUBSCRIPTIONS : "plan_id"

  PAYOUT_BATCHES {
    uuid id PK
    uuid merchant_id
    varchar status
  }
Loading

Indexing & isolation

  • Payment intents: partial unique index on idempotency_key (where not null); indexes on customer_id, status.
  • Ledger posting: SERIALIZABLE transaction when posting journals; immutable ledger_entries (insert-only).
  • Wallets: optimistic locking via @Version + pessimistic read for debit/credit paths.

Local development

Prerequisites

  • JDK 17+, Maven 3.9+
  • Docker (for Postgres, Redis, Kafka, Prometheus, Grafana)

Infrastructure

docker compose up -d postgres redis zookeeper kafka prometheus grafana

Create DB once (image default): user afripay / password afripay / database afripay.

Configuration

  • JWT: shared secret JWT_SECRET (min 256 bits for HS256). Same value on gateway and all resource servers.
  • Orchestrator URL (payment → orchestrator): ORCHESTRATOR_URL=http://localhost:8083/orchestrator
  • PSP secrets (webhooks): FLW_WEBHOOK_SECRET, PAYSTACK_WEBHOOK_SECRET, CINETPAY_WEBHOOK_SECRET (empty = dev mode accepts unsigned for local testing).

Run services (separate terminals)

mvn -pl auth-service -am spring-boot:run
mvn -pl gateway-orchestrator-service -am spring-boot:run
mvn -pl payment-service -am spring-boot:run
mvn -pl api-gateway -am spring-boot:run
# …wallet, ledger, webhook, billing, payout as needed

API examples

  • Register / login: POST http://localhost:8080/auth/v1/register, POST http://localhost:8080/auth/v1/login
  • Create payment: POST http://localhost:8080/payments with Authorization: Bearer <token> and body:
{
  "amount": 10000,
  "currency": "XOF",
  "customerId": "00000000-0000-0000-0000-000000000001",
  "provider": "AUTO",
  "countryCode": "CI"
}
  • Webhook (dev, no secret): POST http://localhost:8080/webhooks/flutterwave with raw JSON body.

OpenAPI / Swagger

  • Auth: http://localhost:8081/auth/swagger-ui.html (direct) or via gateway where aggregated.
  • Each service exposes /v3/api-docs on its port.

Postman

Import postman/AfriPay.postman_collection.json and set baseUrl to http://localhost:8080.

Docker image (per module)

mvn -pl auth-service -am package -DskipTests
docker build -f Dockerfile --build-arg MODULE=auth-service -t afripay/auth-service .

Testing

mvn clean verify
  • Unit tests: JUnit 5 + Mockito (e.g. LedgerPostingServiceTest).
  • Integration tests: add Testcontainers (Postgres/Kafka) per service as you harden flows.

Observability

  • JSON-friendly logging: configure Logback JSON encoder in production (see Spring Boot docs); ship to ELK/OpenSearch.
  • Prometheus (http://localhost:9090) scrapes every service on /actuator/prometheus with a service label per target; config: infra/prometheus/prometheus.yml.
  • Grafana (http://localhost:3000, default admin / admin) ships with an AfriPay overview dashboard (HTTP rate, scrape health, JVM heap). Details: docs/MONITORING.md.

OAuth2 readiness

Resource servers use JWT today. To add OAuth2 Authorization Server or external IdP (Keycloak, Cognito), replace JwtDecoder beans with issuer URIs and register clients; gateway continues to validate JWTs the same way.

License

Proprietary — adjust for your organization.

About

Multi-service payment backend for African markets: mobile money, cards (via PSPs), bank transfers, wallets, double-entry ledger, webhooks, subscriptions, and payouts. PSP integrations use an adapter layer (Flutterwave, Paystack, CinetPay) with routing, failover, and Resilience4j circuit breakers.

Resources

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages