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. Gateway-owned tenant modeling is organization/location based.
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, and third-party integrators) authenticate via OAuth and never access the CDE directly.
┌─────────────────────────────────────────────────────────────────────────────┐
│ CLIENTS (each in their own GCP project, OUT of CDE) │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────────────┐ │
│ │ PeakPOS │ │ E-commerce │ │ Third-party │ │
│ │ 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; containers listen on 8080) │ │
│ │ │ │
│ │ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ Auth Service │ │ Status │ │ │
│ │ │ OAuth2 tokens│ │ Service │ │ │
│ │ │ JWK issuer │ │ Health agg. │ │ │
│ │ │ JWK endpoint │ │ │ │ │
│ │ └──────┬───────┘ └──────────────┘ │ │
│ │ │ JWK (/.well-known/jwks.json) │ │
│ │ │ fetched by ALL services for token validation │ │
│ │ ▼ │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ Management │ │ Online Txn │ │ Card Present │ │ │
│ │ │ Service │ │ Service │ │ Service │ │ │
│ │ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │ │
│ │ │ │ │ │ │
│ │ │ proxy/delegate │ delegate │ in-process engine │ │
│ │ └────────┐ ┌─────┴─────────────────┘ │ │
│ │ ▼ ▼ │ │
│ │ ┌──────────────┐ │ │ │ │
│ │ │ Processing │◄──────────────┘ │ │ │
│ │ │ Service │ internal creds API │ │ │
│ │ └──────┬───────┘ │ │ │
│ │ │ │ │ │
│ │ ┌──────────────┐│ │ │ │
│ │ │ Merchant ││ │ │ │
│ │ │ Onboarding │◄───────────────────────────────────┘ │ │
│ │ │ Service │ credential/profile APIs │ │
│ │ └──────┬───────┘ │ │
│ └──────────────────┼───────────────────────────────────┼───────────────┘ │
│ │ │ │
│ ┌──────────────────┼───────────────────────────────────┼───────────────┐ │
│ │ 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,
Card Present services
Local development maps these services to host ports 8181 through 8187; Cloud Run and Docker containers publish port 8080.
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/ # Vendor PDFs, TransIT workbook, and JSON samples
├── 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
│ │ ├── 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
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
Core payment engine for card-not-present, subscriptions, settlement, and shared transaction execution reused by the card-present service boundary.
| 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 credential metadata for transaction routing. Authoritative source is the credential_profiles lookup keyed off the merchant's assigned profile; the internal credentials route returns API-safe metadata and never echoes the TSYS transaction key. |
| 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 | Card-Present Service | In-process library dependency | Card-present sale/auth execution through the dedicated in-person service boundary |
Management Service
Backend for the Management Portal. It is both a domain owner (merchants,
portal users, audit, webhook config) and an explicit BFF / proxy tier: it
terminates portal auth, authorizes, shapes DTOs, and forwards to the owning
service for everything it does not own. Because processing and
merchant-onboarding are internal-only (Cloud Run IAM, no LB route), this
proxy tier is the only path from the public portal into the internal core —
see Management as a BFF / proxy tier
for the authorize-then-forward seam and governance rules.
| Direction | Target | Protocol | Purpose |
|---|---|---|---|
| Outbound | Processing Service | HTTPS (VPC-internal) | Proxy: transaction search, void, refund, settlement queries, subscription management, customers, gift cards, tax config, reports (processingClient) |
| Outbound | Merchant Onboarding Service | HTTPS (VPC-internal) | Proxy: credential-profile lifecycle, card-present device admin, merchant TransIT activation (merchantOnboardingClient) |
| Outbound | Online Txn Service | HTTPS (VPC-internal) | Proxy: checkout config, hosted-payment buttons/sessions, webhook delivery retry (onlineTxnClient) |
| Outbound | Cloud Spanner | JDBC (PGAdapter) | merchants, portal_users, user_merchant_access; appends rows to the multi-writer audit_log |
| Outbound | Firebase Auth | Admin SDK | User creation, custom claims (RBAC), SAML provider CRUD |
| Outbound | Auth Service | HTTPS | Fetch JWK for inbound token validation; proxy OAuth-client and API-key admin (authClient) |
| Inbound | Management Portal | HTTPS | All portal API calls (merchants, users, reports, SAML config) |
Online Txn Service
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 |
Card-Present Service
External-facing in-person acceptance boundary. Handles terminal sale/auth requests and terminal-approved transaction/refund sync.
| Direction | Target | Protocol | Purpose |
|---|---|---|---|
| Outbound | Processing transaction engine | In-process library dependency | Reuse sale/auth execution while keeping in-person requests behind the card-present boundary |
| Outbound | Cloud Spanner | JDBC (PGAdapter) | Terminal lookup, transaction sync idempotency, transaction/refund sync records |
| Outbound | Webhook delivery Pub/Sub topic | Publish | Fan out persisted card-present webhook events through WebhookEventPublisher |
| Outbound | Auth Service | HTTPS GET | Fetch JWK for inbound token validation |
| Inbound | PeakPOS Terminal API / Kotlin SDK | HTTPS | Card-present sale/auth and terminal-approved sync/refund APIs |
Merchant Onboarding Service
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, 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 credential-profile metadata for transaction routing. The internal credentials route never echoes the TSYS transaction key. |
| 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
Lightweight health aggregation. No database, no Pub/Sub, no external API calls.
| Direction | Target | Protocol | Purpose |
|---|---|---|---|
| Outbound | All other services | HTTPS GET | /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
card-present also fetches JWKs from auth. Its sale/auth path reuses the processing transaction engine inside the card-present service process; terminal sync and sync-refund stay owned by card-present.
5.3 Frontend-to-Backend Mapping
| Frontend | Backend Service | Auth Method | What it does |
|---|---|---|---|
| Management Portal (React) | Management Service | Firebase Auth ID token | Merchant CRUD, user mgmt, SAML config, reports, audit log |
| Online Payment Website | Online Txn Service | Session-based (checkout session ID) | Checkout flow, payment form submission |
| Kotlin SDK (server) | Auth Service | OAuth2 client credentials | Obtain scoped access token |
| Kotlin SDK (server) | Online Txn Service | OAuth2 bearer token / API key | Checkout sessions, token payments, subscriptions |
| Kotlin SDK / terminal clients | Card-Present Service | Firebase/device/OAuth bearer by endpoint | Card-present sale/auth, transaction sync, sync refund |
5.4 External Client-to-Service Mapping
| External Client | Services Called | Authorization | Purpose |
|---|---|---|---|
| PeakPOS Management API | Auth → Merchant Onboarding | merchant:activate, provision:request | Merchant onboarding, merchant onboarding |
| PeakPOS Terminal API | Auth → Card-Present | location/device identity; txn:sync for sync endpoints | Card-present sale/auth, transaction sync, sync refund |
| 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, Card-Present Service | libs/transit-client via the processing transaction engine | 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 |
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.canceled, 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 | Services using WebhookEventPublisher (management, processing, online-txn, card-present) | Management Service consumer and webhook retry workers | Persisted webhook-event fanout and queued 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 |
transactions.terminal_transaction_id, transit_devices | Card-Present | Terminal lookup plus terminal-approved transaction/refund sync idempotency on existing transaction rows |
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 locations. See libs/schema/.../CredentialProfile.sq. |
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).
Direct terminal firmware calls are not part of the current card-present boundary. If that boundary changes so payment terminal firmware calls the gateway without an app/server mediation layer, reassess mTLS as part of the design. 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 firmware-direct terminal authentication.
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 |