NTP-15268 | Qr mandate account selection (#13930)

Co-authored-by: Ujjwal Kumar <ujjwal.kumar@navi.com>
This commit is contained in:
Akshita Singh
2024-12-03 19:21:42 +05:30
committed by GitHub
parent 83b5f8dd15
commit 7e967dc0b4
3 changed files with 88 additions and 51 deletions

View File

@@ -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 {

View File

@@ -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<Deferred<Unit>>()
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<LinkedAccountEntity>
private suspend fun executeLinkedAccountsFetchAndUpdateBankAccountState(
linkedAccountsInDB: List<LinkedAccountEntity>,
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<LinkedAccountEntity>
private suspend fun postProcessLinkedAccounts(
linkedAccounts: List<LinkedAccountEntity>,
validateVpaResponse: ValidateVpaResponse?
): List<LinkedAccountEntity> {
// Account eligibility status update
val updatedLinkedAccountsWithEligibility =
updateAccountEligibilityStatusInLinkedAccounts(linkedAccounts = linkedAccounts)
updateAccountEligibilityStatusInLinkedAccounts(
linkedAccounts = linkedAccounts,
validateVpaResponse = validateVpaResponse
)
return updatedLinkedAccountsWithEligibility
}
private fun updateAccountEligibilityStatusInLinkedAccounts(
linkedAccounts: List<LinkedAccountEntity>
private suspend fun updateAccountEligibilityStatusInLinkedAccounts(
linkedAccounts: List<LinkedAccountEntity>,
validateVpaResponse: ValidateVpaResponse?
): List<LinkedAccountEntity> {
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<ValidateVpaResponse>
) {
// If mandate name is not present, then use the name from validateVpa API response
mandateEntity =

View File

@@ -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()
}
)
},