Skip to main content

SECURITY_MODEL — payment-gateway-service

Sibling: DATA_MODEL · API_CONTRACTS · SYNC_CONTRACT · AI_INTEGRATION

Strategic anchors: 07 Security, Compliance & Tenancy · 10 Payments Architecture · ADR-0002 Multi-tenancy

This document defines the security posture of payment-gateway-service. The single most important property is that the platform stays in PCI-DSS SAQ A scope: no card data (PAN, full magnetic stripe, CVV/CVV2, PIN/PIN block) ever traverses or rests on Ghasi infrastructure. Card capture is delegated entirely to processor-hosted UI (Stripe Elements, PayPal hosted fields, HesabPay hosted page); we only ever see and store opaque processor tokens plus display metadata (brand, last4, expiry month/year).

1. PCI-DSS scope

1.1 SAQ A applicability

We qualify for SAQ A because all of the following hold:

CriterionEvidence in this service
Card data accepted only via processor-hosted UIAPI §4 — clientSecret flow; no endpoint accepts PAN
No card data stored, processed, or transmitted by usDATA_MODEL §2.6 — only encrypted opaque tokens; CI scanner blocks PAN-shaped patterns
All processing, storage, transmission outsourced to PCI-DSS validated providersStripe (L1), PayPal (L1), HesabPay (regional validation)
Only e-commerce channelNo POS or card-present hardware on our servers; cash-on-arrival is non-card
Service-provider compliance evidence on fileVendor AOCs collected annually by tenant-service; pointers in vendor_credentials_pointer.account_ref

1.2 Out-of-scope by design

  • No pan, card_number, full_pan, cvv, cvc, cv2, track1, track2, pin, pin_block columns in any schema, log, event payload, or test fixture.
  • A repository-wide grep gate (pnpm pci:scan) fails CI on any commit introducing those identifiers, including in comments.
  • Logs go through a redaction proxy that rejects anything matching \b(?:\d[ -]*?){13,19}\b with Luhn validity — the service emits a MELMASTOON.PAYMENT.PAN_EXPOSURE_BLOCKED error and pages the on-call security engineer.

1.3 Annual evidence

  • Quarterly ASV scan of webhooks.payments.melmastoon.ghasi.io and api.melmastoon.ghasi.io.
  • Annual SAQ A self-assessment, signed by the SecOps lead.
  • Annual penetration test of the public surface (separate from the ASV scan).

2. Tenant isolation

2.1 Schema-per-tenant enforcement (per ADR-0002)

  • Each tenant has its own Postgres schema tnt_<tenantId>.
  • The application connects as role payments_app, which has no GRANTs on any tenant schema.
  • Every request opens a transaction and calls payments_central.set_tenant_context(tenantId), a SECURITY DEFINER function that:
    1. Validates the tenantId against tenant_schema_registry.
    2. Sets search_path = tnt_<id>, payments_central, public for the transaction.
    3. Sets a session GUC app.tenant_id = '<tenantId>'.
    4. Issues SET LOCAL ROLE to a per-tenant role with USAGE on that schema only.
  • Cross-tenant query is structurally impossible: even a SQL-injection finding cannot reach another tenant's tables because the connection role lacks the privilege.

2.2 Tenant resolution at the edge

  • iam-service issues JWTs containing tenantId in a signed claim.
  • The API gateway rejects requests where header X-Tenant-Id ≠ JWT tenantId with MELMASTOON.TENANT.NOT_A_MEMBER.
  • Webhook receivers resolve tenantId from the processor-issued event metadata (e.g., Stripe account field on Connect events, or per-tenant secret index for HesabPay) and persist it on the inbox row. Mis-resolved webhooks are quarantined to DLQ.

3. Vendor credential management

3.1 Storage

  • All vendor secrets (API keys, webhook signing secrets, OAuth refresh tokens) live in GCP Secret Manager, namespaced as payment-gateway-service/<tenantId>/<processor>/<env>/<secret_name>.
  • The application receives only a secret URI, resolved at request time and cached in process for at most 5 minutes.
  • Workload Identity binds the GKE service account to a Google service account with secretmanager.secretAccessor scoped to that namespace prefix.

3.2 Rotation

  • Webhook signing secrets rotate on a 90-day schedule, with overlapping validity windows (24 h grace) so neither vendor nor platform sees an outage.
  • Application API keys rotate on a 365-day schedule, or immediately on any HSM-detected anomaly.
  • Rotation is automated via tenant-service and tracked in audit-log-service.

3.3 Tenant-deleted state

  • Tenant deletion triggers secrets:disable on all related secrets (24-h grace), then secrets:destroy after the 30-day soft-delete window. The corresponding row in vendor_credentials_pointer is enabled = FALSE.

4. Webhook security

4.1 Signature verification

  • Each receiver loads the per-tenant webhook secret from Secret Manager.
  • Signature verification runs on the raw request body before any parsing. Tampered or replay-suspicious signatures (timestamp drift > 5 minutes from server clock) are rejected with MELMASTOON.PAYMENT.WEBHOOK_SIGNATURE_INVALID.
  • The receiver never trusts a signature for tenant resolution; tenant resolution uses an out-of-band field (account id) and is then cross-checked against the secret used for verification.

4.2 Replay protection

  • webhook_inbox has a unique constraint on (processor, external_event_id); duplicates are dropped silently and reported as webhook.duplicate_dropped.v1.
  • A Date / Idempotency-Key window of 24 h is enforced; older signed events are accepted only via a manual admin endpoint with explicit operator approval.

