HyperSwitch: The Open-Source Payments Orchestrator That Replaces Your PSP Stack

You integrate Stripe. Then Adyen for Europe. Then Braintree for PayPal. Then a local acquirer for Brazil. Each has its own SDK, webhook format, retry logic, and fee structure. Your codebase grows if (provider === 'stripe') branches like kudzu.

This is the problem HyperSwitch solves.

What Actually Is HyperSwitch?

HyperSwitch is an open-source payments orchestration platform — 45.6k GitHub stars, Apache 2.0, written in Rust, battle-tested at 300M daily transactions and $1T+ annual TPV by Juspay (India's largest payments processor). It sits between your application and 120+ payment processors, providing a single API that handles routing, retries, vaulting, reconciliation, and cost observability.

Think of it as the Linux of payments: you integrate once, then plug in whichever processors you need.

The Architecture That Makes It Work

Router + Scheduler: Two Services, One Job

HyperSwitch runs as two stateless Rust binaries:

Router — The hot path. Handles every payment request: authentication, FRM checks, 3DS decisions, connector selection via the Euclid routing engine, credential resolution from the vault, and connector dispatch through the Connector Abstraction Layer (CAL).

Scheduler — Background jobs. Producer batches scheduled tasks (card cleanup, API key expiry notices) into Redis queues; Consumer executes them. Horizontal scaling is trivial — any instance can handle any request.

All state lives in PostgreSQL. Redis handles caching, distributed locks, and the scheduler queue.

The Payment State Machine: Intent vs Attempt

This is where most orchestrators get it wrong. HyperSwitch separates two concepts:

  • payment_intent — The merchant's request. One per checkout session. Holds amount, currency, customer, capture method, setup_future_usage.
  • payment_attempt — A single authorization try against a specific PSP. Multiple attempts per intent (retries). Each has its own connector, connector_transaction_id, error_code, error_details.

When a retry happens, a new payment_attempt is created. The merchant-facing payment_id never changes — only the internal suffix (pay_abc_1, pay_abc_2) sent to the PSP changes.

Euclid: Routing That Isn't Just Round-Robin

The Euclid routing engine evaluates strategies in strict priority order:

  1. Rule-based — Merchant-defined conditions: payment_method_type = visa AND country = DE AND amount > 10000 → use Adyen
  2. Volume-based — Percentage split across eligible connectors
  3. Cost-based — Select lowest MDR (merchant discount rate) among eligible
  4. Default fallback — Ordered priority list from merchant config

After selection, eligibility analysis runs: Does this connector support the payment method + currency? Is it enabled for this profile? If any check fails, Euclid moves to the next candidate. Exhaust the list → payment fails without a single PSP call.

GSM: Normalizing 120+ Error Languages

Every PSP returns proprietary error codes. Stripe's card_declined ≠ Adyen's REFUSED ≠ Braintree's 2001. GSM (Global Status Monitoring) maps (connector, error_code, error_message) → standardized unified_code + retry flags:

Flag Meaning
retriable Any retry eligible
step_up_possible 3DS challenge could succeed
clear_pan_possible Network token failed, try raw PAN
alternate_network_possible Switch debit network (Visa → Maestro)

The retry decision flow is deterministic: GSM lookup → check flags in order (Step-Up → Clear PAN → Network → Cascading) → do_retry() creates new payment_attempt → re-enter Euclid.

The Vault: PCI Compliance Without the Pain

The Card Vault is a separate microservice. Sensitive data never touches your Router — or your application if you use the client-side SDK.

Tokenization flow (client-side): SDK → Vault /session_tokens → JWE-encrypted request → Vault decrypts, stores PAN, returns payment_method_id → Your backend sends payment_method_id to Router → Router calls Vault Locker API → Vault returns credential (PAN, network token, or proxy reference) → Router dispatches to PSP.

Key management: Custodian-based. Master key derived from multiple custodian shares (Shamir's Secret Sharing). Vault only activates when quorum of custodians unlock it. Zero data egress in self-hosted mode.

Reconciliation: Double-Entry for Payments

Merchants routing across PSPs get separate settlement reports — different formats, different schedules. HyperSwitch's reconciliation engine applies double-entry bookkeeping: every payment event generates balanced debit/credit ledger entries.

  • 2-way: Hyperswitch ledger ↔ PSP settlement report
  • 3-way: Hyperswitch ledger ↔ PSP settlement ↔ OMS order data

Auto-fetch pipelines pull reports from PSPs (SFTP, API, email), normalize via connector-specific rules, match on connector_transaction_id + amount + date with configurable tolerance windows, age unmatched entries, and surface exceptions in the Control Center.

What It Replaces

Paid Stack HyperSwitch Module
Stripe + Adyen + Braintree integrations Single API, 120+ connectors
Custom routing logic Euclid engine (rules, volume, cost)
Manual retry code GSM-driven smart retries (4 strategies)
PCI vault (TokenEx, VGS) Built-in Card Vault + Network Tokens
Spreadsheet reconciliation Automated 2/3-way engine
DataDog/New Relic for payments Cost Observability dashboards

Self-Hosting: What You Actually Need

# docker-compose.yml (simplified) services: router: image: juspaydotin/hyperswitch-router:latest environment: - DATABASE_URL=postgres://... - REDIS_URL=redis://... - LOCKER_URL=http://vault:8080 ports: ["8080:8080"] vault: image: juspaydotin/hyperswitch-locker:latest environment: - DATABASE_URL=postgres://... - CUSTODIAN_KEYS=... ports: ["8081:8081"] scheduler: image: juspaydotin/hyperswitch-scheduler:latest environment: - DATABASE_URL=postgres://... - REDIS_URL=redis://... postgres: ... redis: ... 

Deploy via Docker Compose, Kubernetes (Helm), or AWS CloudFormation. Control Center (React dashboard) runs separately for routing config, analytics, and exception management.

The Trade-offs

Not a drop-in Stripe replacement. You operate infrastructure. PCI-SSS validation requires the Encryption Service + custodian ceremony. Connector coverage is broad but not infinite — niche local methods may need custom connectors (template provided). The learning curve is real: routing rules, GSM mappings, vault activation.

But you get: Zero vendor lock-in. Full visibility into every hop. Routing logic in config, not code. No per-transaction markup. Apache 2.0 — fork it, extend it, audit it.

When to Adopt

  • Multiple PSPs already, or planning to add them
  • Team capable of running PostgreSQL + Redis + Rust services
  • Need routing/retries/vault/reconciliation as configurable infrastructure
  • Willing to invest in ops for long-term control

Start Here

# Quick local test git clone https://github.com/juspay/hyperswitch cd hyperswitch/docker docker compose up -d # Router at localhost:8080, Control Center at localhost:3000 

Read the architecture deep-dive for the Router internals, Vault architecture for tokenization internals, and Reconciliation architecture for the ledger model.

The payments stack you'd build if you had 12 years and 300M daily transactions to refine it. Now you don't have to.