forked from thijooree/android
update docs: update payMV QR Design
This commit is contained in:
@@ -31,10 +31,10 @@ Tags and lengths are always exactly 2 decimal digits. Fields are concatenated di
|
||||
| `35` | BML/gateway merchant info | Container — present in combined EMV+BML QRs and in BML POS QRs |
|
||||
| `52` | Merchant category code | `"0000"` (generic) |
|
||||
| `53` | Transaction currency | `"462"` = MVR (ISO 4217 numeric) |
|
||||
| `54` | Transaction amount | Decimal string (e.g. `"1.50"`); absent for open-amount QRs |
|
||||
| `54` | Transaction amount | Decimal string (e.g. `"1.50"`). Open-amount QRs: absent (Thijooree BML) or `"***"` (BML's and Fahipay's own QRs) |
|
||||
| `58` | Country code | `"MV"` |
|
||||
| `59` | Merchant / recipient name | Max 25 characters |
|
||||
| `60` | Merchant city / store code | BML POS QRs only |
|
||||
| `59` | Merchant / recipient name | Max 25 characters, uppercase in every real QR seen |
|
||||
| `60` | Merchant city / store code | `LD` + 4 digits (e.g. `LD0442`, `LD0745`). Seen in BML POS QRs and in BML's and Fahipay's own receive QRs; meaning of the digits unknown |
|
||||
| `62` | Additional data field | Container — see sub-tags below |
|
||||
| `63` | CRC | `6304` prefix + 4-char hex checksum — always last |
|
||||
| `80` | Supplementary data | Container — timestamp and domain |
|
||||
@@ -66,8 +66,8 @@ Tags and lengths are always exactly 2 decimal digits. Fields are concatenated di
|
||||
|
||||
| Sub-Tag | Field | Notes |
|
||||
|---|---|---|
|
||||
| `05` | Reference / bill number | 9 random uppercase alphanumeric characters |
|
||||
| `08` | Payment purpose | Free-form text entered by the payee |
|
||||
| `05` | Reference / bill number | Bank-specific — see [Reference (tag 62→05)](#reference-tag-6205). Also printed vertically on the QR card |
|
||||
| `08` | Payment purpose | Free-form text entered by the payee. BML's app defaults it to `Quickpay Transfer`, Fahipay to `PAYMENT` |
|
||||
|
||||
---
|
||||
|
||||
@@ -77,6 +77,7 @@ Tags and lengths are always exactly 2 decimal digits. Fields are concatenated di
|
||||
|---|---|---|
|
||||
| `00` | Domain | `"mv.favara.mpqr"` |
|
||||
| `01` | Timestamp | ISO 8601 format: `"yyyy-MM-dd'T'HH:mm:ss.00000"` |
|
||||
| `02` | Unknown | `"0005"` — only seen in Fahipay's own QR **with an amount**; not generated by Thijooree |
|
||||
|
||||
---
|
||||
|
||||
@@ -115,18 +116,98 @@ To create a QR that others can scan to pay you:
|
||||
10 04 IPAY
|
||||
52 04 0000 ← MCC
|
||||
53 03 462 ← MVR
|
||||
54 <len> <amount> ← Omit tag entirely if open-amount
|
||||
54 <len> <amount> ← "%.2f". Open amount: omit (BML) / "***" (Fahipay)
|
||||
58 02 MV
|
||||
59 <len> <name up to 25 chars>
|
||||
59 <len> <NAME UP TO 25 CHARS> ← Uppercased
|
||||
60 06 LD<4 random digits> ← Fahipay only
|
||||
62 <len>
|
||||
05 09 <9 random alphanum chars> ← Reference
|
||||
08 <len> <purpose text>
|
||||
05 <len> <reference> ← Bank-specific, see below
|
||||
08 <len> <purpose text> ← Omit if blank (BML) / "PAYMENT" (Fahipay)
|
||||
80 <len>
|
||||
00 15 mv.favara.mpqr
|
||||
01 <len> <yyyy-MM-dd'T'HH:mm:ss.00000> ← Timestamp
|
||||
6304<CRC>
|
||||
```
|
||||
|
||||
For Fahipay, tag `01` is `12` (dynamic) when an amount is set.
|
||||
|
||||
---
|
||||
|
||||
## Reference (tag 62→05)
|
||||
|
||||
The reference is also the **vertical text** printed beside the QR on both banks' cards, so it is calculated once and used for both (`PayMvQrFragment.generateQr()`).
|
||||
|
||||
### BML — base-32 account number + amount
|
||||
|
||||
```
|
||||
reference = base32(accountNumber) + amountAsTyped
|
||||
```
|
||||
|
||||
- `base32` is BML's `AccountNumbertoBase32`: treat the account number as an integer and convert it to base 32 with the alphabet `0123456789ABCDEFGHIJKLMNOPQRSTUV`, most significant digit first
|
||||
- `amountAsTyped` is the amount field with commas removed and **no forced decimals** (`100` stays `100`, `100.5` stays `100.5`). Empty for open-amount QRs
|
||||
- Capped at 25 characters. A non-numeric account number falls back to 9 random characters
|
||||
|
||||
| Account | Amount | Reference / vertical text |
|
||||
|---|---|---|
|
||||
| `7730000188362` | — | `70V3UKKUA` |
|
||||
| `7730000188362` | `100` | `70V3UKKUA100` |
|
||||
|
||||
Confirmed by decoding a QR from BML's app: `62→05` = `70V3UKKUA`, the same as the vertical text on its card.
|
||||
|
||||
### Fahipay — `P` + 9 random characters
|
||||
|
||||
Fahipay's server issues references like `P135KOKXJY` and `P2KVTPYL4E`: `P` followed by 9 uppercase alphanumerics. The vertical text shows the reference only — **the amount is not appended** (confirmed on a QR carrying amount `55`). Thijooree generates `"P" + 9 random chars`.
|
||||
|
||||
### Others
|
||||
|
||||
9 random uppercase alphanumeric characters.
|
||||
|
||||
---
|
||||
|
||||
## Real Receive QRs (Reference Samples)
|
||||
|
||||
Decoded from QR images generated by the official apps (CRC verified).
|
||||
|
||||
**BML app** (open amount):
|
||||
|
||||
```
|
||||
00020101021126920014mv.favara.mpqr0108MALBMVMV0208MALBMVMV031377300001883620511+96091980261004IPAY6006LD04425204000053034625403***5802MV5915SHIHAM A.RAHMAN6234050970V3UKKUA0817Quickpay Transfer80470014mv.favara.mpqr01252026-09-26T02:29:53.000006304F8E6
|
||||
```
|
||||
|
||||
Note that BML's app places tag `60` *inside* tag `26` here (after `10 IPAY`, as `6006LD0442`).
|
||||
|
||||
**Fahipay app**, open amount:
|
||||
|
||||
```
|
||||
00020101021126810014mv.favara.mpqr0108FAHIMVMV0208FAHIMVMV03125008500611080511+96098074051004IPAY5204000053034625403***5802MV5912MOHAMED RAIF6006LD074562250510P135KOKXJY0807PAYMENT80470014mv.favara.mpqr01252026-09-26T03:44:36.0000063042707
|
||||
```
|
||||
|
||||
**Fahipay app**, amount `55`:
|
||||
|
||||
```
|
||||
00020101021226810014mv.favara.mpqr0108FAHIMVMV0208FAHIMVMV03125003600510030511+96091980261004IPAY5204000053034625402555802MV5919SHIHAM ABDUL RAHMAN6006LD097062250510P2KVTPYL4E0807PAYMENT80550014mv.favara.mpqr01252026-09-26T03:22:08.000000204000563045304
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Fahipay (Work in Progress)
|
||||
|
||||
> ⚠️ **Fahipay QRs generated by Thijooree do not work yet.** The Fahipay app rejects them as **"Invalid QR"**. BML QRs from Thijooree scan fine in the BML app.
|
||||
|
||||
Fahipay's app doesn't build its QR locally: it fetches it from `GET api/app/qr/?lang=…&type=p2p&amount=…`, and the server returns the finished card image and payload. Things tried so far, with the Fahipay app still reporting invalid:
|
||||
|
||||
| Change | Status |
|
||||
|---|---|
|
||||
| Mobile `26→05` normalised from Fahipay's stored `960XXXXXXX` to `+960XXXXXXX` | Done (was a real bug) |
|
||||
| Name `59` uppercased | Done |
|
||||
| `54` = `***` for open amount, `01` = `12` with an amount | Done |
|
||||
| `60` = `LD` + 4 random digits | Done |
|
||||
| `62→08` defaults to `PAYMENT` | Done |
|
||||
| `62→05` shaped `P` + 9 chars | Done |
|
||||
| `80→02` = `0005` (amount QRs only) | Not done |
|
||||
|
||||
The CRC is correct (verified against all samples). With no amount, Thijooree's payload now has the same fields in the same order as Fahipay's own. The leading theory is that Fahipay's scanner looks up the `P…` reference on Fahipay's server, which issued it. If so, no locally generated QR can pass, and the fix would be to fetch the payload from `api/app/qr/` and render the card around it.
|
||||
|
||||
---
|
||||
|
||||
## Parsing a PayMV QR (Incoming Scan)
|
||||
@@ -219,11 +300,11 @@ exactly one request is made either way.
|
||||
|
||||
## Example Payload
|
||||
|
||||
Static QR for account `7700000000123`, holder `"AHMED ALI"`, open amount, purpose `"Rent"`:
|
||||
Static BML QR for account `7730000188362`, holder `"AHMED ALI"`, open amount, purpose `"Rent"`:
|
||||
|
||||
```
|
||||
000201010211268...520400005303462
|
||||
5802MV5909AHMED ALI6225050912345ABCDEF0804Rent
|
||||
5802MV5909AHMED ALI6221050970V3UKKUA0804Rent
|
||||
80...63044A2B
|
||||
```
|
||||
|
||||
|
||||
Reference in New Issue
Block a user