ooredoo billpay via bml card
This commit is contained in:
@@ -12,10 +12,10 @@ import java.util.Locale
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/**
|
||||
* Ooredoo prepaid recharge (Raastas) through the ooredoo.mv Quick Pay page, paid by card on
|
||||
* BML's merchant gateway. Ooredoo only creates the order; the money moves on the BML Merchant
|
||||
* Services transaction it hands back, which is paid like any card-only BML merchant link. See
|
||||
* `docs/ooredooapi/02-raastas.md`.
|
||||
* Ooredoo prepaid recharge (Raastas) and bill payment through the ooredoo.mv Quick Pay pages,
|
||||
* paid by card on BML's merchant gateway. Ooredoo only creates the order; the money moves on the
|
||||
* BML Merchant Services transaction it hands back, which is paid like any card-only BML merchant
|
||||
* link. See `docs/ooredooapi/02-raastas.md` and `docs/ooredooapi/03-bill-pay.md`.
|
||||
*
|
||||
* Every call blocks, so run it on an IO thread.
|
||||
*/
|
||||
@@ -32,15 +32,29 @@ class OoredooPaymentClient {
|
||||
* that plus GST. Returns the 24-hex BML transaction id. Throws with Ooredoo's wording when
|
||||
* the order is refused.
|
||||
*/
|
||||
fun createRaastasTransaction(number: String, amount: Int, charged: BigDecimal): String {
|
||||
fun createRaastasTransaction(number: String, amount: Int, charged: BigDecimal): String =
|
||||
createOrder(number, charged, amount.toString(), transType = "recharge", serviceType = "prepaid")
|
||||
|
||||
/**
|
||||
* Creates the bill payment order for postpaid [number] (7 digits) and the BML transaction
|
||||
* paying for it. [amount] is MVR, up to 2 decimal places; no GST. Returns the 24-hex BML
|
||||
* transaction id. Throws with Ooredoo's wording when the order is refused.
|
||||
*/
|
||||
fun createBillPayTransaction(number: String, amount: BigDecimal): String =
|
||||
createOrder(number, amount, money(amount), transType = "billpay", serviceType = "Mobile")
|
||||
|
||||
/** `POST PaymentGateway/bml` — one call makes the order and its BML transaction. */
|
||||
private fun createOrder(
|
||||
number: String, charged: BigDecimal, amountWithoutGst: String, transType: String, serviceType: String,
|
||||
): String {
|
||||
val msisdn = "960$number"
|
||||
val body = JSONObject()
|
||||
.put("msisdn", msisdn)
|
||||
.put("purchaseAmount", String.format(Locale.US, "%.2f", charged.setScale(2, RoundingMode.HALF_UP)))
|
||||
.put("amountWithoutGst", amount.toString())
|
||||
.put("purchaseAmount", money(charged))
|
||||
.put("amountWithoutGst", amountWithoutGst)
|
||||
.put("receiverMsisdn", msisdn)
|
||||
.put("transType", "recharge")
|
||||
.put("serviceType", "prepaid")
|
||||
.put("transType", transType)
|
||||
.put("serviceType", serviceType)
|
||||
.put("serviceTypeDisplayName", "Mobile")
|
||||
val text = client.newCall(
|
||||
Request.Builder().url("$BASE/ooredoo-prod/PaymentGateway/bml")
|
||||
@@ -48,7 +62,6 @@ class OoredooPaymentClient {
|
||||
.header("User-Agent", UA)
|
||||
.header("Accept", "application/json")
|
||||
.header("Origin", BASE)
|
||||
.header("Referer", "$BASE/payment?source=Recharge&transType=2")
|
||||
.build()
|
||||
).execute().use { r ->
|
||||
if (r.code in 500..599) throw BankServerException("Ooredoo")
|
||||
@@ -58,13 +71,16 @@ class OoredooPaymentClient {
|
||||
throw Exception("Unexpected response from Ooredoo")
|
||||
}
|
||||
if (obj.optString("status") != "OK" || obj.optString("code") != "2000") {
|
||||
throw Exception(obj.optString("msg").ifBlank { "Ooredoo refused the recharge" })
|
||||
throw Exception(obj.optString("msg").ifBlank { "Ooredoo refused the payment" })
|
||||
}
|
||||
val data = obj.optJSONObject("data") ?: throw Exception("Ooredoo didn't create the order")
|
||||
return TXN_URL.find(data.optString("bmlUrl"))?.groupValues?.get(1)
|
||||
?: throw Exception("BML didn't create the transaction")
|
||||
}
|
||||
|
||||
private fun money(amount: BigDecimal) =
|
||||
String.format(Locale.US, "%.2f", amount.setScale(2, RoundingMode.HALF_UP))
|
||||
|
||||
companion object {
|
||||
private const val BASE = "https://www.ooredoo.mv"
|
||||
private const val UA = "Mozilla/5.0 (X11; Linux x86_64; rv:150.0) Gecko/20100101 Firefox/150.0"
|
||||
|
||||
@@ -22,8 +22,8 @@ import java.math.RoundingMode
|
||||
* A carrier service a verified BML card can pay, through the carrier's own website and its BML
|
||||
* merchant gateway. The limits are the carrier website's, not Fahipay's.
|
||||
*
|
||||
* No Ooredoo bill pay yet. Add it as a constant once its send path lands; the exhaustive `when`s
|
||||
* over this enum point at every site that needs updating.
|
||||
* Add new services as constants; the exhaustive `when`s over this enum point at every site that
|
||||
* needs updating.
|
||||
*/
|
||||
enum class CardPayoutService(
|
||||
override val label: String,
|
||||
@@ -46,7 +46,9 @@ enum class CardPayoutService(
|
||||
minAmount = 1, maxAmount = null, decimalsAllowed = true, gstPercent = null),
|
||||
// Ooredoo credits the whole amount and charges the card 8% GST on top
|
||||
OOREDOO_RAASTAS("Raastas", "Ooredoo · Raastas", R.drawable.ooredoo_logo,
|
||||
minAmount = 20, maxAmount = null, decimalsAllowed = false, gstPercent = 8, gstAdded = true);
|
||||
minAmount = 20, maxAmount = null, decimalsAllowed = false, gstPercent = 8, gstAdded = true),
|
||||
OOREDOO_BILL("Ooredoo Bill Pay", "Ooredoo · Bill Pay", R.drawable.ooredoo_logo,
|
||||
minAmount = 10, maxAmount = null, decimalsAllowed = true, gstPercent = null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,6 +97,9 @@ class CardPayoutTransferHandler(
|
||||
if (result.ooredoo == OoredooClient.CustType.PRE || result.ooredoo == OoredooClient.CustType.HYBRID) {
|
||||
add(CardPayoutService.OOREDOO_RAASTAS)
|
||||
}
|
||||
if (result.ooredoo == OoredooClient.CustType.POST || result.ooredoo == OoredooClient.CustType.HYBRID) {
|
||||
add(CardPayoutService.OOREDOO_BILL)
|
||||
}
|
||||
}.map { TransferType.Card(it, result.ownerName, cards) }
|
||||
}
|
||||
|
||||
@@ -166,6 +171,8 @@ class CardPayoutTransferHandler(
|
||||
DhiraaguPaymentClient().createBillPayTransaction(number, amount)
|
||||
CardPayoutService.OOREDOO_RAASTAS ->
|
||||
OoredooPaymentClient().createRaastasTransaction(number, amount.intValueExact(), charged)
|
||||
CardPayoutService.OOREDOO_BILL ->
|
||||
OoredooPaymentClient().createBillPayTransaction(number, amount)
|
||||
}
|
||||
BmlMerchantTxnClient().fetchPayPage(txnId)
|
||||
}
|
||||
|
||||
+1
-1
@@ -18,4 +18,4 @@
|
||||
| [mibapi/](mibapi/README.md) | MIB Faisanet — Blowfish-encrypted API + WebView session, accounts, transfers, contacts |
|
||||
| [fahipayapi/](fahipayapi/README.md) | Fahipay digital wallet — login, balance, history, contacts |
|
||||
| [dhiraaguapi/](dhiraaguapi/README.md) | Dhiraagu Easy Pay / Easy TopUp — number lookup, reload and bill pay by BML card |
|
||||
| [ooredooapi/](ooredooapi/README.md) | Ooredoo Quick Pay — number validation, Raastas by BML card |
|
||||
| [ooredooapi/](ooredooapi/README.md) | Ooredoo Quick Pay — number validation, Raastas and bill pay by BML card |
|
||||
|
||||
@@ -104,4 +104,4 @@ cookie; okhttp from the phone gets through without one, as with
|
||||
**Related:** [Number Validation](01-number-validation.md) · [BML Merchant Card Payment](../bmlapi/16-card-payment.md) ·
|
||||
App side: [Transfer Flows](../thijooree/20-transfer-flows.md#carrier-services-by-bml-card)
|
||||
|
||||
[← Number Validation](01-number-validation.md)
|
||||
[← Number Validation](01-number-validation.md) · [Bill Pay →](03-bill-pay.md)
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
# Bill Pay (Quick Pay, paid by BML card)
|
||||
|
||||
Pay an Ooredoo postpaid bill through the ooredoo.mv **Quick Pay** bill-pay page. It is the same
|
||||
single order call as [Raastas](02-raastas.md) with a different `transType` / `serviceType`, and
|
||||
no GST. From the order on, it's the BML card-only merchant flow and the same return to Ooredoo.
|
||||
|
||||
Reconstructed from `docs/ooredooapi/tmp/ooredoo_billpay_bml_card.har` (a Firefox HAR).
|
||||
|
||||
---
|
||||
|
||||
## Flow overview
|
||||
|
||||
```
|
||||
GET /bill-pay
|
||||
GET /ooredoo-prod/QuickPayPackage/v1/numberTypeValidation?… → custType POST (Number Validation)
|
||||
POST /ooredoo-prod/PaymentGateway/bml → orderID, bmlUrl
|
||||
── BML card-only merchant flow ── → TRANSACTION_CONFIRMED
|
||||
GET transaction.merchants…/<id>?wait=1 → 302 my.ooredoo.mv/bml/response_new.php?…state=CONFIRMED
|
||||
(200, auto-submits, transtype=1) → POST www.ooredoo.mv/ooredoo-prod/PaymentGateway/redirect/bml
|
||||
→ /payment-status?statusDesc=sucess&status=1&order_id=<orderID>
|
||||
```
|
||||
|
||||
The page doesn't look up the outstanding bill: the amount is whatever is typed.
|
||||
|
||||
---
|
||||
|
||||
## Order
|
||||
|
||||
`POST https://www.ooredoo.mv/ooredoo-prod/PaymentGateway/bml`, headers as in
|
||||
[Raastas → Order](02-raastas.md#1-order).
|
||||
|
||||
```json
|
||||
{"msisdn":"9609XXXXXX","purchaseAmount":"10.01","amountWithoutGst":"10.01",
|
||||
"receiverMsisdn":"9609XXXXXX","transType":"billpay","serviceType":"Mobile",
|
||||
"serviceTypeDisplayName":"Mobile"}
|
||||
```
|
||||
```json
|
||||
{"status":"OK","msg":"Successfully generated order id","code":"2000",
|
||||
"data":{"orderID":"36447962","purchaseAmount":"1001","shortUrl":"https://pay.bml.com.mv/…",
|
||||
"bmlUrl":"https://transaction.merchants.bankofmaldives.com.mv/6ac011e099f9d890856e2d33", …}}
|
||||
```
|
||||
|
||||
| | Raastas | Bill Pay |
|
||||
|---|---|---|
|
||||
| `transType` | `recharge` | `billpay` |
|
||||
| `serviceType` | `prepaid` | `Mobile` |
|
||||
| `amountWithoutGst` | amount credited | = `purchaseAmount` |
|
||||
| GST | 8% added on top | none |
|
||||
| Return form `transtype` | `2` | `1` |
|
||||
|
||||
### Limits
|
||||
|
||||
Minimum **MVR 10**, decimals allowed (up to 2 places). The maximum isn't known.
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
**Related:** [Number Validation](01-number-validation.md) · [Raastas](02-raastas.md) ·
|
||||
[BML Merchant Card Payment](../bmlapi/16-card-payment.md) ·
|
||||
App side: [Transfer Flows](../thijooree/20-transfer-flows.md#carrier-services-by-bml-card)
|
||||
|
||||
[← Raastas](02-raastas.md)
|
||||
@@ -82,6 +82,7 @@ The API expects the full MSISDN including country code `960` (e.g. `9609654321`)
|
||||
|---|---|---|
|
||||
| 1 | [Number Validation](01-number-validation.md) | Validate an Ooredoo number and determine account type |
|
||||
| 2 | [Raastas](02-raastas.md) | Quick Pay recharge order → BML merchant transaction, paid by card |
|
||||
| 3 | [Bill Pay](03-bill-pay.md) | Quick Pay bill payment order → BML merchant transaction, paid by card |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ A phone number searched with no source yet (or from a BML card that can pay by c
|
||||
|---|---|---|
|
||||
| Favara Transfer | bank account behind the number | MIB or BML account |
|
||||
| Fahipay service | Raastas, Ooredoo Bill Pay, Dhiraagu Reload, Dhiraagu Bill Pay | Fahipay wallet |
|
||||
| Card service | Dhiraagu Reload, Dhiraagu Bill Pay, Raastas (BML badge) | Verified BML card |
|
||||
| Card service | Dhiraagu Reload, Dhiraagu Bill Pay, Raastas, Ooredoo Bill Pay (BML badge) | Verified BML card |
|
||||
|
||||
One option is applied straight away; with more, a picker opens and Send stays disabled until one is chosen. Picking a type also picks a source that can pay it. Fahipay and card services clear and disable the Remarks field and apply their own amount rules (minimum, maximum, whole amounts, 8% GST note). Details: [Transfer Flows → Transfer Type picker](20-transfer-flows.md#transfer-type-picker).
|
||||
|
||||
@@ -122,10 +122,10 @@ When the source is a BML USD account and the destination is a MIB account but no
|
||||
|
||||
See [Transfer Flows → Fahipay source](20-transfer-flows.md#fahipay-source).
|
||||
|
||||
### Carrier Service by BML Card (Dhiraagu Reload / Bill Pay, Ooredoo Raastas)
|
||||
### Carrier Service by BML Card (Dhiraagu Reload / Bill Pay, Ooredoo Raastas / Bill Pay)
|
||||
|
||||
1. Checks the amount against the carrier's rules (Dhiraagu reload: MVR 20–1000, whole amounts, 8% GST included; Dhiraagu bill pay: from MVR 1, up to 2 decimals, no GST; Raastas: from MVR 20, whole amounts, 8% GST added on top)
|
||||
2. A "Processing..." dialog shows while the carrier creates the order and its BML merchant transaction ([Dhiraagu API → Reload](../dhiraaguapi/02-reload.md), [→ Bill Pay](../dhiraaguapi/03-bill-pay.md), [Ooredoo API → Raastas](../ooredooapi/02-raastas.md))
|
||||
1. Checks the amount against the carrier's rules (Dhiraagu reload: MVR 20–1000, whole amounts, 8% GST included; Dhiraagu bill pay: from MVR 1, up to 2 decimals, no GST; Raastas: from MVR 20, whole amounts, 8% GST added on top; Ooredoo bill pay: from MVR 10, up to 2 decimals, no GST)
|
||||
2. A "Processing..." dialog shows while the carrier creates the order and its BML merchant transaction ([Dhiraagu API → Reload](../dhiraaguapi/02-reload.md), [→ Bill Pay](../dhiraaguapi/03-bill-pay.md), [Ooredoo API → Raastas](../ooredooapi/02-raastas.md), [→ Bill Pay](../ooredooapi/03-bill-pay.md))
|
||||
3. From there it is the card-only merchant flow: the same confirm dialog and warning, biometric gate, card + 3-D Secure payment, and the return to the carrier (`?wait=1`) that tops the number up or posts the bill payment. A decline (e.g. insufficient funds) or a rejected token code ends it with the bank's message
|
||||
4. On success, the result shows inside the dialog; if Dhiraagu couldn't be notified, a toast gives the BML transaction id
|
||||
|
||||
|
||||
@@ -152,7 +152,7 @@ None of the Fahipay services take a reference. Picking one clears the Reference
|
||||
|
||||
A carrier service can also be paid with a verified BML card, through the carrier's own website
|
||||
and its BML merchant gateway, instead of the Fahipay wallet: **Dhiraagu Reload**, **Dhiraagu
|
||||
Bill Pay** and **Ooredoo Raastas** so far (`CardPayoutService`, `ui/home/transfer/CardPayoutTransferHandler.kt`).
|
||||
Bill Pay**, **Ooredoo Raastas** and **Ooredoo Bill Pay** (`CardPayoutService`, `ui/home/transfer/CardPayoutTransferHandler.kt`).
|
||||
|
||||
**Which cards.** A card qualifies when it's verified and its BML login has an OTP seed, the same
|
||||
rule as card-only merchant links (`BmlVerifiedCards`, see
|
||||
@@ -167,6 +167,7 @@ drops the pick, like any other source that can't pay the picked type.
|
||||
| Dhiraagu `RELOAD` | Dhiraagu Reload |
|
||||
| Dhiraagu `BILL_PAY` | Dhiraagu Bill Pay |
|
||||
| Ooredoo `PRE` or `HYBRID` | Raastas |
|
||||
| Ooredoo `POST` or `HYBRID` | Ooredoo Bill Pay |
|
||||
|
||||
**Amount rules.** The carrier website's, not Fahipay's. They're checked the same way, through
|
||||
the shared `PayoutAmountField`:
|
||||
@@ -176,9 +177,10 @@ the shared `PayoutAmountField`:
|
||||
| Dhiraagu Reload | 20 | 1,000 | no | 8%, included (credit = amount − round2(amount × 0.08 / 1.08)) |
|
||||
| Dhiraagu Bill Pay | 1 | none | up to 2 places | none |
|
||||
| Raastas | 20 | none | no | 8%, **added** (charged = amount + round2(amount × 0.08)) |
|
||||
| Ooredoo Bill Pay | 10 | none | up to 2 places | none |
|
||||
|
||||
Easy Pay itself sets no minimum or maximum; the MVR 1 floor is Thijooree's. Raastas' maximum
|
||||
isn't known.
|
||||
Easy Pay itself sets no minimum or maximum; the MVR 1 floor is Thijooree's. The Ooredoo maximums
|
||||
aren't known.
|
||||
|
||||
Raastas by card is the one service where GST is added on top (`PayoutService.gstAdded`): the
|
||||
number is credited what's typed, the card pays more, and the note under the amount says what's
|
||||
@@ -192,9 +194,9 @@ BML transaction comes from:
|
||||
|
||||
1. `CardPayoutTransferHandler.submit()` has the carrier create it for the number and amount
|
||||
(`DhiraaguPaymentClient.createReloadTransaction` / `createBillPayTransaction`,
|
||||
`OoredooPaymentClient.createRaastasTransaction`, see
|
||||
`OoredooPaymentClient.createRaastasTransaction` / `createBillPayTransaction`, see
|
||||
[Dhiraagu API → Reload](../dhiraaguapi/02-reload.md), [→ Bill Pay](../dhiraaguapi/03-bill-pay.md)
|
||||
and [Ooredoo API → Raastas](../ooredooapi/02-raastas.md)).
|
||||
and [Ooredoo API → Raastas](../ooredooapi/02-raastas.md), [→ Bill Pay](../ooredooapi/03-bill-pay.md)).
|
||||
Bill pay looks the number up again first, for the billing account the order is made out to.
|
||||
That takes a few round trips, so the payment's "Processing..." box shows meanwhile
|
||||
(`TransferFragment.showProcessingDialog`) and closes before the confirm dialog opens.
|
||||
@@ -275,7 +277,7 @@ Source: Fahipay
|
||||
Transfer type: Card (verified BML card)
|
||||
|
||||
└── Carrier creates a BML merchant transaction → card-only merchant flow
|
||||
DHIRAAGU_RELOAD, DHIRAAGU_BILL, OOREDOO_RAASTAS
|
||||
DHIRAAGU_RELOAD, DHIRAAGU_BILL, OOREDOO_RAASTAS, OOREDOO_BILL
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -9,7 +9,7 @@ Two linked features:
|
||||
whose merchant has **no BML Pay** is paid with a verified card via the Pomelo + 3-D Secure flow
|
||||
([BML API → Merchant Card Payment](../bmlapi/16-card-payment.md)). The same flow pays
|
||||
[carrier services by BML card](20-transfer-flows.md#carrier-services-by-bml-card) (Dhiraagu
|
||||
Reload and Bill Pay, Ooredoo Raastas), once the carrier has created the transaction.
|
||||
Reload and Bill Pay, Ooredoo Raastas and Bill Pay), once the carrier has created the transaction.
|
||||
|
||||
> ⚠️ The merchant card flow is scraped browser/ACS traffic, not a stable API. Storing the CVV is a
|
||||
> security/PCI liability. See the API doc's
|
||||
|
||||
Reference in New Issue
Block a user