NTP-60662 | Vedant Aggarwal | Multibank Support in Delayed VPA Validation (#16123)

This commit is contained in:
vedant aggarwal
2025-05-12 13:59:30 +05:30
committed by GitHub
parent 0b00e4a58d
commit 744890c31b
4 changed files with 94 additions and 48 deletions

View File

@@ -19,7 +19,6 @@ import com.navi.pay.common.model.network.ValidateVpaRequest
import com.navi.pay.common.model.network.ValidateVpaResponse
import com.navi.pay.common.model.view.NaviPayFlowType
import com.navi.pay.common.model.view.NaviPaySessionHelper
import com.navi.pay.common.model.view.PspType
import com.navi.pay.common.repository.CommonRepository
import com.navi.pay.common.setup.NaviPayCustomerStatusHandler
import com.navi.pay.common.utils.getMetricInfo
@@ -27,7 +26,6 @@ import com.navi.pay.common.validatevpa.model.view.ValidateVpaEntity
import com.navi.pay.management.common.utils.NaviPayPspManager
import com.navi.pay.management.paytocontacts.model.network.PayToContactRequest
import com.navi.pay.network.di.NaviPayGsonBuilder
import com.navi.pay.onboarding.account.common.repository.AccountsRepository
import com.navi.pay.onboarding.binding.model.view.NaviPayCustomerOnboardingEntity
import com.navi.pay.utils.DEFAULT_RETRY_COUNT
import com.navi.pay.utils.RETRY_INTERVAL_IN_SECONDS
@@ -48,7 +46,6 @@ constructor(
private val naviPaySessionHelper: NaviPaySessionHelper,
private val naviPayPspManager: NaviPayPspManager,
private val naviPayCustomerStatusHandler: NaviPayCustomerStatusHandler,
private val accountsRepository: AccountsRepository,
@NaviPayGsonBuilder private val gson: Gson,
) {
val naviPayAnalytics = NaviPayAnalytics.INSTANCE.NaviPayValidateVpaUseCase()
@@ -65,25 +62,22 @@ constructor(
naviPayAnalytics.onValidateVpaStart(
naviPaySessionAttributes = naviPaySessionHelper.getNaviPaySessionAttributes()
)
val isCustomerExists = isCustomerExistsOnAnySupportedPsp(screenName)
val pspTypesWhereCustomerExists =
naviPayPspManager.getSupportedPspListWhereCustomerExists(
naviPayFlowType = NaviPayFlowType.VALIDATE_VPA,
screenName = screenName,
)
val isCustomerExists = pspTypesWhereCustomerExists.isNotEmpty()
if (!skipCustomerOnboardingCheck && !isCustomerExists) {
naviPayAnalytics.onValidateVpaAbort(naviPaySessionHelper.getNaviPaySessionAttributes())
return null // early return
}
var customerOnboardingEntity: NaviPayCustomerOnboardingEntity? = null
if (isCustomerExists) {
// TODO: Hardcoded PSP to be removed in multibank
customerOnboardingEntity =
naviPayCustomerStatusHandler.getCustomerOnboardingEntity(
pspType = PspType.JUSPAY_AXIS
)
} else {
evaluateAndBindPspForVpaValidation(
onPspEvaluatedSuccess = { customerOnboardingEntity = it }
)
}
evaluatePspForVpaValidation(
isCustomerExists = isCustomerExists,
onPspEvaluatedSuccess = { customerOnboardingEntity = it },
)
if (customerOnboardingEntity == null) {
return null
@@ -137,20 +131,41 @@ constructor(
return validateVpaRepoResultResponse
}
private suspend fun evaluateAndBindPspForVpaValidation(
onPspEvaluatedSuccess: (NaviPayCustomerOnboardingEntity?) -> Unit,
private suspend fun evaluatePspForVpaValidation(
isCustomerExists: Boolean,
onPspEvaluatedSuccess: (NaviPayCustomerOnboardingEntity) -> Unit,
onPspEvaluatedFailed: () -> Unit = {},
) {
naviPayPspManager.evaluateAndBindAnySupportedPspForFlow(
naviPayFlowType = NaviPayFlowType.VALIDATE_VPA,
screenName = screenName,
onPspEvaluated = { pspEvaluationResult ->
if (pspEvaluationResult.onboardingDataEntity == null) {
onPspEvaluatedFailed()
if (isCustomerExists) {
val selectedPsp =
naviPayPspManager.getPspWithoutOnboardingForFlow(
naviPayFlowType = NaviPayFlowType.VALIDATE_VPA,
screenName = screenName,
)
val customerOnboardingEntity =
selectedPsp?.let {
naviPayCustomerStatusHandler.getCustomerOnboardingEntity(pspType = it)
}
onPspEvaluatedSuccess(pspEvaluationResult.onboardingDataEntity)
},
)
if (customerOnboardingEntity != null) {
onPspEvaluatedSuccess(customerOnboardingEntity)
} else {
onPspEvaluatedFailed()
}
} else {
naviPayPspManager.evaluateAndBindAnySupportedPspForFlow(
naviPayFlowType = NaviPayFlowType.VALIDATE_VPA,
screenName = screenName,
onPspEvaluated = { pspEvaluationResult ->
if (pspEvaluationResult.onboardingDataEntity == null) {
onPspEvaluatedFailed()
return@evaluateAndBindAnySupportedPspForFlow
}
onPspEvaluatedSuccess(pspEvaluationResult.onboardingDataEntity)
},
)
}
}
private suspend fun <T> fetchFromNetworkAndSaveToDb(
@@ -234,21 +249,6 @@ constructor(
return apiResponse
}
// returns true if customer has done binding or has at least one account in db
private suspend fun isCustomerExistsOnAnySupportedPsp(screenName: String): Boolean {
val accounts = accountsRepository.getAllAccounts()
if (accounts.isNotEmpty()) {
return true
}
val supportedPspTypes =
naviPayPspManager.getSupportedPspList(
naviPayFlowType = NaviPayFlowType.VALIDATE_VPA,
screenName = screenName,
)
val customerStatusMap = naviPayCustomerStatusHandler.getCustomerStatusMap()
return supportedPspTypes.any { psp -> customerStatusMap[psp]?.isBound == true }
}
private fun <T> getVpaKeyParam(request: T): String {
return when (request) {
is ValidateVpaRequest -> request.vpa

View File

@@ -24,6 +24,7 @@ import com.navi.pay.common.utils.NaviPayOnboardingNavigator
import com.navi.pay.network.di.NaviPayGsonBuilder
import com.navi.pay.onboarding.account.common.model.view.VpaEntity
import com.navi.pay.onboarding.account.common.model.view.VpaStatus
import com.navi.pay.onboarding.account.common.repository.AccountsRepository
import com.navi.pay.onboarding.activatevpa.model.view.VpaActivationData
import com.navi.pay.onboarding.binding.model.view.NaviPayCustomerOnboardingEntity
import com.navi.pay.onboarding.common.NaviPayOnboardingActionsType
@@ -52,6 +53,7 @@ constructor(
private val naviPayOnboardingNavigator: NaviPayOnboardingNavigator,
private val naviCacheRepository: NaviCacheRepository,
private val resourceProvider: ResourceProvider,
private val accountsRepository: AccountsRepository,
@NaviPayGsonBuilder private val gson: Gson,
) {
private lateinit var screenName: String
@@ -299,8 +301,8 @@ constructor(
val pspSelectionConfig = getPspSelectionConfig()
val supportedPspList =
getSupportedPspList(
naviPayFlowType = naviPayFlowType,
getSupportedPspListWhereCustomerExists(
naviPayFlowType,
pspSelectionConfig = pspSelectionConfig,
)
if (supportedPspList.isEmpty()) {
@@ -404,14 +406,55 @@ constructor(
onPspEvaluated(pspEvaluationResult)
}
suspend fun getSupportedPspList(
/**
* Retrieves a list of supported PSP types where a customer exists.
*
* @param naviPayFlowType The flow type for the NaviPay process.
* @param pspSelectionConfig Optional configuration for PSP selection. If null, defaults to the
* standard configuration.
* @param screenName The name of the screen invoking this function. Defaults to the instance's
* screen name.
* @return A list of supported PSP types that have associated accounts or are device bound.
*/
suspend fun getSupportedPspListWhereCustomerExists(
naviPayFlowType: NaviPayFlowType,
pspSelectionConfig: PspSelectionConfigContent? = null,
screenName: String = this.screenName,
): List<PspType> {
this@NaviPayPspManager.screenName = screenName
val flowToRoutingBucketMap =
(pspSelectionConfig ?: getPspSelectionConfig()).flowToRoutingBucketMap
val supportedPspTypes =
getSupportedPspList(
naviPayFlowType = naviPayFlowType,
pspSelectionConfig = pspSelectionConfig ?: getPspSelectionConfig(),
)
val pspTypesWhereCustomerExists = getPspListWhereCustomerExists().toSet()
return supportedPspTypes.filter { it in pspTypesWhereCustomerExists }
}
private suspend fun getPspListWhereCustomerExists(): List<PspType> {
val vpaEntities = accountsRepository.getAllVpaEntities()
val pspTypesWithAccounts =
vpaEntities
.filter { it.pspType != null && it.status != VpaStatus.PENDING_ADDITION }
.mapNotNull { it.pspType }
.toSet()
val customerStatusMap = naviPayCustomerStatusHandler.getCustomerStatusMap()
return PspType.entries.filter { psp ->
pspTypesWithAccounts.contains(psp) || customerStatusMap[psp]?.isBound == true
}
}
private suspend fun getSupportedPspList(
naviPayFlowType: NaviPayFlowType,
pspSelectionConfig: PspSelectionConfigContent,
): List<PspType> {
val flowToRoutingBucketMap = pspSelectionConfig.flowToRoutingBucketMap
val pspDistributionMap =
getPspDistributionMap(
naviPayFlowType = naviPayFlowType,

View File

@@ -48,4 +48,7 @@ interface VpaDao {
"SELECT accountId FROM $NAVI_PAY_DATABASE_VPA_TABLE_NAME WHERE bankAccountUniqueId = :buid LIMIT 1"
)
suspend fun getAccountIdByBuid(buid: String): String?
@Query("SELECT * FROM $NAVI_PAY_DATABASE_VPA_TABLE_NAME")
suspend fun getAllVpaEntities(): List<VpaEntity>
}

View File

@@ -107,7 +107,7 @@ constructor(
suspend fun insertAllAccountEntity(accountEntities: List<AccountEntity>) =
accountDao.insertAll(accountEntities = accountEntities)
suspend fun getAllAccounts() = accountDao.getAllAccounts()
suspend fun getAllVpaEntities() = vpaDao.getAllVpaEntities()
fun getAllAccountByStatusAsFlow(status: AccountStatus) =
accountDao.getAllAccountByStatusAsFlow(status = status)