# Tap-to-Pay (NFC / HCE) BML supports contactless NFC payments via Host Card Emulation (HCE). The app fetches single-use payment tokens from the server, then emulates an EMV mag-stripe contactless card using Android's `HostApduService`. --- ## Overview ``` 1. Fetch tokens → POST /api/mobile/walletpayments/gettoken (TOTP-authenticated) 2. HCE exchange → Android NFC subsystem drives the APDU exchange with the POS terminal ``` --- ## Step 1 — Fetch Payment Tokens ### Endpoint ``` POST https://www.bankofmaldives.com.mv/internetbanking/api/mobile/walletpayments/gettoken ``` ### Headers | Header | Value | |---|---| | `Authorization` | `Bearer ` | | `User-Agent` | `bml-mobile-banking/348 ({manufacturer}; Android {version}; {model})` | | `x-app-version` | `2.1.44.348` | | `Content-Type` | `application/json` | ### Three-Step OTP Flow Token retrieval requires TOTP verification and completes in three POSTs to the same endpoint. #### Step 1a — Initiate ```json { "type": "track2", "cardid": "", "quantity": 3 } ``` Expected response: `{ "code": 99 }` (OTP required) If `"code": 0` is returned directly the payload contains tokens immediately (skip to parsing). #### Step 1b — Request OTP Channel ```json { "type": "track2", "cardid": "", "quantity": 3, "channel": "token" } ``` Expected response: `{ "code": 22 }` (OTP generated on BML side; TOTP is used locally) #### Step 1c — Submit TOTP ```json { "type": "track2", "cardid": "", "quantity": 3, "channel": "token", "otp": "" } ``` Expected response: `{ "code": 0, "payload": [...] }` The OTP is a standard TOTP (RFC 6238, SHA-1, 30-second window, 6 digits) derived from the stored BML authenticator seed. ### Token Response ```json { "code": 0, "payload": [ { "token": "4761360000000000", "expiry": "2512", "app_code": "A0000000031010", "service_code": "000", "data": "0960919802623742", "valid_until": "2025-12-01 12:00:00.000" } ] } ``` ### Token Fields | Field | Description | |---|---| | `token` | PAN-equivalent single-use token (used as Track 2 primary account number) | | `expiry` | Expiry in `YYMM` format (e.g. `"2512"` = December 2025) | | `app_code` | AID (Application Identifier) hex string — identifies the card network | | `service_code` | 3-digit service code for Track 2 | | `data` | Discretionary data appended to Track 2 | | `valid_until` | Server-side expiry timestamp for the token | ### AID to Card Network Mapping | AID prefix | Network | |---|---| | `A0000000031010` | Visa | | `A0000000041010` | Mastercard | | `A000000025...` | Amex | | (other) | BML | --- ## Step 2 — HCE APDU Exchange Once a token is set, Android's NFC subsystem routes contactless commands to the app's `HostApduService`. The flow follows the EMV mag-stripe contactless profile. ### APDU Exchange Flow ``` POS Terminal Android HCE | | | SELECT PPSE (INS=A4) | |--------------------------------------->| | FCI Template (6F) + 9000 | |<---------------------------------------| | | | SELECT AID (INS=A4) | |--------------------------------------->| | FCI Template (6F) + 9000 | |<---------------------------------------| | | | GET PROCESSING OPTIONS (INS=A8) | |--------------------------------------->| | Response Message Template (80) + 9000 | |<---------------------------------------| | | | READ RECORD (INS=B2) | |--------------------------------------->| | Record Template (70) + 9000 | |<---------------------------------------| ``` ### APDU Command Bytes | INS | Hex | Command | |---|---|---| | `SELECT` | `0xA4` | Select PPSE or AID | | `GET PROCESSING OPTIONS` | `0xA8` | Request AIP + AFL | | `READ RECORD` | `0xB2` | Read Track 2 data | ### SELECT PPSE Response PPSE AID: `2PAY.SYS.DDF01` = `325041592E5359532E4444463031` ``` 6F 84 325041592E5359532E4444463031 ← DF Name (PPSE) A5 BF0C 61 4F ← ADF Name 87 01 01 ← Application Priority Indicator 9000 ``` ### SELECT AID Response ``` 6F 84 ← Dedicated File Name A5 50 ← Application Label (e.g. "VISA") 9F38 02 9F6602 ← PDOL: TTQ (2 bytes) 9000 ``` The application label is derived from the AID prefix (see mapping table above). ### GET PROCESSING OPTIONS Response ``` 80 06 0080 08010100 9000 ``` | Field | Value | Meaning | |---|---|---| | Tag `80` | — | Response Message Template 1 | | AIP | `0080` | Mag-stripe mode | | AFL | `08010100` | SFI=1, records 1–1, 0 offline auth records | ### READ RECORD Response ``` 70 57 ← Track 2 Equivalent Data 9000 ``` Track 2 format: ``` {token} D {expiry} {serviceCode} {data} [F] ``` The trailing `F` nibble is appended when the total length is odd (standard Track 2 padding). Example from a real token: ``` 4761360000000000 D 2512 000 0960919802623742 → 4761360000000000D2512000096091980262374 2F (padded) ``` ### Status Words | SW | Meaning | |---|---| | `9000` | Success | | `6F00` | Generic / unknown error | | `6D00` | Instruction not supported | --- ## TLV Encoding All APDU responses use BER-TLV encoding. Tags are 1 or 2 bytes (hex string). Length follows DER short/long form: | Length range | Encoding | |---|---| | 0–127 bytes | `LL` (1 byte) | | 128–255 bytes | `81 LL` (2 bytes) | | 256–65535 bytes | `82 HH LL` (3 bytes) | --- ## Prerequisites - Valid `access_token` from [OAuth Token Exchange](03-oauth-token.md) - TOTP seed enrolled via BML app (same seed used for login 2FA) - `cardId` from the dashboard — see [Dashboard](04-dashboard.md) ---   --- [← Foreign Limits](11-foreign-limits.md) · [Next → QR Payment](13-qr-payment.md)