What frontend engineers building card payment flows actually need to know about PCI DSS, tokenization, and hosted checkout.
You just integrated a card payment flow. You called the payment provider's checkout. The 200 came back. Are you PCI DSS compliant?
The honest answer is: it depends on how you built it. And most frontend engineers have never been told the difference.
PCI DSS compliance is usually framed as a backend or infrastructure concern. But if your frontend ever touches — or could touch — cardholder data, you're in scope. The rules apply to your code too.
This is what frontend engineers building card payment flows actually need to know.
The Payment Card Industry Data Security Standard (PCI DSS) is a set of security requirements established by the PCI Security Standards Council — a body founded by Visa, Mastercard, American Express, Discover, and JCB.
It applies to any system that stores, processes, or transmits cardholder data. "Cardholder data" means the Primary Account Number (PAN — the 16-digit card number), the cardholder name, expiry date, service code, and the sensitive authentication data (CVV/CVC, PIN).
The standard has 12 top-level requirements covering network security, data protection, access control, monitoring, and regular testing. But your obligations depend heavily on your implementation scope — specifically, which Self-Assessment Questionnaire (SAQ) applies to you.
This is the question that determines everything. Before writing a line of payment code, you need to understand which PCI scope level you're operating in.
SAQ A
You redirect users to a hosted payment page, or embed an iframe that the payment provider controls entirely. Your frontend never touches card data. Lowest burden — roughly 22 requirements.
SAQ A-EP
You serve the payment page but use hosted fields (iframes) for card entry. You control the page but not the card inputs. Medium burden — your server and delivery infrastructure matter.
SAQ D
Your frontend or backend handles raw card data directly. Full 300+ control assessment. You almost certainly don't want this. If your code ever sees a PAN, you're here.
If you're building the payment page and the user types a card number into an input field your code controls — you're in SAQ D scope. That's the category where card breaches happen. Aim for SAQ A.
The safest — and frankly, correct — approach for most applications is to never let card data touch your infrastructure at all.
Hosted checkout works in two patterns:
Redirect
User leaves your site and lands on the provider's payment page. After payment, they're redirected back. You never see the card.
Hosted Fields
Card inputs are iframes from the provider. They sit inside your design, but your JavaScript cannot read their contents. The provider tokenizes directly.
What your frontend does in either case: collect non-sensitive data (amount, customer ID, billing address if needed), initiate a session or intent with your backend, receive a token or redirect, and handle the result.
Key rule
Your JavaScript must never be able to read the card number, CVV, or full expiry from a hosted field iframe. If you can — you're out of scope for SAQ A.
These aren't just "bad practice." Each one is a PCI violation that can result in fines, loss of ability to process card payments, and — more importantly — real harm to real people.
Never do this
The third-party script restriction deserves extra attention. Magecart attacks — where attackers inject card-skimming code into payment pages via compromised third-party scripts — have stolen millions of card numbers from merchants who thought they were secure. Your first-party code can be clean and still get you breached through a widget you loaded.
Even with hosted checkout (SAQ A), your payment page has security obligations. The page that loads the payment iframe is still your responsibility.
Content Security Policy
CSP is your primary defense against XSS and script injection. For payment pages, restrict aggressively.
Content-Security-Policy:
default-src 'self';
script-src 'self' https://js.stripe.com;
frame-src 'self' https://js.stripe.com;
connect-src 'self' https://api.stripe.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data:;Notice there's no unsafe-eval and no wildcard origins. Any script that can run on your payment page is a potential attack vector.
Subresource Integrity
If you load any third-party script on a payment page — even the payment provider's own SDK — use SRI hashes to ensure the script hasn't been tampered with.
<script
src="https://example.com/payment-sdk.js"
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
crossorigin="anonymous"
></script>Additional headers
| Strict-Transport-Security | max-age=31536000; includeSubDomains |
| X-Content-Type-Options | nosniff |
| X-Frame-Options | DENY |
| Referrer-Policy | strict-origin-when-cross-origin |
Tokenization is what makes the hosted model work. Here's what actually happens when a user submits a payment form:
The key insight
A token is useless without the provider's secret key. It can't be charged by anyone except your backend with the correct credentials. You can safely pass it through URLs, log it, and store it — it's meaningless without the key.
If your codebase gets a Fortify scan (or similar static analysis), here's what the scanner will flag in payment-adjacent code:
Hardcoded secrets
Secret keys, API tokens in source code — the scanner checks string literals and environment variable names. Publishable/public keys are fine; secret keys are not.
Potential card data in logs
Any console.log, error logging, or network request that references variables named 'card', 'pan', 'cvv', 'expiry' near form fields.
Mixed content
Loading any resource (scripts, iframes, API endpoints) over HTTP from an HTTPS page.
Overly permissive CSP
unsafe-eval, unsafe-inline, or wildcard * in script-src — especially on pages with payment forms.
Reflected XSS vectors
Success or failure pages that display URL parameters (returnUrl, message, status) without sanitization.
Insecure direct object references
Payment confirmation pages that accept transaction IDs from the URL and display them without validation.
Use this before shipping any card payment feature. Each item maps to a real PCI DSS control.
0 / 17 checked
HTTPS & Transport
Card Data
Hosted Widgets
Scripts & CSP
Secrets & Keys
XSS & Input
PCI DSS isn't bureaucracy for its own sake. The rules exist because the internet showed us what happens without them — and what happens is that real people lose money, accounts get drained, and trust gets destroyed.
Every requirement in this article traces back to an actual breach pattern. Card data in logs. Skimming scripts. Redirect vulnerabilities. These aren't theoretical — they're documented, they've happened to production systems at scale, and they'll happen again to someone who skipped the checklist.
Understanding PCI scope before you write a line of payment code isn't optional. It shapes your architecture, your component design, your header configuration, and your third-party choices. Get it right early.