Credit Card Payments, PCI DSS, and Tokenization
The architect's visual guide to payments that won't get you fired.
You're building a checkout. The instinct is to treat it like any other API call—post JSON, get a response, store the result. That instinct will land you in PCI scope with 300+ compliance controls, potential fines, and legal exposure. Let's fix that.
The Mental Model: Think Like a Coat Check
Before diving into protocols and standards, anchor the entire payment flow to one analogy:
Tokenization is a coat check.
The ticket (token) is useless to a thief—it only works at that coat check. Similarly, a payment token only works with that gateway. You never carry the coat (card number) around the building (your infrastructure). You carry the ticket.
The rule: If someone breaks into your database and finds
pm_1Axyz, they can't buy anything. That's the point.
Trust Zones: Where Data Lives Matters
Every payment architecture has three trust zones. Your goal is to stay out of the red one.
| Zone | Contains | Who Manages It | Your PCI Burden |
|---|---|---|---|
| Zone 1 — Red | Raw card data | Gateway (Stripe, Adyen, Braintree) | None — it's their problem |
| Zone 2 — Yellow | Tokens, charge IDs, metadata | Your server | Minimal — SAQ A or A-EP |
| Zone 3 — Green | Last 4, brand, expiry | Your frontend | Display only — no PCI concern |
Architect's rule: If raw card data ever enters Zone 2, you've just inherited Zone 1's compliance burden. Design to prevent that.
Decision Flowchart: Picking Your Payment Architecture
How a Card Payment Works
| Phase | What Happens | Timing |
|---|---|---|
| Authorization | Issuing bank checks balance, fraud rules, 3DS challenges. Reserves funds. | Real-time (~1-3s) |
| Capture | Tells the acquirer to move the reserved amount. Can be immediate or delayed (e.g., ship-then-capture). | Immediate or up to 7 days |
| Settlement | Funds move from issuer → card network → acquirer → your merchant account, minus interchange + assessment fees. | T+1 to T+3 business days |
| Funding | Acquirer deposits net amount into your bank account. | T+2 to T+7 depending on processor |
Key insight: Your app never touches card data. The gateway collects it directly from the customer's browser via an iframe, then gives you a token. The full chain (acquirer → network → issuer) is invisible to you.
What You Can and Cannot Store
The CVV rule is absolute. Even encrypted, even for one millisecond, even in logs—CVV must never be stored post-authorization. PCI DSS Requirement 3.2 makes this non-negotiable.
| Your Database Field | Example | Why |
|---|---|---|
payment_token | pm_1Axyz | Charge the card again |
last4 | 4242 | "Visa ending in 4242" |
brand | visa | Display card icon |
exp_month/year | 12/2027 | Notify before expiry |
fingerprint | gUi3... | Detect duplicate cards across customers |
charge_id | ch_3Mxyz | Reference for refunds and disputes |
PCI DSS Compliance: Pick Your Architecture Wisely
Your integration pattern determines how many compliance controls you face. PCI DSS v4.0 became mandatory March 2025 — all assessments now use v4.0.
| Pattern | SAQ Type | Controls | When to Use |
|---|---|---|---|
| Redirect (Stripe Checkout, PayPal) | SAQ A | ~22 | Simple checkout, fastest compliance |
| Hosted Fields (iframe on your page) | SAQ A-EP | ~140 | Custom branded checkout |
| Direct API | SAQ D | ~300+ | Avoid unless you're a payment processor |
Key PCI DSS v4.0 Changes Architects Must Know
| Requirement | What Changed | Impact |
|---|---|---|
| 6.4.3 | All payment page scripts must be inventoried, authorized, and integrity-checked | You need CSP headers + Subresource Integrity (SRI) on every <script> tag on checkout pages |
| 11.6.1 | Tamper-detection mechanism for payment pages | Monitor your checkout page DOM for unauthorized changes (skimming attacks) |
| 8.3.6 | Minimum 12-character passwords for system accounts | Update all service account credentials |
| 12.3.1 | Targeted risk analysis for flexible requirements | Document why you chose specific control implementations |
v4.0 headline: Client-side security is no longer optional. If you serve a checkout page, you must prove no unauthorized scripts can execute on it.
3D Secure 2 (3DS2) — Strong Customer Authentication
3DS2 shifts fraud liability from you to the issuing bank when the cardholder authenticates. It's required in the EU (PSD2/SCA) and increasingly expected elsewhere.
| Outcome | Liability | What Happened |
|---|---|---|
| Frictionless pass | Bank | Issuer approved silently — best UX |
| Challenge pass | Bank | Customer completed OTP/biometric |
| Challenge fail | You | Customer failed auth — don't proceed |
| 3DS not attempted | You | No liability shift — you eat the chargeback |
Architect's tip: Always send 3DS data (device fingerprint, browser metadata) with the charge request. Even if 3DS isn't triggered, it improves authorization rates.
How Tokenization Works
Network Tokens vs. Gateway Tokens
| Type | Issued By | Survives Card Re-issue? | Portable? | Auth Rate Impact |
|---|---|---|---|---|
| Network Token | Visa / Mastercard | Yes — auto-rotates with new card | Works across gateways | +2-4% improvement |
| Gateway Token | Stripe / Braintree / Adyen | Depends on gateway's updater service | Locked to one processor | Baseline |
Network tokens improve authorization rates because issuers trust them more — they're cryptographically bound to your merchant ID. Use them for subscriptions and card-on-file scenarios. Most major gateways now support enabling them transparently.
Webhooks: Handling Async Payment Events
Payments are not purely synchronous. Refunds, disputes, subscription renewals, and failed charges arrive via webhooks.
Critical Webhook Events to Handle
| Event | What to Do |
|---|---|
payment.succeeded | Confirm order, send receipt, trigger fulfillment |
payment.failed | Notify customer, retry or cancel order |
charge.refunded | Update order status, adjust revenue records |
charge.dispute.created | Alert team, gather evidence, respond within deadline |
customer.source.updated | Card was replaced (lost/expired) — network token auto-handled this |
payment_method.expiring | Notify customer to update card before next billing |
Idempotency rule: Webhooks can be delivered more than once. Always check if you've already processed an event ID before acting on it.
Refunds, Disputes, and Chargebacks
Refund (you initiate)
Dispute / Chargeback (bank initiates)
| Metric | Healthy | Dangerous |
|---|---|---|
| Chargeback rate | < 0.5% | > 1% (card networks may fine or terminate you) |
| Refund processing | 5-10 business days | Varies by issuer |
| Evidence deadline | Typically 7-21 days | Miss it = automatic loss |
Always refund proactively when the customer has a valid complaint. A refund costs you the transaction. A chargeback costs you the transaction + a fee ($15-25) + damage to your chargeback ratio.
Idempotency and Retry Logic
Network failures happen. Without idempotency, a retry can double-charge the customer.
POST /v1/charges
Idempotency-Key: order_12345_attempt_1
| Scenario | Without Idempotency Key | With Idempotency Key |
|---|---|---|
| Request times out, client retries | Double charge | Gateway returns original result |
| Network error mid-response | Unknown state — manual reconciliation | Safe to retry |
| Webhook arrives before API response | Race condition | Deduplicate on charge_id |
Rules:
- Generate the key from your order ID + attempt number, not random UUIDs.
- Keys typically expire after 24-48 hours at the gateway.
- Store the idempotency key alongside the order so you can retry with the same key.
Architect's Cheat Sheet
| Decision | Recommendation |
|---|---|
| Integration pattern | Hosted fields or redirect — never direct API |
| What to store | Token, last4, brand, expiry, fingerprint, charge_id |
| CVV | Authorization only. Never stored. Period. |
| Gateway keys | Secret Manager / environment variables — never in code |
| Idempotency | Always send an idempotency key with charge requests |
| Subscriptions | Use network tokens for higher auth rates (+2-4%) |
| 3DS2 | Always send browser/device data. Required in EU (PSD2/SCA) |
| Webhooks | Verify signatures. Process async. Deduplicate on event ID |
| Refunds vs. chargebacks | Refund proactively — chargebacks cost more + hurt your ratio |
| Logging | Scrub all payment request/response bodies. Never log card data. |
| Checkout page scripts | CSP headers + SRI on all scripts (PCI DSS v4.0 Req 6.4.3) |
| Page tamper detection | Monitor checkout DOM for unauthorized changes (Req 11.6.1) |
Conclusion
Keep card data in the gateway's environment, not yours. Use hosted fields or a redirect, receive tokens, store only the safe subset. Your compliance drops from 300+ controls to ~22, and the sensitive data problem is solved at the infrastructure layer — not patched at the application layer.
Think coat check: you never carry the coat, you carry the ticket. Design your entire payment stack around that principle, and PCI compliance becomes a paperwork exercise rather than an engineering overhaul.