PPact
Compliance & Privacy

PCI DSS

How Pact keeps cardholder data out of scope by delegating every payment surface to Stripe's hosted pages — Pact's billing never stores, processes, or transmits a card number.

The shortest true statement about Pact and PCI DSS is this: Pact's billing never touches a card number. Every payment surface is a hosted Stripe page. Your card details are entered on Stripe's domain, tokenized by Stripe, and stored by Stripe — they never transit or land in Pact's backend, database, or logs.

Why this matters for your PCI scope

Because cardholder data never enters Pact's billing, the billing integration does not pull Pact into the card-data portion of your own PCI assessment. It is built on the same outsourced-payment pattern (SAQ A shape) that a merchant uses when redirecting to a hosted payment page: the sensitive surface belongs to the payment provider. Recorded calls are the exception to watch; see Card numbers spoken on calls.

How billing is wired

Billing lives behind a provider abstraction (core/billing.py) so no route imports the stripe SDK directly. The two customer-facing entry points are both hosted by Stripe:

  • POST /v1/billing/checkout — opens a Stripe Checkout session for an upgrade. The customer lands on checkout.stripe.com, enters their card there, and returns.
  • POST /v1/billing/portal — opens the Stripe Customer Portal, where the customer manages their subscription and payment method on Stripe's domain.

There is also a setup-mode checkout (create_setup_checkout_session) that puts a card on file for a later one-click upgrade. Even here, the card is captured by Stripe in setup mode and is never charged inline — Pact only ever receives a Stripe customer/payment-method reference, not the PAN.

code
Customer ──card──▶ checkout.stripe.com  (Stripe's PCI-scoped environment)
                        │
                        ▼
Stripe ──customer_id, subscription_id──▶ Pact backend  (no card data)

When the platform Stripe credential is not configured, every billing write returns 503 stripe_not_configured rather than attempting to collect payment details itself — there is no in-app card-entry fallback by design.

What Pact stores

Pact persists only non-sensitive Stripe references — customer IDs, subscription IDs, price/plan identifiers, and usage counters for metered billing. It does not store, and has no schema column for, a primary account number, CVV, or full magnetic-stripe data.

Webhook signature verification (verify_stripe_signature) re-implements Stripe's documented HMAC-SHA256 scheme so inbound webhook events are authenticated, but the events Pact consumes carry subscription state, not card data.

Where the formal attestation stands

No signed PCI attestation exists today

The architecture above keeps cardholder data out of Pact's billing. Pact has not completed a self-assessment questionnaire (SAQ A) or an Attestation of Compliance (AOC), so there is no signed PCI document to attach to a vendor-security questionnaire, and Pact has not committed to a date for one. For a procurement review, this page and the billing design it describes are what Pact can provide today.

Card numbers spoken on calls

Billing is not the only place a card number could appear. If a caller reads a card number aloud on a recorded call, it can end up in that call's stored transcript. The billing design above does not prevent this, because the card number never goes near Stripe.

What Pact does today:

  • Before the call summarizer. With phi_redaction_enabled on (Voice compliance), card-number-shaped digit runs are redacted from the transcript before it goes to the summarizer.
  • On the way to an AI model. Unless an admin has set a feature to send cleartext (AI data sharing), Pact's AI client redacts Luhn-valid card numbers from what it sends to the model provider.
  • In the stored transcript: nothing. Neither step rewrites the transcript Pact keeps. The compliance vault is designed to encrypt originals and redact display copies, but it does not receive live call transcripts yet.

If callers might read card numbers on your recorded calls, stop them from doing it (for example, take payment through a separate channel), or keep recording off for those calls.

What's next