Project 5: Mini Payment Gateway with PCI-Compliant Architecture
Goal: Build practical expertise in payment security by implementing core controls (validation, tokenization, encryption), understanding PCI scope, and producing auditable, compliant artifacts.
Payment Data Boundaries
Payment systems live or die by data boundaries: where PANs can exist, how they move, and who can touch them. You need to draw a clear boundary between the Cardholder Data Environment (CDE) and everything else to reduce scope and risk.
Cryptographic Controls and Key Management
Payments rely on strong symmetric encryption, deterministic tokenization, and strict key lifecycle controls. Key custody, rotation, and HSM-backed operations are as important as the algorithms themselves.
Transaction Flow and Compliance Guarantees
Authorization, capture, and settlement have different security requirements. Compliance (PCI DSS, PCI PIN, 3DS) enforces minimal guarantees that must be reflected in system design.
Concept Summary Table
| Concept Cluster | What You Need to Internalize |
|---|---|
| Data classification | PAN vs token, CDE boundaries, data minimization. |
| Cryptography | AES, KDFs, tokenization, key hierarchy. |
| Transaction security | Auth vs settlement, 3DS, P2PE. |
| Compliance | PCI DSS scope, audit controls, evidence. |
| Risk controls | Rate limits, fraud signals, logging. |
Deep Dive Reading by Concept
| Concept | Book & Chapter |
|---|---|
| PCI DSS | PCI DSS v4.0 — Requirements overview |
| Tokenization | PCI Tokenization Guidelines — Implementation sections |
| Crypto in payments | Cryptography Engineering — Ch. 6-9 |
| Payment flows | Payment Systems in the U.S. — transaction chapters |
| Fraud controls | The Anatomy of the Payment Card Industry — risk sections |
Project Overview
| Attribute | Value |
|---|---|
| Difficulty | Level 3: Advanced |
| Time Estimate | 3-4 weeks |
| Programming Language | Java or C# (production languages for gateways) |
| Knowledge Area | Payment Security / Compliance |
| Key Technologies | PCI DSS, Network Segmentation, Audit Logging |
| Coolness Level | Level 1: Pure Corporate Snoozefest (but deeply educational) |
| Business Potential | 4. The “Open Core” Infrastructure |
Learning Objectives
By completing this project, you will:
- Understand PCI DSS requirements - Learn why each security control exists by implementing them
- Design Card Data Environment (CDE) isolation - Implement network segmentation
- Build comprehensive audit logging - Immutable logs with tamper-evident hashing
- Implement role-based access control - Least-privilege principle for card data
- Handle data retention and purging - Know what can be stored and for how long
- Apply key management with split knowledge - Dual control principles
The Core Question You’re Answering
“How do you build a system that handles credit card data while meeting compliance requirements?”
PCI DSS (Payment Card Industry Data Security Standard) isn’t just bureaucracy—each requirement exists because of real breaches. Building a compliant gateway teaches you why security controls matter.
┌─────────────────────────────────────────────────────────────────────────┐
│ THE PAYMENT GATEWAY'S ROLE │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Merchant │───►│ Payment │───►│ Card │───►│ Issuing │ │
│ │ Website │ │ Gateway │ │ Network │ │ Bank │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ │ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────┐ │
│ │ GATEWAY RESPONSIBILITIES │ │
│ ├────────────────────────────────────┤ │
│ │ │ │
│ │ • Accept card data from merchant │ │
│ │ • Validate and format data │ │
│ │ • Route to correct network │ │
│ │ • Handle responses │ │
│ │ • Store transaction records │ │
│ │ │ │
│ │ SECURITY REQUIREMENTS: │ │
│ │ • Never store CVV/CVV2 │ │
│ │ • Encrypt PAN at rest │ │
│ │ • Log all access │ │
│ │ • Segment network │ │
│ │ • Control who sees data │ │
│ │ │ │
│ └────────────────────────────────────┘ │
│ │
│ THE GATEWAY IS THE MOST SENSITIVE COMPONENT │
│ It touches EVERY card number that flows through │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Deep Theoretical Foundation
1. PCI DSS Requirements Overview
PCI DSS 4.0 has 12 top-level requirements grouped into 6 goals:
┌─────────────────────────────────────────────────────────────────────────┐
│ PCI DSS 4.0 REQUIREMENTS │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ BUILD AND MAINTAIN SECURE NETWORK │
│ ═════════════════════════════════ │
│ 1. Install and maintain network security controls │
│ 2. Apply secure configurations to all system components │
│ │
│ PROTECT ACCOUNT DATA │
│ ════════════════════ │
│ 3. Protect stored account data ← CRITICAL FOR THIS PROJECT │
│ 4. Protect cardholder data with strong cryptography during │
│ transmission over open, public networks │
│ │
│ MAINTAIN A VULNERABILITY MANAGEMENT PROGRAM │
│ ════════════════════════════════════════════ │
│ 5. Protect all systems and networks from malicious software │
│ 6. Develop and maintain secure systems and software │
│ │
│ IMPLEMENT STRONG ACCESS CONTROL MEASURES │
│ ═════════════════════════════════════════ │
│ 7. Restrict access to cardholder data by business need-to-know │
│ 8. Identify users and authenticate access to system components │
│ 9. Restrict physical access to cardholder data │
│ │
│ REGULARLY MONITOR AND TEST NETWORKS │
│ ═══════════════════════════════════ │
│ 10. Log and monitor all access to system components and │
│ cardholder data ← CRITICAL FOR THIS PROJECT │
│ 11. Test security of systems and networks regularly │
│ │
│ MAINTAIN AN INFORMATION SECURITY POLICY │
│ ═══════════════════════════════════════ │
│ 12. Support information security with organizational policies │
│ and programs │
│ │
└─────────────────────────────────────────────────────────────────────────┘
2. Card Data Environment (CDE)
The CDE is the environment where cardholder data is processed, stored, or transmitted. Everything in CDE scope must comply with PCI DSS.
┌─────────────────────────────────────────────────────────────────────────┐
│ CARD DATA ENVIRONMENT BOUNDARIES │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ CORPORATE NETWORK │ │
│ │ (Out of PCI scope if properly segmented) │ │
│ │ │ │
│ │ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │ │
│ │ │ HR System │ │ Email │ │ Intranet │ │ │
│ │ └───────────────┘ └───────────────┘ └───────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ │ (Firewall/Segmentation) │
│ ▼ │
│ ╔═════════════════════════════════════════════════════════════════╗ │
│ ║ CARD DATA ENVIRONMENT ║ │
│ ║ (PCI DSS SCOPE) ║ │
│ ║ ║ │
│ ║ ┌─────────────────────────────────────────────────────────┐ ║ │
│ ║ │ ZONE 1: DMZ │ ║ │
│ ║ │ ┌───────────────┐ ┌───────────────┐ │ ║ │
│ ║ │ │ Load Balancer │ │ WAF (Web App │ │ ║ │
│ ║ │ │ │ │ Firewall) │ │ ║ │
│ ║ │ └───────┬───────┘ └───────────────┘ │ ║ │
│ ║ └──────────┼──────────────────────────────────────────────┘ ║ │
│ ║ │ ║ │
│ ║ ┌──────────▼──────────────────────────────────────────────┐ ║ │
│ ║ │ ZONE 2: APPLICATION │ ║ │
│ ║ │ ┌───────────────┐ ┌───────────────┐ │ ║ │
│ ║ │ │ Gateway API │ │ 3DS Server │ │ ║ │
│ ║ │ │ Server │ │ │ │ ║ │
│ ║ │ └───────┬───────┘ └───────────────┘ │ ║ │
│ ║ └──────────┼──────────────────────────────────────────────┘ ║ │
│ ║ │ ║ │
│ ║ ┌──────────▼──────────────────────────────────────────────┐ ║ │
│ ║ │ ZONE 3: DATA │ ║ │
│ ║ │ ┌───────────────┐ ┌───────────────┐ ┌─────────────┐ │ ║ │
│ ║ │ │ Token Vault │ │ Transaction │ │ HSM │ │ ║ │
│ ║ │ │ (encrypted │ │ Database │ │ (keys) │ │ ║ │
│ ║ │ │ PANs) │ │ │ │ │ │ ║ │
│ ║ │ └───────────────┘ └───────────────┘ └─────────────┘ │ ║ │
│ ║ └─────────────────────────────────────────────────────────┘ ║ │
│ ║ ║ │
│ ║ EVERYTHING HERE MUST: ║ │
│ ║ • Have access controls ║ │
│ ║ • Have logging enabled ║ │
│ ║ • Be hardened (secure configuration) ║ │
│ ║ • Be regularly patched ║ │
│ ║ • Be included in penetration tests ║ │
│ ║ ║ │
│ ╚═════════════════════════════════════════════════════════════════╝ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
3. What Can and Cannot Be Stored
┌─────────────────────────────────────────────────────────────────────────┐
│ DATA STORAGE RULES (PCI DSS Req 3) │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ CARDHOLDER DATA │ │
│ ├─────────────────────────────────────────────────────────────────┤ │
│ │ │ │
│ │ Data Element │ Storage Permitted │ Must Encrypt │ │
│ │ ═══════════════════════│═══════════════════│══════════════ │ │
│ │ Primary Account Number │ YES │ YES (always) │ │
│ │ (PAN) │ │ │ │
│ │ │ │ │ │
│ │ Cardholder Name │ YES │ NO (recommended) │ │
│ │ │ │ │ │
│ │ Service Code │ YES │ NO (recommended) │ │
│ │ │ │ │ │
│ │ Expiration Date │ YES │ NO (recommended) │ │
│ │ │ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ SENSITIVE AUTH DATA │ │
│ │ (NEVER store after authorization!) │ │
│ ├─────────────────────────────────────────────────────────────────┤ │
│ │ │ │
│ │ Data Element │ Storage Permitted │ │ │
│ │ ═══════════════════════│═══════════════════│ │ │
│ │ Full Track Data │ ❌ NEVER │ │ │
│ │ (magnetic stripe) │ │ │ │
│ │ │ │ │ │
│ │ CVV2/CVC2/CID │ ❌ NEVER │ │ │
│ │ (3-4 digit code) │ │ │ │
│ │ │ │ │ │
│ │ PIN / PIN Block │ ❌ NEVER │ │ │
│ │ │ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ WHY THESE RULES? │
│ ═══════════════ │
│ • PAN: Needed for chargebacks, recurring payments │
│ • CVV: Proves card-present. If stolen from DB, attacker could use │
│ for CNP fraud. Delete immediately after auth. │
│ • Track Data: Contains everything needed to clone card physically │
│ • PIN: Obviously critical. Must never exist outside HSM. │
│ │
└─────────────────────────────────────────────────────────────────────────┘
4. Audit Logging Requirements
PCI DSS Requirement 10 mandates comprehensive logging:
┌─────────────────────────────────────────────────────────────────────────┐
│ AUDIT LOGGING REQUIREMENTS │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ WHAT MUST BE LOGGED (10.2): │
│ ═══════════════════════════ │
│ │
│ • All individual user access to cardholder data │
│ • All actions taken by any individual with root or admin privileges │
│ • Access to all audit trails │
│ • Invalid logical access attempts │
│ • Use of identification and authentication mechanisms │
│ • Initialization, stopping, or pausing of audit logs │
│ • Creation and deletion of system-level objects │
│ │
│ WHAT EACH LOG MUST CONTAIN (10.3): │
│ ═══════════════════════════════════ │
│ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ { │ │
│ │ "timestamp": "2024-01-15T14:32:17.123Z", // When │ │
│ │ "user_id": "admin_jsmith", // Who │ │
│ │ "event_type": "DATA_ACCESS", // What type │ │
│ │ "action": "READ", // What action │ │
│ │ "resource": "token_vault", // On what │ │
│ │ "resource_id": "tok_abc123", // Which record │ │
│ │ "source_ip": "10.0.1.50", // From where │ │
│ │ "outcome": "SUCCESS", // Result │ │
│ │ "details": "PAN revealed for chargeback" // Why (if app) │ │
│ │ } │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
│ LOG INTEGRITY (10.5): │
│ ═════════════════════ │
│ │
│ • Logs must be tamper-evident │
│ • Write-once storage or hash chaining │
│ • Centralized log collection (can't delete on source) │
│ • Regular backups │
│ • Retain for at least 1 year, 3 months immediately available │
│ │
└─────────────────────────────────────────────────────────────────────────┘
5. Access Control Model
┌─────────────────────────────────────────────────────────────────────────┐
│ ROLE-BASED ACCESS CONTROL │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ PRINCIPLE: Least Privilege │
│ Only grant access necessary for job function │
│ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ Role │ Can See PAN? │ Can Decrypt? │ Other │ │
│ │ ═════════════════════│══════════════│══════════════│═════════│ │
│ │ │ │ │ │ │
│ │ TRANSACTION_PROCESSOR │ Masked only │ NO │ Auth │ │
│ │ (most operators) │ ****0366 │ │ only │ │
│ │ │ │ │ │ │
│ │ CHARGEBACK_ANALYST │ Full (temp) │ YES │ Read │ │
│ │ (disputes team) │ Must log │ Time-limited │ only │ │
│ │ │ │ │ │ │
│ │ SUPPORT_AGENT │ Last 4 only │ NO │ Lookup │ │
│ │ (customer service) │ │ │ only │ │
│ │ │ │ │ │ │
│ │ SECURITY_ADMIN │ NO │ NO │ Config │ │
│ │ (manages access) │ (ironically) │ │ Audit │ │
│ │ │ │ │ │ │
│ │ KEY_CUSTODIAN_1 │ NO │ Partial key │ Key ops │ │
│ │ KEY_CUSTODIAN_2 │ NO │ Partial key │ Key ops │ │
│ │ (split knowledge) │ │ │ │ │
│ │ │ │ │ │ │
│ │ SYSTEM_ACCOUNT │ N/A │ Automated │ Service │ │
│ │ (batch processes) │ (no human) │ │ account │ │
│ │ │ │ │ │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
│ DUAL CONTROL PRINCIPLE: │
│ ═══════════════════════ │
│ Some operations require two people: │
│ • Key generation: Key Custodian 1 + Key Custodian 2 │
│ • Key rotation: Key Custodian 1 + Key Custodian 2 │
│ • Bulk PAN export: Chargeback Analyst + Security Admin approval │
│ │
└─────────────────────────────────────────────────────────────────────────┘
6. Key Management Requirements
┌─────────────────────────────────────────────────────────────────────────┐
│ KEY MANAGEMENT (PCI DSS Req 3.5-3.7) │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ KEY LIFECYCLE: │
│ ══════════════ │
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │Generate │───►│ Store │───►│ Use │───►│ Rotate │──────┐ │
│ │ │ │ Securely│ │ │ │ │ │ │
│ └─────────┘ └─────────┘ └─────────┘ └────┬────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌───────────┐ │ │
│ │ │ Archive │ │ │
│ │ │ Old Keys │ │ │
│ │ └─────┬─────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌───────────┐ │ │
│ │ │ Destroy │◄─────┘ │
│ │ │ (secure) │ │
│ │ └───────────┘ │
│ │ │
│ ▼ │
│ SPLIT KNOWLEDGE / DUAL CONTROL: │
│ ═══════════════════════════════ │
│ │
│ No single person should know the complete key: │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Custodian A Custodian B Combined │ │
│ │ ═══════════ ═══════════ ════════ │ │
│ │ KEY_PART_1 ⊕ KEY_PART_2 = MASTER_KEY │ │
│ │ (A knows) (B knows) (neither knows) │ │
│ │ │ │
│ │ Both must be present for key ceremonies │ │
│ │ Neither can act alone │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ KEY ROTATION REQUIREMENTS: │
│ ══════════════════════════ │
│ • Cryptoperiod defined (typically 1 year) │
│ • Old keys retained for decryption of historical data │
│ • Rotation process documented and tested │
│ • Emergency rotation procedure for suspected compromise │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Project Specification
What You’ll Build
A complete mini payment gateway including:
- API Server: Authorization, capture, refund endpoints
- Token Vault Integration: Store PANs securely
- Network Segmentation: Simulate CDE isolation
- Audit System: Comprehensive, tamper-evident logging
- Admin Dashboard: Role-based card data access
- Compliance Documentation: Map implementation to PCI DSS requirements
Expected Output
╔═══════════════════════════════════════════════════════════════════════╗
║ PAYMENT GATEWAY - ADMIN DASHBOARD ║
╠═══════════════════════════════════════════════════════════════════════╣
║ ║
║ User: jsmith@acme.com ║
║ Role: CHARGEBACK_ANALYST ║
║ Session: Active (expires in 14:32) ║
║ ║
║ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ║
║ ║
║ TRANSACTION SEARCH ║
║ ────────────────── ║
║ Transaction ID: [TXN-987654321 ] ║
║ [SEARCH] ║
║ ║
║ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ║
║ ║
║ TRANSACTION DETAILS ║
║ ─────────────────── ║
║ ID: TXN-987654321 ║
║ Date: 2024-01-15 14:32:17 UTC ║
║ Merchant: ACME Store (MID: M001234) ║
║ Amount: $149.99 USD ║
║ Status: CAPTURED ║
║ Auth Code: A12345 ║
║ ║
║ CARD INFORMATION ║
║ ──────────────── ║
║ Token: tok_a1b2c3d4e5f6 ║
║ Last 4: 0366 ║
║ Network: Visa ║
║ Expiry: 12/25 ║
║ ║
║ ⚠️ FULL PAN ACCESS ║
║ ───────────────── ║
║ Your role permits PAN reveal for chargeback investigation. ║
║ This action will be logged with your user ID. ║
║ ║
║ Reason for access: [Chargeback CB-2024-0123 ] ║
║ [REVEAL FULL PAN] ║
║ ║
╚═══════════════════════════════════════════════════════════════════════╝
[User clicks REVEAL FULL PAN]
╔═══════════════════════════════════════════════════════════════════════╗
║ ⚠️ SENSITIVE DATA DISPLAYED ║
╠═══════════════════════════════════════════════════════════════════════╣
║ ║
║ Full PAN: 4532015112830366 ║
║ ║
║ This reveal expires in: 00:29 ║
║ Screen capture detection: ACTIVE ║
║ ║
║ AUDIT LOG ENTRY CREATED: ║
║ ───────────────────────── ║
║ Time: 2024-01-15T14:35:42.123Z ║
║ User: jsmith@acme.com ║
║ Action: PAN_REVEAL ║
║ Token: tok_a1b2c3d4e5f6 ║
║ Reason: CB-2024-0123 ║
║ IP: 10.0.1.50 ║
║ ║
║ [CLOSE - I have recorded this information] ║
║ ║
╚═══════════════════════════════════════════════════════════════════════╝
Audit Log Viewer
╔═══════════════════════════════════════════════════════════════════════╗
║ AUDIT LOG VIEWER ║
╠═══════════════════════════════════════════════════════════════════════╣
║ ║
║ Filter: [All Events ▼] Date: [2024-01-15] to [2024-01-15] ║
║ [SEARCH] ║
║ ║
║ ┌────────────────────────────────────────────────────────────────┐ ║
║ │ Time │ User │ Event │ Details │ Hash │ ║
║ │───────────────│──────────│───────────────│──────────────│──────│ ║
║ │ 14:35:42.123 │ jsmith │ PAN_REVEAL │ tok_a1b... │ ✓ │ ║
║ │ 14:32:17.456 │ SYSTEM │ AUTH_SUCCESS │ TXN-987... │ ✓ │ ║
║ │ 14:32:17.234 │ SYSTEM │ AUTH_REQUEST │ MID: M001... │ ✓ │ ║
║ │ 14:30:00.000 │ admin │ LOGIN │ 10.0.1.100 │ ✓ │ ║
║ │ 14:29:45.789 │ admin │ LOGIN_FAIL │ 10.0.1.100 │ ✓ │ ║
║ │ 14:15:22.333 │ SYSTEM │ KEY_ROTATION │ v3 → v4 │ ✓ │ ║
║ │───────────────│──────────│───────────────│──────────────│──────│ ║
║ │ │ ║
║ │ ✓ = Hash chain verified (tamper-evident) │ ║
║ └────────────────────────────────────────────────────────────────┘ ║
║ ║
║ HASH CHAIN VERIFICATION: ║
║ Each log entry hash = SHA256(previous_hash + entry_data) ║
║ Genesis hash: 0x3a7f... (from audit initialization) ║
║ Chain status: VALID (4,231 entries since initialization) ║
║ ║
╚═══════════════════════════════════════════════════════════════════════╝
Project Structure
payment_gateway/
├── src/
│ ├── main/
│ │ ├── Program.cs # Entry point (C#) or Application.java
│ │ ├── Startup.cs # DI configuration
│ │ └── appsettings.json # Configuration
│ ├── api/
│ │ ├── AuthorizationController.cs
│ │ ├── CaptureController.cs
│ │ ├── RefundController.cs
│ │ └── AdminController.cs
│ ├── domain/
│ │ ├── Transaction.cs
│ │ ├── Merchant.cs
│ │ └── Card.cs
│ ├── vault/
│ │ ├── TokenVaultService.cs
│ │ ├── ITokenVault.cs
│ │ └── EncryptionService.cs
│ ├── security/
│ │ ├── AccessControlService.cs
│ │ ├── RoleDefinitions.cs
│ │ └── AuthenticationMiddleware.cs
│ ├── audit/
│ │ ├── AuditLogger.cs
│ │ ├── HashChainService.cs
│ │ └── AuditEntry.cs
│ ├── network/
│ │ ├── ProcessorClient.cs # Mock card network
│ │ └── IssuerSimulator.cs
│ └── compliance/
│ ├── DataRetention.cs
│ └── PCIRequirements.cs
├── web/
│ ├── admin-dashboard/
│ │ ├── index.html
│ │ ├── transactions.html
│ │ └── audit-log.html
│ └── assets/
├── tests/
│ ├── AuthorizationTests.cs
│ ├── AccessControlTests.cs
│ ├── AuditLogTests.cs
│ └── PenetrationTests/ # Security test cases
├── docs/
│ ├── pci-compliance-mapping.md
│ ├── architecture.md
│ └── security-controls.md
├── docker/
│ ├── docker-compose.yml # Simulated network zones
│ ├── Dockerfile.gateway
│ ├── Dockerfile.vault
│ └── Dockerfile.audit
└── README.md
Core API Design
// IPaymentGateway.cs
public interface IPaymentGateway
{
Task<AuthorizationResponse> Authorize(AuthorizationRequest request);
Task<CaptureResponse> Capture(CaptureRequest request);
Task<RefundResponse> Refund(RefundRequest request);
Task<VoidResponse> Void(VoidRequest request);
}
public class AuthorizationRequest
{
public string MerchantId { get; set; }
public string CardToken { get; set; } // Already tokenized
// OR for first-time cards:
public string Pan { get; set; } // Will be tokenized
public string ExpiryDate { get; set; }
public string Cvv { get; set; } // NEVER stored after auth
public decimal Amount { get; set; }
public string Currency { get; set; }
public string OrderId { get; set; }
}
public class AuthorizationResponse
{
public string TransactionId { get; set; }
public string AuthCode { get; set; }
public TransactionStatus Status { get; set; }
public string DeclineReason { get; set; }
public string CardToken { get; set; } // For future use
public string MaskedPan { get; set; } // ****0366
}
// IAuditLogger.cs
public interface IAuditLogger
{
Task Log(AuditEntry entry);
Task<IEnumerable<AuditEntry>> Query(AuditQuery query);
Task<bool> VerifyIntegrity();
}
public class AuditEntry
{
public DateTime Timestamp { get; set; }
public string UserId { get; set; }
public AuditEventType EventType { get; set; }
public string Action { get; set; }
public string ResourceType { get; set; }
public string ResourceId { get; set; }
public string SourceIp { get; set; }
public string Outcome { get; set; }
public string Details { get; set; }
public string PreviousHash { get; set; } // For chain
public string EntryHash { get; set; }
}
// IAccessControl.cs
public interface IAccessControl
{
Task<bool> CanAccess(string userId, string resource, string action);
Task<bool> CanRevealPan(string userId, string reason);
Task<string> GetMaskedPan(string token);
Task<string> GetFullPan(string token, string userId, string reason);
}
Solution Architecture
Network Segmentation (Docker Simulation)
# docker-compose.yml
version: '3.8'
networks:
dmz:
driver: bridge
ipam:
config:
- subnet: 172.16.1.0/24 # DMZ network
application:
driver: bridge
ipam:
config:
- subnet: 172.16.2.0/24 # App network
data:
driver: bridge
internal: true # No external access
ipam:
config:
- subnet: 172.16.3.0/24 # Data network
services:
# DMZ Zone
load-balancer:
image: nginx:alpine
networks:
- dmz
ports:
- "443:443"
# Application Zone
gateway-api:
build:
context: .
dockerfile: docker/Dockerfile.gateway
networks:
- dmz
- application
depends_on:
- token-vault
# Data Zone
token-vault:
build:
context: .
dockerfile: docker/Dockerfile.vault
networks:
- application
- data
environment:
- VAULT_KEY_PATH=/run/secrets/vault_key
secrets:
- vault_key
transaction-db:
image: postgres:15
networks:
- data
volumes:
- tx-data:/var/lib/postgresql/data
audit-db:
image: postgres:15
networks:
- data
volumes:
- audit-data:/var/lib/postgresql/data
secrets:
vault_key:
file: ./secrets/vault_key.enc # Would be HSM in production
volumes:
tx-data:
audit-data:
System Design
┌─────────────────────────────────────────────────────────────────────────┐
│ PAYMENT GATEWAY ARCHITECTURE │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ EXTERNAL │
│ ════════ │
│ ┌─────────────────┐ │
│ │ Merchant │ │
│ │ Application │ │
│ └────────┬────────┘ │
│ │ HTTPS (TLS 1.2+) │
│ ▼ │
│ ╔════════════════════════════════════════════════════════════════╗ │
│ ║ DMZ (172.16.1.0/24) ║ │
│ ║ ┌─────────────────────────────────────────────────────────┐ ║ │
│ ║ │ Load Balancer / WAF │ ║ │
│ ║ │ • TLS termination │ ║ │
│ ║ │ • Rate limiting │ ║ │
│ ║ │ • Request filtering │ ║ │
│ ║ └─────────────────────────┬───────────────────────────────┘ ║ │
│ ╚════════════════════════════│═══════════════════════════════════╝ │
│ │ │
│ ╔════════════════════════════▼═══════════════════════════════════╗ │
│ ║ APPLICATION (172.16.2.0/24) ║ │
│ ║ ┌─────────────────────────────────────────────────────────┐ ║ │
│ ║ │ Gateway API │ ║ │
│ ║ │ ┌────────────────┐ ┌────────────────┐ │ ║ │
│ ║ │ │ Authorization │ │ Access Control │ │ ║ │
│ ║ │ │ Service │ │ Service │ │ ║ │
│ ║ │ └───────┬────────┘ └───────┬────────┘ │ ║ │
│ ║ │ │ │ │ ║ │
│ ║ │ ┌───────▼───────────────────▼────────┐ │ ║ │
│ ║ │ │ Business Logic │ │ ║ │
│ ║ │ │ • Validate request │ │ ║ │
│ ║ │ │ • Route to processor │ │ ║ │
│ ║ │ │ • Handle response │ │ ║ │
│ ║ │ └───────┬─────────────────┬──────────┘ │ ║ │
│ ║ │ │ │ │ ║ │
│ ║ └───────────┼─────────────────┼──────────────────────────┘ ║ │
│ ╚══════════════│═════════════════│══════════════════════════════╝ │
│ │ │ │
│ ╔══════════════▼═════════════════▼══════════════════════════════╗ │
│ ║ DATA (172.16.3.0/24) - INTERNAL ONLY ║ │
│ ║ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────┐ ║ │
│ ║ │ Token Vault │ │ Transaction │ │ Audit Log │ ║ │
│ ║ │ │ │ Database │ │ Database │ ║ │
│ ║ │ • Encrypted │ │ │ │ │ ║ │
│ ║ │ PANs │ │ • TXN records │ │ • Append- │ ║ │
│ ║ │ • Token map │ │ • Masked PAN │ │ only │ ║ │
│ ║ │ │ │ • No CVV ever │ │ • Hash │ ║ │
│ ║ │ │ │ │ │ chain │ ║ │
│ ║ └─────────────────┘ └─────────────────┘ └─────────────┘ ║ │
│ ║ ║ │
│ ║ ┌─────────────────────────────────────────────────────────┐ ║ │
│ ║ │ Key Management (simulated HSM) │ ║ │
│ ║ │ • Master Key (split knowledge) │ ║ │
│ ║ │ • Data Encryption Key │ ║ │
│ ║ │ • Key rotation schedule │ ║ │
│ ║ └─────────────────────────────────────────────────────────┘ ║ │
│ ╚════════════════════════════════════════════════════════════════╝ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Implementation Guide
Phase 1: Basic Authorization Flow
Goal: Implement auth/capture/refund with mock processor.
public class AuthorizationService : IAuthorizationService
{
private readonly ITokenVault _vault;
private readonly IProcessorClient _processor;
private readonly IAuditLogger _audit;
public async Task<AuthorizationResponse> Authorize(AuthorizationRequest request)
{
// 1. Validate request
ValidateRequest(request);
// 2. Tokenize PAN if provided (CVV passed to processor, NOT stored)
string token;
if (!string.IsNullOrEmpty(request.Pan))
{
token = await _vault.Tokenize(request.Pan);
// CVV is passed to processor but NEVER touches our storage
}
else
{
token = request.CardToken;
}
// 3. Send to processor (they decrypt/route to issuer)
var processorResponse = await _processor.Authorize(new ProcessorRequest
{
Token = token,
Amount = request.Amount,
Currency = request.Currency,
MerchantId = request.MerchantId
});
// 4. Create transaction record (NO CVV, NO full PAN)
var transaction = new Transaction
{
Id = GenerateTransactionId(),
MerchantId = request.MerchantId,
Token = token,
MaskedPan = await _vault.GetMaskedPan(token), // ****0366
Amount = request.Amount,
Currency = request.Currency,
Status = processorResponse.Approved ? TransactionStatus.Authorized : TransactionStatus.Declined,
AuthCode = processorResponse.AuthCode,
CreatedAt = DateTime.UtcNow
};
await _transactionRepo.Save(transaction);
// 5. Audit log
await _audit.Log(new AuditEntry
{
EventType = AuditEventType.Authorization,
Action = processorResponse.Approved ? "AUTH_SUCCESS" : "AUTH_DECLINED",
ResourceId = transaction.Id,
Details = $"Amount: {request.Amount} {request.Currency}"
});
return new AuthorizationResponse
{
TransactionId = transaction.Id,
AuthCode = transaction.AuthCode,
Status = transaction.Status,
CardToken = token,
MaskedPan = transaction.MaskedPan
};
}
}
Phase 2: Audit Logging with Hash Chain
Goal: Implement tamper-evident audit logs.
public class HashChainAuditLogger : IAuditLogger
{
private readonly IAuditRepository _repo;
private string _lastHash;
public async Task Log(AuditEntry entry)
{
// 1. Get previous hash
entry.PreviousHash = _lastHash ?? await GetGenesisHash();
// 2. Add timestamp and system info
entry.Timestamp = DateTime.UtcNow;
entry.NodeId = Environment.MachineName;
// 3. Calculate hash of this entry
entry.EntryHash = CalculateHash(entry);
// 4. Store (append-only table)
await _repo.Append(entry);
// 5. Update last hash
_lastHash = entry.EntryHash;
}
private string CalculateHash(AuditEntry entry)
{
// Hash = SHA256(previousHash + timestamp + userId + eventType + action + resourceId + details)
var data = $"{entry.PreviousHash}|{entry.Timestamp:O}|{entry.UserId}|" +
$"{entry.EventType}|{entry.Action}|{entry.ResourceId}|{entry.Details}";
using var sha = SHA256.Create();
var hashBytes = sha.ComputeHash(Encoding.UTF8.GetBytes(data));
return Convert.ToHexString(hashBytes);
}
public async Task<bool> VerifyIntegrity()
{
// Walk entire chain and verify each hash
var entries = await _repo.GetAllInOrder();
string expectedPrevious = await GetGenesisHash();
foreach (var entry in entries)
{
if (entry.PreviousHash != expectedPrevious)
{
return false; // Chain broken!
}
var calculatedHash = CalculateHash(entry);
if (calculatedHash != entry.EntryHash)
{
return false; // Entry tampered!
}
expectedPrevious = entry.EntryHash;
}
return true;
}
}
Phase 3: Role-Based Access Control
Goal: Implement least-privilege access to card data.
public class AccessControlService : IAccessControl
{
private readonly IUserRepository _users;
private readonly IAuditLogger _audit;
private readonly ITokenVault _vault;
private static readonly Dictionary<string, HashSet<string>> RolePermissions = new()
{
["TRANSACTION_PROCESSOR"] = new() { "VIEW_MASKED_PAN", "CREATE_AUTH", "CAPTURE", "REFUND" },
["CHARGEBACK_ANALYST"] = new() { "VIEW_MASKED_PAN", "REVEAL_PAN", "VIEW_TRANSACTION" },
["SUPPORT_AGENT"] = new() { "VIEW_LAST4", "VIEW_TRANSACTION" },
["SECURITY_ADMIN"] = new() { "VIEW_AUDIT", "MANAGE_USERS", "VIEW_CONFIG" },
["KEY_CUSTODIAN"] = new() { "KEY_CEREMONY_PARTIAL" }
};
public async Task<bool> CanAccess(string userId, string resource, string action)
{
var user = await _users.GetById(userId);
var permissions = RolePermissions.GetValueOrDefault(user.Role, new HashSet<string>());
var allowed = permissions.Contains(action);
await _audit.Log(new AuditEntry
{
UserId = userId,
EventType = AuditEventType.AccessCheck,
Action = action,
ResourceId = resource,
Outcome = allowed ? "ALLOWED" : "DENIED"
});
return allowed;
}
public async Task<string> GetFullPan(string token, string userId, string reason)
{
// Check permission
if (!await CanAccess(userId, token, "REVEAL_PAN"))
{
throw new UnauthorizedAccessException("User not authorized for PAN reveal");
}
// Validate reason
if (string.IsNullOrEmpty(reason) || !reason.StartsWith("CB-"))
{
throw new ArgumentException("PAN reveal requires valid chargeback reference");
}
// Log the reveal (CRITICAL)
await _audit.Log(new AuditEntry
{
UserId = userId,
EventType = AuditEventType.SensitiveDataAccess,
Action = "PAN_REVEAL",
ResourceId = token,
Details = $"Reason: {reason}"
});
// Get PAN from vault
return await _vault.Detokenize(token);
}
}
Phase 4: Data Retention Service
Goal: Implement automatic data purging.
public class DataRetentionService : IDataRetentionService
{
// PCI DSS: Define retention periods
private static readonly Dictionary<string, TimeSpan> RetentionPeriods = new()
{
["TRANSACTION"] = TimeSpan.FromDays(7 * 365), // 7 years for tax/legal
["AUDIT_LOG"] = TimeSpan.FromDays(365), // 1 year minimum
["CVV"] = TimeSpan.Zero, // NEVER stored
["FULL_PAN_IN_LOG"] = TimeSpan.Zero, // NEVER logged
};
public async Task EnforcePolicies()
{
// 1. Transactions older than retention period
var cutoff = DateTime.UtcNow - RetentionPeriods["TRANSACTION"];
var oldTransactions = await _transactionRepo.GetOlderThan(cutoff);
foreach (var txn in oldTransactions)
{
// Delete token mapping (PAN no longer needed)
await _vault.DeleteToken(txn.Token);
// Keep transaction record but clear token reference
txn.Token = "[PURGED]";
txn.MaskedPan = "[PURGED]";
await _transactionRepo.Update(txn);
}
await _audit.Log(new AuditEntry
{
EventType = AuditEventType.DataRetention,
Action = "PURGE_OLD_TOKENS",
Details = $"Purged {oldTransactions.Count} tokens"
});
}
public bool CanStore(string dataType)
{
return dataType switch
{
"CVV" => false, // NEVER
"TRACK_DATA" => false, // NEVER
"PIN" => false, // NEVER
"PAN" => true, // Only in vault, encrypted
"MASKED_PAN" => true, // Safe
"EXPIRY" => true, // Safe
_ => false // Default deny
};
}
}
Phase 5: Admin Dashboard
Goal: Build web UI with proper access controls.
Key security considerations:
- Session timeout (PCI: 15 minutes idle)
- Re-authentication for sensitive actions
- No PAN in URLs or client-side storage
- Audit all actions
Phase 6: Compliance Documentation
Goal: Map implementation to PCI DSS requirements.
# PCI DSS Compliance Mapping
## Requirement 3: Protect Stored Account Data
| Sub-Req | Description | Implementation |
|---------|-------------|----------------|
| 3.2 | Do not store SAD after auth | DataRetentionService.CanStore("CVV") = false |
| 3.4 | Render PAN unreadable | TokenVaultService uses AES-256-GCM |
| 3.5 | Protect keys | KeyManagementService with split knowledge |
| 3.6 | Document key procedures | docs/key-management-procedures.md |
## Requirement 7: Restrict Access to CHD
| Sub-Req | Description | Implementation |
|---------|-------------|----------------|
| 7.1 | Limit access by job | AccessControlService.RolePermissions |
| 7.2 | Deny all, allow by exception | Default deny in CanAccess() |
## Requirement 10: Track and Monitor Access
| Sub-Req | Description | Implementation |
|---------|-------------|----------------|
| 10.2 | Implement audit trails | HashChainAuditLogger |
| 10.3 | Record specific elements | AuditEntry structure |
| 10.5 | Secure audit trails | Hash chain, append-only |
Testing Strategy
Unit Tests
[Test]
public async Task Authorize_NeverStoresCvv()
{
var request = new AuthorizationRequest
{
Pan = "4111111111111111",
Cvv = "123",
Amount = 100
};
var response = await _gateway.Authorize(request);
// CVV should not appear anywhere in storage
var transaction = await _transactionRepo.GetById(response.TransactionId);
Assert.That(transaction.ToString(), Does.Not.Contain("123"));
var auditLogs = await _auditRepo.GetAll();
Assert.That(auditLogs.All(l => !l.Details.Contains("123")));
}
[Test]
public async Task RevealPan_RequiresValidReason()
{
var user = await CreateUser("CHARGEBACK_ANALYST");
// No reason
Assert.ThrowsAsync<ArgumentException>(() =>
_accessControl.GetFullPan("tok_123", user.Id, ""));
// Invalid reason format
Assert.ThrowsAsync<ArgumentException>(() =>
_accessControl.GetFullPan("tok_123", user.Id, "just curious"));
// Valid reason
var pan = await _accessControl.GetFullPan("tok_123", user.Id, "CB-2024-0123");
Assert.That(pan, Is.EqualTo("4111111111111111"));
}
Security Tests
[Test]
public async Task AuditLog_DetectsTampering()
{
// Create some entries
await _audit.Log(new AuditEntry { Action = "TEST1" });
await _audit.Log(new AuditEntry { Action = "TEST2" });
await _audit.Log(new AuditEntry { Action = "TEST3" });
// Verify integrity
Assert.That(await _audit.VerifyIntegrity(), Is.True);
// Tamper with middle entry
var entries = await _auditRepo.GetAll();
entries[1].Details = "TAMPERED";
await _auditRepo.Update(entries[1]);
// Should detect tampering
Assert.That(await _audit.VerifyIntegrity(), Is.False);
}
[Test]
public async Task NetworkSegmentation_DataZoneNotExternallyAccessible()
{
// Attempt direct connection to data zone from outside
var client = new HttpClient();
// Should fail - data zone has no external routes
Assert.ThrowsAsync<HttpRequestException>(() =>
client.GetAsync("http://172.16.3.1:5432")); // Direct DB access
}
Penetration Test Scenarios
# Security Test Cases
## SQL Injection
- [ ] Test all API inputs for SQL injection
- [ ] Test search functionality with payloads
## Access Control
- [ ] Verify TRANSACTION_PROCESSOR cannot reveal PAN
- [ ] Verify SUPPORT_AGENT only sees last 4
- [ ] Test horizontal privilege escalation (access other merchant's data)
## Session Management
- [ ] Verify session timeout (15 min)
- [ ] Test session fixation
- [ ] Test concurrent session limits
## Data Exposure
- [ ] Verify no PAN in URLs
- [ ] Verify no PAN in error messages
- [ ] Verify no PAN in client-side storage
- [ ] Verify no CVV anywhere post-auth
Common Pitfalls & Debugging
Pitfall 1: Logging PANs
Symptom: PAN appears in application logs.
Cause: Generic exception logging includes request body.
Fix: Scrub sensitive data before logging:
public class PanScrubber
{
private static readonly Regex PanPattern = new(@"\b\d{13,19}\b");
public static string Scrub(string input)
{
return PanPattern.Replace(input, m =>
m.Value.Length > 4 ? $"****{m.Value[^4..]}" : "****");
}
}
Pitfall 2: CVV in Memory
Symptom: CVV persists in memory after authorization.
Cause: String interning or GC delays.
Fix: Use SecureString or immediately clear:
public void ClearSensitiveData()
{
if (_cvv != null)
{
var chars = _cvv.ToCharArray();
Array.Clear(chars, 0, chars.Length);
_cvv = null;
}
}
Pitfall 3: Broken Hash Chain
Symptom: Audit verification fails after system restart.
Cause: Last hash not persisted.
Fix: Store last hash in database:
public async Task<string> GetLastHash()
{
return await _repo.GetLastEntry()?.EntryHash ?? GetGenesisHash();
}
Extensions & Challenges
Extension 1: Real HSM Integration
Integrate with SoftHSM2 for realistic key management.
Extension 2: Merchant Onboarding
Add merchant registration with API key management.
Extension 3: Fraud Detection
Integrate velocity checks and rule-based fraud detection.
Extension 4: Settlement/Clearing
Add batch settlement processing.
Interview Questions This Prepares You For
- “What data can never be stored after authorization?”
- CVV/CVV2, PIN, full track data
- “How do you implement least privilege for card data?”
- Role-based access, default deny, audit all access
- “How do you make audit logs tamper-evident?”
- Hash chaining, append-only storage, centralized collection
- “What is the CDE and how do you reduce its scope?”
- Card Data Environment; reduce via tokenization, segmentation
- “Explain split knowledge in key management.”
- No single person knows complete key; requires multiple custodians
Resources
Standards
- PCI DSS v4.0 (pcisecuritystandards.org)
- PCI PA-DSS
- NIST SP 800-57 (Key Management)
Books
| Topic | Book |
|---|---|
| PCI Compliance | “PCI DSS: An Integrated Data Security Standard Guide” by Jim Seaman |
| Secure Architecture | “Fundamentals of Software Architecture” by Richards & Ford |
| Audit Logging | “Designing Data-Intensive Applications” by Kleppmann |
Self-Assessment Checklist
- Authorization flow works without storing CVV
- PAN is tokenized and encrypted at rest
- Audit logs capture all card data access
- Hash chain detects log tampering
- Role-based access prevents unauthorized PAN reveal
- Network zones properly isolated (Docker simulation)
- Documentation maps to PCI DSS requirements
- CVV never appears in any logs or storage
What’s Next?
You’ve built a compliant payment gateway. For the ultimate learning experience, proceed to Project 6: Full Payment Processing Simulator to build an entire payment ecosystem with multiple parties, settlement, and fraud detection.