293 lines
8.0 KiB
Markdown
293 lines
8.0 KiB
Markdown
# Account Lookup & Transfer
|
|
|
|
Two-step process: look up the recipient to validate the account and get the holder name, then execute the transfer.
|
|
|
|
All endpoints are on the encrypted API (`sfunc=n`, session key). See [README](README.md) for the request format. The source account must belong to the **currently selected** profile — call `P47` for its profile first (see [03-accounts.md](03-accounts.md)).
|
|
|
|
---
|
|
|
|
## Step 1 — Account Lookup — `routePath: B40`
|
|
|
|
One route resolves every supported input:
|
|
|
|
| Input format | Example | Resolves to |
|
|
|---|---|---|
|
|
| Starts with `7`, exactly 13 digits | `7700000000000` | BML / local bank account |
|
|
| Starts with `9`, exactly 17 digits | `90100000000000000` | MIB account |
|
|
| Starts with `7` or `9`, exactly 7 digits | `9600001` | Favara mobile alias |
|
|
| `A` followed by 6 digits | `A000001` | Favara National ID alias |
|
|
| Contains `@` | `user@example.com` | Favara email alias |
|
|
|
|
**Request**:
|
|
```json
|
|
{
|
|
"sfunc": "n",
|
|
"xxid": "<session xxid>",
|
|
"data": {
|
|
"search": "7700000000000",
|
|
"nonce": "<computed nonce>",
|
|
"appId": "<appId>",
|
|
"sodium": "<random 20-bit int>",
|
|
"routePath": "B40",
|
|
"xxid": "<session xxid>"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Input Rules
|
|
|
|
- **Mobile aliases** must be the bare 7 digits. `+960 9600001` and `+9609600001` return no match.
|
|
- **National ID aliases** need an uppercase `A` — `a000001` returns no match, `A000001` resolves. The client uppercases the prefix before sending (`MibTransferClient.kt`).
|
|
- Partial input (e.g. `9600`) returns an empty `data` array.
|
|
|
|
### Response
|
|
|
|
**BML account**:
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": [
|
|
{
|
|
"benefName": "ACCOUNT HOLDER NAME",
|
|
"benefAccount": "7700000000000",
|
|
"bankCode": "MALBMVMV",
|
|
"benefBankName": "Bank of Maldives"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
**MIB account**:
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": [
|
|
{
|
|
"benefName": "ACCOUNT HOLDER NAME",
|
|
"benefAccount": "90100000000000000",
|
|
"bankCode": "MIB",
|
|
"benefBankName": "MIB",
|
|
"transferCy": "462",
|
|
"transferCyDesc": "MVR"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
**Favara alias** (resolving to an MIB account):
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": [
|
|
{
|
|
"benefName": "Account Holder Name ",
|
|
"benefAccount": "90100000000000000",
|
|
"bankCode": "MADVMVMV",
|
|
"benefBankName": "Maldives Islamic Bank PLC."
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
| Field | Description |
|
|
|---|---|
|
|
| `benefName` | Account holder name — trim whitespace |
|
|
| `benefAccount` | Resolved account number to use for the transfer |
|
|
| `bankCode` | `"MIB"` or a bank BIC (`MADVMVMV` = MIB, `MALBMVMV` = BML). Normalize `"MIB"` to `MADVMVMV`. |
|
|
| `benefBankName` | Bank display name |
|
|
| `transferCy` / `transferCyDesc` | Account currency — present for MIB account lookups only |
|
|
|
|
### Lookup Errors
|
|
|
|
No match returns `success: true` with an empty `data` array:
|
|
|
|
```json
|
|
{ "success": true, "data": [] }
|
|
```
|
|
|
|
Show "account not found" in that case.
|
|
|
|
### Currency
|
|
|
|
| Lookup | Currency |
|
|
|---|---|
|
|
| MIB account | From `transferCyDesc` (`"MVR"` / `"USD"`) |
|
|
| Favara alias → MIB | Not returned — unknown |
|
|
| BML / local | `"MVR"` — cross-bank lookups only resolve MVR accounts |
|
|
|
|
> **USD cross-bank accounts**: a BML account denominated in USD cannot be resolved here. For USD BML→MIB transfers the user must first save a BML contact (see Notes below).
|
|
|
|
---
|
|
|
|
## Step 2 — Execute Transfer
|
|
|
|
Two routes depending on the destination bank:
|
|
|
|
| `bankNo` | `routePath` | Destination |
|
|
|---|---|---|
|
|
| `2` | `T50` | MIB internal account |
|
|
| `3` | `T51` | BML or other local bank (via Favara) |
|
|
|
|
**Request**:
|
|
```json
|
|
{
|
|
"sfunc": "n",
|
|
"xxid": "<session xxid>",
|
|
"data": {
|
|
"benefName": "ACCOUNT HOLDER NAME",
|
|
"benefNo": "0",
|
|
"fromAccountNo": "90101000000001000",
|
|
"benefAccountNo": "7700000000000",
|
|
"transferCy": "462",
|
|
"benefCurrencyCode": "462",
|
|
"amount": "100.00",
|
|
"bankNo": "3",
|
|
"purpose": "",
|
|
"otp": "123456",
|
|
"otpType": "3",
|
|
"nonce": "<computed nonce>",
|
|
"appId": "<appId>",
|
|
"sodium": "<random 20-bit int>",
|
|
"routePath": "T51",
|
|
"xxid": "<session xxid>"
|
|
}
|
|
}
|
|
```
|
|
|
|
| Field | Description |
|
|
|---|---|
|
|
| `benefName` | Recipient name (from lookup) |
|
|
| `benefNo` | `"0"` (not a saved contact) |
|
|
| `fromAccountNo` | Source account number |
|
|
| `benefAccountNo` | Destination account number |
|
|
| `transferCy` | Currency numeric code (`"462"` = MVR, `"840"` = USD) |
|
|
| `benefCurrencyCode` | Same as `transferCy` |
|
|
| `amount` | Amount as string (e.g. `"100.00"`, `".01"`) |
|
|
| `bankNo` | `"2"` = MIB internal, `"3"` = local/BML |
|
|
| `purpose` | Transfer remarks; may be empty |
|
|
| `otp` | Current TOTP code |
|
|
| `otpType` | `"3"` (authenticator OTP) |
|
|
|
|
### Response — Success
|
|
|
|
**`T50`** (internal):
|
|
```json
|
|
{
|
|
"success": true,
|
|
"responseCode": "2",
|
|
"reasonCode": "2011",
|
|
"reasonText": "Internal transfer success",
|
|
"data": [
|
|
{ "date": "2026-05-16 15:10:25", "trxId": "97999863", "availableBalance": "37.41" }
|
|
],
|
|
"accountBalance": [ ... ]
|
|
}
|
|
```
|
|
|
|
**`T51`** (local):
|
|
```json
|
|
{
|
|
"success": true,
|
|
"responseCode": "2",
|
|
"reasonCode": "2011",
|
|
"reasonText": "Local transfer completed successfully via Favara",
|
|
"data": [
|
|
{ "date": "2026-05-16 15:10:25", "trxId": "817054125" }
|
|
]
|
|
}
|
|
```
|
|
|
|
| Field | Description |
|
|
|---|---|
|
|
| `trxId` | Transaction ID |
|
|
| `date` | Completion timestamp |
|
|
| `availableBalance` | `T50` only — source account balance after the transfer |
|
|
| `accountBalance` | `T50` only — refreshed balances for the selected profile (same shape as in [03-accounts.md](03-accounts.md)) |
|
|
|
|
### Response — Failure
|
|
|
|
Failures return HTTP `400` with an encrypted body:
|
|
|
|
```json
|
|
{
|
|
"success": false,
|
|
"responseCode": "1",
|
|
"reasonCode": "213",
|
|
"reasonText": "Invalid OTP",
|
|
"data": [ { "otpId": "50487016" } ]
|
|
}
|
|
```
|
|
|
|
| `reasonCode` | `reasonText` | Meaning |
|
|
|---|---|---|
|
|
| `213` | `Invalid OTP` | Wrong or stale OTP |
|
|
| `707` | `That OTP is already used!` | The OTP was already accepted for an earlier request |
|
|
| `201` | `Transaction currency does not match From Account currency!` | `transferCy` doesn't match the source account |
|
|
|
|
Show `reasonText` to the user.
|
|
|
|
### OTP Retry
|
|
|
|
On `213` or `707` the client waits for the next 30-second TOTP window (plus 1 second for clock skew), generates a new code and resends the transfer **once**. A second failure of any kind is shown to the user (`MibTransferHandler.kt`).
|
|
|
|
---
|
|
|
|
## Transfer Type Summary
|
|
|
|
| Recipient | Lookup | `bankNo` | Transfer |
|
|
|---|---|---|---|
|
|
| MIB (17-digit `9…`) | `B40` | `2` | `T50` |
|
|
| BML (13-digit `7…`) | `B40` | `3` | `T51` |
|
|
| Favara alias → MIB | `B40` | `2` | `T50` |
|
|
| Favara alias → BML | `B40` | `3` | `T51` |
|
|
|
|
---
|
|
|
|
## Currency Codes
|
|
|
|
| `transferCy` | Currency |
|
|
|---|---|
|
|
| `"462"` | MVR (Maldivian Rufiyaa) |
|
|
| `"840"` | USD |
|
|
|
|
---
|
|
|
|
## Other Transfer-Screen Routes
|
|
|
|
Called by the official app when opening the transfer screen. Not consumed by thijooree.
|
|
|
|
| `routePath` | Request | Returns |
|
|
|---|---|---|
|
|
| `T63` | — | Currencies with rates (`numericCode`, `code`, `name`, `rate`) |
|
|
| `T66` | `currencyCode` | Sell rate for a currency (`sellRate`) |
|
|
| `T70` | — | Upload settings (`numFiles`, `fileSize`) |
|
|
| `T71` | optional `transferCy` | AML thresholds (`eddAmount`, `cddAmount`, `currencyCode`) |
|
|
|
|
---
|
|
|
|
## `MibIpsAccountInfo` (client model)
|
|
|
|
`B40` lookups are mapped into this structure (`MibModels.kt`):
|
|
|
|
| Field | Description |
|
|
|---|---|
|
|
| `accountName` | Account holder name (trimmed) |
|
|
| `accountNumber` | Resolved account number |
|
|
| `bankId` | Bank BIC (`MADVMVMV` = MIB, `MALBMVMV` = BML, etc.) |
|
|
| `currency` | `"MVR"`, `"USD"`, or `""` (unknown) — see Currency above |
|
|
|
|
---
|
|
|
|
## Notes
|
|
|
|
- **BML → MIB USD transfers** require a saved BML contact first. Because USD BML accounts can't be resolved via `B40`, the app cannot validate the account number directly. The workaround in `TransferFragment.kt` is to add the BML account as a beneficiary (see [09-contacts.md](09-contacts.md)), then transfer to that beneficiary.
|
|
- **Session expiry**: see [README](README.md) for the unified expiry detection rules.
|
|
|
|
---
|
|
|
|
|
|
|
|
---
|
|
|
|
[← Personal Profile](07-profile.md) **Next →** [Contacts](09-contacts.md)
|