Building a payment app for SA means hard-coding ZAR EMV tags, complying with POPIA before you write your first log line, surviving load-shedding with pre-provisioned LUKs, and testing on devices the Android docs never mention. This page is everything that doesn't show up in the EMV spec or the Android developer guide but will absolutely fail you in production south of the Limpopo.
Every EMV transaction carries country-specific data in its TLV tags. Get these wrong and the acquirer rejects the transaction before it reaches the issuer. South Africa also has its own data protection law (POPIA), a payment system regulator (PASA under SARB), and an infrastructure reality — rolling blackouts — that no other major market shares.
This isn't a compliance checklist. It's the set of decisions you make once, hard-code, and never revisit — because changing a currency code or timezone calculation in production means re-certifying with the payment schemes.
| Parameter | Value | EMV tag | Notes |
|---|---|---|---|
| Currency code | 0710 (ZAR) |
5F2A |
Transaction Currency Code. ISO 4217 numeric. Always 0710 for Rand. |
| Country code | 0710 (ZA) |
9F1A |
Terminal Country Code. ISO 3166 numeric. Same value as currency — coincidence, not a rule. |
| Timezone | SAST (UTC+2) | — | Hard-code Africa/Johannesburg. SA has no daylight saving time. Never use device locale. |
// Hard-coded SA payment defaults — never derive from device locale object SaPaymentDefaults { // ISO 4217 numeric: South African Rand val CURRENCY_CODE = byteArrayOf(0x07.toByte(), 0x10.toByte()) // ISO 3166 numeric: South Africa val COUNTRY_CODE = byteArrayOf(0x07.toByte(), 0x10.toByte()) // SAST — no DST, ever val TIMEZONE = ZoneId.of("Africa/Johannesburg") fun transactionTimestamp(): String { val now = ZonedDateTime.now(TIMEZONE) // EMV date format: YYMMDD val date = now.format(DateTimeFormatter.ofPattern("yyMMdd")) // EMV time format: HHmmss val time = now.format(DateTimeFormatter.ofPattern("HHmmss")) return "$date$time" } }
Three constraints that shape every architectural decision in a South African payment app. None of them are optional.
POPIA compliance. The Protection of Personal Information Act is South Africa's data protection law. For payment apps, four rules dominate.
Display at most the last four digits of the DPAN in any user-facing screen, notification, receipt, or analytics event. Full DPAN in logs is a POPIA violation and a PCI DSS finding. Build the masking into your UI layer from day one — retrofitting it is painful.
Transaction data older than 120 days must be purged or anonymised unless you have a documented lawful basis for longer retention (e.g., regulatory requirement, active dispute). "We might need it" is not a lawful basis under POPIA section 14.
If cardholder data is compromised, notify the Information Regulator within 72 hours and affected data subjects "as soon as reasonably possible." Build your incident response playbook before launch. After the breach is too late to figure out who to call.
If your provisioning server, TSP integration, or analytics pipeline runs outside South Africa, document the lawful basis for transferring personal information across borders. Options: binding corporate rules, data subject consent, or an adequacy finding. Pick one, document it, and be ready to produce it on request.
User data export. POPIA gives data subjects the right to request a copy of their personal information. Build the export endpoint from day one. It's trivial when you have 100 users. It's a scramble when you have 100,000 and the Information Regulator sends a section 23 request.
Load-shedding resilience. South Africa's rolling blackouts take infrastructure offline for 2–4 hour blocks, sometimes multiple times per day. For a payment app, this means your provisioning and LUK replenishment servers may be unreachable. The app must work anyway.
Keep 5–10 LUKs on device at all times. A typical user taps 3–5 times per day. Ten LUKs gives you a full day plus margin. Replenish when inventory drops below threshold, not when you're already at zero.
processCommandApdu must never make a network call. The entire tap flow — SELECT through GENERATE AC — runs from local state. LUKs are already on device. Card metadata is cached. The cryptogram is computed locally. Network is for replenishment, not for taps.
When connectivity returns after a blackout, replenish LUKs with exponential backoff. Don't hammer the server — every other wallet on the network is trying to replenish at the same time. Never show the user a replenishment error unless they're genuinely out of keys and cannot tap.
Device testing matrix. The phones that dominate the South African market are not the phones in the Android documentation. If you only test on Pixel, you'll ship bugs.
| Device class | SA market position | HCE-specific issues |
|---|---|---|
| Samsung A-series | Dominant mid-market. A15, A25, A55 are volume models. | NFC antenna placement varies per model. Tap positioning that works on Pixel may fail on an A15. Test each model's sweet spot. |
| Huawei (without GMS) | Significant installed base. No Google Play Services. | No Play Integrity API. Use Android Key Attestation as fallback. Huawei's HMS SafetyDetect is an option but adds SDK dependency. Test AppGallery distribution. |
| Transsion (Tecno, Itel, Infinix) | Massive share in entry-level and prepaid segment. | NFC support is inconsistent. Some models have NFC hardware but ship with it disabled in firmware. Check NfcAdapter at runtime, not just manifest capabilities. Many models lack NFC entirely. |
| Xiaomi / Redmi | Growing mid-market share. | MIUI's aggressive battery optimisation kills HCE services in the background. Users must manually exempt your app. Document this in onboarding and detect when it hasn't been done. |
These won't show up in your dev environment. They show up at a Checkers in Soweto at 6pm on a Friday during stage 4 load-shedding.
Users travel, change settings, or have phones that default to UTC. If you derive transaction timestamps from System.currentTimeMillis() without forcing SAST, your cryptogram's date component won't match the acquirer's expectation. Hard-code Africa/Johannesburg in all timestamp generation.
Stage 6 load-shedding can mean 6+ hours without connectivity per day. If your replenishment window is too tight, users run out of LUKs. Pre-provision aggressively: 10 LUKs minimum, trigger replenishment at 5 remaining, not at 1.
Some Tecno/Infinix devices report NFC support in PackageManager features but the adapter returns null at runtime. Always check NfcAdapter.getDefaultAdapter(context) != null before enabling any HCE UI. Don't trust the manifest alone.
If you send transaction metadata (amounts, timestamps, merchant names) to an analytics service outside South Africa, you need explicit consent or a documented lawful basis. Firebase Analytics to a US region counts as cross-border transfer of personal information. Either host analytics in-country or get the consent flow right before launch.
SARB (South African Reserve Bank) oversees payment instruments. PASA (Payments Association of South Africa) manages membership for card scheme participants. If your app issues a payment instrument, you may need SARB approval beyond the Visa/Mastercard scheme licence. Engage your legal team early — the regulatory process takes months, not weeks.
The South African payments ecosystem has specific regulatory bodies that don't exist in most markets. Understanding the hierarchy saves you from building something that's technically functional but legally inoperable.
| Body | Role | When you engage |
|---|---|---|
| SARB | South African Reserve Bank. Oversees all payment instruments and financial stability. | If your app issues or facilitates a payment instrument. Early engagement recommended — approval timeline is 6–12 months. |
| PASA | Payments Association of South Africa. Manages payment system participation and rules. | Card scheme participants must be PASA members or operate through a PASA member (your acquiring bank). |
| Information Regulator | Enforces POPIA. Investigates breaches, handles complaints, issues enforcement notices. | 72-hour breach notification. Section 23 data subject requests. Compliance audits. |
| Visa / Mastercard (SA office) | Scheme certification and compliance for SA-specific requirements. | Certification testing, production go-live approval, ongoing compliance monitoring. |
Most wallet startups operate under their acquiring bank's PASA membership and payment system participation. The bank takes regulatory responsibility in exchange for control over your risk management. This is faster than getting your own licences but means the bank can shut you down if your fraud rates spike or your compliance documentation is insufficient.