Skip to main content

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?

ConcernsSingle projectTwo projects (chosen)
DecisionOption consideredChosen architecture - separate project for CDE (PCI scoped)
PCI audit scopeEntire project in scopeOnly CDE project in scope
IAM isolationShared IAM policiesHard boundary - separate IAM
Blast radiusCompromise affects everythingCDE isolated from client infra
CostShared resources~$65/mo extra for CDE Spanner
ComplexitySimplerCross-project IAM + HTTPS (trivial with Cloud Run)

3. Technology Stack

LayerTechnologyJustification
LanguageKotlin (services), TypeScript (frontends)Kotlin for all backend services; TS for modern web UIs
FrameworkSpring Boot 4.0.2Industry standard for payment processing, excellent observability
BuildBazelMonorepo support, hermetic builds, caching
DatabaseCloud Spanner (PostgreSQL dialect)ACID compliance, horizontal scaling, matches PeakPOS monorepo
MessagingCloud Pub/SubAsync event processing, webhook delivery
Auth (Portal)Firebase Auth + SAML SSOManaged auth with enterprise SSO support
Auth (API)OAuth2 + JWT (Spring Authorization Server)Standard API authentication via Auth Service
SecretsGCP Secret ManagerAPI keys, TransIT credentials, signing keys
ComputeCloud RunAuto-scaling, pay-per-use, no cluster management
IaCTerraformDeclarative infrastructure, state management
CI/CDGitHub ActionsNative GitHub integration, monorepo support
ContainerDocker (Ubuntu / Debian)Branded as "Peak Gateway"
MonitoringCloud Monitoring + Cloud LoggingNative GCP observability stack
CDN/WAFCloud Armor + Cloud Load BalancingDDoS 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.

DirectionTargetProtocolPurpose
OutboundCloud SpannerJDBC (PGAdapter)OAuth client registrations, token metadata
OutboundFirebase AuthAdmin SDKPortal user identity verification
InboundAll servicesHTTPS GETJWK endpoint (/auth/.well-known/jwks.json) on the public gateway or direct auth service URL for token validation
InboundAll clientsHTTPS POSTOAuth2 token issuance (/auth/oauth2/token) on the public gateway
InboundPortalHTTPSOAuth 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.

