4.2 KiB
Ooredoo M-Faisa API Documentation
Reverse-engineered from traffic captures and live Frida hooks of the official Ooredoo SuperApp (com.mventus.ooredoomaldives).
Overview
M-Faisa is Ooredoo Maldives' mobile wallet, exposed via a JSON/form-encoded REST API on superapp.ooredoo.mv. The wire format is unusual in three ways:
- Field-level RSA encryption. The MSISDN (
mdnId,mobileNumber,userName,initiatingMDN,identifier) and the mPIN (mPin) are each encrypted with a different RSA public key before being placed in the request body. See 01-encryption.md. - Anti-replay envelope. Every session-scoped form-encoded POST carries an
rndValue(RSA-encrypted timestamp) and acsValue(Adler32 offormDataJson + nonceStr). See 01-encryption.md →rndValue/csValue. - Cloudflare-fingerprinted header order. A
User-Agentheader sent explicitly (instead of letting OkHttp add it last) returns HTTP 400.
Base URL
https://superapp.ooredoo.mv
All M-Faisa endpoints are mounted at /api/mfaisaa-bff/mfino/v1.1/web/....
Authentication Model
| Value | How obtained | How used |
|---|---|---|
loginExchangeKey |
Returned by doMobileLogin on success |
Held in memory only; identifies the session |
| Session timeout | mobileLoginSessionTimeout field, default 240 seconds |
After expiry the user must re-login (no refresh-token flow) |
Because there is no refresh, Thijooree re-runs fetchSubscriberByMDN + doMobileLogin on every cold-start refresh, using the saved msisdn + mPIN from CredentialStore. The SESSION_EXPIRED error envelope is also caught at runtime and the session is silently re-established before retrying the failed request.
Common Request Headers
Content-Type: application/json; charset=UTF-8 (fetchSubscriberByMDN)
Content-Type: application/x-www-form-urlencoded (every other endpoint)
Host: superapp.ooredoo.mv
Connection: Keep-Alive
Accept-Encoding: gzip
User-Agent: okhttp/4.12.0
Do NOT set User-Agent in code. Cloudflare fingerprints the header order; an explicit
User-Agentheader is pushed to the front of the request and the request is rejected with HTTP 400. Let OkHttp'sBridgeInterceptoradd the defaultokhttp/4.12.0at the end.
Login Flow
Client Server
| |
| POST /fetchSubscriberByMDN |
| { mdnId: encryptMobile(msisdn) } |
|---------------------------------------------------->|
| { success, subscriberRegistered, kycStatus, ... } |
|<----------------------------------------------------|
| |
| (abort if subscriberRegistered=false |
| or kycStatus != "Full KYC") |
| |
| POST /doMobileLogin |
| channel=C03 |
| formData={ deviceGeoInfo, mPin: encryptPin(mpin), |
| mobileNumber: ..., userName: ..., |
| role:"RETAIL_SUBSCRIBER", |
| tenantCode:"ooredoo" } |
| formDataCs=null |
|---------------------------------------------------->|
| { success, loginExchangeKey, pocketDetails: [...]} |
|<----------------------------------------------------|
Documents
| # | File | Description |
|---|---|---|
| 1 | Encryption & Anti-Replay | Mobile / mPin RSA, the rndValue + csValue envelope, the Gson = quirk, key-extraction story |
| 2 | Login | Subscriber lookup + mPIN login |
| 3 | Transaction History | Paginated history per session |
| 4 | Transfer Money | Three-step wallet-to-wallet send: recipient lookup → initiate (server SMSes OTP) → confirm |
Next → Encryption & Anti-Replay