This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
# Ooredoo M-Faisa API Documentation
|
||||
|
||||
Reverse-engineered from traffic captures and live Frida hooks of the official Ooredoo SuperApp (`com.mventus.ooredoomaldives`).
|
||||
|
||||
[Play Store](https://play.google.com/store/apps/details?id=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:
|
||||
|
||||
1. **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](01-encryption.md).
|
||||
2. **Anti-replay envelope.** Every session-scoped form-encoded POST carries an `rndValue` (RSA-encrypted timestamp) and a `csValue` (Adler32 of `formDataJson + nonceStr`). See [01-encryption.md → `rndValue` / `csValue`](01-encryption.md#anti-replay-envelope-rndvalue--csvalue).
|
||||
3. **Cloudflare-fingerprinted header order.** A `User-Agent` header 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-Agent` header is pushed to the front of the request and the request is rejected with HTTP 400. Let OkHttp's `BridgeInterceptor` add the default `okhttp/4.12.0` at 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](01-encryption.md) | Mobile / mPin RSA, the `rndValue` + `csValue` envelope, the Gson `=` quirk, key-extraction story |
|
||||
| 2 | [Login](02-login.md) | Subscriber lookup + mPIN login |
|
||||
| 3 | [Transaction History](03-history.md) | Paginated history per session |
|
||||
| 4 | [Transfer Money](04-transfer.md) | Three-step wallet-to-wallet send: recipient lookup → initiate (server SMSes OTP) → confirm |
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
> **Next →** [Encryption & Anti-Replay](01-encryption.md)
|
||||
Reference in New Issue
Block a user