DirectionTargetProtocolPurpose
OutboundTSYS TransIT Multipass APIHTTPSSale, Auth, Capture, Void, Refund, Batch, Tip Adjust, L2/L3 Enrich (via transit-client lib)
OutboundMerchant Onboarding ServiceHTTPS (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.
OutboundCloud SpannerJDBC (PGAdapter)transactions, settlement_batches, subscriptions, subscription_billing_events, batch_settlement_config
OutboundCloud Pub/SubPublishTopics: transaction-events, subscription-events
OutboundAuth ServiceHTTPS GETFetch JWK for inbound token validation
InboundManagement ServiceHTTPSProxied transaction queries, void, refund, settlement, subscription reports
InboundOnline Txn ServiceHTTPSDelegated payment processing (sale, auth, token charge)
InboundCloud SchedulerPub/SubHourly trigger for recurring billing engine
InboundCard-Present ServiceIn-process library dependencyCard-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.

DirectionTargetProtocolPurpose
OutboundProcessing ServiceHTTPS (VPC-internal)Proxy: transaction search, void, refund, settlement queries, subscription management, customers, gift cards, tax config, reports (processingClient)
OutboundMerchant Onboarding ServiceHTTPS (VPC-internal)Proxy: credential-profile lifecycle, card-present device admin, merchant TransIT activation (merchantOnboardingClient)
OutboundOnline Txn ServiceHTTPS (VPC-internal)Proxy: checkout config, hosted-payment buttons/sessions, webhook delivery retry (onlineTxnClient)
OutboundCloud SpannerJDBC (PGAdapter)merchants, portal_users, user_merchant_access; appends rows to the multi-writer audit_log
OutboundFirebase AuthAdmin SDKUser creation, custom claims (RBAC), SAML provider CRUD
OutboundAuth ServiceHTTPSFetch JWK for inbound token validation; proxy OAuth-client and API-key admin (authClient)
InboundManagement PortalHTTPSAll 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.

DirectionTargetProtocolPurpose
OutboundProcessing ServiceHTTPSDelegate: sale, auth, token charge for checkout sessions and card-on-file payments
OutboundCloud SpannerJDBC (PGAdapter)checkout_sessions, stored_tokens, api_keys, webhook_deliveries, subscription_webhook_config
OutboundCloud Pub/SubSubscribe + PublishSubscribe: transaction-events (for webhook triggers). Publish: webhook-delivery
OutboundMerchant webhook URLsHTTPS POSTWebhook delivery with HMAC-SHA256 signature
OutboundAuth ServiceHTTPS GETFetch JWK for inbound token validation
InboundOnline Payment WebsiteHTTPSCheckout session UI, payment form submissions
InboundE-commerce backendsHTTPSCheckout session creation, token payments, subscription creation, API key management
InboundKotlin SDKHTTPSServer-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.

DirectionTargetProtocolPurpose
OutboundProcessing transaction engineIn-process library dependencyReuse sale/auth execution while keeping in-person requests behind the card-present boundary
OutboundCloud SpannerJDBC (PGAdapter)Terminal lookup, transaction sync idempotency, transaction/refund sync records
OutboundWebhook delivery Pub/Sub topicPublishFan out persisted card-present webhook events through WebhookEventPublisher
OutboundAuth ServiceHTTPS GETFetch JWK for inbound token validation
InboundPeakPOS Terminal API / Kotlin SDKHTTPSCard-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.

DirectionTargetProtocolPurpose
OutboundTSYS TransIT MerchantActivation APIHTTPSGenerate deviceID + transactionKey (via transit-client lib)
OutboundNexGO XTMS Cloud Open APIHTTPSCreate merchant, bind device, push credentials (via xtms-client lib)
OutboundCloud SpannerJDBC (PGAdapter)provisioning_jobs, credential_profiles, provisioning_audit_log
OutboundCloud KMSgRPCEncrypt/decrypt TransIT credentials (AES-256-GCM) before Spanner storage
OutboundSecret ManagergRPCRead TransIT developer credentials (developerID, developerKey)
OutboundCloud Pub/SubPublishTopic: provisioning-events
OutboundAuth ServiceHTTPS GETFetch JWK for inbound token validation
InboundProcessing ServiceHTTPS (VPC-internal)Returns credential-profile metadata for transaction routing. The internal credentials route never echoes the TSYS transaction key.
InboundPeakPOS Management APIHTTPS (cross-project)POST /api/v1/provisioning/jobs - trigger provisioning workflow
InboundManagement Portal (via gateway)HTTPSProvisioning job status, audit logs

Status Service

Lightweight health aggregation. No database, no Pub/Sub, no external API calls.

DirectionTargetProtocolPurpose
OutboundAll other servicesHTTPS GET/health - aggregate health status
InboundCloud MonitoringHTTPS GETUptime checks
InboundAny clientHTTPS GETAggregated 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

FrontendBackend ServiceAuth MethodWhat it does
Management Portal (React)Management ServiceFirebase Auth ID tokenMerchant CRUD, user mgmt, SAML config, reports, audit log
Online Payment WebsiteOnline Txn ServiceSession-based (checkout session ID)Checkout flow, payment form submission
Kotlin SDK (server)Auth ServiceOAuth2 client credentialsObtain scoped access token
Kotlin SDK (server)Online Txn ServiceOAuth2 bearer token / API keyCheckout sessions, token payments, subscriptions
Kotlin SDK / terminal clientsCard-Present ServiceFirebase/device/OAuth bearer by endpointCard-present sale/auth, transaction sync, sync refund

5.4 External Client-to-Service Mapping

External ClientServices CalledAuthorizationPurpose
PeakPOS Management APIAuth → Merchant Onboardingmerchant:activate, provision:requestMerchant onboarding, merchant onboarding
PeakPOS Terminal APIAuth → Card-Presentlocation/device identity; txn:sync for sync endpointsCard-present sale/auth, transaction sync, sync refund
E-commerce backendsAuth → Online Txntxn:process, session:createCard-not-present transactions, hosted checkout
Gateway PortalAuth → Managementadmin:*Full gateway administration

5.5 External System Integrations

External SystemCalled ByLibraryProtocolPurpose
TSYS TransIT Multipass APIProcessing Service, Card-Present Servicelibs/transit-client via the processing transaction engineHTTPS (JWT + signature)Transaction processing: Sale, Auth, Capture, Void, Refund, Batch, L2/L3
TSYS TransIT MerchantActivationMerchant Onboarding Servicelibs/transit-clientHTTPS (JWT + signature)Generate deviceID + transactionKey for new merchants
NexGO XTMS Cloud Open APIMerchant Onboarding Servicelibs/xtms-clientHTTPS (SHA-256 signature)Device management, credential push, parameter configuration
Firebase Auth / Identity PlatformAuth Service, Management ServiceFirebase Admin SDKgRPCUser identity, SAML SSO providers, custom claims
Merchant webhook URLsOnline Txn Service(direct)HTTPS POSTWebhook delivery for payment and subscription events

5.6 Shared Library Usage

LibraryPurposeUsed By
libs/transit-clientTSYS TransIT Multipass API client (HTTP client, JWT auth, request signing)Processing, Merchant Onboarding
libs/xtms-clientNexGO XTMS Cloud Open API client (HTTP client, SHA-256 signature auth)Merchant Onboarding
libs/securityOAuth/JWT validation filters, @RequireScope and @RequireRole annotations, JWK integrationAll services
libs/schemaSQLDelight-generated Spanner schema and Kotlin typesAll services with database access
libs/commonShared utilities, error models, ApiResponse DTOsAll services

5.7 Pub/Sub Topics and Subscribers

TopicPublisherSubscribersEvents
transaction-eventsProcessing ServiceOnline Txn Service (webhook delivery), Management Portal (real-time updates)transaction.approved, transaction.declined, transaction.voided, transaction.refunded, transaction.settled
subscription-eventsProcessing ServiceOnline Txn Service (webhook delivery)subscription.created, subscription.billed, subscription.payment_failed, subscription.past_due, subscription.canceled, subscription.paused, subscription.resumed
provisioning-eventsMerchant Onboarding ServiceManagement Portal (status updates), callback delivery, alerting (PagerDuty)PROVISION_COMPLETED, PROVISION_FAILED, XTMS_PUSH_FAILED
webhook-deliveryServices using WebhookEventPublisher (management, processing, online-txn, card-present)Management Service consumer and webhook retry workersPersisted webhook-event fanout and queued delivery attempts

5.8 Cloud Scheduler Jobs

ScheduleTargetPurpose
HourlyProcessing 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 groupOwner ServicePurpose
merchants, merchant_configsManagementMerchant profiles, configurations, boarding data
portal_users, user_merchant_accessManagementPortal users, roles, permissions
audit_logManagementAdministrative action audit trail
transactions, settlement_batches, batch_settlement_configProcessingTransaction records, settlement batches, scheduling config
subscriptions, subscription_billing_eventsProcessingRecurring billing lifecycle
transactions.terminal_transaction_id, transit_devicesCard-PresentTerminal lookup plus terminal-approved transaction/refund sync idempotency on existing transaction rows
checkout_sessions, stored_tokensOnline TxnCheckout sessions, tokenized payment data
api_keysOnline TxnMerchant API keys for SDK/API access
webhook_deliveries, subscription_webhook_configOnline TxnWebhook delivery tracking and merchant webhook config
credential_profilesMerchant OnboardingKMS-encrypted TransIT credentials scoped to an organization and assignable to locations. See libs/schema/.../CredentialProfile.sq.
provisioning_jobs, provisioning_audit_logMerchant OnboardingProvisioning 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

BoundaryMethod
Gateway Portal usersFirebase 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 APITransIT deviceID + transactionKey (JWT + signature)
NexGO XTMSSHA-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)
ClientScopesPurpose
PeakPOS Management APImerchant:activate, provision:requestMerchant onboarding, merchant onboarding
PeakPOS Terminal APItxn:process, batch:manageCard-present transactions via SmartConnect
E-commerce backendtxn:process, session:createCard-not-present transactions
Gateway Portaladmin:*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) and peakpos-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

