PCI Compliance in Practice: Building Secure Payment Integrations
Real-world experience implementing PCI-compliant payment systems. IVR integration, tokenization strategies, and audit preparation.
PCI Compliance in Practice: Building Secure Payment Integrations
At Interflora, we achieved PCI-DSS Level 1 compliance for a telesales platform processing millions of transactions annually. The journey taught me that PCI compliance isn't about checking boxes—it's about fundamentally rethinking how cardholder data flows through your systems. Here's a practical guide based on that experience.
Understanding PCI-DSS Scope
Compliance Levels
| Level | Transaction Volume | Requirements |
|---|---|---|
| Level 1 | 6M+ annually | On-site audit by QSA |
| Level 2 | 1M-6M annually | SAQ-D or on-site audit |
| Level 3 | 20K-1M annually | SAQ-D |
| Level 4 | <20K annually | SAQ (varies) |
The Scope Equation
Everything that stores, processes, or transmits cardholder data is in scope. This includes:
- Systems directly handling card numbers
- Systems connected to those systems
- Systems that could affect the security of those systems
Critical insight: The most effective PCI strategy is scope reduction, not scope protection.
Scope Reduction Strategies
Strategy 1: Tokenization
Replace actual card numbers with tokens before they enter your systems:
WITHOUT Tokenization:
Customer → Your App → Your DB → Payment Processor
[PAN data flows through everything]
[Everything is in PCI scope]
WITH Tokenization:
Customer → Payment Processor → Your App → Your DB
[Card entered at processor]
[Your systems only see tokens]
[Minimal PCI scope]Implementation approach:
// Frontend: Direct card entry to payment provider
const stripe = await loadStripe(publishableKey);
const elements = stripe.elements();
const cardElement = elements.create('card');
// Card data goes directly to Stripe, never touches your server
const { token, error } = await stripe.createToken(cardElement);
// Your backend only receives the token
await fetch('/api/checkout', {
method: 'POST',
body: JSON.stringify({
paymentToken: token.id, // Safe to store
orderId: 'order-123'
})
});Strategy 2: Hosted Payment Pages
Redirect card entry entirely to a payment processor's hosted page:
Customer → Your Checkout → Processor Hosted Page → Processor
↓ ↓
Order details Card entry
(in scope: minimal) (in scope: processor)Considerations:
| Approach | UX Impact | Scope Reduction | Control |
|---|---|---|---|
| Hosted page (redirect) | More friction | Maximum | Minimal |
| Hosted iframe | Moderate | High | Moderate |
| Hosted fields | Minimal | High | Good |
| Direct integration | None | None | Maximum |
Strategy 3: IVR for Telesales
The Interflora solution: agent-assisted IVR payment capture.
The problem: Call center agents traditionally hear and enter card numbers, putting the entire call center environment in PCI scope.
The solution: Mid-call IVR handoff
Traditional Telesales:
Agent → Hears card number → Enters in system → Processes payment
[PCI scope: agent environment, agent systems, workstations,
network, call recordings, training materials...]
IVR-Assisted:
Agent → Transfers to IVR → Customer enters card via DTMF → IVR processes
[Agent remains on line but audio is muted during card entry]
[PCI scope: IVR system only]Technical implementation:
Call Flow:
1. Agent takes order details
2. Agent initiates payment transfer: "I'll now transfer you to our secure payment line"
3. System places agent on silent hold (can monitor, cannot hear DTMF)
4. IVR prompts: "Please enter your 16-digit card number"
5. Customer enters card via phone keypad
6. IVR captures and tokenizes in real-time
7. Payment processed, result returned
8. Agent reconnected: "Your payment was successful"Key requirements:
- DTMF tones suppressed from agent audio
- No card data in call recordings
- IVR system isolated in separate network segment
- Agent screens show only masked data (****1234)
Technical Controls Implementation
Network Segmentation
Create a Cardholder Data Environment (CDE) with strict boundaries:
Corporate Network CDE (Cardholder Data Environment)
┌──────────────────────┐ ┌──────────────────────┐
│ │ │ │
│ App Servers │ FW/IDS │ Payment Gateway │
│ Web Servers │ ◄────────► │ IVR System │
│ Databases (no PAN) │ │ Token Vault │
│ │ │ │
└──────────────────────┘ └──────────────────────┘
Network Rules:
- Only specific ports/protocols allowed between zones
- All traffic logged and monitored
- No direct internet access from CDE
- Jump host required for administrative accessEncryption Requirements
In Transit:
- TLS 1.2+ for all connections
- Strong cipher suites only
- Certificate pinning for payment APIs
- No SSL/early TLS (deprecated)At Rest:
// Key management with HSM or cloud KMS
const encryptPAN = async (pan: string): Promise<string> => {
const key = await kms.getKey('pan-encryption-key');
// AES-256-GCM encryption
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
let encrypted = cipher.update(pan, 'utf8', 'base64');
encrypted += cipher.final('base64');
const authTag = cipher.getAuthTag();
// Store: IV + AuthTag + Encrypted data
return Buffer.concat([iv, authTag, Buffer.from(encrypted, 'base64')]).toString('base64');
};Key management principles:
- Dual control for key creation
- Split knowledge for key custodians
- Annual key rotation (minimum)
- Secure key destruction procedures
Access Control
Role-Based Access Control (RBAC):
Role: payment_admin
├── Can: View masked PANs, manage configurations
├── Cannot: View full PANs, export data
└── MFA: Required
Role: support_agent
├── Can: View last 4 digits, process refunds (with supervisor)
├── Cannot: View full PANs, change configurations
└── MFA: Required
Role: developer
├── Can: Access non-production environments
├── Cannot: Access production CDE, view any PAN data
└── MFA: Required for VPN
All Access:
- Unique user IDs (no shared accounts)
- Automatic lockout after 6 failed attempts
- 90-day password rotation
- Session timeout: 15 minutes idleLogging and Monitoring
PCI DSS Requirement 10 mandates comprehensive logging:
// Structured security logging
interface SecurityAuditLog {
timestamp: string;
eventType: 'access' | 'modify' | 'delete' | 'authenticate';
userId: string;
sourceIp: string;
resource: string;
action: string;
result: 'success' | 'failure';
details: Record<string, unknown>;
}
// Log all access to cardholder data
const logCardDataAccess = async (
userId: string,
action: string,
maskedPan: string
): Promise<void> => {
await auditLogger.log({
timestamp: new Date().toISOString(),
eventType: 'access',
userId,
sourceIp: getClientIp(),
resource: 'cardholder_data',
action,
result: 'success',
details: {
maskedPan, // Never log full PAN
sessionId: getSessionId()
}
});
};Log retention:
- Minimum 1 year retention
- 3 months immediately accessible
- Tamper-evident storage
- Regular integrity verification
Audit Preparation
The QSA Visit
A Qualified Security Assessor (QSA) audit involves:
- Document Review: Policies, procedures, network diagrams
- Technical Testing: Vulnerability scans, penetration tests
- Interviews: Staff at all levels about security procedures
- Evidence Collection: Logs, configurations, access records
Evidence Organization
PCI Evidence Repository:
├── policies/
│ ├── information-security-policy.pdf
│ ├── access-control-policy.pdf
│ └── incident-response-plan.pdf
├── network/
│ ├── network-diagram-current.vsdx
│ ├── data-flow-diagram.vsdx
│ └── firewall-rules-export.csv
├── testing/
│ ├── quarterly-vuln-scans/
│ ├── annual-pentest-report.pdf
│ └── segmentation-test-results.pdf
├── access/
│ ├── user-access-review-Q1.xlsx
│ ├── terminated-users-log.csv
│ └── privilege-access-list.xlsx
└── training/
├── security-awareness-completion.csv
└── training-materials/Continuous Compliance
Don't treat PCI as an annual event:
Monthly:
- Review access logs for anomalies
- Verify terminated user access removed
- Check for unpatched systems
Quarterly:
- ASV vulnerability scans
- Firewall rule review
- Wireless network scans (if applicable)
Annually:
- Penetration testing
- Risk assessment update
- Policy review and update
- Security awareness trainingCommon Pitfalls
1. Scope Creep
Problem: Systems gradually connect to CDE, expanding scope
Solution: Change management requires PCI impact assessment
2. Test Data Issues
Problem: Using production card data in test environments
Solution: Data masking and synthetic test data
// Generate valid test card numbers (Luhn-valid, non-real)
const testCards = {
visa: '4111111111111111',
mastercard: '5500000000000004',
amex: '340000000000009'
};3. Logging Sensitive Data
Problem: Full PANs appearing in logs, error messages, or debug output
Solution: Mask at the source
const maskPan = (pan: string): string => {
if (!pan || pan.length < 13) return '****';
return pan.slice(0, 6) + '****' + pan.slice(-4);
};
// Ensure masking in all log contexts
logger.addTransform((log) => {
return JSON.parse(JSON.stringify(log, (key, value) => {
if (key.toLowerCase().includes('pan') ||
key.toLowerCase().includes('card')) {
return typeof value === 'string' ? maskPan(value) : value;
}
return value;
}));
});4. Third-Party Risk
Problem: Assuming vendors handle PCI compliance
Solution: Validate and monitor third-party compliance
- Request AOC (Attestation of Compliance) annually
- Include PCI requirements in contracts
- Monitor for security incidents at vendors
Key Takeaways
- Scope reduction is your best strategy: Use tokenization, hosted pages, or IVR to minimize what's in your PCI scope
- Network segmentation is foundational: Isolate the CDE with strict access controls
- Encryption requires key management: Strong encryption is useless without proper key handling
- Logging enables detection: Comprehensive, tamper-evident logs are essential
- Continuous compliance beats annual panic: Monthly and quarterly activities prevent audit surprises
- Third parties share responsibility: Validate their compliance and monitor for issues
- Document everything: If it's not documented, it didn't happen (according to auditors)
PCI compliance is demanding, but it forces you to think systematically about data security. The disciplines you build for PCI—scope management, access control, logging, and continuous monitoring—improve your security posture far beyond payment processing.