forked from shihaam/thijooree
contacts, contact picker and trnasfer account look up ui
This commit is contained in:
@@ -263,7 +263,8 @@ class MibLoginFlow(private val prefs: android.content.SharedPreferences) {
|
||||
currentBalance = a.optString("currentBalance"),
|
||||
blockedAmount = a.optString("blockedAmount"),
|
||||
mvrBalance = a.optString("mvrBalance"),
|
||||
statusDesc = a.optString("statusDesc")
|
||||
statusDesc = a.optString("statusDesc"),
|
||||
profileImageHash = profile.customerImage
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -283,11 +284,22 @@ class MibLoginFlow(private val prefs: android.content.SharedPreferences) {
|
||||
name = p.optString("name"),
|
||||
cifType = p.optString("cifType"),
|
||||
profileType = p.optString("profileType"),
|
||||
color = p.optString("color")
|
||||
color = p.optString("color"),
|
||||
customerImage = p.optString("customerImage").takeIf { it.isNotBlank() }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Fetches a profile image via P41. Returns base64 JPEG string, or null if not found. */
|
||||
fun fetchProfileImage(session: MibSession, imageHash: String): String? {
|
||||
val payload = baseData(session, "P41").apply {
|
||||
put("imageHash", imageHash)
|
||||
}
|
||||
val resp = doRequest(session, payload, "n")
|
||||
if (!resp.optBoolean("success", false)) return null
|
||||
return resp.optString("profileImage").takeIf { it.isNotBlank() }
|
||||
}
|
||||
|
||||
private fun post(body: FormBody): String {
|
||||
val request = Request.Builder()
|
||||
.url(BASE_URL)
|
||||
|
||||
@@ -15,7 +15,8 @@ data class MibProfile(
|
||||
val name: String,
|
||||
val cifType: String,
|
||||
val profileType: String,
|
||||
val color: String
|
||||
val color: String,
|
||||
val customerImage: String?
|
||||
)
|
||||
|
||||
data class MibAccount(
|
||||
@@ -29,7 +30,8 @@ data class MibAccount(
|
||||
val currentBalance: String,
|
||||
val blockedAmount: String,
|
||||
val mvrBalance: String,
|
||||
val statusDesc: String
|
||||
val statusDesc: String,
|
||||
val profileImageHash: String?
|
||||
)
|
||||
|
||||
data class MibBeneficiaryCategory(
|
||||
@@ -53,6 +55,12 @@ data class MibBeneficiary(
|
||||
val benefCategoryId: String // "0" = uncategorized
|
||||
)
|
||||
|
||||
data class MibIpsAccountInfo(
|
||||
val accountName: String,
|
||||
val accountNumber: String,
|
||||
val bankId: String
|
||||
)
|
||||
|
||||
data class MibFinanceDeal(
|
||||
val dealNo: String,
|
||||
val productDesc: String,
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
package sh.sar.basedbank.api.mib
|
||||
|
||||
import android.util.Log
|
||||
import okhttp3.FormBody
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import org.json.JSONObject
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class MibLookupException(message: String) : Exception(message)
|
||||
|
||||
class MibTransferClient {
|
||||
|
||||
private val TAG = "MibTransferClient"
|
||||
private val BASE_WV_URL = "https://faisamobilex-wv.mib.com.mv"
|
||||
|
||||
private val client = OkHttpClient.Builder()
|
||||
.connectTimeout(15, TimeUnit.SECONDS)
|
||||
.readTimeout(15, TimeUnit.SECONDS)
|
||||
.build()
|
||||
|
||||
private fun cookieHeader(session: MibSession) =
|
||||
"mbmodel=IOS-1.0; xxid=${session.xxid}; IBSID=${session.xxid}; " +
|
||||
"mbnonce=${session.nonceGenerator}; time-tracker=597"
|
||||
|
||||
private fun Request.Builder.withWvHeaders(session: MibSession): Request.Builder = this
|
||||
.header("Cookie", cookieHeader(session))
|
||||
.header(
|
||||
"User-Agent",
|
||||
"Mozilla/5.0 (Linux; Android 14; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/129.0.6668.70 Mobile Safari/537.36"
|
||||
)
|
||||
.header("X-Requested-With", "XMLHttpRequest")
|
||||
.header("Accept", "*/*")
|
||||
.header("Origin", BASE_WV_URL)
|
||||
.header("Referer", "$BASE_WV_URL/transfer/quick")
|
||||
|
||||
/**
|
||||
* Routes the lookup to the correct endpoint based on input format:
|
||||
*
|
||||
* - 15 digits starting with 7 → IPS account lookup (BML / local bank)
|
||||
* - 17 digits starting with 9 → MIB internal account name lookup
|
||||
* - 7 digits starting with 7 or 9,
|
||||
* A + 6 digits,
|
||||
* or email address → Favara alias lookup
|
||||
*/
|
||||
fun lookup(session: MibSession, input: String): MibIpsAccountInfo {
|
||||
val trimmed = input.trim()
|
||||
return when {
|
||||
trimmed.matches(Regex("^7\\d{12}$")) -> lookupIpsAccount(session, trimmed)
|
||||
trimmed.matches(Regex("^9\\d{16}$")) -> lookupAccountName(session, trimmed)
|
||||
trimmed.matches(Regex("^[79]\\d{6}$")) -> lookupAlias(session, trimmed)
|
||||
trimmed.matches(Regex("^[Aa]\\d{6}$")) -> lookupAlias(session, trimmed)
|
||||
trimmed.contains("@") -> lookupAlias(session, trimmed)
|
||||
else -> throw MibLookupException("Unrecognized account number format")
|
||||
}
|
||||
}
|
||||
|
||||
/** BML / local bank: 15-digit account starting with 7. */
|
||||
private fun lookupIpsAccount(session: MibSession, accountNumber: String): MibIpsAccountInfo {
|
||||
val body = FormBody.Builder().add("benefAccount", accountNumber).build()
|
||||
val request = Request.Builder()
|
||||
.url("$BASE_WV_URL/AjaxAlias/getIPSAccount")
|
||||
.post(body)
|
||||
.withWvHeaders(session)
|
||||
.build()
|
||||
|
||||
return client.newCall(request).execute().use { response ->
|
||||
Log.d(TAG, "lookupIpsAccount: HTTP ${response.code}")
|
||||
val bodyStr = response.body?.string() ?: ""
|
||||
val json = try { JSONObject(bodyStr) } catch (_: Exception) { null }
|
||||
if (!response.isSuccessful || json == null || !json.optBoolean("success")) {
|
||||
throw MibLookupException(
|
||||
json?.optString("reasonText")?.trim()?.takeIf { it.isNotBlank() }
|
||||
?: "Request failed (${response.code})"
|
||||
)
|
||||
}
|
||||
MibIpsAccountInfo(
|
||||
accountName = json.optString("accountName").trim(),
|
||||
accountNumber = accountNumber,
|
||||
bankId = json.optString("bankBic")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** MIB internal: 17-digit account starting with 9. */
|
||||
private fun lookupAccountName(session: MibSession, accountNumber: String): MibIpsAccountInfo {
|
||||
val body = FormBody.Builder().add("accountNo", accountNumber).build()
|
||||
val request = Request.Builder()
|
||||
.url("$BASE_WV_URL/ajaxBeneficiary/getAccountName")
|
||||
.post(body)
|
||||
.withWvHeaders(session)
|
||||
.build()
|
||||
|
||||
return client.newCall(request).execute().use { response ->
|
||||
Log.d(TAG, "lookupAccountName: HTTP ${response.code}")
|
||||
val bodyStr = response.body?.string() ?: ""
|
||||
val json = try { JSONObject(bodyStr) } catch (_: Exception) { null }
|
||||
if (!response.isSuccessful || json == null || !json.optBoolean("success")) {
|
||||
throw MibLookupException(
|
||||
json?.optString("reasonText")?.trim()?.takeIf { it.isNotBlank() }
|
||||
?: "Request failed (${response.code})"
|
||||
)
|
||||
}
|
||||
// accountName may be at root or inside a "data" object
|
||||
val name = json.optString("accountName").takeIf { it.isNotBlank() }
|
||||
?: json.optJSONObject("data")?.optString("accountName") ?: ""
|
||||
MibIpsAccountInfo(
|
||||
accountName = name.trim(),
|
||||
accountNumber = accountNumber,
|
||||
bankId = "MADVMVMV" // MIB
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Favara alias: 7-digit shortcode (7/9 prefix), A-format ID, or email. */
|
||||
private fun lookupAlias(session: MibSession, aliasName: String): MibIpsAccountInfo {
|
||||
val body = FormBody.Builder().add("aliasName", aliasName).build()
|
||||
val request = Request.Builder()
|
||||
.url("$BASE_WV_URL/AjaxAlias/getAlias")
|
||||
.post(body)
|
||||
.withWvHeaders(session)
|
||||
.build()
|
||||
|
||||
return client.newCall(request).execute().use { response ->
|
||||
Log.d(TAG, "lookupAlias: HTTP ${response.code}")
|
||||
val bodyStr = response.body?.string() ?: ""
|
||||
val json = try { JSONObject(bodyStr) } catch (_: Exception) { null }
|
||||
if (!response.isSuccessful || json == null || !json.optBoolean("success")) {
|
||||
throw MibLookupException(
|
||||
json?.optString("reasonText")?.trim()?.takeIf { it.isNotBlank() }
|
||||
?: "Request failed (${response.code})"
|
||||
)
|
||||
}
|
||||
val data = json.getJSONObject("data")
|
||||
val cdtrAcct = data.getJSONObject("CdtrAcct")
|
||||
MibIpsAccountInfo(
|
||||
accountName = data.optString("BfyNm").trim(),
|
||||
accountNumber = cdtrAcct.optString("Acct"),
|
||||
bankId = cdtrAcct.optString("FinInstnId")
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user