PCI DSS is written for organisations, not for engineers, which is why most developers meet it as a list of things someone in compliance says they cannot do. This page covers the parts that actually change how you write code: which data you may store, which you may never store, what “reducing scope” means in practice, and why your test environment is not allowed to contain real card numbers.

This is general technical information, not compliance advice. The authoritative source is the PCI Security Standards Council, and a real compliance programme needs a Qualified Security Assessor.

What PCI DSS is

The Payment Card Industry Data Security Standard is published by the PCI Security Standards Council, founded by Visa, Mastercard, American Express, Discover, and JCB.

It is not a law. It is a contractual condition of accepting card payments, which in practice makes it more immediate than most regulation: the consequences of non-compliance are financial penalties, liability for the cost of a breach, and ultimately losing the ability to take card payments at all. There is no regulator to appeal to, only your acquirer.

The current standard is PCI DSS v4, with v4.0.1 the revision published in the Council’s document library. Version 3.2.1 has been retired, and the future-dated requirements introduced with v4 are now in force. Requirement numbering changed between the two versions — the rule about test data, for instance, moved from 6.4.3 to 6.5.5 — so a Stack Overflow answer citing a requirement number may be pointing at the right rule under the wrong index.

The data categories

This table is the part worth committing to memory. Everything else follows from it.

DataStorage permitted?Must be protected if stored?
Cardholder Data
Primary Account Number (PAN)YesYes — rendered unreadable
Cardholder nameYesYes
Expiry dateYesYes
Service codeYesYes
Sensitive Authentication Data
Full magnetic stripe or chip track dataNever after authorisation
CAV2 / CVC2 / CVV2 / CIDNever after authorisation
PIN and PIN blockNever after authorisation

Sensitive Authentication Data may not be retained after authorisation — not encrypted, not hashed, not in a log file, not “temporarily”. The prohibition is absolute. This is the single rule most likely to be violated by accident, because security codes end up in application logs, error tracker payloads, and support screenshots without anyone deciding to store them.

The distinction is easier to remember through what each field is for. A PAN identifies an account, which is why it can legitimately persist. A security code proves that whoever is using the card is holding it at that moment, which is why it has no purpose after authorisation and no justification for existing afterwards. Ours is a random value of the right length precisely because a real one cannot be derived from the number — see the CVV generator for what that means when you need test input.

Where card data accidentally lands

Almost nobody decides to store card data improperly. It arrives through infrastructure built for other purposes:

LocationHow it gets therePrevention
Application logsLogging the whole request bodyRedact by field name at the logger, not the call site
Error trackersException context carries form dataConfigure denyUrls and scrub payment fields in the SDK
Analytics eventsForm values sent as event propertiesNever pass raw input as event data
localStorage / sessionStoragePersisting form state for UXExclude payment fields from any state persistence
Database backupsAn unencrypted PAN column, copied off-siteEncrypt the field, not just the disk
Support ticketsCustomers paste details; agents screenshotAuto-redact card-shaped strings on ingest
Session replay toolsInput fields recorded by defaultMask all inputs, then allowlist — never the reverse
Crash dumps and core filesRaw memory contentsDisable dumps on payment services
CDN and proxy logsQuery strings and POST bodiesNever put card data in a URL; audit access logs
Git historyOne test fixture with a real card, committed oncePre-commit secret scanning; treat history as permanent

The last two deserve emphasis because they are rarely discussed. Session replay tools record input fields unless configured otherwise, and the default is usually to capture. Git history is worse: a card number committed once and deleted in the next commit is still in the repository, in every clone, on every developer’s laptop, and in your CI cache. Rotating that out means rewriting history across every fork.

Reducing scope — the only real strategy

Every system that stores, processes, or transmits cardholder data is in scope. The most effective compliance strategy is not to secure more systems; it is to have fewer systems touch the data.

