Clients request a capability (Card, SEPA, SWIFT, USDC, EURe, …). The Routing Engine picks a provider adapter based on cost, availability, and compliance. The frontend never names Stripe, Bridge, Compose, or Monerium.
Layered view
Client
Your app, our SDKs, or the QashX Pay checkout. Talks to the public API only.
Public API
api-v1-* edge functions. Authenticates keys, enforces scopes, rate-limits, and validates payloads.
Orchestrator
PaymentOrchestrator writes the unified qashx_payments row and posts to the ledger.
Capability Registry
Maps method_kind + currency + region to eligible capabilities.
Routing Engine
Selects an adapter by policy — cost, latency, provider health, compliance rules.
Provider Adapters
Isolated modules per provider. Never share state. Called via a common ProviderAdapter interface.
Providers
Stripe, Bridge, Compose, Monerium, on-chain settlement — the outside world.
Ledger
qashx_ledger_entries — double-entry, atomic via SECURITY DEFINER RPCs.
Reconciliation
Hourly sweeps match provider records to ledger. Drift raises alerts to Ops.
Payment flow
Routing decision inputs
| Input | Source | Effect |
|---|---|---|
| Capability match | Capability Registry | Filters to adapters that can serve the request. |
| Provider health | Ops metrics | Excludes adapters currently degraded. |
| Cost tier | Fee config | Prefers lower-fee provider when tie-broken. |
| Compliance rules | qashx_pay_risk_rules | Blocks sanctioned corridors, region mismatches. |
| Settlement preference | Merchant config | Pins EURC/USDC to Base per stablecoin guard. |
| Explicit override | Admin toggle | Force-routes a corridor during incident response. |
Provider isolation
- Each adapter lives in its own module and its own edge function.
- No adapter reads another adapter's state or secrets.
- Provider names never leak into public API responses.
- Swapping a provider — or adding a new one — is a config change, not a client change.
This is why your integration survives provider changes. You wrote against a capability; we route it.