
August 29, 2026
13 min read
Table of Contents
By Kokil Thapa | Last reviewed: August 2026
If your application touches payments—even indirectly—you need PCI DSS Essentials for Developers on your checklist before you ship checkout code. PCI DSS (Payment Card Industry Data Security Standard) is not a certificate you paste on a footer; it is a set of technical and operational controls that define how cardholder data must be protected from the moment it enters your system until it reaches a qualified processor. On real client projects—Laravel eCommerce stores, legal-tech portals with deposit payments, WooCommerce sites accepting international cards—I have seen teams assume that using Stripe or Khalti means PCI is "handled." That is only half true. The gateway handles processing; you still own scope, logging, server hardening, and how checkout is wired. This guide maps what developers actually control, with patterns that work on Laravel payment integrations and similar stacks in 2026.
What is PCI DSS and why does it matter to application developers?
PCI DSS is a global security standard maintained by the PCI Security Standards Council. Any organisation that stores, processes, or transmits cardholder data must comply. For developers, the practical impact is architectural: your code, database schema, logs, backups, and third-party integrations all determine scope—how much of your infrastructure falls inside the Cardholder Data Environment (CDE).
PCI DSS 4.0 is the current baseline in 2026. Version 4.0 introduced stricter requirements around multi-factor authentication, targeted risk analysis for vulnerability management, and more explicit expectations for custom software. You do not need to memorise all 300+ sub-requirements, but you must understand the twelve requirement domains at a high level and know which ones your code directly affects.
The twelve domains span network security, access control, vulnerability management, monitoring, and policy. Developers touch Requirements 3 (protect stored cardholder data), 4 (encrypt transmission), 6 (secure development), 8 (identify and authenticate), and 10 (log and monitor). Ignore these during sprint planning and you will expand scope, fail an audit, or—worse—leak card data into logs and backups that nobody thought to encrypt.
Cardholder data vs sensitive authentication data
PCI defines two categories you must never confuse:
- Cardholder data (CHD): Primary Account Number (PAN), cardholder name, expiration date, service code.
- Sensitive authentication data (SAD): Full magnetic stripe data, CAV2/CVC2/CVV2/CID, PIN/PIN blocks. SAD must never be stored after authorization—even encrypted.
You may store PAN if you have a documented business need and strong encryption (Requirement 3), but most web applications should never store PAN at all. Tokenization—replacing PAN with a non-reversible or gateway-scoped token—is the standard pattern on production Laravel and WooCommerce builds I maintain.
How do you reduce PCI scope in a web application?
Scope reduction is the highest-leverage PCI work a developer can do. Every server, container, database table, queue worker, log aggregator, and backup vault that touches cardholder data sits inside the CDE. Connected systems that could affect CDE security fall into connected-to-scope. Everything else is out of scope.
The goal is simple: card numbers never touch your application layer. Achieve that and you may qualify for SAQ A or SAQ A-EP instead of the full SAQ D (hundreds of controls).
Proven scope-reduction patterns
- Hosted payment page or redirect: Customer pays on the gateway domain (eSewa, Khalti, Stripe Checkout). Your app receives only a token or transaction reference via callback.
- Hosted fields / iframe tokenization: Card UI appears embedded but PAN goes directly to the gateway JavaScript SDK. Stripe Elements and similar tools keep PAN off your origin.
- Server-side token charges only: Your Laravel backend stores
pm_xxxor gateway tokens—not PAN—and calls charge APIs over TLS. - Segment networks: Place payment microservices on isolated subnets with strict firewall rules (Requirement 1).
On a digital gift card platform built with Laravel, we used Stripe Checkout exclusively. The application database held order IDs and Stripe payment intent references—never card numbers. That architecture kept the merchant on SAQ A, which is manageable for a small team. Compare that with a custom card form posting to /api/pay—suddenly PHP-FPM workers, MySQL, Redis session stores, and log files all need PCI-grade controls.
Which SAQ type applies to your integration architecture?
Self-Assessment Questionnaires (SAQs) determine which PCI controls your merchant must attest to annually. Developers influence SAQ eligibility through architecture choices. Pick the wrong integration pattern and you force the business into SAQ D-Merchant—roughly 300 controls instead of 30.
| SAQ Type | Typical Integration | Developer Responsibility | Approx. Control Count |
|---|---|---|---|
| SAQ A | Fully outsourced: redirect, iframe, or JS that sends PAN directly to processor | Ensure your servers never receive PAN; secure callback/webhook endpoints | ~30 |
| SAQ A-EP | Embedded checkout where your site affects delivery of payment page (some JS on your domain) | Secure JS delivery, CSP headers, integrity checks, no PAN in your logs | ~180 |
| SAQ D — Merchant | You store, process, or transmit PAN on your systems | Full secure SDLC, encryption, access control, quarterly scans, pen tests | ~300+ |
Local gateways in Nepal—eSewa, Khalti, IME Pay, ConnectIPS—typically use redirect or API-token models where card data stays with banks and wallets. That is favourable for scope. International Stripe/PayPal integrations follow the same principle: use Checkout Sessions or Payment Element rather than rolling your own card form. For a detailed comparison of Nepal gateway flows, see the guide on eCommerce payment gateway options for Nepal.
What secure coding practices does PCI DSS require from developers?
Requirement 6 is where PCI DSS meets your daily pull requests. It mandates secure development practices, code reviews, separation of duties, and protection against common software attacks (SQL injection, XSS, CSRF). PCI DSS 4.0 adds Requirement 6.4.3—change detection on payment pages to detect unauthorised script injection, often implemented via Subresource Integrity (SRI) hashes and Content-Security-Policy headers.
Never log cardholder data
This is the most common developer violation I encounter during production debugging. Someone adds Log::debug($request->all()) on a checkout controller and suddenly CVV values sit in storage/logs/laravel.log, get shipped to centralized logging, and appear in backup snapshots. PCI Requirement 10 explicitly covers log protection; Requirement 3 covers what must not appear in those logs at all.
/* Laravel — safe payment logging pattern */
Log::info('Payment initiated', [
'order_id' => $order->id,
'gateway' => 'stripe',
'amount' => $order->total,
'last_four' => $paymentMethod->card->last4, /* OK — not full PAN */
'intent_id' => $paymentIntent->id,
]);
/* NEVER do this */
Log::debug('Checkout payload', $request->all()); Configure log redaction at the infrastructure level too. Filter patterns matching 13–19 digit sequences before logs leave the application server. Run secrets scanning in Git and CI with gitleaks to catch accidental commits of API keys and test card numbers.
Encrypt data in transit and at rest
Requirement 4 mandates strong cryptography for PAN in transit—TLS 1.2 or higher (TLS 1.3 preferred in 2026). Disable legacy protocols on Nginx or Apache. Use HSTS headers. For any stored PAN you cannot avoid, apply database encryption at rest and in transit with AES-256 and proper key management—never hard-code encryption keys in .env files committed to Git.
# Nginx — minimum TLS for payment endpoints
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; Validate and authenticate every payment callback
Webhook endpoints for Stripe, Khalti, or eSewa callbacks must verify cryptographic signatures before updating order status. Treat callbacks as untrusted input. Use idempotency keys to prevent duplicate charges on retry. Follow established patterns in a webhook design guide for reliability—PCI cares about integrity and non-repudiation, not just uptime.
// Laravel — verify Stripe webhook signature
$payload = $request->getContent();
$sigHeader = $request->header('Stripe-Signature');
try {
$event = \Stripe\Webhook::constructEvent(
$payload,
$sigHeader,
config('services.stripe.webhook_secret')
);
} catch (\UnexpectedValueException | \Stripe\Exception\SignatureVerificationException $e) {
abort(400, 'Invalid signature');
} Apply OWASP controls to payment routes
Requirement 6.2.4 expects protection against OWASP Top 10 vulnerabilities. Payment forms are high-value XSS and CSRF targets. Use Laravel Form Requests, CSRF tokens on every state-changing route, parameterized queries via Eloquent, and output encoding in Blade. Rate-limit checkout and webhook endpoints. The practical checklist in secure Laravel OWASP Top 10 in practice aligns closely with PCI Requirement 6 expectations.
How do you handle card data storage, retention, and database design?
Requirement 3 defines storage rules. Default answer for web developers: do not store cardholder data. Let the payment gateway vault it. Store only tokens and the last four digits for customer support display.
If the business insists on storing PAN—for recurring billing without a gateway vault, for example—you need:
- Strong encryption with documented key rotation (Requirement 3.5–3.7)
- Truncated or masked display (show at most first six and last four)
- Access restricted via RBAC on a need-to-know basis (Requirement 7)
- Data retention limits with automated purge jobs (Requirement 3.1)
- Encrypted backups that are included in scope (a frequent surprise)
Schema design should make violations difficult:
/* Good — orders table */
Schema::create('orders', function (Blueprint $table) {
$table->id();
$table->string('gateway')->index();
$table->string('gateway_transaction_id')->unique();
$table->string('payment_method_token')->nullable();
$table->char('card_last_four', 4)->nullable();
$table->unsignedInteger('amount_paisa');
$table->string('status');
$table->timestamps();
});
/* Bad — never create columns like these */
/* card_number, cvv, expiry, track_data */ Redis and queue payloads deserve the same scrutiny. Serializing a full request object into a Laravel queue job can copy PAN into Redis memory and persistence files. Strip payment fields before dispatching jobs.
What infrastructure and DevOps tasks fall under developer influence?
PCI is not purely an application concern. Requirement 2 covers secure system configuration. Requirement 11 covers vulnerability scanning and penetration testing. Developers who also handle deployment—common on Nepal client projects—must understand how their choices affect compliance cost.
Server hardening checklist
- Disable default accounts and unnecessary services on Ubuntu web servers
- Apply security patches within 30 days for critical CVEs (Requirement 6.3.3)
- Configure UFW or cloud security groups to allow only 443, 22 (restricted IP), and database ports internally
- Run quarterly Approved Scanning Vendor (ASV) external scans on all in-scope IP addresses
- Separate production and staging; never use real PAN in staging—even "test" card numbers belong in sandbox environments only
Shared hosting is generally a poor fit for anything beyond SAQ A redirect checkout because you cannot prove network segmentation or patch control. VPS or cloud instances where you control the OS—paired with guidance from how to secure your website and server in Nepal—give you auditable configuration.
Third-party and supply chain risk
Requirement 12.8 expects due diligence on service providers. If you embed a JavaScript payment library from a CDN, document the vendor, pin versions, and use SRI. PCI DSS 4.0 Requirement 6.4.3 expects you to maintain an inventory of payment page scripts and justify each one. A compromised npm package on checkout is a card skimmer.
Testing and change management
Requirement 6.3.2 mandates pre-production separation. Your CI pipeline should include static analysis (PHPStan level 6+), dependency scanning (Composer audit), and DAST on staging. Document every change to payment pages in your change log—auditors will ask. PCI DSS 4.0 also expects multi-factor authentication for all access into the CDE; if developers SSH into payment servers, MFA is not optional.
Official reference material lives on the PCI Security Standards Council document library. Download the SAQ appropriate to your architecture and the PCI DSS Quick Reference Guide for exact wording—not blog summaries—before attestation.
What should you document before launching a payment feature?
Compliance is partly evidence. Before go-live, produce an architecture diagram showing data flows, list every system component in scope, and record which SAQ you believe applies with written justification from your QSA or acquiring bank. Developers should contribute:
- Data flow diagram: Where PAN enters, which systems touch it, where it is stored (hopefully nowhere).
- Integration inventory: Gateway name, API version, authentication method, webhook URLs.
- Logging policy: Fields allowed in logs; redaction rules; retention period.
- Encryption inventory: TLS versions, database encryption status, backup encryption.
- Access matrix: Who can SSH to production, who can read payment configs, MFA status.
- Incident response contact: What to do if logs show suspected PAN exposure.
On legal-tech portals where clients pay consultation deposits, I treat payment documentation the same as document-upload security policies—business stakeholders sign off, engineering implements, and both sides keep copies for annual SAQ completion. The engineering work is not exotic; the failure mode is skipping documentation until the bank asks for evidence.
Put PCI DSS Essentials for Developers into your next checkout sprint
Payment compliance is an architecture decision first and a checklist second. The developers who stay out of trouble choose hosted checkout, store tokens instead of PAN, verify every webhook, redact logs aggressively, and run TLS 1.3 on every payment route. They know which SAQ their code qualifies for—and they re-evaluate when someone proposes a "simple custom card form." That discipline is the core of PCI DSS Essentials for Developers: shrink scope, write secure code, document data flows, and treat cardholder data as radioactive unless you have a vault built for it.
If you are building or auditing a payment-enabled Laravel app, WooCommerce store, or Nepal eCommerce platform and want a second pair of eyes on scope and integration patterns, get in touch through the contact page. I review checkout architecture, gateway wiring, and server configuration on production systems regularly—and catching a PAN-in-logs mistake before launch beats explaining it to an acquirer after a breach.

