System Architecture
1. Overview
The Pinpoint Payment Gateway is a cloud-native, microservices-based payment processing platform built on GCP, integrating with TSYS TransIT via the Multipass API. The system follows a monorepo structure built with Bazel, deployed via Terraform, and automated through GitHub Actions CI/CD.
Rename note: the current gateway data model is Organization -> Location. This architecture page still contains some legacy
merchantnaming in endpoint examples, claims, and older service descriptions where the shipped wire/API alias remains/merchantsormerchantId; in those cases, treat the value as the concrete location record.
2. High-Level Architecture
The gateway is deployed across two GCP projects for PCI-DSS compliance. The CDE (Cardholder Data Environment) project contains all services that handle TransIT credentials or cardholder data. External clients (PeakPOS, e-commerce backends, future integrators) authenticate via OAuth and never access the CDE directly.
┌─────────────────────────────────────────────────────────────────────────────┐
│ CLIENTS (each in their own GCP project, OUT of CDE) │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────────────┐ │
│ │ PeakPOS │ │ E-commerce │ │ Future Clients │ │
│ │ Management API │ │ Backends │ │ (other POS, SaaS) │ │
│ │ │ │ │ │ │ │
│ │ Scopes: │ │ Scopes: │ │ Scopes: │ │
│ │ merchant:activate│ │ txn:process │ │ (per agreement) │ │
│ │ provision:request│ │ session:create │ │ │ │
│ └──────┬───────────┘ └──────┬───────────┘ └───────────┬──────────────┘ │
└─────────┼──────────────────────┼───────────────────────────┼────────────────┘
│ │ │
│ OAuth tokens (scoped per client) │
│ │ │
┌─────────┼──────────────────────┼───────────────────────────┼────────────────┐
│ │ Cloud Armor / LB (WAF + DDoS + TLS) │ │
│ │ API Gateway (Cloud Endpoints) │ │
│ ▼ ▼ ▼ │
│ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ FRONTENDS │ │
│ │ │ │
│ │ ┌─────────────────────┐ ┌─────────────────────┐ │ │
│ │ │ Management Portal │ │ Online Payment │ │ │
│ │ │ (React 19 / Vite 7) │ │ Website │ │ │
│ │ │ Firebase Auth login │ │ Hosted checkout UI │ │ │
│ │ └─────────┬───────────┘ └──────────┬──────────┘ │ │
│ │ │ │ │ │
│ └────────────┼────────────────────────────────┼───────────────────────┘ │
│ │ │ │
│ │ REST/JSON │ REST/JSON │
│ ▼ ▼ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ BACKEND SERVICES (all Cloud Run) │ │
│ │ │ │
│ │ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ Auth Service │ │ Status │ │ │
│ │ │ :8081 │ │ Service :8086│ │ │
│ │ │ OAuth2 issuer│ │ Health agg. │ │ │
│ │ │ JWK endpoint │ │ │ │ │
│ │ └──────┬───────┘ └──────────────┘ │ │
│ │ │ JWK (/.well-known/jwks.json) │ │
│ │ │ fetched by ALL services for token validation │ │
│ │ ▼ │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────────┐ │ │
│ │ │ Management │ │ Online Txn │ │ Merchant Onboarding │ │ │
│ │ │ Service │ │ Service │ │ Service │ │ │
│ │ │ :8083 │ │ :8084 │ │ :8085 │ │ │
│ │ └──────┬───────┘ └──────┬───────┘ └──┬───────────┬───────────┘ │ │
│ │ │ │ │ │ │ │
│ │ │ proxy/delegate │ delegate │ │ │ │
│ │ └────────┐ ┌─────┘ │ │ │ │
│ │ ▼ ▼ │ │ │ │
│ │ ┌──────────────┐ │ │ │ │
│ │ │ Processing │◄──────────────┘ │ │ │
│ │ │ Service │ internal creds API │ │ │
│ │ │ :8082 │ │ │ │
│ │ └──────┬───────┘ │ │ │
│ │ │ │ │ │
│ └──────────────────┼───────────────────────────────────┼───────────────┘ │
│ │ │ │
│ ┌──────────────────┼───────────────────────────────────┼───────────────┐ │
│ │ CDE INFRASTRUCTURE (peakpos-cde, PCI scoped) │ │ │
│ │ │ │ │ │
│ │ ┌────────────┐ │ ┌───────────┐ ┌────────────┐ │ │ │
│ │ │ Spanner │◄─┘ │ Pub/Sub │ │ Cloud │ │ │ │
│ │ │ (CDE DB) │ │ (Events) │ │ Scheduler │ │ │ │
│ │ └────────────┘ └───────────┘ └────────────┘ │ │ │
│ │ │ │ │
│ │ ┌────────────┐ ┌────────────┐ ┌───────────┐ │ │ │
│ │ │ Secret │ │ Cloud KMS │ │ Cloud │ │ │ │
│ │ │ Manager │ │ (cred enc) │ │ Storage │ │ │ │
│ │ └────────────┘ └────────────┘ └───────────┘ │ │ │
│ └──────────────────────────────────────────────────────┼───────────────┘ │
│ │ │
│ GCP Project: peakpos-cde (PCI SCOPED) │ │
└─────────────────────────────────────────────────────────┼────────────────────┘
│
┌────────────────┼────────────────┐
▼ ▼ │
┌──────────────────┐ ┌──────────────┐ │
│ TSYS TransIT │ │ NexGO XTMS │ │
│ Multipass API │ │ (device TMS) │ │
└──────────────────┘ └──────┬───────┘ │
│ │
▼ │
┌──────────────┐ │
│ NexGO Device │ │
│ SmartConnect │ │
│ (holds creds │ │
│ internally) │ │
└──────┬───────┘ │
│ │
┌────────┘ │
▼ │
┌──────────────────┐ │
│ Kotlin SDK │ │
│ (on device/server)│──────────────┘
└──────────────────┘
calls Auth, Online Txn,
Processing services
Why Two GCP Projects?
| Concerns | Single project | Two projects (chosen) |
|---|---|---|
| Decision | Option considered | Chosen architecture - separate project for CDE (PCI scoped) |
| PCI audit scope | Entire project in scope | Only CDE project in scope |
| IAM isolation | Shared IAM policies | Hard boundary - separate IAM |
| Blast radius | Compromise affects everything | CDE isolated from client infra |
| Cost | Shared resources | ~$65/mo extra for CDE Spanner |
| Complexity | Simpler | Cross-project IAM + HTTPS (trivial with Cloud Run) |
3. Technology Stack
| Layer | Technology | Justification |
|---|---|---|
| Language | Kotlin (services), TypeScript (frontends) | Kotlin for all backend services; TS for modern web UIs |
| Framework | Spring Boot 4.0.2 | Industry standard for payment processing, excellent observability |
| Build | Bazel | Monorepo support, hermetic builds, caching |
| Database | Cloud Spanner (PostgreSQL dialect) | ACID compliance, horizontal scaling, matches PeakPOS monorepo |
| Messaging | Cloud Pub/Sub | Async event processing, webhook delivery |
| Auth (Portal) | Firebase Auth + SAML SSO | Managed auth with enterprise SSO support |
| Auth (API) | OAuth2 + JWT (Spring Authorization Server) | Standard API authentication via Auth Service |
| Secrets | GCP Secret Manager | API keys, TransIT credentials, signing keys |
| Compute | Cloud Run | Auto-scaling, pay-per-use, no cluster management |
| IaC | Terraform | Declarative infrastructure, state management |
| CI/CD | GitHub Actions | Native GitHub integration, monorepo support |
| Container | Docker (Ubuntu / Debian) | Branded as "Peak Gateway" |
| Monitoring | Cloud Monitoring + Cloud Logging | Native GCP observability stack |
| CDN/WAF | Cloud Armor + Cloud Load Balancing | DDoS protection, TLS termination |
4. Monorepo Structure
gateway/
├── MODULE.bazel
├── BUILD.bazel
├── .bazelrc
├── docker-compose.dev.yml
├── .github/
│ └── workflows/
│ ├── ci.yml
│ ├── deploy-staging.yml
│ └── deploy-production.yml
├── docs/specs/ # Legacy spec sources (mirrored into support-docs)
├── websites/support-docs/docs/ # Support docs (Docusaurus)
├── infra/
│ └── terraform/
│ ├── main.tf
│ ├── variables.tf
│ ├── outputs.tf
│ ├── environments/
│ │ ├── staging/
│ │ └── production/
│ └── modules/
│ ├── cloud-run/
│ ├── spanner/
│ ├── iam/
│ ├── networking/
│ ├── pubsub/
│ ├── secrets/
│ ├── firebase/
│ ├── load-balancer/
│ ├── artifact-registry/
│ ├── monitoring/
│ ├── project/
│ ├── kms/
│ ├── security/
│ ├── storage/
│ ├── identity-platform/
│ └── apphub/
├── services/
│ ├── processing/ # Processing Microservice
│ │ ├── BUILD.bazel
│ │ ├── src/main/kotlin/
│ │ └── src/test/kotlin/
│ ├── management/ # Management Microservice
│ │ ├── BUILD.bazel
│ │ ├── src/main/kotlin/
│ │ └── src/test/kotlin/
│ ├── online-txn/ # Online Transaction Microservice
│ │ ├── BUILD.bazel
│ │ ├── src/main/kotlin/
│ │ └── src/test/kotlin/
│ ├── merchant-onboarding/ # Merchant Onboarding Microservice
│ │ ├── BUILD.bazel
│ │ ├── src/main/kotlin/
│ │ └── src/test/kotlin/
│ ├── auth/ # Gateway Auth Service
│ │ ├── BUILD.bazel
│ │ ├── src/main/kotlin/
│ │ └── src/test/kotlin/
│ └── status/ # Status Microservice
│ ├── BUILD.bazel
│ ├── src/main/kotlin/
│ └── src/test/kotlin/
├── portal/ # Management Portal (web)
│ ├── BUILD.bazel
│ ├── package.json
│ └── src/
├── website/ # Online Payment Website
│ ├── BUILD.bazel
│ ├── package.json
│ └── src/
├── sdks/
│ └── kotlin/
│ ├── gateway-sdk-core/ # KMP - JVM, Android, iOS (future)
│ │ ├── BUILD.bazel
│ │ └── src/
│ └── gateway-sdk-android/ # Android-only - ViewModel, lifecycle
│ ├── BUILD.bazel
│ └── src/
├── libs/ # Shared libraries
│ ├── common/ # Common utilities
│ │ ├── BUILD.bazel
│ │ └── src/
│ ├── transit-client/ # TransIT Multipass API client (internal)
│ │ ├── BUILD.bazel
│ │ └── src/
│ ├── xtms-client/ # XTMS Cloud Open API client (internal)
│ │ ├── BUILD.bazel
│ │ └── src/
│ ├── schema/ # SQLDelight schema & types
│ │ ├── BUILD.bazel
│ │ └── src/
│ └── security/ # Common security & auth filters
│ ├── BUILD.bazel
│ └── src/
├── scripts/ # Local dev helpers
│ ├── dev.sh
│ └── dev.env
└── tools/
├── lint/
│ └── ktlint.sh
└── scripts/
5. Service Communication
5.1 Per-Service Communication Map
Each service's exact inbound and outbound connections, derived from source code and configuration.
Auth Service (:8081)
Spring Authorization Server. Issues OAuth2 tokens and publishes JWK keys for token validation by all other services.
| Direction | Target | Protocol | Purpose |
|---|---|---|---|
| Outbound | Cloud Spanner | JDBC (PGAdapter) | OAuth client registrations, token metadata |
| Outbound | Firebase Auth | Admin SDK | Portal user identity verification |
| Inbound | All services | HTTPS GET | JWK endpoint (/auth/.well-known/jwks.json) on the public gateway or direct auth service URL for token validation |
| Inbound | All clients | HTTPS POST | OAuth2 token issuance (/auth/oauth2/token) on the public gateway |
| Inbound | Portal | HTTPS | OAuth client admin APIs |
Processing Service (:8082)
Core payment engine. All payment operations flow through this service.
| Direction | Target | Protocol | Purpose |
|---|---|---|---|
| Outbound | TSYS TransIT Multipass API | HTTPS | Sale, Auth, Capture, Void, Refund, Batch, Tip Adjust, L2/L3 Enrich (via transit-client lib) |
| Outbound | Merchant Onboarding Service | HTTPS (VPC-internal) | Fetch merchant TransIT credentials for transaction routing. Authoritative source is the credential_profiles lookup keyed off the merchant's assigned profile; the legacy GET /internal/merchants/{id}/credentials route (backed by merchant_credentials) is still wired on the server but is deprecated and slated for retirement alongside the CredentialService rewrite (ORG-14 A1b). |
| Outbound | Cloud Spanner | JDBC (PGAdapter) | transactions, settlement_batches, subscriptions, subscription_billing_events, batch_settlement_config |
| Outbound | Cloud Pub/Sub | Publish | Topics: transaction-events, subscription-events |
| Outbound | Auth Service | HTTPS GET | Fetch JWK for inbound token validation |
| Inbound | Management Service | HTTPS | Proxied transaction queries, void, refund, settlement, subscription reports |
| Inbound | Online Txn Service | HTTPS | Delegated payment processing (sale, auth, token charge) |
| Inbound | Cloud Scheduler | Pub/Sub | Hourly trigger for recurring billing engine |
| Inbound | Kotlin SDK | HTTPS | Direct transaction processing (card-present via SmartConnect) |
Management Service (:8083)
Backend for the Management Portal. Proxies transaction and subscription operations to the Processing Service.
| Direction | Target | Protocol | Purpose |
|---|---|---|---|
| Outbound | Processing Service | HTTPS | Proxy: transaction search, void, refund, settlement queries, subscription management, reports |
| Outbound | Cloud Spanner | JDBC (PGAdapter) | merchants, portal_users, user_merchant_access, audit_log |
| Outbound | Firebase Auth | Admin SDK | User creation, custom claims (RBAC), SAML provider CRUD |
| Outbound | Auth Service | HTTPS GET | Fetch JWK for inbound token validation |
| Inbound | Management Portal | HTTPS | All portal API calls (merchants, users, reports, SAML config) |
Online Txn Service (:8084)
E-commerce payment flows. Manages checkout sessions, hosted payments, webhooks, and API keys. Delegates actual payment processing to the Processing Service.
| Direction | Target | Protocol | Purpose |
|---|---|---|---|
| Outbound | Processing Service | HTTPS | Delegate: sale, auth, token charge for checkout sessions and card-on-file payments |
| Outbound | Cloud Spanner | JDBC (PGAdapter) | checkout_sessions, stored_tokens, api_keys, webhook_deliveries, subscription_webhook_config |
| Outbound | Cloud Pub/Sub | Subscribe + Publish | Subscribe: transaction-events (for webhook triggers). Publish: webhook-delivery |
| Outbound | Merchant webhook URLs | HTTPS POST | Webhook delivery with HMAC-SHA256 signature |
| Outbound | Auth Service | HTTPS GET | Fetch JWK for inbound token validation |
| Inbound | Online Payment Website | HTTPS | Checkout session UI, payment form submissions |
| Inbound | E-commerce backends | HTTPS | Checkout session creation, token payments, subscription creation, API key management |
| Inbound | Kotlin SDK | HTTPS | Server-to-server payments, token management |
Merchant Onboarding Service (:8085)
Bridges TransIT credentials to physical payment devices. Runs entirely within the CDE project. The only service that communicates with NexGO XTMS.
| Direction | Target | Protocol | Purpose |
|---|---|---|---|
| Outbound | TSYS TransIT MerchantActivation API | HTTPS | Generate deviceID + transactionKey (via transit-client lib) |
| Outbound | NexGO XTMS Cloud Open API | HTTPS | Create merchant, bind device, push credentials (via xtms-client lib) |
| Outbound | Cloud Spanner | JDBC (PGAdapter) | provisioning_jobs, credential_profiles (authoritative), merchant_credentials (legacy, retained server-side until ORG-14 A1b), provisioning_audit_log |
| Outbound | Cloud KMS | gRPC | Encrypt/decrypt TransIT credentials (AES-256-GCM) before Spanner storage |
| Outbound | Secret Manager | gRPC | Read TransIT developer credentials (developerID, developerKey) |
| Outbound | Cloud Pub/Sub | Publish | Topic: provisioning-events |
| Outbound | Auth Service | HTTPS GET | Fetch JWK for inbound token validation |
| Inbound | Processing Service | HTTPS (VPC-internal) | Returns decrypted TransIT credentials for transaction routing. New callers read from the credential_profiles-backed surface; the legacy GET /internal/merchants/{id}/credentials route remains wired server-side for backwards compatibility and is tracked for removal in ORG-14 A1b. |
| Inbound | PeakPOS Management API | HTTPS (cross-project) | POST /api/v1/provisioning/jobs - trigger provisioning workflow |
| Inbound | Management Portal (via gateway) | HTTPS | Provisioning job status, audit logs |
Status Service (:8086)
Lightweight health aggregation. No database, no Pub/Sub, no external API calls.
| Direction | Target | Protocol | Purpose |
|---|---|---|---|
| Outbound | All other services | HTTPS GET | /actuator/health - aggregate health status |
| Inbound | Cloud Monitoring | HTTPS GET | Uptime checks |
| Inbound | Any client | HTTPS GET | Aggregated gateway health |
5.2 Service-to-Service Call Graph
Summary of which services call which. Every arrow represents a synchronous HTTPS call.
┌───────────────────────────────────────────┐
│ Auth Service │
│ JWK keys fetched by ALL services below │
└──────────────────┬────────────────────────┘
│ JWK
┌───────────────┬───────────────┼───────────────┬─────────────────┐
▼ ▼ ▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌───────────┐
│ Management │ │ Online Txn │ │ Processing │ │ Device Prov │ │ Status │
│ Service │ │ Service │ │ Service │ │ Service │ │ Service │
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘ └──────────────┘ └───────────┘
│ │ ▲ │ ▲
│ proxy txn, │ delegate │ │ GET /internal │
│ settlements, │ payments │ │ /credentials │
│ subscriptions │ │ │ │
└────────────────┴───────────────┘ └────────────────┘
both call Processing Processing calls
Merchant Onboarding
5.3 Frontend-to-Backend Mapping
| Frontend | Backend Service | Auth Method | What it does |
|---|---|---|---|
| Management Portal (React) | Management Service (:8083) | Firebase Auth ID token | Merchant CRUD, user mgmt, SAML config, reports, audit log |
| Online Payment Website | Online Txn Service (:8084) | Session-based (checkout session ID) | Checkout flow, payment form submission |
| Kotlin SDK (server) | Auth Service (:8081) | OAuth2 client credentials | Obtain scoped access token |
| Kotlin SDK (server) | Online Txn Service (:8084) | OAuth2 bearer token / API key | Checkout sessions, token payments, subscriptions |
| Kotlin SDK (server) | Processing Service (:8082) | OAuth2 bearer token | Direct transaction processing (card-present) |
5.4 External Client-to-Service Mapping
| External Client | Services Called | OAuth Scopes | Purpose |
|---|---|---|---|
| PeakPOS Management API | Auth → Merchant Onboarding | merchant:activate, provision:request | Merchant onboarding, merchant onboarding |
| PeakPOS Terminal API | Auth → Processing | txn:process, batch:manage | Card-present transactions via SmartConnect |
| E-commerce backends | Auth → Online Txn | txn:process, session:create | Card-not-present transactions, hosted checkout |
| Gateway Portal | Auth → Management | admin:* | Full gateway administration |
5.5 External System Integrations
| External System | Called By | Library | Protocol | Purpose |
|---|---|---|---|---|
| TSYS TransIT Multipass API | Processing Service | libs/transit-client | HTTPS (JWT + signature) | Transaction processing: Sale, Auth, Capture, Void, Refund, Batch, L2/L3 |
| TSYS TransIT MerchantActivation | Merchant Onboarding Service | libs/transit-client | HTTPS (JWT + signature) | Generate deviceID + transactionKey for new merchants |
| NexGO XTMS Cloud Open API | Merchant Onboarding Service | libs/xtms-client | HTTPS (SHA-256 signature) | Device management, credential push, parameter configuration |
| Firebase Auth / Identity Platform | Auth Service, Management Service | Firebase Admin SDK | gRPC | User identity, SAML SSO providers, custom claims |
| Merchant webhook URLs | Online Txn Service | (direct) | HTTPS POST | Webhook delivery for payment and subscription events |
5.6 Shared Library Usage
| Library | Purpose | Used By |
|---|---|---|
libs/transit-client | TSYS TransIT Multipass API client (HTTP client, JWT auth, request signing) | Processing, Merchant Onboarding |
libs/xtms-client | NexGO XTMS Cloud Open API client (HTTP client, SHA-256 signature auth) | Merchant Onboarding |
libs/security | OAuth/JWT validation filters, @RequireScope and @RequireRole annotations, JWK integration | All services (except Status) |
libs/schema | SQLDelight-generated Spanner schema and Kotlin types | All services with database access |
libs/common | Shared utilities, error models, ApiResponse DTOs | All services |
5.7 Pub/Sub Topics and Subscribers
| Topic | Publisher | Subscribers | Events |
|---|---|---|---|
transaction-events | Processing Service | Online Txn Service (webhook delivery), Management Portal (real-time updates) | transaction.approved, transaction.declined, transaction.voided, transaction.refunded, transaction.settled |
subscription-events | Processing Service | Online Txn Service (webhook delivery) | subscription.created, subscription.billed, subscription.payment_failed, subscription.past_due, subscription.cancelled, subscription.paused, subscription.resumed |
provisioning-events | Merchant Onboarding Service | Management Portal (status updates), callback delivery, alerting (PagerDuty) | PROVISION_COMPLETED, PROVISION_FAILED, XTMS_PUSH_FAILED |
webhook-delivery | Online Txn Service | Online Txn Service (retry worker) | webhook.send - queued webhook delivery attempts |
5.8 Cloud Scheduler Jobs
| Schedule | Target | Purpose |
|---|---|---|
| Hourly | Processing Service (via Pub/Sub) | Recurring billing engine: query subscriptions where next_billing_date <= NOW(), execute TOKEN sales |
6. Data Architecture
Cloud Spanner (CDE Project - PostgreSQL dialect)
Dedicated Spanner instance in the CDE project. Keeps the PeakPOS monorepo's existing Spanner instance out of PCI scope.
| Table group | Owner Service | Purpose |
|---|---|---|
merchants, merchant_configs | Management | Merchant profiles, configurations, boarding data |
portal_users, user_merchant_access | Management | Portal users, roles, permissions |
audit_log | Management | Administrative action audit trail |
transactions, settlement_batches, batch_settlement_config | Processing | Transaction records, settlement batches, scheduling config |
subscriptions, subscription_billing_events | Processing | Recurring billing lifecycle |
checkout_sessions, stored_tokens | Online Txn | Checkout sessions, tokenized payment data |
api_keys | Online Txn | Merchant API keys for SDK/API access |
webhook_deliveries, subscription_webhook_config | Online Txn | Webhook delivery tracking and merchant webhook config |
credential_profiles | Merchant Onboarding | KMS-encrypted TransIT credentials scoped to an organization and assignable to merchants/locations (authoritative source). See libs/schema/.../CredentialProfile.sq. |
merchant_credentials | Merchant Onboarding | Legacy per-merchant KMS-encrypted TransIT credentials (BYTEA). Retained server-side for backwards compatibility; being migrated onto credential_profiles in ORG-14 A1b. |
provisioning_jobs, provisioning_audit_log | Merchant Onboarding | Provisioning workflow state and audit trail |
Key Design Decisions
- Separate Spanner instance in CDE project (~$65/mo) - keeps PeakPOS Spanner out of PCI audit scope
- PostgreSQL dialect - consistent with PeakPOS monorepo conventions
- Read replicas for reporting queries - Avoids impacting transaction processing
- Encryption at rest - Customer-managed encryption keys (CMEK) via Cloud KMS
- Point-in-time recovery - Enabled, 7-day retention minimum
- No PAN storage - We never store raw card numbers; all card data is tokenized by TransIT
- Credential encryption - TransIT credentials (deviceID, transactionKey) encrypted via Cloud KMS before storage, stored as BYTEA
7. Security Architecture
Network Security
- All services run in a VPC with private IP
- Cloud Run services communicate over VPC connector (serverless VPC access)
- No public IP on database
- Cloud Armor WAF rules: OWASP top 10, geo-blocking, rate limiting
Authentication & Authorization
| Boundary | Method |
|---|---|
| Gateway Portal users | Firebase Auth (email/password + SAML SSO) on CDE project |
| External clients (PeakPOS, e-commerce) | OAuth2 tokens issued by Gateway Auth Service (scoped) |
| API clients (Kotlin SDK) | API key + OAuth token |
| Service-to-service (within CDE) | GCP IAM + service account identity |
| Cross-project (peakpos → peakpos-cde) | Cloud Run IAM (roles/run.invoker) + OAuth token |
| TransIT API | TransIT deviceID + transactionKey (JWT + signature) |
| NexGO XTMS | SHA-256 signature with appId + key (per-request nonce + timestamp) |
Multi-Tenant Client Model
The gateway serves multiple clients. Each client gets:
- An OAuth client registration with specific scopes
- A client_id for audit trail attribution
- Rate limits per client
- Isolated merchant data (clients cannot see each other's merchants)
| Client | Scopes | Purpose |
|---|---|---|
| PeakPOS Management API | merchant:activate, provision:request | Merchant onboarding, merchant onboarding |
| PeakPOS Terminal API | txn:process, batch:manage | Card-present transactions via SmartConnect |
| E-commerce backend | txn:process, session:create | Card-not-present transactions |
| Gateway Portal | admin:* | Full gateway administration |
mTLS - Not Used (Intentional)
The gateway does not use mTLS. All authentication is handled by OAuth2 tokens and Cloud Run IAM, which fully cover every current communication path. mTLS would add certificate authority management, cert issuance/rotation, and onboarding infrastructure for zero additional security benefit over what Cloud Run IAM already provides (mutual service identity verification).
Future consideration: When the gateway supports direct device-to-gateway card-present transactions (devices calling the gateway to process payments, bypassing SmartConnect), mTLS should be revisited. Physical payment terminals on PCI-compliant firmware cannot use Play Integrity or similar Google-dependent attestation - mTLS with hardware-backed client certificates is the established industry pattern for payment terminal authentication (as used in the PeakPOS monorepo's terminal-api today).
PCI-DSS Compliance
- Two GCP projects:
peakpos(out of CDE) andpeakpos-cde(PCI scoped) - SAQ-D scope for CDE server-side components
- No raw PAN touches our infrastructure (semi-integrated + tokenization)
- All secrets in Secret Manager (not env vars, not config files)
- TransIT credentials encrypted via Cloud KMS before Spanner storage
- Audit logging on all transaction and provisioning operations
- Network segmentation via VPC + project boundary
- External clients never receive raw TransIT credentials
Secret Management
All sensitive values stored in GCP Secret Manager (CDE project):
- TransIT developer credentials (developerID, developerKey)
- Per-merchant credentials encrypted via Cloud KMS in Spanner (not Secret Manager)
- Database connection strings
- Firebase service account keys
- OAuth signing keys
- API keys for SDK authentication
8. Observability
| Concern | Tool | Details |
|---|---|---|
| Logs | Cloud Logging | Structured JSON logging, 30-day retention |
| Metrics | Cloud Monitoring | Custom dashboards, SLO tracking |
| Traces | Cloud Trace | Distributed tracing across services |
| Alerts | Cloud Monitoring Alerting | PagerDuty integration for P0/P1 |
| Uptime | Cloud Monitoring Uptime Checks | External health checks every 60s |
Key Metrics
- Transaction success rate (target: 99.9%)
- Transaction latency p50/p95/p99
- TransIT API latency and error rate
- Settlement batch success rate
- Portal/website availability
9. Disaster Recovery
| Component | RPO | RTO | Strategy |
|---|---|---|---|
| Spanner | Near-zero | < 5 min | Multi-zone by default, point-in-time recovery enabled |
| Cloud Run | N/A | < 5 min | Auto-scaling, multi-zone |
| Secrets | N/A | N/A | Managed by GCP, globally replicated |
10. PeakPOS Monorepo Integration
The PeakPOS monorepo (peaksystems/monorepo) has existing transaction, batch, and payment configuration infrastructure. The gateway integrates with - not replaces - this existing infrastructure.
Existing Infrastructure
| Component | Location | Purpose |
|---|---|---|
| TransactionService | terminal-api | Creates transactions with processor_reference_id, external_auth_code, external_auth_provider |
| BatchService | terminal-api | Batch lifecycle: open → close → settle with settlement response tracking |
| CardAuthorization schema | specifications/schema | Stores processor responses: auth code, card type, EMV data (AID, TVR, TSI), amounts |
| PaymentConfigService | merchant-api | Per-terminal processor configuration management |
| MkonnektService | merchant-api | External API integration pattern with circuit breaker (reference implementation) |
| PaymentInterfaceConfigDto | libs/security | Shared payment interface config model (apiKey, appIdentifier, environment) |
Integration Points
- Processor Type Extension:
card_authorizations.processor_typefield currently defaults to "nmi" - add "tsys" / "transit" as a new option - Payment Config Storage: Store TSYS credentials (merchant ID, TID, API keys) in existing
terminal_payment_configs.interfacesJSONB field - Card Authorization Mapping: Map TransIT/MultiPass responses to existing
card_authorizationsschema fields - Batch Settlement Routing: Extend
BatchService.settleBatch()to route to TransIT when processor is TSYS - Raw Response Storage: TSYS-specific response data in
card_authorizations.raw_responseJSONB - no schema migration needed - EMV Data Fields: Existing
emv_aid,emv_tvr,emv_tsifields map directly to NexGO terminal EMV output
Key Existing Fields
| Field | Table | Purpose |
|---|---|---|
processor_reference_id | transactions | Gateway transaction ID |
external_auth_code | transactions | Authorization code from processor |
external_auth_provider | transactions | Processor name (e.g., "nmi", "tsys") |
processor_type | card_authorizations | Defaults to "nmi", extensible for TSYS |
raw_response | card_authorizations | JSONB for full processor response |
tip_amount | card_authorizations | Tip (adjustable post-auth) |
fee_amount | card_authorizations | Processing fee |
emv_aid, emv_tvr, emv_tsi | card_authorizations | EMV data fields |