Ensuring PCI DSS Compliance for Mobile App (Payment Data)
PCI DSS v4.0—Payment Card Industry Data Security Standard, mandatory for any software transmitting, processing, or storing cardholder data. For mobile apps critical: Section 6 (Secure Development) and Section 4 (Encryption in Transit). Without compliance, major acquirer won't enable direct acquiring—only iframe solutions.
What Most Apps Violate
PAN in Logs. Developer adds Log.d("Payment", "Card: $cardNumber") for debug. Goes to logcat, readable by any app on unprotected Android device. PCI DSS requirement 3.3: PAN must not display in logs unencrypted.
Screenshots of Card Entry Screen. Android allows screenshots by default on any Activity. On card data entry—critical vulnerability:
// Must be on any screen with card data
window.setFlags(
WindowManager.LayoutParams.FLAG_SECURE,
WindowManager.LayoutParams.FLAG_SECURE
)
On iOS similarly—block UI on background transition:
func applicationWillResignActive(_ application: UIApplication) {
// Add overlay over sensitive content
coverSensitiveContent()
}
Missing Certificate Pinning. Traffic to payment server intercepted by Charles Proxy or mitmproxy without obstacles. PCI DSS 6.5.4 demands man-in-the-middle protection.
// iOS: URLSession with custom delegate for certificate pinning
class PinnedURLSessionDelegate: NSObject, URLSessionDelegate {
func urlSession(
_ session: URLSession,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void
) {
guard
let serverTrust = challenge.protectionSpace.serverTrust,
let certificate = SecTrustGetCertificateAtIndex(serverTrust, 0)
else {
completionHandler(.cancelAuthenticationChallenge, nil)
return
}
let pinnedHash = "sha256/AbCdEfGhIjKlMnOpQrStUvWxYz123456789="
let serverHash = certificateHash(certificate)
if serverHash == pinnedHash {
completionHandler(.useCredential, URLCredential(trust: serverTrust))
} else {
completionHandler(.cancelAuthenticationChallenge, nil)
}
}
}
Root/Jailbreak Detection. PCI DSS 6.4.3 recommends (but doesn't require) detecting compromised devices. In practice, major acquirers demand via SDK. Use combination: /bin/bash presence check, Cydia, su command, process signature mismatch.
Tokenization Instead of PAN Storage
Main PCI DSS principle for mobile—never store PAN. App works with token issued by payment provider. Stripe calls it PaymentMethod, CloudPayments—Token, YooKassa—payment_method_id.
// Stripe: never see PAN in code
import StripePaymentSheet
var configuration = PaymentSheet.Configuration()
configuration.merchantDisplayName = "YourShop"
PaymentSheet.FlowController.create(
paymentIntentClientSecret: clientSecret,
configuration: configuration
) { [weak self] result in
switch result {
case .success(let flowController):
self?.paymentSheetFlowController = flowController
case .failure(let error):
// handle
}
}
Card data goes directly Stripe SDK → Stripe tokenizes → app gets paymentMethodId. PAN never touches your server—this reduces PCI DSS scope from SAQ D to SAQ A.
Audit Trail and Logging
PCI DSS requirement 10: log cardholder data environment access. For mobile app means:
- Log each successful/failed payment with timestamp, userId, masked PAN (first 6 + last 4 digits), status
- Retain logs minimum 12 months, first 3 immediately available
- Protect logs from modification (append-only storage)
PAN masking in logs:
fun maskPan(pan: String): String {
return pan.take(6) + "*".repeat(pan.length - 10) + pan.takeLast(4)
}
// "4111111111111111" → "411111******1111"
Assessment and Work Process
Full PCI DSS assessment for mobile app includes several stages.
Scoping—determine if app directly handles cardholder data or via provider (Stripe, CloudPayments). If via provider—scope much smaller.
Gap Analysis—check 12 requirements against code, infrastructure, processes. Typical findings: plaintext logs, missing certificate pinning, missing FLAG_SECURE, insufficient local storage encryption.
Remediation—fix found vulnerabilities: implement certificate pinning, move storage to Keychain/Keystore, add obfuscation, root detection.
Penetration Testing—PCI DSS 11.3 requires pen-test minimum yearly and after major changes. For mobile: static analysis (MobSF, Checkmarx), dynamic with proxy, binary reversing testing.
ASV Scanning—if app directly communicates with server, requires quarterly scanning via Approved Scanning Vendor.
Timeline Estimates
Gap analysis and report—3–5 days. Remediation—depends on findings count/complexity, usually 1–3 weeks. QSA audit prep—additional.
Pricing is calculated individually after code review and current state analysis.