In order of effectiveness:

  1. Hosted payment page or redirect. The customer enters card details on the provider’s page. The data never reaches your infrastructure at all. Narrowest possible scope.
  2. Hosted fields or iframes — Stripe Elements, Braintree Hosted Fields, Adyen Components. The input fields live in the provider’s iframe, so card data never enters your DOM even though the form looks like yours.
  3. Tokenisation. The provider stores the PAN and hands you a token that is meaningless outside their system. Tokens are not cardholder data, so storing them keeps you out of scope — covered in the tokenisation guide.
  4. Network segmentation. Where card data genuinely must be handled, isolate those systems so the rest of your estate is not dragged in with them.
  5. Encryption at rest with your own vault. Broadest scope, highest cost, and the option to choose only when the first four are impossible.

Each step down that list adds systems, audits, and cost. The corresponding self-assessment questionnaires make the trade explicit:

IntegrationSAQRoughly
Redirect or hosted payment pageAShortest
Hosted fields on your own pageA-EPModerate, and your page’s scripts are in scope
Card data handled by your systemsDLongest by a wide margin

Your acquirer decides which questionnaire applies. It is worth asking before you choose an integration pattern rather than after.

One nuance in the middle row catches teams out. Hosted fields keep card data out of your DOM, but every script on the page containing them can still alter that page — swap the iframe, overlay a fake field, or read what the customer types before the real field receives it. That is the mechanism behind digital skimming attacks, and it is why v4 added explicit expectations around managing and monitoring the scripts on payment pages. If your checkout loads a tag manager, a chat widget, an A/B testing snippet, and three analytics libraries, you have four vendors who can modify your payment page, and the compliance question is whether you can say what each of them is doing today.

Requirement 3 — protecting stored data

The parts a developer implements:

  • Sensitive Authentication Data is not retained after authorisation. Absolute, as above.
  • PAN is masked when displayed — a maximum of the first six and last four digits, and only where a role has a documented need for more than the last four.
  • Stored PAN is rendered unreadable by one of: a one-way hash with a salt, truncation, a token, or strong cryptography with proper key management.

That salt is not optional, and the reason is arithmetic. The search space for a card number is small: a known BIN, a known length, and a Luhn-valid final digit leave only the account identifier free — often seven or eight digits, which is trivially brute-forceable against an unsalted hash. Hashing a PAN without a salt produces something that looks protected and is not. The structure guide works through why so few digits are actually unknown.

Requirement 6 — secure development

Requirement 6 is where PCI DSS reads most like a normal engineering standard: secure coding practices and training, code review or automated security testing before release, protection against known classes of vulnerability — the OWASP Top Ten is the usual reference — change management with separated environments, and keeping dependencies patched.

It also contains the requirement this site exists to help with.

Test environments — why synthetic data is required

Production data may not be used for testing or development. This is not a recommendation; it is a requirement — Requirement 6.5.5 in version 4, previously 6.4.3 in version 3.2.1 — and it exists because test environments are consistently less protected than production: looser access control, shared credentials, data copied to laptops, screenshots in tickets.

That leaves two options. Mask or remove the card data before it moves, which is work you must repeat correctly on every refresh forever. Or generate synthetic data that never contained anything real, which is repeatable and cannot leak what it never held.

What a realistic test data set needs:

  • Synthetic PANs across every network you acceptthe generator for individual cases, the bulk generator for fixture files and load tests.
  • Gateway sandbox cards for anything requiring a processor response — approvals, declines, 3-D Secure. The test card reference collects them, and the Stripe set is documented in full.
  • Synthetic names and addresses, because a production dump is not only about card numbers — the identity generator covers the rest of the row.
  • Deliberately invalid data for negative paths: broken check digits, wrong lengths, expired dates. The validator is a quick way to confirm a fixture fails the way you intended.

The single most common violation in this whole area is copying a production database dump into staging. It is done for good reasons — realistic data finds real bugs — and it moves the most sensitive data you hold into the least protected environment you run.

