System Design: Design Stripe Payment Gateway — Merchant Onboarding, Card Processing, Webhooks, PCI, Reconciliation

Stripe processes hundreds of billions of dollars annually for millions of merchants, handling the full payment lifecycle from card tokenization to settlement. Designing a payment gateway like Stripe tests your understanding of financial transaction safety, PCI compliance architecture, webhook reliability, and the clearing/settlement process that moves money between banks. This is distinct from our Venmo guide (P2P) — Stripe is a merchant payment processor.

Card Payment Flow

When a customer pays on a merchant website using Stripe: (1) Tokenization — Stripe.js (client-side) collects card details and sends them directly to Stripe servers. Stripe returns a one-time-use token (tok_xxx). The merchant server NEVER sees the raw card number — this keeps the merchant out of PCI scope. (2) Create PaymentIntent — the merchant backend calls Stripe API: POST /v1/payment_intents with amount, currency, token, and idempotency_key. Stripe creates a PaymentIntent (pi_xxx) with status “requires_confirmation.” (3) Confirm — the merchant confirms the PaymentIntent. Stripe sends an authorization request through the card network (Visa/Mastercard) to the issuing bank: “Does the cardholder have $50? Hold it.” (4) Authorization response — the bank approves or declines. Decline reasons: insufficient funds, suspected fraud, expired card, incorrect CVV. Stripe returns the result to the merchant. (5) Capture — for immediate capture: the authorized amount is captured (moved to settlement). For delayed capture (hotels, pre-orders): the merchant captures later. Uncaptured authorizations expire after 7 days. (6) Settlement — Stripe batches captured payments and submits to the acquiring bank. The acquiring bank settles with the card network. Funds arrive in the merchant bank account in 2 business days (T+2). Stripe deducts its fee (2.9% + $0.30 per transaction) before payout.

Idempotency and Retry Safety

Network failures between the merchant and Stripe can cause ambiguous states: did the charge succeed? The merchant retries and the customer might be charged twice. Stripe idempotency: every mutating API request includes an Idempotency-Key header (a UUID generated by the merchant, typically the order_id). First request: Stripe processes normally, stores the result keyed by the idempotency key (24-hour TTL). Duplicate request (same key): Stripe returns the stored result without re-processing. Implementation: (1) On receiving a request, hash the idempotency key and check a Redis/database lookup. (2) If found with status COMPLETED: return the stored response. (3) If found with status IN_PROGRESS: return 409 Conflict (another request with this key is being processed). (4) If not found: create a record with status IN_PROGRESS, process the request, update to COMPLETED with the response, return the response. The merchant must also implement idempotency: before calling Stripe, check if this order has already been charged (database unique constraint on order_id + charge_status). This prevents the merchant code from issuing duplicate Stripe API calls even if the merchant own retry logic fires.

Webhook Architecture

Stripe uses webhooks to notify merchants of asynchronous events: payment_intent.succeeded, charge.refunded, invoice.paid, subscription.updated. The merchant cannot rely solely on synchronous API responses because: (1) 3D Secure authentication is asynchronous (the customer completes a bank challenge after the API call returns). (2) Disputes/chargebacks arrive days later. (3) Subscription billing happens on a schedule. Webhook delivery: Stripe sends an HTTP POST to the merchant registered webhook URL. The payload is signed with a webhook secret (HMAC-SHA256). The merchant verifies the signature before processing. Reliability: if the merchant returns non-2xx or times out (5 seconds), Stripe retries with exponential backoff for up to 3 days: 1 hour, 2 hours, 4 hours, etc. After all retries fail, the event is marked as failed. The merchant can manually retry from the Stripe dashboard. Ordering: Stripe does not guarantee event delivery order. The merchant might receive payment_intent.succeeded before payment_intent.created. The handler must be order-independent: check the current state of the object before applying transitions. Best practice: on receiving a webhook, enqueue for async processing and return 200 immediately. Do not perform slow operations synchronously.

PCI Compliance Architecture

PCI DSS has 4 compliance levels based on transaction volume. Stripe architecture minimizes merchant PCI burden: Stripe.js / Stripe Elements: the card form is an iframe hosted by Stripe. Card data goes directly from the browser to Stripe servers over HTTPS. The merchant server receives only a token. This qualifies the merchant for SAQ-A (simplest compliance level — a 22-question self-assessment). If the merchant handles card data directly (server-side collection): SAQ-D applies (300+ requirements, annual audit by a QSA, network segmentation, encryption at rest/transit, access controls, vulnerability scanning, penetration testing). Cost: $50K-$500K per year for SAQ-D compliance. Stripe internal PCI architecture: Stripe IS a Level 1 Service Provider (the highest PCI level). Their infrastructure: card data is encrypted at rest (AES-256) and in transit (TLS 1.2+). Card numbers are stored in a dedicated, isolated “vault” service with strict access controls. Internal services access card data only via tokenized references. Network segmentation isolates the cardholder data environment (CDE) from other systems. Regular penetration testing, vulnerability scanning, and security audits. Stripe publishes their PCI DSS Attestation of Compliance (AOC) for merchant audit purposes.

Merchant Onboarding and Connect

Stripe Connect enables platforms (marketplaces, SaaS) to accept payments on behalf of their users. Three models: (1) Standard — the connected account has a full Stripe dashboard. The platform redirects users to Stripe for onboarding. Stripe handles KYC, compliance, and payouts. Simplest for the platform. (2) Express — streamlined onboarding (fewer fields). The connected account has a limited Stripe dashboard. The platform controls the payment flow. (3) Custom — full platform control. The platform collects all information and manages the experience. Most complex but most flexible. Onboarding: KYC (Know Your Customer) verification. Stripe collects: legal business name, tax ID (EIN/SSN), business address, beneficial owner information, and a bank account for payouts. Stripe verifies against government databases and sanctions lists. For individuals: identity verification via government ID photo + selfie match. Onboarding can be completed in minutes (instant verification) or take 1-2 days (manual review for complex cases). Payouts: Stripe sends funds to the merchant bank account on a configurable schedule (daily, weekly, monthly). Payout timing: T+2 for most countries (funds available 2 business days after the charge). Instant payouts (to debit card): available within 30 minutes for an additional fee.

Scroll to Top