Files
thijooree/docs/bmlapi/12-tap-to-pay.md
T
shihaam 7f87c9e13f
Auto Tag on Version Change / check-version (push) Failing after 12m39s
update docs
2026-05-29 18:58:43 +05:00

6.2 KiB
Raw Blame History

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 <access_token>
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

{
  "type": "track2",
  "cardid": "<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

{
  "type": "track2",
  "cardid": "<cardId>",
  "quantity": 3,
  "channel": "token"
}

Expected response: { "code": 22 } (OTP generated on BML side; TOTP is used locally)

Step 1c — Submit TOTP

{
  "type": "track2",
  "cardid": "<cardId>",
  "quantity": 3,
  "channel": "token",
  "otp": "<TOTP>"
}

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

{
  "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 <len>
  84 <len> 325041592E5359532E4444463031    ← DF Name (PPSE)
  A5 <len>
    BF0C <len>
      61 <len>
        4F <len> <AID>                     ← ADF Name
        87 01 01                           ← Application Priority Indicator
9000

SELECT AID Response

6F <len>
  84 <len> <AID>                           ← Dedicated File Name
  A5 <len>
    50 <len> <label-ascii-as-hex>          ← 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 11, 0 offline auth records

READ RECORD Response

70 <len>
  57 <len> <track2-data>                   ← 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
0127 bytes LL (1 byte)
128255 bytes 81 LL (2 bytes)
25665535 bytes 82 HH LL (3 bytes)

Prerequisites

  • Valid access_token from OAuth Token Exchange
  • TOTP seed enrolled via BML app (same seed used for login 2FA)
  • cardId from the dashboard — see Dashboard

 


← Foreign Limits · Next → QR Payment