ConcernToolDetails
LogsCloud LoggingStructured JSON logging, 30-day retention
MetricsCloud MonitoringCustom dashboards, SLO tracking
TracesCloud TraceDistributed tracing across services
AlertsCloud Monitoring AlertingPagerDuty integration for P0/P1
UptimeCloud Monitoring Uptime ChecksExternal 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

ComponentRPORTOStrategy
SpannerNear-zero< 5 minMulti-zone by default, point-in-time recovery enabled
Cloud RunN/A< 5 minAuto-scaling, multi-zone
SecretsN/AN/AManaged 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

ComponentLocationPurpose
TransactionServiceterminal-apiCreates transactions with processor_reference_id, external_auth_code, external_auth_provider
BatchServiceterminal-apiBatch lifecycle: open → close → settle with settlement response tracking
CardAuthorization schemaspecifications/schemaStores processor responses: auth code, card type, EMV data (AID, TVR, TSI), amounts
PaymentConfigServicemerchant-apiPer-terminal processor configuration management
MkonnektServicemerchant-apiExternal API integration pattern with circuit breaker (reference implementation)
PaymentInterfaceConfigDtolibs/securityShared payment interface config model (apiKey, appIdentifier, environment)

Integration Points

  1. Processor Type Extension: card_authorizations.processor_type field currently defaults to "nmi" - add "tsys" / "transit" as a new option
  2. Payment Config Storage: Store TSYS credentials (merchant ID, TID, API keys) in existing terminal_payment_configs.interfaces JSONB field
  3. Card Authorization Mapping: Map TransIT/MultiPass responses to existing card_authorizations schema fields
  4. Batch Settlement Routing: Extend BatchService.settleBatch() to route to TransIT when processor is TSYS
  5. Raw Response Storage: TSYS-specific response data in card_authorizations.raw_response JSONB - no schema migration needed
  6. EMV Data Fields: Existing emv_aid, emv_tvr, emv_tsi fields map directly to NexGO terminal EMV output

Key Existing Fields

FieldTablePurpose
processor_reference_idtransactionsGateway transaction ID
external_auth_codetransactionsAuthorization code from processor
external_auth_providertransactionsProcessor name (e.g., "nmi", "tsys")
processor_typecard_authorizationsDefaults to "nmi", extensible for TSYS
raw_responsecard_authorizationsJSONB for full processor response
tip_amountcard_authorizationsTip (adjustable post-auth)
fee_amountcard_authorizationsProcessing fee
emv_aid, emv_tvr, emv_tsicard_authorizationsEMV data fields