4.3 Network controls

  • Webhook receivers live on a separate subdomain behind Cloud Armor with:
    • WAF policies blocking known malicious bots and OWASP Top 10 patterns.
    • IP allowlists per processor (Stripe, PayPal, HesabPay publish their CIDRs; pulled hourly into the policy).
    • Per-tenant rate limit of 1000 events/min; bursts beyond that quarantine to DLQ.

5. Authentication, authorization, RBAC

5.1 AuthN

  • Inter-service calls: mTLS plus JWT (workload-issued by iam-service) carrying caller service identity.
  • Operator calls (BFFs): JWT with user identity, tenant id, property scope, and roles.
  • Webhook calls: vendor signature only — no JWT.

5.2 AuthZ (RBAC)

ActionRequired permission
POST /payments/intentspayments.intent.create (system or front_desk_manager)
POST /payments/intents/:id/refundspayments.refund.create (front_desk_manager or accountant)
POST /payments/cash/receiptspayments.cash.receive (front_desk_clerk+)
POST /payments/cash/refunds (above tenant threshold)payments.cash.refund.elevated + dual sign-off (two operators with this perm)
POST /payments/reconciliations/runpayments.reconciliation.run (accountant or tenant_admin)
POST /payments/chargebacks/:id/evidencepayments.chargeback.submit (accountant or tenant_admin)

Permission resolution is delegated to iam-service via the standard policy-decision call; we cache positive decisions for 60 s, negative for 0 s.

6. Input validation & abuse

  • All bodies validated against Zod schemas; unknown fields rejected (strict mode).
  • Idempotency-key reuse with different body returns 409 (no replay-attack laundering).
  • Rate limiting: per-tenant 30 RPS authorize, 10 RPS refund, 100 RPS reads. Exceeding triggers MELMASTOON.RATE_LIMIT.EXCEEDED.
  • Numeric overflow: amountMicro is bigint; values > 10^15 micro units rejected as invalid.

7. Encryption

DataAt restIn transit
payment_methods.processor_token_encEnvelope-encrypted with per-tenant CMEK in Cloud KMS; rotated annuallyTLS 1.3 only
webhook_inbox.raw_payload_ref (GCS)CMEK on bucket; bucket lifecycle to Coldline at 30 daysTLS 1.3 only
Cloud SQL backupsCMEKTLS 1.3
Pub/Sub messagesGoogle-managed encryption + per-message envelope for high-sensitivity eventsTLS 1.3
Local desktop SQLiteSQLCipher AES-256, key sealed in OS keychainn/a

8. Logging & observability hygiene

  • Structured JSON logs only; never console.log.
  • A central log filter strips fields named cardNumber, pan, cvv, cvc, processorToken, clientSecret, apiKey, webhookSecret regardless of nesting depth.
  • Trace spans never include request bodies; only field names and shapes.
  • Audit-relevant events flow to audit-log-service via a dedicated outbox topic and are immutable for 7 years.

9. Threat model (highlights)

ThreatMitigation
Compromised tenant credentialsWorkload Identity + signed JWTs; secrets never on disk; IP allowlisting on admin endpoints
Webhook spoofingSignature verification + IP allowlist + replay window
Tenant boundary bypass via SQLiSchema-per-tenant + role separation + parameterized queries via Drizzle ORM
Insider exfil of tokensTokens encrypted with CMEK; access requires elevated break-glass procedure with paged approval
Operator double-charge attemptIdempotency-Key required; same-body duplicates collapse, different-body collisions rejected
Card-not-present fraudOptional 3DS at intent time; AI-assisted post-authorize scoring (advisory only)
Refund-policy bypass via direct API callRefunds always include reason; reservation-service policy decisions are logged and required as audit.policyDecisionId
PAN exposure attemptLogged as MELMASTOON.PAYMENT.PAN_EXPOSURE_BLOCKED; CI scanner; redaction proxy; SAQ A scan

10. Incident response

  • Suspected card data exposure: invoke the PAN exposure runbook (paged P0). Stop affected adapter, snapshot logs, notify processors within 24 h, notify SecOps and legal, run a forensic schema dump under 4-eyes.
  • Webhook signing secret leak: rotate immediately, replay last 24 h of events from raw inbox storage to validate state, audit for fraudulent state changes.
  • Tenant cross-talk: theoretical; the schema-per-tenant model + role separation makes a hard break required to surface this. Runbook still exists: freeze writes, snapshot, restore from backup, re-issue all sessions.

11. Compliance with regional regulations

  • EU/UK GDPR — guest payment metadata is personal data; subject access requests served via tenant-service SAR pipeline; right to erasure honored except for legal-hold financial records (10-year retention overrides).
  • MFS markets (e.g., Afghanistan, Pakistan): adhere to local KYC and AML reporting where Ghasi acts as a payment facilitator (we do not — we are a merchant on record's processor); any transition to facilitator status triggers a re-scope to PCI SAQ D and a separate compliance program.
  • Sanctions screening: out of scope for this service; processors handle OFAC at their layer. Future: a compliance-service may pre-screen high-value transactions.

12. Boundary with iam-service, tenant-service, audit-log-service

  • iam-service owns all human and machine identity, JWT issuance, RBAC policy decisions.
  • tenant-service owns tenant lifecycle including schema provisioning and Secret Manager namespacing.
  • audit-log-service is the canonical sink for security-relevant events; this service is a producer only.