juspay/hyperswitch. 가맹점(상점)과 100개가 넘는 결제 처리사(Stripe·Adyen·PayPal·Cybersource…) 사이에 끼어, 결제를 똑똑한 라우팅 · 자동 재시도 · PCI 금고 · 비용 관측으로 다루는 결제 오케스트레이션 플랫폼이다. 100% Rust로 작성됐고, 워크스페이스 안에 38개 크레이트 · 147개 커넥터 모듈 · 3개의 실행 바이너리(router·scheduler·drainer)가 들어 있다. 스스로를 "결제계의 리눅스(The Linux for Payments)"라 부르며, 특정 결제사에 묶이지 않고(no vendor lock-in) 직접 서버에 올려 쓸 수 있는 게 핵심이다. (저장소: juspay/hyperswitch · 언어 Rust(edition 2021, MSRV 1.85) · 라이선스 Apache-2.0 · ★ 약 43.4k · 만든 곳 Juspay · GitHub 트렌딩 역대 46회 등장)
"하나의 결제사에 직접 붙는 대신, 수많은 결제사 위에 '교통 정리원'을 한 명 둔다 — 그 정리원이 Hyperswitch다."
온라인 상점이 카드 결제를 받으려면 보통 Stripe 같은 결제사 한 곳에 코드를 붙입니다. 그런데 그 한 곳이 장애가 나거나, 특정 카드의 승인율이 낮거나, 수수료가 비싸면? 상점은 손해를 봅니다. Hyperswitch는 결제사 여러 곳을 한꺼번에 연결해 두고, 결제가 들어올 때마다 규칙이나 예측 승인율에 따라 가장 좋은 곳으로 보냅니다. 실패하면 자동으로 다른 결제사로 재시도하고, 카드 정보는 PCI 규격 금고에 안전하게 보관하며, 결제사별 숨은 수수료까지 대시보드로 보여 줍니다. 그리고 이 모든 걸 남의 서비스가 아니라 내 서버에 직접 올려 쓸 수 있습니다.
좀 더 구체적으로, Hyperswitch는 Rust로 만든 백엔드 서버 묶음입니다. 가맹점은 Hyperswitch의 API(POST /payments) 한 곳에만 코드를 붙이면 되고, 그 뒤에서 Hyperswitch가 100개 넘는 결제사와의 대화를 대신 처리합니다. 필요하면 부품처럼 골라서 쓸 수 있습니다 — 라우팅(어디로 보낼지), Revenue Recovery(실패 결제 되살리기), Vault(카드 금고), Cost Observability(비용 관측), Reconciliation(대사) 중에서 원하는 것만 기존 결제 스택 위에 얹는 식입니다.
이 문서가 파고드는 건 사용법보다 그 안의 설계입니다 — "147개 결제사를 어떻게 단 하나의 인터페이스로 다루나(커넥터 트레이트)", "결제 지연을 줄이려 왜 DB가 아니라 Redis에 먼저 쓰나(드레이너)", "어디로 보낼지를 어떻게 데이터로 표현하나(Euclid·제약 그래프)". Rust로 만든 대형 백엔드, 트레이트 기반 플러그인, 결제 도메인을 처음 공부하는 사람에게 드물게 실전적인 교본입니다.
"닫힌 결제사 한 곳에 갇히는 대신, 오픈소스로 결제사 100곳을 내 마음대로 — 그게 셀링 포인트다."
대부분의 상점은 Stripe·Braintree 같은 결제사 한 곳에 직접 연동합니다. 빠르고 쉽지만, 약점이 분명합니다 — 그 결제사가 장애 나면 결제가 통째로 멈추고, 수수료를 올려도 끌려가야 하며, 다른 나라·다른 카드의 승인율이 낮아도 대안이 없습니다. 결제사를 바꾸려면 코드를 처음부터 다시 짜야 하고, 저장해 둔 카드 정보(토큰)도 옮길 수 없어 사실상 갇히게(lock-in) 됩니다.
결제사 한 곳에 올인하면 그 회사의 장애·수수료·승인율·정책 변화가 그대로 내 매출 리스크가 됩니다. 특히 글로벌 서비스라면 나라·카드사마다 잘 통하는 결제사가 다른데, 한 곳만 쓰면 승인 실패(매출 누수)를 그냥 떠안게 됩니다. 게다가 저장된 카드 정보가 그 결제사에 묶여 있어 갈아타기도 어렵습니다.
Hyperswitch의 공식은 "가맹점은 API 한 곳만 보고, 그 뒤는 우리가 100곳으로 분배한다"입니다. 결제마다 예측 승인율이 가장 높은 결제사로 보내고(스마트 라우팅), 실패하면 다른 곳으로 자동 재시도하며, 카드 정보는 가져올 수 있는(BYO) 금고에 보관해 결제사를 갈아타도 재토큰화가 필요 없습니다. 전부 오픈소스(Apache-2.0)라 코드·데이터·라우팅 로직을 내가 소유합니다.
| 관점 | Hyperswitch | 전형적 단일 결제 SaaS |
|---|---|---|
| 구조 | 여러 결제사 오케스트레이션 | 결제사 한 곳(닫힌 SaaS) |
| 코드/데이터 소유 | 오픈소스, 셀프호스팅 가능 | 벤더 종속, 데이터는 벤더 보관 |
| 라우팅 | 규칙·승인율 기반 스마트 라우팅 | 해당 결제사로 고정 |
| 금고(Vault) | PCI 금고 + 외부 금고 연결(VGS·TokenEx) | 그 결제사 토큰에 종속 |
| 채택 방식 | 필요한 모듈만 골라 얹기(composable) | 대체로 한 묶음 |
| 안전성 | Rust + unsafe 전면 금지 | 비공개(언어·구현 불투명) |
단일 결제사 = 한 항공사만 타는 것. 편하지만 그 항공사가 결항하면 못 가고, 노선·요금도 그 회사에 끌려갑니다.
Hyperswitch = 모든 항공사를 한 번에 비교·예약하는 여행 플랫폼. 목적지(결제)마다 가장 잘 가는(승인되는) 항공사를 골라 주고, 결항(실패) 시 자동으로 다른 편을 잡아 줍니다. 게다가 이 플랫폼을 내가 직접 소유합니다.
Hyperswitch는 인도의 결제 인프라 회사 Juspay가 만들었고, 그들은 "400곳 넘는 대기업"의 결제를 처리한다고 소개합니다 — 즉 실전에서 굴린 노하우가 바탕입니다. 코드 전체가 Rust이고 워크스페이스 차원에서 unsafe_code = "forbid"(메모리 위험 코드 전면 금지)를 못 박아, 돈을 다루는 시스템답게 안전성을 최우선으로 둡니다. 여기에 필요한 기능만 부품처럼 골라 얹는 모듈성이 더해져, 기존 결제 스택을 통째로 갈아엎지 않고도 라우팅·금고·재시도만 추가할 수 있다는 점이 빠른 채택을 이끌었습니다.
솔직히 짚자면, Hyperswitch는 결제라는 본질적으로 무거운 도메인을 다룹니다 — 직접 운영하려면 PostgreSQL·Redis·여러 서비스 바이너리를 띄우고 PCI 준수까지 신경 써야 합니다. 가볍게 시작하려는 1인 상점보다는, 결제사 종속을 벗고 인프라를 소유하려는 팀·기업에 맞는 도구로 읽는 게 정확합니다.
"코어는 전부 Rust(actix-web + Tokio). 저장은 PostgreSQL(Diesel) + Redis, 분석은 ClickHouse, 대시보드는 ReScript→React."
Hyperswitch는 백엔드 중심 프로젝트입니다. 무게중심이 crates/router에 쏠려 있고, 화면(대시보드·SDK)은 별도 저장소로 분리돼 있습니다. 의존성 목록만 봐도 "고성능·안전·관측 가능"이라는 설계 의도가 읽힙니다.
| 기술 | 역할 | 한 줄 설명 |
|---|---|---|
| Rust 2021 (MSRV 1.85) | 전체 언어 | 워크스페이스로 38개 크레이트를 묶는다. unsafe 금지·unwrap/panic 거부 등 엄격한 린트로 안전성 확보. |
| actix-web 4.11 | 웹 프레임워크 | HTTP API 서버. 결제 링크 페이지는 maud로 서버사이드 렌더링. |
| Tokio 1.48 | 비동기 런타임 | 수많은 동시 결제 요청을 적은 스레드로 처리하는 async 엔진. |
| Diesel 2.2 (PostgreSQL) | DB ORM | 타입 안전한 SQL. bb8/async-bb8-diesel로 커넥션 풀링, 마스터/리플리카 분리(읽기 분산). |
| Redis 7 (redis_interface) | 캐시 · 큐 · 스트림 | 자체 래퍼 크레이트. 백엔드를 redis-rs 또는 fred 중 골라 쓸 수 있다. |
| rdkafka 0.36 (Kafka) | 이벤트 스트림 | 결제·커넥터 이벤트 로그를 Kafka로 흘려보낸다(KafkaMessage 트레이트). |
| reqwest 0.11 | 커넥터 HTTP | 결제사로 나가는 HTTPS 호출(rustls-tls). serde로 직렬화, rust_decimal로 금액 계산. |
| 기술 | 역할 | 한 줄 설명 |
|---|---|---|
| ClickHouse + OpenSearch 2.3 | 분석 | 결제 분석용 컬럼 저장소(ClickHouse) + 검색(OpenSearch). 쿼리는 sqlx로. |
| gRPC(proto/) | 동적 라우팅 | success_rate·elimination_rate·contract_routing 등 ML 기반 라우팅 결정을 별도 gRPC 서비스로 분리. |
| Euclid (→ WASM) | 정적 라우팅 DSL | "이런 결제는 이 결제사로" 규칙을 표현하는 작은 언어. WASM으로 컴파일돼 대시보드에서 시각적으로 편집된다. |
| external_services | 비밀 · 클라우드 | AWS KMS(키 관리)·S3·SES(메일), HashiCorp Vault(비밀) 연동. |
| hyperswitch_masking | PII 마스킹 | 로그·디버그 출력에서 카드번호 같은 민감정보를 자동으로 가리는 래퍼 타입. |
| 구성 | 언어 | 역할 |
|---|---|---|
| Control Center | ReScript→React | 가맹점 대시보드. 커넥터 추가·라우팅 규칙·분석·API 키 관리 + 시각적 라우팅 빌더. |
| Web SDK | ReScript→React | 웹 결제창(체크아웃) 컴포넌트. react-hyper-js 래퍼 제공. |
| Mobile SDK | Kotlin·Swift·Dart·TS | Android·iOS·Flutter·React Native 결제 SDK. |
| 항목 | 내용 |
|---|---|
| 컨테이너 | 멀티스테이지 Dockerfile(debian:trixie) + docker-compose로 로컬 풀스택. |
| 오케스트레이션 | Helm(쿠버네티스) + Terraform으로 AWS/GCP/Azure 풀스택 배포. |
| 재현 환경 | Nix flake로 nix develop 재현 가능한 개발 셸(default/dev/qa). |
| 관측 | OpenTelemetry(OTLP) → Prometheus(지표)·Loki(로그)·Tempo(추적)·Grafana(대시보드)까지 compose에 통째로 연결. |
"세 개의 일꾼 — 요청을 처리하는 router, 지연 작업을 도는 scheduler, Redis를 DB로 흘려보내는 drainer."
핵심은 세 개의 독립 일꾼입니다. router가 API 요청과 결제 로직을 처리하고, scheduler가 "30일 뒤 저장카드 삭제" 같은 지연·재시도 작업을 돌리며, drainer가 Redis에 먼저 쓴 데이터를 DB로 안전하게 옮깁니다. 아래에서 설계 포인트를 하나씩 봅니다.
Hyperswitch의 심장입니다. 모든 결제사는 두 개의 트레이트(trait, Rust의 인터페이스)를 구현합니다 — ConnectorCommon(공통: id·기본 URL·인증 헤더·에러 파싱)과 ConnectorIntegration<Flow, Req, Resp>(흐름별 통합). 후자는 router가 모든 커넥터에 똑같이 시키는 요청 생애주기를 정의합니다.
각 커넥터는 BoxedConnectorIntegration(트레이트 객체)으로 다뤄져, router는 147개 결제사를 단 하나의 다형적(polymorphic) 인터페이스로 호출합니다. 새 API 세대를 위한 ConnectorIntegrationV2도 따로 있습니다.
커넥터 트레이트 = 만국 전원 어댑터. 나라마다 콘센트 모양(결제사 API)이 다르지만, 어댑터 규격(트레이트)을 정해 두면 노트북(router)은 어느 나라에서든 똑같은 플러그로 꽂아 씁니다. 새 나라가 생겨도 그 나라용 어댑터(커넥터)만 하나 만들면 끝 — 노트북 본체는 손대지 않습니다.
결제 로직은 두 축으로 나뉩니다. operations/는 무엇을 하는지 — 생성(create)·확정(confirm)·매입(capture)·조회(sync)·취소(cancel)·정기결제 설정(mandate). flows/는 그 동작을 커넥터 호출로 어떻게 빚는지 — authorize_flow·capture_flow·psync_flow 등입니다. 이 둘을 제네릭(payments_core<F, Req, Op>)으로 묶어, 서로 다른 결제 동작을 같은 골격으로 처리합니다 — 일종의 상태기계식 오케스트레이션입니다.
가장 독특한 패턴입니다. 결제는 응답이 빨라야 하는데, DB(PostgreSQL) 쓰기는 상대적으로 느립니다. 그래서 Hyperswitch는 뜨거운 쓰기를 일단 Redis에 먼저 던지고(빠름), drainer라는 별도 일꾼이 Redis 스트림(DRAINER_STREAM)을 읽어 → PostgreSQL에 반영 → 스트림에서 제거하는 식으로 나중에 따로 영구 저장합니다. 이른바 '쓰기 뒤로 미루기(write-behind)' 패턴입니다.
"어디로 보낼지"는 코드에 박는 대신 설정 가능한 데이터로 표현됩니다. Euclid(정적 라우팅 DSL)는 규칙을 표현하고 WASM으로 컴파일돼 대시보드에서 시각적으로 편집됩니다. 제약 그래프(KGraph)는 "이 결제수단↔이 결제사 조합이 유효한가"를 그래프로 모델링합니다. 그리고 동적 라우팅(승인율·제거율·계약 기반)은 별도 gRPC 서비스가 ML로 판단합니다. 덕분에 라우팅 정책을 코드 배포 없이 바꿀 수 있습니다.
① 클라이언트가 POST /payments → ② operations 계층이 검증·멱등성(idempotency)·잠금 처리 → ③ Euclid+제약그래프+gRPC가 보낼 커넥터 결정 → ④ call_connector_service()가 그 커넥터의 트레이트 생애주기(get_url→build_request)를 실행 → ⑤ reqwest로 결제사에 HTTPS 호출, 응답을 도메인 모델로 변환 → ⑥ 결과를 Redis(KV)에 빠르게 기록하고 클라이언트에 응답 → ⑦ drainer가 비동기로 Redis 스트림을 PostgreSQL에 반영, 이후 이벤트는 웹훅으로, 재시도/조회는 scheduler가 담당.
"Cargo 워크스페이스 하나에 38개 크레이트. 무게는 router(코어)와 hyperswitch_connectors(147개사)에 쏠려 있다."
| 크레이트 | 역할 |
|---|---|
| router | 코어 앱. actix-web API + 결제 코어(operations·flows) + 라우팅 + 웹훅. 바이너리: router, scheduler. |
| hyperswitch_ connectors | 147개 결제사 통합 코드(플러그인 본체). |
| hyperswitch_ interfaces | 커넥터 트레이트 계약: ConnectorCommon·ConnectorIntegration(+V2)·웹훅·인증. |
| diesel_models | DB 행 타입 + 생성된 schema.rs(payment_intent·attempt·refund·dispute·mandate…). kv 하위모듈 포함. |
| storage_impl | 저장 백엔드. Redis-KV-후-드레인 쓰기 경로(kv_router_store.rs·reverse_lookup.rs). |
| drainer | Redis 스트림을 읽어 PostgreSQL에 반영(write-behind). |
| scheduler | producer/consumer 프로세스 트래커(지연·비동기 작업). |
| euclid / euclid_wasm | 정적 라우팅 DSL + 그 WASM 바인딩(대시보드 라우팅 빌더). |
| hyperswitch_ constraint_graph | 유효한 결제수단↔커넥터 조합을 모델링하는 제약 그래프. |
| analytics | ClickHouse + OpenSearch + sqlx 리포팅/검색. |
| api_models / hyperswitch_ domain_models | API 요청·응답 DTO / 비즈니스 도메인 모델. |
| external_services | AWS KMS·S3·SES, HashiCorp Vault 연동. |
| cards / payment_methods | 카드번호 마스킹·검증(Luhn·BIN) / 결제수단 저장·금고 로직. |
"이 레포는 'Rust로 만든 진짜 대형 백엔드'를 통째로 들여다볼 수 있는 드문 교본이다."
장난감이 아니라 실제 운영되는 actix-web + Tokio 서비스를, 어떻게 도메인/API/DB 모델 크레이트로 나누는지 볼 수 있습니다. 특히 unwrap_used·panic·indexing_slicing을 전부 거부하고 unsafe를 금지한 엄격한 린트 설정은 "돈 다루는 코드"의 안전 문화를 보여 줍니다.
볼 곳: 루트 Cargo.toml의 [workspace.lints], 그리고 크레이트 간 의존 방향.
Rust의 트레이트·제네릭·트레이트 객체를 써서 147개의 교체 가능한 통합을 단 하나의 인터페이스 뒤에 두는 설계입니다. 새 결제사가 늘어도 코어는 그대로 — 확장에는 열려 있고 수정에는 닫힌(OCP) 구조의 교과서적 사례입니다.
볼 곳: hyperswitch_interfaces/src/api.rs와 아무 커넥터(예: connectors/stripe) 하나를 나란히.
drainer는 "쓰기 지연과 내구성을 떼어 놓는" write-behind 패턴의 깔끔한 격리 예제입니다. 또 결제는 같은 요청이 두 번 와도 두 번 청구되면 안 되므로, 멱등성 키와 API 잠금(core/api_locking.rs)으로 안전하게 재시도를 다룹니다.
# 멱등성: 같은 키로 두 번 와도 결제는 한 번만
POST /payments
Idempotency-Key: order-2026-0624-abc
{ "amount": 4900, "currency": "USD", ... }
카드 데이터를 별도 금고(locker)로 분리하고 마스킹 타입으로 로그 유출을 막는 PCI 준수 설계, 그리고 비즈니스 규칙(라우팅)을 DSL + 그래프 + ML로 설정 가능하게 만든 방식은 '규칙을 코드에 박지 않는' 좋은 본보기입니다.
볼 곳: crates/euclid(DSL), hyperswitch_constraint_graph, masking 크레이트.
"학습용이라면 Docker가 정답. 소스 빌드는 무겁다(Rust LTO + 결제 흐름의 깊은 스택)."
| 항목 | 요구사항 |
|---|---|
| 가장 쉬운 시작 | scripts/setup.sh 실행 → Docker/Podman 감지 후 Standard/Full/Minimal 프로필 선택. |
| 핵심 포트 | router 8080 · Control Center 9000 · Web SDK 9050 · PostgreSQL 5432 · Redis 6379 · Grafana 3000. |
| 프로필 | Minimal(앱 서버만) · Standard(+대시보드) · Full(+모니터링·스케줄러). |
| 소스 빌드 의존성 | Rust stable ≥ 1.85, diesel_cli, libpq-dev·libssl-dev·pkg-config·protobuf-compiler, PostgreSQL + Redis. |
| 빌드 명령 | cargo build --release --no-default-features --features release --features v1 |
| 주의 | 릴리스 빌드는 LTO + codegen-units=1이라 수 GB RAM·긴 빌드 시간 필요. RUST_MIN_STACK도 ~6MB로 키운다 → 초보자는 Docker 권장. |
"읽기만 하면 안 남는다 — Docker로 띄우고, 결제 한 건을 코드로 따라가 보자."
scripts/setup.sh(Standard)로 띄운 뒤 Control Center(:9000)에서 내장 dummy connector를 추가하고, API(:8080)로 테스트 결제를 보냅니다. 그 결제가 Grafana(:3000)에 어떻게 찍히는지 확인하세요.
routes/payments.rs → core/payments/operations/payment_confirm.rs → core/payments.rs의 call_connector_service → 한 커넥터의 build_request까지, 호출 사슬을 손으로 적어 보세요. 4번 섹션의 '결제 한 건의 여정'과 맞춰 보면 이해가 단단해집니다.
커넥터를 둘 연결하고 Control Center에서 볼륨 분배 또는 규칙 기반 라우팅을 만들어, 요청이 어떻게 나뉘는지 관찰합니다. 그다음 crates/euclid를 열어 그 규칙이 어떤 데이터 구조로 표현되는지 확인하세요.
결제를 한 건 만든 뒤 RedisInsight(:8001)로 DRAINER_STREAM을 보고, drainer가 PostgreSQL로 반영하기 전/후를 비교하세요. drainer/src/handler.rs로 그 경로를 설명해 보면 패턴이 손에 잡힙니다.
connector-template/과 add_connector.md를 보고, 가짜 결제사 하나를 만들어 ConnectorCommon + ConnectorIntegration의 Authorize 흐름을 목(mock) HTTP 엔드포인트에 대고 구현해 보세요. 트레이트 플러그인 설계가 온몸으로 이해됩니다.
새 Diesel 마이그레이션을 작성하고 schema.rs를 재생성한 뒤 diesel_models + api_models를 고쳐 엔드포인트로 노출해 보세요. DB에서 API까지의 전체 경로를 한 번에 배웁니다.
"Rust 기초 → 웹/DB → 결제 도메인 → 분산/관측 순으로 4주."
| 주차 | 주제 | 학습 자료 · 키워드 |
|---|---|---|
| 1주차 | Rust + 트레이트 | The Rust Book(소유권·트레이트·제네릭), 트레이트 객체 vs 제네릭. 목표: ConnectorIntegration을 읽을 기초. |
| 2주차 | actix-web + Diesel | actix-web 핸들러·미들웨어, Diesel 마이그레이션·schema, bb8 커넥션 풀, Tokio async 기본. |
| 3주차 | 결제 도메인 | PCI DSS 개요, 토큰화·금고, 멱등성, 3DS·웹훅, 스마트 라우팅. 자료: Hyperswitch 공식 docs + add_connector.md. |
| 4주차 | 분산 · 관측 | Redis Streams(write-behind), Kafka 이벤트, gRPC, OpenTelemetry→Prometheus/Grafana. 목표: drainer·scheduler·분석 흐름 파악. |
"결제 도메인 + Rust 인프라 용어를 한자리에."
| 용어 | 의미 |
|---|---|
| 결제 오케스트레이션 | 여러 결제사를 한 API로 묶어 라우팅·재시도를 자동 지휘하는 계층(= Hyperswitch). |
| PSP / 커넥터 | 결제 서비스 제공자(Stripe·Adyen…). Hyperswitch에선 결제사 하나를 통합하는 플러그인을 '커넥터'라 부름. |
| PCI DSS | 카드 데이터를 안전하게 다루는 국제 보안 규격. 별도 금고(locker)를 두는 이유. |
| Vault / 금고 | 카드·토큰을 보관하는 PCI 준수 저장소. 외부 금고(VGS·TokenEx)도 연결 가능(BYO-vault). |
| 토큰화 | 진짜 카드번호(PAN)를 무의미한 토큰으로 바꿔, 앱이 원본 카드데이터를 저장하지 않게 하는 것. |
| 멱등성 키 | 클라이언트가 보내는 고유 키. 재시도가 와도 결제가 중복되지 않게 한다. |
| 3DS | 3-D Secure. 은행 측 본인인증(OTP 등) 단계. |
| 웹훅(webhook) | 결제사가 상태 변화(매입·환불·분쟁)를 비동기 HTTP로 알려 주는 콜백. |
| 스마트 라우팅 | 결제마다 예측 승인율(또는 비용·지연)이 가장 좋은 결제사로 보내는 것. |
| 드레이너(drainer) | Redis 스트림을 읽어 PostgreSQL로 흘려보내는 write-behind 일꾼. |
| Euclid / KGraph | Euclid=라우팅 규칙 DSL(→WASM), KGraph=유효한 결제수단↔커넥터 조합 그래프. |
| Revenue Recovery | 실패·만료된 결제를 똑똑히 재시도해 비자발적 이탈(매출 누수)을 줄이는 것. |
| Control Center | 커넥터·라우팅·분석을 다루는 가맹점 대시보드(ReScript→React). |
"공식 문서가 충실하다 — 커넥터 추가 가이드부터 시작하면 좋다."