System Architecture
Overview
Peak Gateway is an eight-service Kotlin/Spring Boot payment platform on Google Cloud Run. Public clients enter through two global load-balancer hosts; internal payment and onboarding services are reachable only through authenticated service-to-service calls.
The same unsuffixed resource names exist in both environments:
| Environment | GCP project | Region |
|---|---|---|
| Staging | peakgateway-staging | us-east1 |
| Production | pinpoint-gateway | us-east1 |
Environment is determined by the GCP project, not a -staging service-name
suffix.
Request and Service Topology
Portal users (Firebase) API clients (OAuth2) Checkout shoppers
| | |
+-------------------------------+----------------------------+
|
Global HTTPS load balancer
+-----------------+----------------+
| |
api.peakgateway.co pay.peakgateway.co
| |
+--------------+-------------+ +--------+---------+
| | | | |
auth management status online-txn card-present
| | | |
| +-------------+-------------+------------------+
| IAM
| +-----------+-----------+
| | |
| processing merchant-onboarding
| | |
| payment processors TransIT / XTMS
|
mcp --IAM--> processing / management
All containers listen on 8080. Local Docker mappings use 8181 through
8188; see Local Development.
Security Boundaries
Publicly routed services
authmanagementonline-txncard-presentstatusmcp
Public traffic reaches these services only through the global load balancer and Cloud Armor. OAuth/JWT or Firebase authorization remains an application-level requirement except for explicitly public health and shopper-session routes.
Internal-only services
processingmerchant-onboarding
Neither service is present in Terraform's externally_routed_services map.
Cloud Run IAM validates caller identity before the request reaches the
application. Callers use InternalServiceClient, which obtains a Google IAM ID
token from the metadata server. Unknown or unauthenticated callers fail closed.
See Internal-Only Services for the source and live verification checklist.
Service Catalog
| Service | Responsibility | Durable store |
|---|---|---|
auth | OAuth2 client credentials, API-key exchange, JWK publication, device identity | Spanner |
processing | Payment orchestration, subscriptions, settlement, customers, routing, processor adapters | Spanner |
management | Portal BFF, organizations, locations, users, agents, audit, configuration | Spanner |
online-txn | Checkout sessions, hosted payments, webhooks, API keys, storefront APIs | Spanner |
card-present | In-person acceptance, terminal sync/refund, device orchestration | Spanner |
merchant-onboarding | Credential-profile ownership, processor activation, XTMS binding | Spanner |
status | Aggregate health | None |
mcp | Scoped MCP read, dunning, and refund operations | None |
management is both a domain owner and a BFF. It authorizes portal users,
clamps every request to the caller's organization/location/agent scope, then
forwards non-owned operations to the owning internal service.
Public Routing
| Host and path | Service | Rewrite |
|---|---|---|
api.peakgateway.co/.well-known/* | auth | none |
api.peakgateway.co/oauth2/* | auth | none |
api.peakgateway.co/auth/* | auth | remove /auth |
api.peakgateway.co/management/* | management | remove /management |
api.peakgateway.co/status/* | status | remove /status |
api.peakgateway.co/mcp and /mcp/* | mcp | exact transport path retained; nested prefix removed |
pay.peakgateway.co/online-txn/* | online-txn | remove /online-txn |
pay.peakgateway.co/card-present/* | card-present | remove /card-present |
The API host defaults to management. The payment host defaults to online-txn.
Staging uses staging-api.peakgateway.co and
staging-pay.peakgateway.co with the same path rules.
Payment Processor Architecture
The processing service is the canonical payment engine. Requests flow through
PaymentProcessorGateway and the processor router, which selects an adapter
from the location's typed processor configuration. Current processor clients
include:
- TSYS TransIT (
libs/transit-client) - Fiserv CardPointe Gateway (
libs/cardpointe-client) - Sierra (
libs/sierra-client) - Fiserv (
libs/fiserv-client) - Elavon (
libs/elavon-client)
Card-present CardPointe terminal operations use the Bolt client in
libs/bolt-client. Vendor-specific request shaping remains behind adapters;
controllers and caller services use the shared payment contract.
Unknown processor selection is an explicit error. The system never silently falls back to a different processor for a monetary operation.
Settlement uses explicit processor rails rather than a public batch-control API:
(TSYS, TRANSIT) has an internal close command plus report verification,
CardPointe is report-only, and every other processor/platform pair fails closed.
Public settlement and reconciliation surfaces expose read-only historical
evidence with active statuses SETTLED and REVIEW_REQUIRED; close, retry,
force-close, and adjudication are not public operations.
Service-to-Service Calls
| Caller | Callee | Purpose |
|---|---|---|
| management | processing | Transactions, settlements, subscriptions, customers, gift cards, tax, reports |
| management | merchant-onboarding | Credential profiles, activation, provisioning |
| management | online-txn | Checkout and hosted-payment administration, webhook operations |
| management | auth | OAuth clients, API keys, device identity |
| management | card-present | Terminal and device administration |
| online-txn | processing | Card-not-present payment execution |
| card-present | processing | Shared payment execution for in-person requests |
| processing | merchant-onboarding | API-safe credential-profile metadata for routing |
| mcp | processing / management | Scoped reads and approved mutations |
| status | all services | Health aggregation |
Cloud Run IAM is the authorization boundary for every internal call. Processing and merchant-onboarding do not expose public load-balancer routes.
Data Ownership and Durable Transitions
Cloud Spanner uses the PostgreSQL dialect through SQLDelight. A table has one owning service unless it is explicitly designated as a shared append-only or multi-writer contract.
Payment-critical transitions follow these invariants:
- Validate tenant, authorization, amount, currency, and state before side effects.
- Persist idempotent intent before processor or other external I/O.
- Persist the observed outcome before returning success.
- Treat an unknown processor outcome as in-doubt reconciliation work, never as a blind retry.
- Preserve audit and processor evidence needed to explain the transition.
Schema changes are bounded, immutable, forward-only revisions under
db/schema-revisions/. See Schema Migrations.
Infrastructure
| Layer | Technology |
|---|---|
| Services | Kotlin 2.2, Java 25, Spring Boot 4.0.5, Spring Security 7.0 |
| Frontends | React 19, Vite 7, Tailwind 4, Docusaurus |
| Database | Cloud Spanner PostgreSQL dialect, SQLDelight 2.2.1 |
| Messaging | Cloud Pub/Sub |
| Identity | Spring Authorization Server, Firebase Auth, SAML SSO, Cloud Run IAM |
| Edge | Global external Application Load Balancer, Certificate Manager, Cloud Armor |
| Build | Bazel with bzlmod and BuildBuddy remote execution/cache |
| Infrastructure | Terraform managed through Terrakube |
| Deployment | GitHub Actions to Cloud Run and Cloudflare Pages |
Repository Map
gateway/
├── services/ # Eight Spring Boot services
│ ├── auth/
│ ├── processing/
│ ├── management/
│ ├── online-txn/
│ ├── card-present/
│ ├── merchant-onboarding/
│ ├── status/
│ └── mcp/
├── libs/ # Shared security, schema, processor, messaging, and resilience libraries
├── websites/ # Admin, merchant, checkout, marketing, public docs, support docs
├── sdks/
│ ├── kotlin/
│ └── typescript/
├── infra/tf/gcp/ # Terraform root and modules
├── db/schema-revisions/ # Immutable forward-only schema revisions
├── artifacts/openapi/ # Canonical SDK-enabled OpenAPI artifacts
├── tools/
└── scripts/
The implementation and generated contracts are authoritative. See Repository Source of Truth before adding or relocating architecture documentation.