# Tap to Pay BML contactless payment via Android Host Card Emulation. The app emulates a BML magnetic-stripe contactless card using a single-use wallet token fetched from the [BML Tap-to-Pay API](../bmlapi/12-tap-to-pay.md). --- ## Entry Points | Trigger | Path | |---|---| | Manifest NFC service launches `BmlTapToPayActivity` | Redirects to `MainActivity` with `TAP_TO_PAY` | | `TAP_TO_PAY` intent | `R.id.nav_pay_with_card` + `auto_tap_mode=true` | | "Tap to Pay" button on the [Cards](22-cards.md) screen | `CardsFragment.setTapMode(true, item)` | | Dashboard NFC button on a BML card | Same | `BmlTapToPayActivity` (`nfc/BmlTapToPayActivity.kt`) is a 7-line trampoline — it sets the `TAP_TO_PAY` action and `FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK` and finishes. --- ## Pre-flight — `NfcPaymentUtil` Located at `util/NfcPaymentUtil.kt`. Every tap-to-pay entry runs `checkAndProceed(context, onReady)` first: 1. `NfcAdapter.getDefaultAdapter()` — null → "NFC unsupported" dialog 2. `nfcAdapter.isEnabled` — false → "Turn on NFC" dialog with a button that opens `Settings.ACTION_NFC_SETTINGS` 3. `CardEmulation.isDefaultServiceForCategory(component, CATEGORY_PAYMENT)` — false → "Set this app as the default payment app" dialog with a button that opens `Settings.ACTION_NFC_PAYMENT_SETTINGS` 4. All three pass → `onReady()` is invoked If the user has `biometrics_transfer_confirm` enabled, `CardsFragment.showBiometricPromptForTap()` runs a `BIOMETRIC_STRONG` prompt before entering tap mode. --- ## Token Fetch `fetchAndArmToken()` (in `CardsFragment`): 1. Locate the BML session and OTP seed for the selected card's login 2. Generate a fresh TOTP via `util/Totp` 3. `BmlTapToPayClient().fetchTokens(session, internalId, otp)` returns a list of `BmlWalletToken`; the first is used 4. `BmlHostCardEmulatorService.setToken(token)` arms the HCE service 5. `BmlHostCardEmulatorService.onTransactionComplete = { success -> ... }` — re-enters the carousel and shows a success toast / refresh on success If any step fails, a toast is shown and tap mode exits. --- ## HCE Service — `BmlHostCardEmulatorService` Subclass of `HostApduService`. Implements the minimal EMV mag-stripe contactless flow: ``` SELECT PPSE → SELECT AID → GET PROCESSING OPTIONS → READ RECORD ``` ### APDU Handling | INS | Handler | Response | |---|---|---| | `A4` SELECT (data = PPSE name `2PAY.SYS.DDF01`) | `handleSelect` | FCI containing app entry: tag 4F = AID, tag 87 = priority `01`, DF Name `84` = PPSE | | `A4` SELECT (data = AID bytes from token) | `handleSelect` | FCI containing AID, ASCII label (`VISA` / `MASTERCARD` / `AMEX` / `BML`), PDOL `9F38 = 9F6602` (TTQ 2 bytes) | | `A8` GPO | `handleGpo` | AIP `0080` (mag-stripe mode), AFL `08010100` (SFI=1, record 1-1) | | `B2` READ RECORD | `handleReadRecord` | Tag `70` containing tag `57` Track 2 — fires `onTransactionComplete(true)` | | Anything else | — | `6D00` (INS not supported) | PPSE selection when no active token is set fires `launchPromptActivity()` which starts `BmlTapToPayActivity` to bring the user into the app. ### Track 2 `buildTrack2(token)`: `"${token.token}D${token.expiry}${token.serviceCode}${token.data}"`, padded with `F` to even length. ### Companion State | Member | Use | |---|---| | `activeToken: BmlWalletToken?` | The armed token (volatile) | | `onTransactionComplete: ((Boolean) -> Unit)?` | UI callback invoked from READ RECORD or `onDeactivated` | | `setToken(token)` / `clearToken()` | Called from `CardsFragment.fetchAndArmToken()` / `exitTapMode()` | | `applicationLabel(aidHex)` | Maps Visa/MC/Amex AIDs to ASCII labels | `onDeactivated()` fires `onTransactionComplete(false)` if the reader dropped before GPO was sent. --- ## Failure Modes - No NFC, NFC off, or app not the default payment service — handled by `NfcPaymentUtil` with dialogs that deep-link to Settings - Missing OTP seed or session — toast, tap mode exits - Token fetch fails — toast, tap mode exits - Reader removed before READ RECORD — `onDeactivated` reports `success = false`; no toast is shown ---   --- [← Cards](22-cards.md)     **Next →** [Notifications](24-notifications.md)