Payment Service
Service Type: Core REST Microservice with Asynchronous Event Processing Language: Kotlin / Micronaut Framework Database: PostgreSQL (owned) Messaging: Kafka consumers for bond and deal events Status: Implemented (v0.1)
Overview
Core Payment Service orchestrates all financial transactions within DEUSS, managing bond purchases between buyers and sellers, coupon payments (interest distributions), and principal repayments at maturity. It acts as the orchestrator between on-chain deal events (via Indexer), company IBAN data (Company Service), external Payment Service Provider (PSP) for fund settlement, and blockchain state tracking. The service is highly stateful and event-driven, coordinating complex multi-step payment workflows.
Business Responsibilities
- Bond Purchase Payments — Process buyer-to-seller transactions when bonds are purchased; track payment lifecycle (created → paid → settled)
- Coupon Payments (Interest) — Generate and process periodic interest distributions based on bond payment frequencies (ANNUAL, SEMI_ANNUAL, QUARTERLY, MONTHLY, ZERO_COUPON)
- Principal Repayments — Schedule and execute maturity-date principal repayments to bondholders
- Payment Status Tracking — Maintain audit trail of payment state transitions with timestamps and reasons
- Payday Management — Calculate paydays based on bond terms; validate and trigger payment generation
- Fee Resolution — Determine applicable fees for transactions (integration with Partner Service broker fee schedules)
- Settlement Coordination — Coordinate with external Payment Service Provider (PSP) for fund transfers
- Blockchain Reconciliation — Sync payment states with on-chain MarketDeal states via Indexer
- Company Integration — Fetch IBANs and wallet addresses from Company Service and Registry Service
Owned Data
Core Entities:
bond_purchase_payment— Bond purchase transactions (buyer, seller, ISIN, deal_id, status, transaction references)bond_purchase_payment_statusenum — States: CREATED, PAID, SETTLED, UNPAIDpurchase_history— Audit trail of bond purchase status changesbond_issuance— Bond metadata (coupon schedule, maturity date, nominal value)payday— Scheduled coupon payment dates for bondscoupon_payment— Individual coupon/interest payment records (seller aggregate + individual buyer payments)settlement_payment— Issuer and buyer settlement recordspayment_status_history— Audit trail of all payment state transitionspayment_provider— Payment Service Provider credentials and configuration Payment Types:- BOND_PURCHASE — Buyer purchases bond from seller
- COUPON_PAYMENT — Interest distribution to bondholders
- PRINCIPAL_REPAYMENT — Principal return at maturity
- ISSUER_SETTLEMENT — Issuer receives from PSP
- BUYER_SETTLEMENT — Buyer receives from PSP Payment States:
- CREATED — Payment record created, awaiting PSP submission
- PENDING — Submitted to PSP, awaiting confirmation
- BUYER_PAID / ISSUED — Buyer paid PSP; issuer paid PSP
- SETTLED / RECEIVED — PSP sent funds to seller/buyer; verified complete
Inbound Interfaces
REST API
Authentication: JWT Bearer token; most endpoints ANONYMOUS or ADMIN
Payments Query (/payments)
| Endpoint | Method | Operation | Purpose |
|---|---|---|---|
/payments/{paymentReference} | GET | getPayment | Fetch payment details by reference |
/payments/{paymentReference}/statusHistory | GET | getPaymentStatusHistory | Get status change audit trail (with ordering & filtering) |
/payments/deal/{dealId} | GET | getPaymentByDealId | Get bond purchase payment by on-chain deal ID |
/payments | GET | getByWallet | List payments for wallet (filters: ISIN, type, status, amount, date range; sortable, paginated) |
Bond Paydays
| Endpoint | Method | Operation | Purpose |
|---|---|---|---|
/payments/bonds/{isin}/paydays | GET | getBondPaydays | List upcoming paydays for bond |
External Payment Provider Callbacks (/payments)
| Endpoint | Method | Operation | Purpose | Auth |
|---|---|---|---|---|
/payments/{paymentReference}/received | PATCH | paymentReceived | PSP calls when payment received/failed (dispatches to bond purchase or settlement) | Basic Auth |
/payments/sent | PATCH | paymentsSent | PSP calls when settlement funds sent to recipients (batch operation) | Basic Auth |
Admin Operations (/payments)
| Endpoint | Method | Operation | Purpose |
|---|---|---|---|
/payments/reset | POST | resetPayments | Reset failed payments to original state (testing/recovery) |
/payments (admin) | GET | getPaymentsAdmin | List all payments with advanced filters (wallet, ISIN, type, status, amount, date) |
Testing/Debug Endpoints (/payday)
| Endpoint | Method | Operation | Purpose |
|---|---|---|---|
/payday/generate | POST | generatePaydays | Manually trigger payday generation for bonds (testing) |
/payday/check | POST | checkPaydays | Manually trigger payday processing (testing) |
Outbound Interfaces
Internal Service Calls
- Company Service — Fetch company IBAN and wallet addresses via OpenAPI client
- Registry Service — Fetch bond owners, issuer/buyer details via OpenAPI client
- Partner Service — Query broker fee schedules for fee resolution via OpenAPI client
- Indexer (GraphQL) — Query current bond ownership, MarketDeal states (async via IndexerClient)
External Integrations
- Payment Service Provider (PSP) — External HTTP API for settlement coordination
- Credentials: Username/Argon2-hashed password (Bearer token)
- Endpoints: Submit payments, retrieve status, receive callbacks
- Configuration:
PAYMENT_PROVIDER_HOST,PAYMENT_PROVIDER_TOKEN,PAYMENT_PROVIDER_USERNAME,PAYMENT_PROVIDER_PASSWORD_HASH
Database Only
- PostgreSQL direct access via Hibernate/JPA
Kafka Integration
Status: Enabled (consumers only, no producers)
| Topic | Event | Listener | Purpose |
|---|---|---|---|
BondRegistryEvents.BOND_PUBLISHED | BondPublishedEvent | BondPublishedListener | New bond published → validate & generate paydays |
BlockchainEvents.COUNTER_OFFER | CounterOfferEvent | CounterOfferListener | Counter-offer created → trigger payment creation |
BlockchainEvents.DEAL_CREATED | DealCreatedEvent | DealCreatedListener | Deal created → record payment, notify PSP |
Configuration:
kafka:
group-id: ${KAFKA_GROUP_ID:`core-payment-service`}
Scheduled Jobs & Checkers
Core Payment Service uses scheduled tasks (not JobRunr) for polling-based workflow:
| Job | Frequency | Purpose |
|---|---|---|
BondPurchaseCreateChecker | Periodic | Check pending bond purchases; create payment if needed |
BondPurchaseDealPaidChecker | Periodic | Poll Indexer for buyer payments; validate with chain |
BondPurchaseSellerPaidChecker | Periodic | Poll Indexer for seller settlement confirmations |
BondPurchaseBuyerPaidChecker | Periodic | Verify buyer received funds from PSP |
BondPurchasePendingExpiryChecker | Periodic | Expire stale pending payments |
PaydayChecker | Periodic | Check for payday occurrences; create coupon payments |
SettlementPaymentCreatedChecker | Periodic | Submit settlement payments to PSP |
SettlementPaymentReceivedChecker | Periodic | Check settlement payment status |
Key Business Rules
- Bond Purchase Lifecycle:
- Deal created on chain → Payment Service records transaction
- Buyer pays PSP → PSP notifies (callback) → Payment status: BUYER_PAID
- Validation passes → Payment status: PAID
- PSP sends to seller → PSP notifies → Payment status: SETTLED
- Coupon Payment Lifecycle:
- Payday occurs (based on frequency) → Coupon payments generated
- Summary payment created (issuer → PSP) + individual payments (PSP → each bondholder)
- PSP notifies when received and when sent
- Payments tracked through CREATED → ISSUED → RECEIVED
- Principal Repayment:
- Maturity date reached → Special payday for principal created
- Seller sends principal + final interest to PSP
- PSP distributes to all current bondholders
- Payment States:
- CREATED — Not yet submitted to PSP
- PAID — Submitted and received from payer
- UNPAID — Failed to receive
- SETTLED — PSP distributed to recipient
- RECEIVED — Recipient confirmed receipt
- Fee Resolution:
- Broker fees from Partner Service applied per transaction
- Fee classification per broker determined by deal type and amount
- Blockchain Reconciliation:
- All payments validated against on-chain MarketDeal state via Indexer
- State mismatch → Error recorded, manual intervention required
- PSP Callback Routing:
- Callbacks dispatched based on payment reference lookup
- Same endpoint handles bond purchases and settlement payments
- IBAN Caching:
- IBANs fetched from Company Service once per deal creation
- Reused for all subsequent payments on that deal
Deployment
Runtime Stack:
- Java 25 (OpenJDK JRE in Alpine container)
- Micronaut on Netty
- PostgreSQL database with Flyway migrations
- Kafka consumers for event processing
- Kotlin coroutines for async/await patterns
- Spring Security Crypto for Argon2 hashing Configuration:
- Port: 8080
- Health check:
/health - Environment variables:
DATABASE_URL,DATABASE_USER,DATABASE_PASSWORDPAYMENT_PROVIDER_HOST,PAYMENT_PROVIDER_TOKENPAYMENT_PROVIDER_USERNAME,PAYMENT_PROVIDER_PASSWORD_HASHAUTO_APPROVE_PAYMENTS_ENABLED,AUTO_VALIDATE_PAYMENTS_ENABLEDPAYMENT_PROVIDER_ENABLE_API_CALLSREGISTRY_SERVICE_URL,REGISTRY_SERVICE_MOCK_ENABLEDPARTNER_SERVICE_URL,PARTNER_SERVICE_MOCK_ENABLEDKAFKA_GROUP_IDCOMPANY_SERVICE_ATTEMPTS,COMPANY_SERVICE_ATTEMPTS_DELAY,COMPANY_SERVICE_CIRCUIT_BREAKER_RESETContainer:
- Non-root user (
runtime) for security - Init process (
tini) for signal handling - JVM container support enabled
Observability
- Structured Logging — Semantic logging with action tracking, parameters, and timing
- Health Endpoint —
/healthfor K8s liveness/readiness - Payment Status History — Immutable audit trail of all state changes
- Scheduler Metrics — Checker success/failure counts tracked internally
- Metrics — Unknown (Micrometer integration status not verified)
Technical Notes
- Event-Driven & Polling Hybrid — Kafka events trigger flows; schedulers poll for state changes to avoid missed events
- Chain State Validation — Every payment update verified against current on-chain MarketDeal state before accepting
- Callback Routing — Single PSP endpoint dispatches to bond purchase or settlement pipelines based on payment reference lookup
- Immutable Audit Trail — All status changes recorded with timestamp; supports full payment history replay
- Argon2 Password Hashing — PSP credentials use Argon2i for secure password storage (configurable iterations)
- Circuit Breaker — Company Service calls protected with retry/circuit-breaker logic
- Two-Path Payment Processing — Bond purchases use deal events + immediate creation; coupons use time-based payday scheduling
- CBOR Serialization — Jackson CBOR support for compact Kafka event serialization
Relationships with Other Services
- Partner Service — Queries broker fee schedules to determine applicable transaction fees
- Registry Service — Fetches current bond owners and issuer details for coupon distribution calculation
- Company Service — Fetches IBANs and wallet addresses for payers/recipients
- Indexer (GraphQL) — Queries on-chain MarketDeal states, bond ownership snapshots, deal details
- Payment Service Provider (PSP) — External gateway for fund settlement; bidirectional callbacks
Documentation Sources:
- README:
core/core-payment-service/README.md - Source:
core/core-payment-service/src/ - Migrations:
core/core-payment-service/src/main/resources/db/migration/(19 versions) - OpenAPI Public:
api-specifications/.../core-payment-service/public/ - Config:
core/core-payment-service/src/main/resources/application.yaml - Container:
core/core-payment-service/Dockerfile