From b4e1f57347e3a1e665a6369fe1ca08cc9ae85b92 Mon Sep 17 00:00:00 2001 From: Shihaam Abdul Rahman Date: Thu, 28 May 2026 20:29:24 +0500 Subject: [PATCH] fix paymv qr generation part 1 --- .../sar/basedbank/ui/home/PayMvQrFragment.kt | 46 +++++++++++++++++-- .../main/res/layout/fragment_pay_mv_qr.xml | 42 ++++++++++++++++- app/src/main/res/values/strings.xml | 4 +- 3 files changed, 86 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/sh/sar/basedbank/ui/home/PayMvQrFragment.kt b/app/src/main/java/sh/sar/basedbank/ui/home/PayMvQrFragment.kt index 7c768d9..c45406b 100644 --- a/app/src/main/java/sh/sar/basedbank/ui/home/PayMvQrFragment.kt +++ b/app/src/main/java/sh/sar/basedbank/ui/home/PayMvQrFragment.kt @@ -36,6 +36,7 @@ import sh.sar.basedbank.BasedBankApp import sh.sar.basedbank.R import sh.sar.basedbank.api.models.BankAccount import sh.sar.basedbank.databinding.FragmentPayMvQrBinding +import sh.sar.basedbank.util.CredentialStore import sh.sar.basedbank.databinding.ItemAccountDropdownBinding import sh.sar.basedbank.util.AccountListParser import sh.sar.basedbank.util.PaymvQrParser @@ -98,6 +99,8 @@ class PayMvQrFragment : Fragment() { } setupDropdown() binding.etAmount.addTextChangedListener { scheduleGenerate() } + binding.etReference.addTextChangedListener { scheduleGenerate() } + binding.switchIncludePhone.setOnCheckedChangeListener { _, _ -> scheduleGenerate() } binding.btnShare.isEnabled = false binding.btnSave.isEnabled = false binding.btnShare.setOnClickListener { shareQr() } @@ -147,8 +150,28 @@ class PayMvQrFragment : Fragment() { ?.let { "%.2f".format(it) } val ctx = requireContext() + val includePhone = binding.switchIncludePhone.isChecked + val loginId = sh.sar.basedbank.util.ProfileImageStore.loginIdFromTag(account.loginTag) + val store = CredentialStore(ctx) + val mobile = if (includePhone) { + when (account.bank) { + "BML" -> store.loadBmlUserProfile(loginId)?.mobile + "FAHIPAY" -> store.loadFahipayUserProfile(loginId)?.mobile + else -> null + }?.let { m -> + when { + m.startsWith("+") -> m + m.length == 7 -> "+960$m" + else -> m + } + } + } else null + + val purpose = binding.etReference.text?.toString()?.trim() + ?.takeIf { it.isNotBlank() } ?: getString(R.string.paymvqr_reference_default) + val bmp = withContext(Dispatchers.Default) { - val payload = buildQrPayload(account.accountNumber, account.accountBriefName, acquirer, amountFormatted) + val payload = buildQrPayload(account.accountNumber, account.accountBriefName, acquirer, amountFormatted, mobile, purpose) renderQrCard(ctx, account, payload, amountFormatted) } if (_binding == null) return @@ -166,7 +189,9 @@ class PayMvQrFragment : Fragment() { accountNumber: String, accountName: String, acquirer: String, - amountStr: String? + amountStr: String?, + mobile: String?, + purpose: String ): String { fun tlv(tag: String, value: String): String { val len = value.length @@ -176,17 +201,30 @@ class PayMvQrFragment : Fragment() { val poi = tlv("01", "11") val sub00 = tlv("00", "mv.favara.mpqr") val sub01 = tlv("01", acquirer) + val sub02 = tlv("02", acquirer) // repeated acquirer, as per official PayMV app val sub03 = tlv("03", accountNumber) + val sub05 = if (!mobile.isNullOrBlank()) tlv("05", mobile) else "" val sub10 = tlv("10", "IPAY") - val merchantAcct = tlv("26", sub00 + sub01 + sub03 + sub10) + val merchantAcct = tlv("26", sub00 + sub01 + sub02 + sub03 + sub05 + sub10) + val mcc = tlv("52", "0000") val currency = tlv("53", "462") val amountTLV = if (!amountStr.isNullOrBlank()) tlv("54", amountStr) else "" val country = tlv("58", "MV") val name = tlv("59", accountName.take(25)) - val prefix = format + poi + merchantAcct + currency + amountTLV + country + name + "6304" + val ref = generateReference() + val addlData = tlv("62", tlv("05", ref) + tlv("08", purpose)) + val timestamp = java.time.LocalDateTime.now() + .format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.00000")) + val tag80 = tlv("80", tlv("00", "mv.favara.mpqr") + tlv("01", timestamp)) + val prefix = format + poi + merchantAcct + mcc + currency + amountTLV + country + name + addlData + tag80 + "6304" return prefix + crc16(prefix) } + private fun generateReference(): String { + val chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + return (1..9).map { chars.random() }.joinToString("") + } + private fun crc16(data: String): String { var crc = 0xFFFF for (c in data) { diff --git a/app/src/main/res/layout/fragment_pay_mv_qr.xml b/app/src/main/res/layout/fragment_pay_mv_qr.xml index 1daeb6c..4157028 100644 --- a/app/src/main/res/layout/fragment_pay_mv_qr.xml +++ b/app/src/main/res/layout/fragment_pay_mv_qr.xml @@ -64,7 +64,6 @@ android:layout_height="wrap_content" android:layout_marginBottom="12dp" android:hint="@string/paymvqr_amount_hint" - app:helperText="@string/paymvqr_amount_helper" app:prefixText="MVR "> + + + + + + + + + + + + + + + + Select account Amount (optional) - Leave empty to allow payer to enter any amount Share Save Image QR saved to gallery Failed to save image + Include phone number + Reference (optional) + PayMV QR Transfer Lock app