Security and compliance
The Android Secure Fields SDK is designed so that raw card data never passes through your application code. This page describes what Purse guarantees, what your application must do, and what is prohibited.
Share this page with your qualified security assessor (QSA). Formal PCI DSS scope determination requires an independent assessment.
What Purse guarantees
Card data never reaches your application code
Card input fields are rendered as native views that are internal to the SDK module. There is no public API that returns a PAN, CVV, expiry date, or cardholder name. The only outputs your application receives are:
| Output | What it contains |
|---|---|
FieldStatePayload | Field validity (Boolean), character count (Int), touched state (Boolean) |
BrandDetectedPayload | Detected brand(s) — e.g. [Brand.VISA] |
CardInfo | BIN prefix (first 8 digits), detected brand(s), last four digits |
SubmitResult.Success | vaultFormToken — an opaque server-side token |
SDK-level hardening
| Protection | Detail |
|---|---|
| No value events | onChange, onFocus, and onBlur carry only validity and character count — never the raw card value |
| Keyboard learning blocked | IME_FLAG_NO_PERSONALIZED_LEARNING on all fields — the keyboard never stores PAN or CVV digits for autocomplete |
| Autofill blocked | IMPORTANT_FOR_AUTOFILL_NO on all fields — autofill providers cannot read or cache card values |
| Accessibility readout blocked | IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS on all fields — accessibility services cannot read field content |
| Card data not logged | The SDK logger is disabled by default; enableDebugLogging() is a no-op on non-debuggable (release) APKs |
| Memory cleared on destroy | destroy() clears all internal card state; field views are detached and nulled |
| TLS enforced | All API calls use HTTPS; plain HTTP is rejected |
Merchant obligations
Enable FLAG_SECURE on your checkout Activity
FLAG_SECURE prevents the checkout screen from appearing in the Android task switcher thumbnail and blocks MediaProjection-based screen recording apps. Set it in your checkout Activity's onCreate():
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
setContentView(R.layout.activity_checkout)
}
FLAG_SECURE can only be set on a window — the SDK owns individual field views, not your Activity's window, so it cannot set the flag itself. What the SDK does do is warn you if you forget: it checks your Activity window at render() time and, if FLAG_SECURE is missing, dispatches onSecurityWarning(payload) on your SecureFieldsListener (payload.code == "FLAG_SECURE_MISSING"). This is advisory only — it never blocks rendering. Setting the flag remains entirely your responsibility.
secureFields.addListener(object : SecureFieldsListener {
override fun onSecurityWarning(payload: SecurityWarningPayload) {
// payload.code == "FLAG_SECURE_MISSING" today; treat unknown codes as informational
Log.w("Checkout", "Security warning: ${payload.message}")
}
})
Disable Android backup for your checkout data
Set allowBackup="false" in your app manifest, or use a data_extraction_rules.xml that excludes any directories where the SDK might write temporary state:
<!-- AndroidManifest.xml -->
<application
android:allowBackup="false"
...>
This prevents card-related data from being included in device backups.
Keep the checkout Activity in the foreground
Do not background or minimize the Activity while the card form is visible. If your flow requires a context switch (for example, to open a camera for ID verification), call destroy() and re-initialize the SDK when the user returns.
Prohibited practices
The following actions undermine the card data isolation model and are prohibited:
| Practice | Why it is prohibited |
|---|---|
| Screen recording or screen casting while the form is visible | Captures raw card input from the screen buffer, bypassing the SDK's field-level isolation |
Extending or subclassing VaultFieldView | Internal field views are internal to the SDK module; attempting to subclass or wrap them to access their content violates the isolation boundary |
Injecting views into the VaultFieldView container | The SDK marks attachFieldView() as internal; you cannot insert overlay views that could intercept touch events or capture input |
| Using accessibility services to read field content | IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS is set on all fields; building or enabling a service to bypass this is prohibited |
| Logging event payloads that include card data | SDK events never include card data, but do not log field events alongside card-identifying information you hold (e.g. a stored PAN) in a way that could reconstruct card data |
| Downgrading or intercepting TLS | The gateway endpoint is derived internally from VaultEnvironment and all calls use HTTPS (TLS 1.2/1.3); do not modify the build, proxy, or network security config to downgrade or intercept them |
| Reading memory from the SDK process | Attaching a debugger or memory scanner to extract card data from the SDK's heap is prohibited and constitutes a PCI DSS violation |
SAQ A-EP scope
The SDK architecture is designed to support SAQ A-EP eligibility:
- Card data is captured by an embedded component (the SDK) provided by Purse
- Your application code never directly processes, stores, or transmits raw card data
- Tokenization is handled entirely within the SDK and the Purse gateway
For SAQ A-EP to apply, you must:
- Use
VaultEnvironment.PRODUCTIONin your release build - Set
FLAG_SECUREon your checkout Activity - Set
allowBackup="false"(or equivalent data extraction rules) - Not implement any of the prohibited practices listed above
- Obtain a
vault_form_tokenfrom the SDK and pass it to your backend — never handle raw card data server-side either
Your QSA will verify these controls during assessment. Purse can provide an attestation letter describing the SDK's PCI scope on request.