Architecture for a multi-bank payment aggregator

A blueprint the client's team can build from, covering everything from the domain model to the failover runbook.

Domain
Fintech, payments
Our role
Solution architecture, domain modeling, security and infrastructure design
Year
2026

We designed the solution architecture for a payment aggregator that connects merchants to several acquiring banks through a single API and a hosted checkout, for a client launching a payment platform in a regional market. The deliverable is a blueprint for the client's own engineering team, not a system we operate. It covers the domain model, a nominal account with an append-only ledger, payment and refund flows on Temporal, smart routing between banks, IAM with Keycloak, network topology, high availability with a failover runbook, CI/CD and reference code for the core patterns.

Challenge

  • Model a multi-level money chain (acquiring bank, aggregator, merchant, shop) where merchant funds sit on a nominal account and never mix with the aggregator's own money.
  • Route payments between banks per merchant and per payment method, and switch banks quickly when one fails or rejects a merchant's traffic.
  • Run long, failure-prone flows (bank calls, webhooks, timeouts) without losing or double-counting money.
  • Meet fiscal receipt rules for payments, cancellations and partial refunds, including multi-vendor marketplace baskets.
  • Keep the operator back office off the public internet, and plan for growth from about 100 RPS to 1,000+ RPS at peak with a clear failover story.

Solution

We delivered architecture documents, sequence and component diagrams, and small reference implementations, organized by concern. Business domain: a shared vocabulary, a domain model and flows for deposit, merchant and shop onboarding, cancel and marketplace partial refund. Backend: a layered FastAPI service (routes → services → data access), the data model and Temporal workflow patterns for payment and refund sagas. Frontend: patterns for the merchant portal and back office in React and TypeScript with OIDC and PKCE. IAM: Keycloak realm and role design, with identity configuration applied as versioned migrations in code. Infrastructure: a VPC with public and private subnets, corporate VPN, database HA options, an active-passive failover runbook and a GitLab CI/CD layout with separated responsibilities.

Architecture

Platform components, trust zones and external integrations
Platform components, trust zones and external integrations

What the system does

Hosted checkout and API
Merchants create a payment over a REST API and redirect the payer to a hosted page that offers card, QR bank transfer and wallet payments.
Smart routing
Bank priorities per merchant and per payment method, a manual force-switch, and automatic bank disablement when errors pass a threshold.
Split settlement on a nominal account
Each payment carries a split configuration, so the bank separates the merchant's share from the aggregator's commission when it settles to the nominal account.
Full payment lifecycle
Bills with a TTL and statuses, several payment attempts per bill, full and partial refunds, pre-settlement void and chargebacks. Marketplace refunds return only the affected vendor's share, and receipt data travels with every payment and reversal.
Merchant and shop onboarding
Identity first, then business verification (KYB) through a third-party provider; the company record is created only after a positive result. Shops prove domain ownership, pass a compliance review and then get production API keys.
Merchant portal and back office
Merchant teams get 40+ fine-grained permissions across statistics, balances, payments, payouts, shops, webhooks and users. Operators get routing rules, bank health monitoring, individual commissions, access blocking and audit logs.

Engineering decisions

  • Payment and refund sagas are Temporal workflows. Bank webhooks become workflow signals, and a payment not confirmed within its TTL expires cleanly. The cost is one more stateful component; in return the team gets retries, timeouts and a visible state per payment instead of hand-written state machines and cron jobs.
  • The ledger is separate from payment state. A bill is the state of a payment session; a transaction is the immutable fact of a money movement, with a per-merchant sequence number and the balance before and after. Balances derive from the ledger, so they can be audited and reconstructed at any time.
  • Triple-match validation for reversals: the refund amount, the sum of vendor shares and the sum of receipt line items must match before any bank call. Inconsistencies are caught on the platform side, not at the bank or in the receipt.
  • Banks plug in through adapters, and routing lives in data: banks, terminals and merchant-to-bank priorities are database records, with terminal credentials encrypted at the column level. Adding a bank means an adapter and configuration, not a schema change.
  • For high availability we compared two options with their costs: a managed PostgreSQL cluster with synchronous replication across zones (RPO = 0, automatic failover, higher cost) and an active-passive pair with asynchronous replication. For the active-passive pair we wrote a step-by-step runbook: detection, promotion, traffic switch and rebuilding the old primary as a replica.

Diagrams

Deposit saga through the hosted checkout: from the merchant's request to the ledger entry and merchant webhook
Deposit saga through the hosted checkout: from the merchant's request to the ledger entry and merchant webhook
Active-passive failover runbook for the database and API
Active-passive failover runbook for the database and API

Stack

  • Python
  • FastAPI
  • SQLAlchemy
  • Temporal
  • PostgreSQL
  • Keycloak
  • OAuth 2.0 / OIDC
  • React
  • TypeScript
  • GitLab CI
  • OpenTofu
  • Helm
  • Docker
  • Nginx

What this shows

  • A business brief turned into a buildable architecture for a regulated payments product before any production code was written.
  • Money flows modeled through the domain: aggregator hierarchy, nominal accounts, splits, ledger, refunds and chargebacks.
  • Reliable distributed workflows around unreliable bank APIs and webhooks, with routing and failover between providers.
  • Security and infrastructure for fintech: centralized IAM, fine-grained RBAC, a private back office, encrypted secrets, signed webhooks, IP allow-listing and an HA plan with explicit trade-offs.

Questions about this case

Did you build and run this platform?

No. We delivered the architecture: documents, diagrams and reference code for the core patterns. The client's own team builds and operates the platform from that blueprint.

How does the design prevent lost or double-counted money?

Each payment runs as a durable Temporal workflow. A bank webhook is accepted only after its signature is verified and is passed to the workflow as a signal, and a payment not confirmed within its TTL expires. Money movements go to an append-only ledger with a sequence number and balance before and after, and partial refunds pass a triple-match check first.

What happens when a bank or the database fails?

A bank whose errors pass a threshold is disabled automatically, and operators can force-switch traffic by hand. For the database we compared a managed cluster with synchronous replication against an active-passive pair, and wrote a step-by-step failover and recovery runbook for the latter.