Files
thijooree/docs/bmlapi/13-qr-payment.md
T
shihaam 86e1e66a20
Auto Tag on Version Change / check-version (push) Failing after 14m45s
update docs
2026-05-30 19:33:15 +05:00

5.8 KiB

QR Payment

BML supports QR-based payments via the PayMV network. There are two QR types — static merchant QRs (no preset amount) and gateway QRs (amount preset by merchant). Both are paid via the same 3-step TOTP-authenticated flow.


QR Code Types

Type code Name Amount
QRS Static QR 0.00 — user enters amount
QRR Gateway / dynamic QR Preset by merchant

QR Code Formats

BML QR codes appear in two formats.

1. Plain URL QR

https://pay.bml.com.mv/app/<base64-encoded-url>

The entire URL is base64-encoded and passed directly to the payrequest lookup API.

2. Combined EMV-style QR

Used in Fahipay/PayMV combo QRs that embed multiple payment networks. The BML gateway URL is embedded as a TLV value at a fixed path.

TLV path: root tag 35 → sub-tag 20 → sub-sub-tag 01

The value at tag 01 is the full https://pay.bml.com.mv/app/... URL.


PayMV QR Format (TLV)

PayMV QRs (static, PayMV-native) use a decimal TLV encoding (not BER-TLV):

<2-digit decimal tag><2-digit decimal length><value>...

Root-level tags (key fields for scanning)

Tag Field
26 Merchant account information (container)
54 Transaction amount
59 Merchant / recipient name
62 Additional data (container)

Sub-tags

Parent Tag Field
26 03 Account number
62 08 Payment purpose / reference

For the full PayMV QR format spec including generation (receive-payment QRs), acquirer BIC mapping, CRC algorithm, and all tags — see PayMV QR Format.


Step 1 — Resolve QR to Merchant Details

Endpoint

GET https://www.bankofmaldives.com.mv/internetbanking/api/mobile/walletpayments/payrequest/{base64Url}

{base64Url} is the full QR URL (e.g. https://pay.bml.com.mv/app/...) base64-encoded with standard encoding (with padding).

Headers

Header Value
Authorization Bearer <access_token>
User-Agent bml-mobile-banking/348 ({manufacturer}; Android {version}; {model})
x-app-version 2.1.44.348
curl --request GET \
  --url 'https://www.bankofmaldives.com.mv/internetbanking/api/mobile/walletpayments/payrequest/<base64Url>' \
  --header 'Authorization: Bearer <access_token>' \
  --header 'User-Agent: bml-mobile-banking/348 ({manufacturer}; Android {version}; {model})' \
  --header 'x-app-version: 2.1.44.348'

Response

{
  "success": true,
  "payload": {
    "trxn_hash": "<base64Url>",
    "narrative1": "Merchant Name",
    "narrative2": "Address Line 1",
    "narrative3": "Address Line 2",
    "amount": "1.03",
    "currency": "MVR"
  }
}

Response Fields

Field Description
trxn_hash The base64 URL — used as requestId in payment steps
narrative1 Merchant name
narrative2 Merchant address line 1
narrative3 Merchant address line 2
amount Payment amount ("0.00" for static QRS)
currency Currency code (typically "MVR")

Step 2 — Pay (3-Step TOTP Flow)

All three steps POST to the same endpoint:

POST https://www.bankofmaldives.com.mv/internetbanking/api/mobile/walletpayments/pay

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
Accept application/json

Step 2a — Initiate (no channel)

{
  "action": "approve",
  "debitAccount": "<internalAccountId>",
  "requestId": "<trxn_hash>",
  "amount": 1.03,
  "currency": "MVR"
}

Expected response: { "success": true, "code": 99 } (OTP required)

Note: This step may be skipped. The app proceeds directly to Step 2b if the gateway already indicates OTP is required.

Step 2b — Request OTP Channel

{
  "action": "approve",
  "debitAccount": "<internalAccountId>",
  "requestId": "<trxn_hash>",
  "amount": 1.03,
  "currency": "MVR",
  "channel": "token"
}

Expected response: { "success": true, "code": 22 } (OTP generated)

Step 2c — Confirm with TOTP

{
  "action": "approve",
  "debitAccount": "<internalAccountId>",
  "requestId": "<trxn_hash>",
  "amount": 1.03,
  "currency": "MVR",
  "channel": "token",
  "otp": "<TOTP>"
}

Expected response:

{
  "success": true,
  "code": 0,
  "payload": {
    "merchant": "Merchant Name",
    "amount": "1.03",
    "currency": "MVR"
  }
}

On failure:

{
  "success": false,
  "message": "Payment failed"
}

Request Fields

Field Type Description
action string Always "approve"
debitAccount string Internal account UUID (not the display account number) — from dashboard internalId field
requestId string The trxn_hash from the payrequest lookup
amount number Payment amount as a number (e.g. 1.03)
currency string Currency code (e.g. "MVR")
channel string "token" — present in steps 2b and 2c only
otp string TOTP code — present in step 2c only

The debitAccount field takes the internal UUID from the dashboard response, not the displayed account number. See Dashboard for the account object structure.


OTP

The OTP is a standard TOTP (RFC 6238, SHA-1, 30-second window, 6 digits) derived from the stored BML authenticator seed — the same seed used for login 2FA.


Prerequisites


 


← Tap-to-Pay