This also intersects with data protection law. A production dump in staging is still personal data under the GDPR, with the same lawful basis requirements and the same breach obligations, sitting in an environment that is usually easier to compromise. Two regimes, one bad habit.

What developers most often get wrong

  1. Logging the entire request body, payment fields included.
  2. Sending form data to an error tracker as exception context.
  3. Copying a production dump into staging.
  4. Keeping the CVV in session “just until the retry”.
  5. Using a real card in a test fixture — and committing it.
  6. Hashing a PAN without a salt.
  7. Leaving input recording on in a session replay tool.
  8. Masking the PAN in the interface while storing it in full in the database.

Every one of these is an accident of convenience rather than a decision, which is why they survive code review: nothing in the diff looks like a compliance decision.

A practical developer checklist

  • No card data in application logs — verified by grepping logs, not by reading code
  • Payment fields scrubbed in the error tracker SDK
  • No card data in analytics or product telemetry
  • Session replay masks all inputs by default
  • No card data in localStorage, sessionStorage, or cookies
  • Card data never appears in a URL or query string
  • CVV never persisted anywhere, including caches and session stores
  • Stored PAN encrypted, tokenised, truncated, or salted-hashed
  • Display masking applied at the API boundary, not only in the UI
  • Test and staging environments contain synthetic data only
  • Test fixtures generated, never derived from production
  • Pre-commit secret scanning covers card-shaped strings
  • Database backups encrypted, with access logged
  • Crash dumps disabled on services that handle payments
  • Dependency scanning running in CI
  • Integration pattern and its SAQ confirmed with your acquirer

The test data management guide covers how to keep such a fixture set healthy over time, and the payment form checklist covers what to assert once you have one.

Where to get authoritative answers

  • The PCI Security Standards Council — the standard itself, plus the SAQs and the Prioritized Approach
  • The document library for the current version and its summary of changes
  • The Qualified Security Assessor directory when you need a formal opinion
  • Your payment provider’s compliance documentation, which will tell you which SAQ its integrations map to
  • Your acquirer, who ultimately determines your validation requirements

Again: this page is technical background, not compliance advice. Requirement numbers change between versions, interpretations vary by acquirer, and only a QSA can tell you where your specific systems stand.

The requirement numbers and version details above were last checked against provider documentation on . Providers do change what they publish — the official link beside each claim is authoritative.

Frequently Asked Questions

Yes, if you have a documented business need and you render the number unreadable wherever it is stored — through strong encryption with proper key management, a one-way hash with a salt, truncation, or a token. The better question is whether you should. Every system holding a PAN is inside your compliance scope, and the cheapest PAN to protect is the one your servers never receive.
No. The security code is Sensitive Authentication Data, and it may not be retained after authorisation under any circumstances — not encrypted, not hashed, not temporarily, not in a log. The prohibition is absolute, and it is the rule most often broken by accident rather than decision, because CVVs reach logs and error trackers without anyone choosing to store them.
Yes, but the obligation is much smaller. Using a hosted payment page or hosted fields means card data never touches your servers, which reduces you to the shortest self-assessment questionnaire rather than removing you from scope. What remains is real: your integration, your redirects, and the scripts running on your payment page are all still yours to secure.
No. PCI DSS states that live PANs are not used in pre-production environments — Requirement 6.5.5 in version 4, previously 6.4.3 in version 3.2.1. The reasoning is practical: test systems have looser access control, shared credentials, data copied to laptops, and screenshots in tickets. Synthetic test data is the prescribed alternative.
At most the first six and the last four digits, and that is a ceiling rather than a target. Most interfaces show only the last four, which is enough for a customer to recognise which card they used. Anything beyond first-six-and-last-four needs a documented business justification for that specific role.
A Self-Assessment Questionnaire — the form a merchant completes to attest to its compliance, with a different version for each integration pattern. A redirect or hosted payment page maps to SAQ A, the shortest; hosted fields on your own page map to SAQ A-EP; handling card data directly maps to SAQ D, which is by far the longest. Your acquirer decides which applies to you.