From 7e967dc0b443459abb58ca5cf400f804ac0ae53d Mon Sep 17 00:00:00 2001 From: Akshita Singh Date: Tue, 3 Dec 2024 19:21:42 +0530 Subject: [PATCH] NTP-15268 | Qr mandate account selection (#13930) Co-authored-by: Ujjwal Kumar --- .../navi/pay/analytics/NaviPayAnalytics.kt | 12 ++ ...MandateDetailOfPendingCategoryViewModel.kt | 125 +++++++++++------- .../scanpay/ui/QrScannerScreen.kt | 2 + 3 files changed, 88 insertions(+), 51 deletions(-) diff --git a/android/navi-pay/src/main/kotlin/com/navi/pay/analytics/NaviPayAnalytics.kt b/android/navi-pay/src/main/kotlin/com/navi/pay/analytics/NaviPayAnalytics.kt index 74fa9cf68d..d8c3d0fd43 100644 --- a/android/navi-pay/src/main/kotlin/com/navi/pay/analytics/NaviPayAnalytics.kt +++ b/android/navi-pay/src/main/kotlin/com/navi/pay/analytics/NaviPayAnalytics.kt @@ -1533,6 +1533,18 @@ class NaviPayAnalytics private constructor() { eventValues = mapOf("qrContent" to qrContent) ) } + + fun onUrlTypeQROpenLinkClicked() { + NaviTrackEvent.trackEventOnClickStream( + eventName = "NaviPay_QrScanner_UrlTypeQRScanned_Openlink" + ) + } + + fun onUrlTypeQRCancelClicked() { + NaviTrackEvent.trackEventOnClickStream( + eventName = "NaviPay_QrScanner_UrlTypeQRScanned_Cancel" + ) + } } inner class NaviPayBankDetailsInput { diff --git a/android/navi-pay/src/main/kotlin/com/navi/pay/management/mandate/viewmodel/MandateDetailOfPendingCategoryViewModel.kt b/android/navi-pay/src/main/kotlin/com/navi/pay/management/mandate/viewmodel/MandateDetailOfPendingCategoryViewModel.kt index a73db91e34..720fc5342f 100644 --- a/android/navi-pay/src/main/kotlin/com/navi/pay/management/mandate/viewmodel/MandateDetailOfPendingCategoryViewModel.kt +++ b/android/navi-pay/src/main/kotlin/com/navi/pay/management/mandate/viewmodel/MandateDetailOfPendingCategoryViewModel.kt @@ -12,6 +12,7 @@ import androidx.lifecycle.viewModelScope import com.navi.base.cache.datastore.DataStoreHelper import com.navi.base.utils.DateUtils import com.navi.base.utils.ResourceProvider +import com.navi.common.network.models.RepoResult import com.navi.common.network.models.isSuccess import com.navi.common.network.models.isSuccessWithData import com.navi.pay.R @@ -19,6 +20,7 @@ import com.navi.pay.analytics.NaviPayAnalytics import com.navi.pay.analytics.NaviPayAnalytics.Companion.NAVI_PAY_PENDING_MANDATE_DETAILS import com.navi.pay.common.connectivity.NaviPayNetworkConnectivity import com.navi.pay.common.model.network.ValidateVpaRequest +import com.navi.pay.common.model.network.ValidateVpaResponse import com.navi.pay.common.repository.CommonRepository import com.navi.pay.common.setup.NaviPayCustomerStatusHandler import com.navi.pay.common.setup.model.NaviPayCustomerStatus @@ -33,6 +35,7 @@ import com.navi.pay.management.common.sendmoney.model.network.BlockSpamUserReque import com.navi.pay.management.common.sendmoney.model.view.BankAccountsState import com.navi.pay.management.common.sendmoney.model.view.EligibilityState import com.navi.pay.management.common.sendmoney.model.view.PayeeEntity +import com.navi.pay.management.common.sendmoney.util.AccountEligibilityMerchantHelper import com.navi.pay.management.mandate.model.network.CreateMandateRequest import com.navi.pay.management.mandate.model.network.MandateDetailRequest import com.navi.pay.management.mandate.model.network.ReviewMandateRequest @@ -49,14 +52,12 @@ import com.navi.pay.management.mandate.repository.MandateRepository import com.navi.pay.npcicl.CredDataProvider import com.navi.pay.npcicl.NpciRepository import com.navi.pay.npcicl.NpciResult -import com.navi.pay.onboarding.account.add.model.view.AccountType import com.navi.pay.onboarding.account.detail.model.view.LinkedAccountEntity import com.navi.pay.utils.DATE_TIME_FORMAT_DATE_MONTH_YEAR_WITHOUT_SEPARATOR import com.navi.pay.utils.DATE_TIME_FORMAT_YEAR_MONTH_DATE_WITH_SLASH_SEPARATOR import com.navi.pay.utils.DS_KEY_NAVI_PAY_CUSTOMER_STATUS import com.navi.pay.utils.NAVI_PAY_PURPLE_CTA_LOADER_LOTTIE import com.navi.pay.utils.REQUEST_TYPE_MANDATE -import com.navi.pay.utils.RESOURCE_DEFAULT_ID import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject import kotlin.time.Duration.Companion.seconds @@ -91,7 +92,8 @@ constructor( private val upiRequestIdUseCase: UpiRequestIdUseCase, private val naviPayCustomerStatusHandler: NaviPayCustomerStatusHandler, private val dataStoreHelper: DataStoreHelper, - private val resourceProvider: ResourceProvider + private val resourceProvider: ResourceProvider, + private val accountEligibilityMerchantHelper: AccountEligibilityMerchantHelper, ) : NaviPayBaseVM() { private val naviPayAnalytics: NaviPayAnalytics.NaviPayManageAutoPay = @@ -229,20 +231,34 @@ constructor( private fun initMandateEntityAndOpenDetailScreen() { viewModelScope.launch(Dispatchers.IO) { initMandateEntity() - getLinkedAccountsInfo() + validateVpaAndGetLinkedAccountsInfo() } } - private fun getLinkedAccountsInfo() { + private fun validateVpaAndGetLinkedAccountsInfo() { viewModelScope.launch(Dispatchers.IO) { + val validateVpaAPIResponse = + validateVpaUseCase.execute( + request = + ValidateVpaRequest( + deviceData = deviceInfoProvider.getDeviceData(), + merchantCustomerId = deviceInfoProvider.getMerchantCustomerId(), + vpa = mandateEntity?.payeeVpa.orEmpty() + ), + screenName = screenName + ) + linkedAccountsUseCase.execute(screenName = screenName).collect { val asyncJobList = mutableListOf>() asyncJobList.add( async { - executeLinkedAccountsFetchAndUpdateBankAccountState(linkedAccountsInDB = it) + executeLinkedAccountsFetchAndUpdateBankAccountState( + linkedAccountsInDB = it, + validateVpaResponse = validateVpaAPIResponse.data + ) } ) - asyncJobList.add(async { validateVpaAndUpdateIsMerchantStateValues() }) + asyncJobList.add(async { updateIsMerchantStateValues(validateVpaAPIResponse) }) asyncJobList.awaitAll() updateUIState(uiState = MandateDetailUIStateOfPendingCategory.MandateDetail) @@ -258,12 +274,16 @@ constructor( payeeEntity?.let { mandateEntity = it.toMandateEntity() } } - private fun executeLinkedAccountsFetchAndUpdateBankAccountState( - linkedAccountsInDB: List + private suspend fun executeLinkedAccountsFetchAndUpdateBankAccountState( + linkedAccountsInDB: List, + validateVpaResponse: ValidateVpaResponse? ) { updateBankAccountsState(state = BankAccountsState.Loading) val postProcessedLinkedAccounts = - postProcessLinkedAccounts(linkedAccounts = linkedAccountsInDB) + postProcessLinkedAccounts( + linkedAccounts = linkedAccountsInDB, + validateVpaResponse = validateVpaResponse + ) if (postProcessedLinkedAccounts.isEmpty()) { updateBankAccountsState(state = BankAccountsState.NoAccountLinked) @@ -276,24 +296,32 @@ constructor( } } - private fun postProcessLinkedAccounts( - linkedAccounts: List + private suspend fun postProcessLinkedAccounts( + linkedAccounts: List, + validateVpaResponse: ValidateVpaResponse? ): List { // Account eligibility status update val updatedLinkedAccountsWithEligibility = - updateAccountEligibilityStatusInLinkedAccounts(linkedAccounts = linkedAccounts) + updateAccountEligibilityStatusInLinkedAccounts( + linkedAccounts = linkedAccounts, + validateVpaResponse = validateVpaResponse + ) return updatedLinkedAccountsWithEligibility } - private fun updateAccountEligibilityStatusInLinkedAccounts( - linkedAccounts: List + private suspend fun updateAccountEligibilityStatusInLinkedAccounts( + linkedAccounts: List, + validateVpaResponse: ValidateVpaResponse? ): List { val updatedLinkedAccounts = linkedAccounts.map { linkedAccount -> val eligibilityState = - getEligibilityStateForLinkedAccount(linkedAccountEntity = linkedAccount) + getEligibilityStateForLinkedAccount( + linkedAccountEntity = linkedAccount, + validateVpaResponse = validateVpaResponse + ) linkedAccount.eligibilityState = eligibilityState linkedAccount } @@ -301,8 +329,9 @@ constructor( return updatedLinkedAccounts } - private fun getEligibilityStateForLinkedAccount( - linkedAccountEntity: LinkedAccountEntity + private suspend fun getEligibilityStateForLinkedAccount( + linkedAccountEntity: LinkedAccountEntity, + validateVpaResponse: ValidateVpaResponse? ): EligibilityState { // PIN set check @@ -313,28 +342,31 @@ constructor( ) } - val isAccountOfTypeCreditCardOrCreditLine = - AccountType.isAccountOfTypeCreditCardOrCreditLine( - type = linkedAccountEntity.accountType - ) - - // Credit account check - if (isAccountOfTypeCreditCardOrCreditLine) { - return EligibilityState( - isAccountEligible = false, - inEligibilityReasonResId = - when (linkedAccountEntity.accountType) { - AccountType.CREDIT.name -> R.string.np_credit_card_transaction_not_supported - AccountType.UPICREDIT.name -> - R.string.np_credit_line_transaction_not_supported - else -> RESOURCE_DEFAULT_ID - } - ) - } - - // For create mandate, all accounts are eligible + // For create mandate, accounts are eligible according to feature tags return if (isPendingMandateOfTypeCreate) { - EligibilityState(isAccountEligible = true) + if (validateVpaResponse == null) { + return EligibilityState(isAccountEligible = true) + } else { + val accountTypeEligibilityMap = + accountEligibilityMerchantHelper.fetchAccountTypeEligibilityMap( + featureTags = validateVpaResponse.featureTags, + screenName = screenName + ) + return EligibilityState( + isAccountEligible = + accountEligibilityMerchantHelper.getAccountEligibility( + eligibility = + accountTypeEligibilityMap[linkedAccountEntity.accountType], + amount = mandateEntity?.amount?.toDouble() ?: 0.0 + ), + inEligibilityReason = + accountEligibilityMerchantHelper.getIneligibilityReason( + eligibility = + accountTypeEligibilityMap[linkedAccountEntity.accountType], + accountType = linkedAccountEntity.accountType + ) + ) + } } else { // For approve mandate, only payer VPA account is eligible EligibilityState(isAccountEligible = linkedAccountEntity.vpa == mandateEntity?.payerVpa) } @@ -350,18 +382,9 @@ constructor( updateSelectedBankAccount(selectedBankAccount = defaultSelectedAccount) } - private suspend fun validateVpaAndUpdateIsMerchantStateValues() { - - val validateVpaAPIResponse = - validateVpaUseCase.execute( - request = - ValidateVpaRequest( - deviceData = deviceInfoProvider.getDeviceData(), - merchantCustomerId = deviceInfoProvider.getMerchantCustomerId(), - vpa = mandateEntity?.payeeVpa.orEmpty() - ), - screenName = screenName - ) + private fun updateIsMerchantStateValues( + validateVpaAPIResponse: RepoResult + ) { // If mandate name is not present, then use the name from validateVpa API response mandateEntity = diff --git a/android/navi-pay/src/main/kotlin/com/navi/pay/management/moneytransfer/scanpay/ui/QrScannerScreen.kt b/android/navi-pay/src/main/kotlin/com/navi/pay/management/moneytransfer/scanpay/ui/QrScannerScreen.kt index 89db54ae2e..4d052023b6 100644 --- a/android/navi-pay/src/main/kotlin/com/navi/pay/management/moneytransfer/scanpay/ui/QrScannerScreen.kt +++ b/android/navi-pay/src/main/kotlin/com/navi/pay/management/moneytransfer/scanpay/ui/QrScannerScreen.kt @@ -416,6 +416,7 @@ fun QrScannerScreen( try { closeSheet() delay(200.milliseconds) + naviPayAnalytics.onUrlTypeQROpenLinkClicked() // For closing bottom sheet before navigating uriHandler.openUri(it) } catch (e: Exception) { @@ -428,6 +429,7 @@ fun QrScannerScreen( onUrlRedirectionSecondaryCtaClicked = { closeSheet() qrScannerViewModel.isQrCodeProcessing.set(false) + naviPayAnalytics.onUrlTypeQRCancelClicked() } ) },