Files
thijooree/docs/mfaisaapi/03-history.md
T
shihaam 43f3cca2aa
Auto Tag on Version Change / check-version (push) Failing after 13m41s
ooredoo mfaisa
2026-06-27 14:25:46 +05:00

150 lines
5.1 KiB
Markdown

# Transaction History
Fetch a paginated list of transactions for the active subscriber session.
---
## Endpoint
```
POST https://superapp.ooredoo.mv/api/mfaisaa-bff/mfino/v1.1/web/transactionInquiry/fetchSummary
```
Requires an active session (i.e. a valid [`loginExchangeKey`](02-login.md#step-2-domobilelogin)) obtained from `doMobileLogin`. Sessions expire after `mobileLoginSessionTimeout` seconds (240s) — see [Session expiry](#session-expiry) below.
---
## Request
**Content-Type:** `application/x-www-form-urlencoded`
| Field | Value |
|---|---|
| `role` | `RETAIL_SUBSCRIBER` (constant) |
| `channel` | `SubscriberApp`**not** `C03` as in login |
| `loginExchangeKey` | From the login response |
| `mdnId` | `<encryptMobile(msisdn), base64>` — same routine as `mdnId` in login |
| `formData` | JSON object (see below), [html-safe-escaped](01-encryption.md#html-safe-gson--escape) |
| `rndValue` | [Anti-replay nonce](01-encryption.md#anti-replay-envelope-rndvalue--csvalue) |
| `csValue` | [Adler32 integrity check](01-encryption.md#anti-replay-envelope-rndvalue--csvalue) |
### `formData` JSON
```json
{
"actorRole": "RETAIL_SUBSCRIBER",
"actorRoleId": "<suscriberId from login response>",
"fromDate": "",
"mdnId": "<encryptMobile(msisdn), base64>",
"pageNo": "1",
"recordSize": "70",
"toDate": "",
"transactionType": ""
}
```
- `actorRoleId` comes from `suscriberId` at the top level of `doMobileLogin`'s success body — see [02-login.md](02-login.md#step-2-domobilelogin). It also appears as `pocketDetails[0].roleId` in the same response.
- The inner `mdnId` is independently encrypted from the outer `mdnId` — same plaintext, different ciphertext (OAEP random padding).
- `fromDate` / `toDate` are empty strings in the official app — the server returns all available history.
---
## curl Example
```bash
# Requires a valid loginExchangeKey + suscriberId from a fresh login call
python tmp/mfaisa_history.py <msisdn> <mpin> 1
```
See `tmp/mfaisa_history.py` for the full Python reference (it does the login, captures the session, then calls fetchSummary).
---
## Response
```json
{
"transactionInquiryDTOList": [
{
"requestId": "<request id>",
"referenceId": "<reference id>",
"sourceMDN": "<Subscriber Name>-DT Pocket-<msisdn>",
"sourcePocketId":"<pocket id>",
"actorRoleType": "RETAIL_SUBSCRIBER",
"actorRoleId": "<subscriber id>",
"commodityType": "WALLET",
"channel": "SubscriberApp",
"transactionAmount": { "amount": 1, "currencyCode": "MVR" },
"userStatus": "CONFIRMED",
"trnStage": "AUTO_REVERSED",
"trnType": "CASH_IN",
"status": "FAILED",
"trnDate": "2026-06-13 13:04:19",
"narrationString": "Load Money",
"typeSummaryString": "[{\"Transaction Type\":\"Load Money\",\"Deposit Pocket\":\"DT Pocket\",\"Reference\":\"<ref>\"}]",
"errorCode": "INT_002",
"errorDesc": "QR_CODE_GENERATED",
"resolutionDetails": "Please reconcile with the payment gateway..."
},
"..."
]
}
```
Key fields used by Thijooree:
| Field | Maps to `BankTransaction` |
|---|---|
| `trnDate` | `date` (already in `YYYY-MM-DD HH:mm:ss` form) |
| `narrationString` | `description` (suffixed with `· Failed` when `status == "FAILED"`) |
| `transactionAmount.amount` | `amount` (signed — see direction rule below) |
| `transactionAmount.currencyCode` | `currency` |
| `referenceId` (fallback `requestId`) | `id` + `reference` |
| `typeSummaryString``Merchant Name` / `Receiver Name` / `Sender Name` | `counterpartyName` |
| `sourceMDN` (e.g. `"<Name>-DT Pocket-<msisdn>"`) | `counterpartyName` fallback (first segment before `-`) |
### Debit / credit direction
The response does not include a signed amount or direction flag. Direction is inferred from `trnType`:
| `trnType` | Direction |
|---|---|
| `CASH_IN`, `RECEIVE_MONEY`, `*_IN` | credit (positive amount) |
| everything else (`PURCHASE`, `TRANSFER`, …) | debit (negative amount) |
### Pagination
The server does not return a `total` field. Thijooree treats "received a full `recordSize` (= 70) records" as the only signal that further pages may exist; the next call uses `pageNo = pageNo + 1`. Once a page comes back with fewer than 70 records, no more pages are fetched.
### Session expiry
When the 240-second session lapses, the server still returns HTTP 200 but the body is its standard error envelope:
```json
[
{
"success": false,
"message": "validation errors",
"error": [
{
"objectName": "LoginLog",
"attributeName": "LoginLog",
"attributeValue": "SESSION_EXPIRED",
"errorCode": "SESSION_EXPIRED",
"errorMessage": "SESSION_EXPIRED"
}
]
}
]
```
`MfaisaHistoryClient` parses this into `MfaisaSessionExpiredException`. Callers (`HistoryFetcher`, `TransferHistoryFragment`) catch it, call `BasedBankApp.refreshMfaisaSession(loginId)` to re-login transparently, and retry the same page once.
---
&nbsp;
---
> **Next →** [Transfer Money](04-transfer.md) | **← Back to** [Login](02-login.md) | [README](README.md)