NTP-14194 | Vedant Aggarwal | Supported PSP changes (#14979)

This commit is contained in:
vedant aggarwal
2025-02-18 13:59:17 +05:30
committed by GitHub
parent eb8bbeda06
commit 15e64fccb5
4 changed files with 101 additions and 84 deletions

View File

@@ -9,7 +9,6 @@ package com.navi.pay.common.model.config
import com.google.gson.annotations.SerializedName
import com.navi.pay.common.model.view.NaviPayFlowType
import com.navi.pay.common.model.view.PspType
data class NaviPayPspSelectionConfig(
@SerializedName("config") val config: PspSelectionConfigContent = PspSelectionConfigContent(),
@@ -17,14 +16,6 @@ data class NaviPayPspSelectionConfig(
)
data class PspSelectionConfigContent(
@SerializedName("flowToMandatoryBucketMap")
val flowToMandatoryBucketMap: Map<NaviPayFlowType, String> =
mapOf(
NaviPayFlowType.LITE_AUTO_TOP_UP to "mandate_1",
NaviPayFlowType.UPI_INTERNATIONAL to "mandate_2",
NaviPayFlowType.RCC_BILL_PAYMENTS to "mandate_3",
NaviPayFlowType.EMI_CONVERSION to "mandate_4",
),
@SerializedName("flowToRoutingBucketMap")
val flowToRoutingBucketMap: Map<NaviPayFlowType, String> =
mapOf(
@@ -34,13 +25,5 @@ data class PspSelectionConfigContent(
NaviPayFlowType.CHECK_BALANCE to "bucket_4",
NaviPayFlowType.PIN_MANAGEMENT to "bucket_4",
NaviPayFlowType.QR_HANDLING to "bucket_5",
),
@SerializedName("mandatoryBucketToPspListMap")
val mandatoryBucketToPspListMap: Map<String, List<PspType>> =
mapOf(
"mandate_1" to listOf(PspType.JUSPAY),
"mandate_2" to listOf(PspType.JUSPAY),
"mandate_3" to listOf(PspType.JUSPAY),
"mandate_4" to listOf(PspType.JUSPAY),
),
)
)

View File

@@ -14,6 +14,8 @@ import com.navi.pay.common.setup.model.NaviPayCustomerStatus
import com.navi.pay.common.utils.toggleNaviPayIntentActivityEnableState
import com.navi.pay.onboarding.binding.repository.NaviPayOnboardingRepository
import javax.inject.Inject
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
class NaviPayCustomerStatusHandler
@Inject
@@ -23,51 +25,61 @@ constructor(
) {
suspend fun getAllCustomerOnboardingData() =
naviPayOnboardingRepository.getAllCustomerOnboardingData()
withContext(Dispatchers.IO) { naviPayOnboardingRepository.getAllCustomerOnboardingData() }
suspend fun getCustomerStatus(pspType: PspType): NaviPayCustomerStatus {
val customerOnboardingEntity =
naviPayOnboardingRepository.getCustomerOnboardingEntity(pspType = pspType)
return customerOnboardingEntity?.customerStatus ?: NaviPayCustomerStatus.NOT_SET
return withContext(Dispatchers.IO) {
val customerOnboardingEntity =
naviPayOnboardingRepository.getCustomerOnboardingEntity(pspType = pspType)
customerOnboardingEntity?.customerStatus ?: NaviPayCustomerStatus.NOT_SET
}
}
suspend fun isUserOnboarded(pspType: PspType): Boolean {
val customerOnboardingEntity =
naviPayOnboardingRepository.getCustomerOnboardingEntity(pspType = pspType)
return customerOnboardingEntity?.customerStatus == NaviPayCustomerStatus.LINKED_VPA
return withContext(Dispatchers.IO) {
val customerOnboardingEntity =
naviPayOnboardingRepository.getCustomerOnboardingEntity(pspType = pspType)
customerOnboardingEntity?.customerStatus == NaviPayCustomerStatus.LINKED_VPA
}
}
suspend fun isUserOnboardedForAnyPsp(): Boolean {
val customerEntityList = naviPayOnboardingRepository.getAllCustomerOnboardingData()
return customerEntityList.any { onboardingDataEntity ->
onboardingDataEntity.customerStatus == NaviPayCustomerStatus.LINKED_VPA
return withContext(Dispatchers.IO) {
val customerEntityList = naviPayOnboardingRepository.getAllCustomerOnboardingData()
customerEntityList.any { onboardingDataEntity ->
onboardingDataEntity.customerStatus == NaviPayCustomerStatus.LINKED_VPA
}
}
}
suspend fun isUserDeviceBoundForAnyPsp(): Boolean {
val customerEntityList = naviPayOnboardingRepository.getAllCustomerOnboardingData()
return customerEntityList.any { onboardingDataEntity ->
onboardingDataEntity.customerStatus.isBound
return withContext(Dispatchers.IO) {
val customerEntityList = naviPayOnboardingRepository.getAllCustomerOnboardingData()
customerEntityList.any { onboardingDataEntity ->
onboardingDataEntity.customerStatus.isBound
}
}
}
suspend fun getCustomerStatusMap(): Map<PspType, NaviPayCustomerStatus> {
val customerEntityList = naviPayOnboardingRepository.getAllCustomerOnboardingData()
return customerEntityList.associate { it.pspType to it.customerStatus }
return withContext(Dispatchers.IO) {
val customerEntityList = naviPayOnboardingRepository.getAllCustomerOnboardingData()
customerEntityList.associate { it.pspType to it.customerStatus }
}
}
suspend fun updateCustomerStatusAndHandleNaviPayIntentActivityState(
pspType: PspType,
customerStatus: NaviPayCustomerStatus,
) {
naviPayOnboardingRepository.upsertCustomerOnboardingDataEntity(
pspType = pspType,
customerStatus = customerStatus,
)
naviPaySessionHelper.setCustomerStatus(customerStatus = customerStatus.name)
checkAndUpdateNaviPayIntentActivity()
withContext(Dispatchers.IO) {
naviPayOnboardingRepository.upsertCustomerOnboardingDataEntity(
pspType = pspType,
customerStatus = customerStatus,
)
naviPaySessionHelper.setCustomerStatus(customerStatus = customerStatus.name)
checkAndUpdateNaviPayIntentActivity()
}
}
suspend fun upsertCustomerOnboardingDataEntity(
@@ -76,28 +88,34 @@ constructor(
merchantCustomerId: String? = null,
deviceFingerPrint: String? = null,
) {
naviPayOnboardingRepository.upsertCustomerOnboardingDataEntity(
pspType = pspType,
customerStatus = customerStatus,
merchantCustomerId = merchantCustomerId,
deviceFingerPrint = deviceFingerPrint,
)
checkAndUpdateNaviPayIntentActivity()
withContext(Dispatchers.IO) {
naviPayOnboardingRepository.upsertCustomerOnboardingDataEntity(
pspType = pspType,
customerStatus = customerStatus,
merchantCustomerId = merchantCustomerId,
deviceFingerPrint = deviceFingerPrint,
)
checkAndUpdateNaviPayIntentActivity()
}
}
private suspend fun checkAndUpdateNaviPayIntentActivity() {
if (isUserOnboardedForAnyPsp()) {
toggleNaviPayIntentActivityEnableState(shouldEnable = true)
} else if (
FirebaseRemoteConfigHelper.getBoolean(
key = FirebaseRemoteConfigHelper.NAVI_PAY_INTENT_ACTIVITY_CHECK_ENABLED,
defaultValue = true,
)
) {
toggleNaviPayIntentActivityEnableState(shouldEnable = false)
withContext(Dispatchers.IO) {
if (isUserOnboardedForAnyPsp()) {
toggleNaviPayIntentActivityEnableState(shouldEnable = true)
} else if (
FirebaseRemoteConfigHelper.getBoolean(
key = FirebaseRemoteConfigHelper.NAVI_PAY_INTENT_ACTIVITY_CHECK_ENABLED,
defaultValue = true,
)
) {
toggleNaviPayIntentActivityEnableState(shouldEnable = false)
}
}
}
suspend fun getCustomerOnboardingEntity(pspType: PspType) =
naviPayOnboardingRepository.getCustomerOnboardingEntity(pspType)
withContext(Dispatchers.IO) {
naviPayOnboardingRepository.getCustomerOnboardingEntity(pspType)
}
}

View File

@@ -72,6 +72,7 @@ constructor(
pspType: PspType,
): Boolean =
linkedAccountEntity.vpaEntityList.any { vpaEntity ->
// TODO: Multibank - Add VPA status check in M1 Account Management
PspType.fromVpa(vpaEntity.vpa) == pspType
}
}

View File

@@ -456,20 +456,8 @@ constructor(
} ?: PspEvaluationResult()
}
private fun getSupportedPspList(
naviPayFlowType: NaviPayFlowType,
pspSelectionConfig: PspSelectionConfigContent,
): List<PspType> {
val mandatoryBucket = pspSelectionConfig.flowToMandatoryBucketMap[naviPayFlowType]
val mandatoryPspList =
mandatoryBucket?.let { pspSelectionConfig.mandatoryBucketToPspListMap[it] }
?: PspType.entries
return mandatoryPspList
}
private suspend fun getBoundPspList(supportedPspList: List<PspType>): List<PspType> {
val pspCustomerStatusMap = naviPayCustomerStatusHandler.getCustomerStatusMap()
if (supportedPspList.isEmpty()) {
return emptyList()
}
@@ -487,6 +475,21 @@ constructor(
}
}
private suspend fun getSupportedPspList(
naviPayFlowType: NaviPayFlowType,
pspSelectionConfig: PspSelectionConfigContent,
): List<PspType> {
val pspDistributionMap =
getPspDistributionMap(
naviPayFlowType = naviPayFlowType,
pspSelectionConfig = pspSelectionConfig,
) ?: return PspType.entries
return pspDistributionMap
.filter { (pspType, weight) -> weight > 0 && PspType.entries.contains(pspType) }
.keys
.toList()
}
/**
* Determines the PSP based on routing logic and weighted distribution. If only one PSP exists,
* it is directly selected. Otherwise, weights are used for random selection.
@@ -505,22 +508,14 @@ constructor(
if (pspList.size == 1) return pspList[0]
val routingBucketsJson =
naviCacheRepository.get(key = NAVI_PAY_PSP_ROUTING_BUCKETS_KEY)?.value
// TODO: Multibank - return null after Olympus is live
if (routingBucketsJson.isNullOrBlank()) return PspType.JUSPAY
val type = object : TypeToken<Map<String, Map<PspType, Double>>>() {}.type
val routingBucketsMap: Map<String, Map<PspType, Double>> =
gson.fromJson(routingBucketsJson, type)
val routingBucket =
pspSelectionConfig.flowToRoutingBucketMap[naviPayFlowType] ?: return pspList[0]
val originalDistribution = routingBucketsMap[routingBucket] ?: return pspList[0]
val pspDistributionMap =
getPspDistributionMap(
naviPayFlowType = naviPayFlowType,
pspSelectionConfig = pspSelectionConfig,
) ?: return PspType.entries[0]
val filteredDistribution =
originalDistribution
pspDistributionMap
.filterKeys { pspType -> pspList.any { it == pspType } }
.toMutableMap()
@@ -543,6 +538,26 @@ constructor(
return null
}
private suspend fun getPspDistributionMap(
naviPayFlowType: NaviPayFlowType,
pspSelectionConfig: PspSelectionConfigContent,
): Map<PspType, Double>? {
val routingBucketsJson =
naviCacheRepository.get(key = NAVI_PAY_PSP_ROUTING_BUCKETS_KEY)?.value
if (routingBucketsJson.isNullOrBlank()) return null
val type = object : TypeToken<Map<String, Map<String, Double>>>() {}.type
val routingBucketsMap: Map<String, Map<String, Double>> =
gson.fromJson(routingBucketsJson, type)
val routingBucket =
pspSelectionConfig.flowToRoutingBucketMap[naviPayFlowType] ?: return null
return routingBucketsMap[routingBucket]
?.mapNotNull { (pspName, weight) -> PspType.fromName(pspName)?.let { it to weight } }
?.toMap()
}
private suspend fun getPspSelectionConfig(): PspSelectionConfigContent {
val naviPayDefaultAccountSelectionConfig =
withContext(Dispatchers.IO) {