202 lines
5.1 KiB
Markdown
202 lines
5.1 KiB
Markdown
# Personal Profile
|
|
|
|
This document covers two unrelated profile-adjacent surfaces:
|
|
|
|
1. **Personal profile** — an HTML page on the WebView host, scraped for display.
|
|
2. **Profile image management** — three encrypted-API endpoints (`P40`/`P41`/`P42`) that fetch, upload, and delete the avatar.
|
|
|
|
---
|
|
|
|
## 1. Personal Profile (HTML scrape)
|
|
|
|
Fetch the user's personal profile details. This endpoint returns an HTML page; data is extracted via HTML scraping.
|
|
|
|
### Endpoint
|
|
|
|
```
|
|
GET https://faisamobilex-wv.mib.com.mv/personalProfile
|
|
```
|
|
|
|
### Authentication
|
|
|
|
Session cookies only — no additional AJAX headers required.
|
|
|
|
```
|
|
Cookie: mbmodel=IOS-1.0; xxid=<session_xxid>; IBSID=<session_xxid>; mbnonce=<nonceGenerator>; time-tracker=597
|
|
```
|
|
|
|
### Response
|
|
|
|
**Content-Type:** `text/html; charset=UTF-8`
|
|
|
|
The page contains an `<h5>` with the user's full name and `<span>` elements with labelled fields.
|
|
|
|
### Parsing Strategy
|
|
|
|
**Full name** — extracted from:
|
|
```html
|
|
<h5 class="mb-1 text-dark fw-semibold">Mohamed Ali</h5>
|
|
```
|
|
|
|
Regex:
|
|
```kotlin
|
|
Regex("""<h5 class="mb-1 text-dark fw-semibold">\s*([^<]+)\s*</h5>""")
|
|
```
|
|
|
|
**Labelled fields** — each follows this pattern:
|
|
```html
|
|
<span ...><b ...>Username:</b ...>...<span ...>myusername</span>
|
|
```
|
|
|
|
Regex (used for each label):
|
|
```kotlin
|
|
Regex(
|
|
"""<span[^>]*>\s*<b[^>]*>\s*$label\s*</b[^>]*>.*?<span[^>]*>([^<]+)</span>""",
|
|
setOf(RegexOption.DOT_MATCHES_ALL, RegexOption.IGNORE_CASE)
|
|
)
|
|
```
|
|
|
|
### Extracted Fields
|
|
|
|
| Label in HTML | Field | Description |
|
|
|---|---|---|
|
|
| `Username:` | `username` | Login username |
|
|
| `Email:` | `email` | Registered email address |
|
|
| `Mobile no:` | `mobile` | Registered mobile number |
|
|
| `Enrolled:` | `enrolled` | Enrollment date or status |
|
|
|
|
Combined with the `fullName` from the `<h5>`:
|
|
|
|
```kotlin
|
|
data class MibPersonalProfile(
|
|
val fullName: String,
|
|
val username: String,
|
|
val email: String,
|
|
val mobile: String,
|
|
val enrolled: String
|
|
)
|
|
```
|
|
|
|
### Notes
|
|
|
|
- Returns `null` if the response cannot be parsed (network error or unexpected HTML structure).
|
|
- This endpoint does not have a JSON equivalent — scraping is the only method.
|
|
|
|
---
|
|
|
|
## 2. Profile Image Management
|
|
|
|
The avatar lives on the **encrypted API** (`faisanet.mib.com.mv`), not the WebView host. Three `sfunc=n` route paths cover fetch, upload, and delete. All three follow the standard encrypted-request format (see [01-encryption.md](01-encryption.md) and [02-login.md](02-login.md)) and include the standard `baseData` fields (`nonce`, `appId`, `sodium`, `routePath`, `xxid`).
|
|
|
|
| `routePath` | Operation | Source |
|
|
|---|---|---|
|
|
| `P41` | Fetch image by hash | `MibLoginFlow.kt:368-375` |
|
|
| `P40` | Upload new image | `MibLoginFlow.kt:382-391` |
|
|
| `P42` | Delete current image | `MibLoginFlow.kt:397-403` |
|
|
|
|
### Image-hash field naming
|
|
|
|
The same conceptual value — "the hash that identifies a customer's avatar" — is exposed under **two different field names** depending on which endpoint returned it:
|
|
|
|
| Source endpoint | Field name |
|
|
|---|---|
|
|
| `A41` login init (`operatingProfiles[]`) | `customerImage` |
|
|
| `C41` registration init (root) | `customerImgHash` |
|
|
| `ajaxBeneficiary/main` (contacts list) | `customerImgHash` |
|
|
|
|
Both are non-empty hash strings that can be passed straight into `P41` as `imageHash`. Treat them as the same value with two names.
|
|
|
|
### Fetch — `routePath: P41`
|
|
|
|
**Request** (encrypted payload):
|
|
```json
|
|
{
|
|
"sfunc": "n",
|
|
"xxid": "<session xxid>",
|
|
"data": {
|
|
"imageHash": "<customerImage or customerImgHash>",
|
|
"nonce": "<computed nonce>",
|
|
"appId": "<appId>",
|
|
"sodium": "<random>",
|
|
"routePath": "P41",
|
|
"xxid": "<session xxid>"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Response**:
|
|
```json
|
|
{
|
|
"success": true,
|
|
"reasonCode": "201",
|
|
"profileImage": "<base64-encoded JPEG>"
|
|
}
|
|
```
|
|
|
|
`profileImage` is raw base64 with no data URI prefix.
|
|
|
|
### Upload — `routePath: P40`
|
|
|
|
**Request** (encrypted payload):
|
|
```json
|
|
{
|
|
"sfunc": "n",
|
|
"xxid": "<session xxid>",
|
|
"data": {
|
|
"profileId": "<active profile ID>",
|
|
"profileImage": "<base64-encoded JPEG>",
|
|
"nonce": "<computed nonce>",
|
|
"appId": "<appId>",
|
|
"sodium": "<random>",
|
|
"routePath": "P40",
|
|
"xxid": "<session xxid>"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Response**:
|
|
```json
|
|
{
|
|
"success": true,
|
|
"imageHash": "<new hash>",
|
|
"customerImage": "<new hash, alternate field name>"
|
|
}
|
|
```
|
|
|
|
The server may populate either `imageHash` or `customerImage` for the new hash — the client reads both (`MibLoginFlow.kt:389-390`) and prefers `imageHash` when present.
|
|
|
|
### Delete — `routePath: P42`
|
|
|
|
**Request** (encrypted payload):
|
|
```json
|
|
{
|
|
"sfunc": "n",
|
|
"xxid": "<session xxid>",
|
|
"data": {
|
|
"profileId": "<active profile ID>",
|
|
"nonce": "<computed nonce>",
|
|
"appId": "<appId>",
|
|
"sodium": "<random>",
|
|
"routePath": "P42",
|
|
"xxid": "<session xxid>"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Response**:
|
|
```json
|
|
{ "success": true }
|
|
```
|
|
|
|
After a successful delete, the `customerImage` field on subsequent `A41` responses is blank.
|
|
|
|
> Note: BML and Fahipay profile images are stored locally on-device only (`util/ProfileImageStore.kt`). Only MIB persists avatars server-side via these endpoints.
|
|
|
|
---
|
|
|
|
|
|
|
|
---
|
|
|
|
[← Financing](06-financing.md) **Next →** [Transfer](08-transfer.md)
|