diff --git a/android/navi-common/src/main/java/com/navi/common/firebaseremoteconfig/FirebaseRemoteConfigHelper.kt b/android/navi-common/src/main/java/com/navi/common/firebaseremoteconfig/FirebaseRemoteConfigHelper.kt index e3047e43dc..5a8fa563e8 100644 --- a/android/navi-common/src/main/java/com/navi/common/firebaseremoteconfig/FirebaseRemoteConfigHelper.kt +++ b/android/navi-common/src/main/java/com/navi/common/firebaseremoteconfig/FirebaseRemoteConfigHelper.kt @@ -151,6 +151,7 @@ object FirebaseRemoteConfigHelper { const val NAVI_PAY_TSTORE_MERCHANT_ICONS_SYNC_ENABLED = "NAVI_PAY_TSTORE_MERCHANT_ICONS_SYNC_ENABLED" const val NAVI_PAY_ENABLE_BANK_SENSE_USE_CASE = "NAVI_PAY_ENABLE_BANK_SENSE_USE_CASE" + const val NAVI_PAY_IS_ARC_ENABLED = "NAVI_PAY_IS_ARC_ENABLED" const val AUTO_REDIRECT_TO_HOME_PAGE_LIMIT_TIME_IN_MIN = "AUTO_REDIRECT_TO_HOME_PAGE_LIMIT_TIME_IN_MIN" diff --git a/android/navi-pay/src/androidTest/kotlin/com/navi/pay/common/usecase/BankSenseUseCaseTest.kt b/android/navi-pay/src/androidTest/kotlin/com/navi/pay/common/usecase/BankSenseUseCaseTest.kt index a481320bc8..3b97e7c260 100644 --- a/android/navi-pay/src/androidTest/kotlin/com/navi/pay/common/usecase/BankSenseUseCaseTest.kt +++ b/android/navi-pay/src/androidTest/kotlin/com/navi/pay/common/usecase/BankSenseUseCaseTest.kt @@ -104,6 +104,7 @@ class BankSenseUseCaseTest { isCreditLineSupported = false, isUpiLiteAutoTopUpSupported = false, isEmiConversionSupported = false, + isRegularVersionSupported = true, ) } diff --git a/android/navi-pay/src/main/kotlin/com/navi/pay/onboarding/binding/util/IntegrityManager.kt b/android/navi-pay/src/main/kotlin/com/navi/pay/onboarding/binding/util/IntegrityManager.kt index 58273e2f4c..48b9d55c5b 100644 --- a/android/navi-pay/src/main/kotlin/com/navi/pay/onboarding/binding/util/IntegrityManager.kt +++ b/android/navi-pay/src/main/kotlin/com/navi/pay/onboarding/binding/util/IntegrityManager.kt @@ -12,6 +12,7 @@ import com.google.android.play.core.integrity.IntegrityManagerFactory import com.google.android.play.core.integrity.StandardIntegrityManager.PrepareIntegrityTokenRequest import com.google.android.play.core.integrity.StandardIntegrityManager.StandardIntegrityTokenProvider import com.google.android.play.core.integrity.StandardIntegrityManager.StandardIntegrityTokenRequest +import com.navi.base.utils.retry import com.navi.common.utils.log import com.navi.pay.BuildConfig import com.navi.pay.analytics.NaviPayAnalytics @@ -53,23 +54,31 @@ class StandardIntegrityManager @Inject constructor(@ApplicationContext val conte .setCloudProjectNumber(BuildConfig.FIREBASE_CLOUD_PROJECT_NUMBER.toLong()) .build() - try { - val integrityTokenProviderRequestHolder = measureTimedValue { - standardIntegrityManager - .prepareIntegrityToken(prepareIntegrityTokenRequest) - .await() - } - integrityTokenProvider = integrityTokenProviderRequestHolder.value - naviPayAnalytics.onIntegrityTokenProviderGeneration( - timeTaken = - integrityTokenProviderRequestHolder.duration.inWholeMilliseconds.toString() - ) - } catch (e: Exception) { - e.log() - naviPayAnalytics.onIntegrityTokenProviderGenerationFailure( - exception = e.message.toString() - ) - } + retry( + execute = { + try { + val integrityTokenProviderRequestHolder = measureTimedValue { + standardIntegrityManager + .prepareIntegrityToken(prepareIntegrityTokenRequest) + .await() + } + integrityTokenProvider = integrityTokenProviderRequestHolder.value + naviPayAnalytics.onIntegrityTokenProviderGeneration( + timeTaken = + integrityTokenProviderRequestHolder.duration.inWholeMilliseconds + .toString() + ) + true + } catch (e: Exception) { + e.log() + naviPayAnalytics.onIntegrityTokenProviderGenerationFailure( + exception = e.message.toString() + ) + false + } + }, + shouldRetry = { providerResult -> !providerResult }, + ) } } @@ -89,7 +98,12 @@ class StandardIntegrityManager @Inject constructor(@ApplicationContext val conte .build() val integrityTokenRequestHolder = measureTimedValue { - integrityTokenProvider?.request(integrityTokenRequest)?.await()?.token() + retry( + execute = { + integrityTokenProvider?.request(integrityTokenRequest)?.await()?.token() + }, + shouldRetry = { token -> token.isNullOrBlank() }, + ) } naviPayAnalytics.onIntegrityTokenGeneration( diff --git a/android/navi-pay/src/main/kotlin/com/navi/pay/onboarding/binding/viewmodel/NaviPayOnboardingViewModel.kt b/android/navi-pay/src/main/kotlin/com/navi/pay/onboarding/binding/viewmodel/NaviPayOnboardingViewModel.kt index 1eeecf42ea..f8f382468e 100644 --- a/android/navi-pay/src/main/kotlin/com/navi/pay/onboarding/binding/viewmodel/NaviPayOnboardingViewModel.kt +++ b/android/navi-pay/src/main/kotlin/com/navi/pay/onboarding/binding/viewmodel/NaviPayOnboardingViewModel.kt @@ -310,8 +310,7 @@ constructor( totalPollingDurationInMillis = MAX_POLLING_DURATION_IN_MILLIS, ) } - private val naviPayDefaultConfig = - MutableStateFlow(NaviPayDefaultConfig()) + private val naviPayDefaultConfig = MutableStateFlow(NaviPayDefaultConfig()) private val _triggerSmsRetrieverInstance = MutableSharedFlow(replay = 1) val triggerSmsRetrieverInstance = _triggerSmsRetrieverInstance.asSharedFlow() @@ -708,6 +707,7 @@ constructor( val isBindingEligibleForRsms = getRsmsEligibilityStatus() if (isBindingEligibleForRsms) { bindingType = BindingType.RSMS() + markRsmsTriggeredStatus(isInitiated = true) startSimBinding() return@safeLaunch } @@ -720,6 +720,7 @@ constructor( } bindingType = BindingType.SMV + markSmvTriggeredStatus(isInitiated = true) // SMV is eligible cases if (!smvEligibilityStatus.isSmsPermissionEnabled) { @@ -939,11 +940,6 @@ constructor( } else { onBindingError(bindDeviceAPIResponse) } - - if (bindingType is BindingType.RSMS) { - markRsmsTriggeredAndFailed() - } - return } @@ -971,12 +967,18 @@ constructor( merchantCustomerId = merchantCustomerId, ) - if (bindingType is BindingType.RSMS) { - processBindDeviceResponseForRsms() - } else if (bindingType is BindingType.SMV) { - processBindDeviceResponseForSmv() - } else { - processBindDeviceResponseForSms(provider = provider) + when (bindingType) { + is BindingType.RSMS -> { + processBindDeviceResponseForRsms() + } + + is BindingType.SMV -> { + processBindDeviceResponseForSmv() + } + + else -> { + processBindDeviceResponseForSms(provider = provider) + } } } @@ -1121,8 +1123,6 @@ constructor( if (initiateSmvResponse.code() == 200 && initiateSmvResponse.body() == null) { startStatusPolling(bindingType = BindingType.SMV) } else { - // Mark in cache that SMV was triggered & failed - markSmvTriggeredAndFailed() updateBottomSheetUIState(showBottomSheet = false) updateDeviceBindingState(deviceBindingState = DeviceBindingState.Failure) notifyError(errorConfig = getGenericErrorConfig().copy(cancelable = false)) @@ -1331,16 +1331,6 @@ constructor( updateDeviceBindingState(DeviceBindingState.Failure) naviApiPoller.stopPolling() onBindingError(bindDeviceStatusAPIResponse) - when (bindingType) { - is BindingType.RSMS -> { - markRsmsTriggeredAndFailed() - } - - BindingType.SMV -> { - markSmvTriggeredAndFailed() - } - else -> Unit - } } } return @@ -1362,6 +1352,12 @@ constructor( } handleDropOffFunnel(funnelStep = FunnelStep.ACCOUNT_ADDITION) + when (bindingType) { + BindingType.RSMS() -> markRsmsTriggeredStatus(isCompleted = true) + BindingType.SMV -> markSmvTriggeredStatus(isCompleted = true) + else -> Unit + } + saveDeviceDataInSharedPreferenceAndUpdateUiState( deviceFingerPrint = bindDeviceStatusResponse.pspDetails[onboardingPsp]?.deviceFingerPrint.orEmpty(), @@ -1399,26 +1395,46 @@ constructor( handleNavigationOnCustomerStatus(customerStatus = customerStatus) } - private suspend fun markSmvTriggeredAndFailed() { - naviCacheRepository.save( - naviCacheEntity = - NaviCacheEntity( - key = NAVI_PAY_DEVICE_BINDING_IS_SMV_TRIGGERED_AND_FAILED, - value = "true", - version = 1, - ) - ) + private suspend fun markSmvTriggeredStatus( + isInitiated: Boolean? = null, + isCompleted: Boolean? = null, + ) { + + isInitiated?.let { + naviCacheRepository.save( + naviCacheEntity = + NaviCacheEntity( + key = NAVI_PAY_DEVICE_BINDING_IS_SMV_TRIGGERED_AND_FAILED, + value = true.toString(), + version = 1, + ) + ) + } + + isCompleted?.let { + naviCacheRepository.clear(key = NAVI_PAY_DEVICE_BINDING_IS_SMV_TRIGGERED_AND_FAILED) + } } - private suspend fun markRsmsTriggeredAndFailed() { - naviCacheRepository.save( - naviCacheEntity = - NaviCacheEntity( - key = NAVI_PAY_DEVICE_BINDING_IS_RSMS_TRIGGERED_AND_FAILED, - value = "true", - version = 1, - ) - ) + private suspend fun markRsmsTriggeredStatus( + isInitiated: Boolean? = null, + isCompleted: Boolean? = null, + ) { + + isInitiated?.let { + naviCacheRepository.save( + naviCacheEntity = + NaviCacheEntity( + key = NAVI_PAY_DEVICE_BINDING_IS_RSMS_TRIGGERED_AND_FAILED, + value = true.toString(), + version = 1, + ) + ) + } + + isCompleted?.let { + naviCacheRepository.clear(key = NAVI_PAY_DEVICE_BINDING_IS_RSMS_TRIGGERED_AND_FAILED) + } } fun declineDeviceBinding() { @@ -2100,7 +2116,7 @@ constructor( ?.variant val isRsmsExperimentEnabled = - rsmsLitmusVariant?.name == "enabled" && rsmsLitmusVariant.enabled == true + rsmsLitmusVariant?.name == "enabled" && rsmsLitmusVariant.enabled naviPayAnalytics.onRsmsLitmusEligibility(isEligible = isRsmsExperimentEnabled) if (isRsmsExperimentEnabled) { diff --git a/android/navi-payments-shared/src/main/java/com/navi/payments/shared/feature/arc/usecase/ArcNudgeSyncUseCase.kt b/android/navi-payments-shared/src/main/java/com/navi/payments/shared/feature/arc/usecase/ArcNudgeSyncUseCase.kt index 6a2e1bb8eb..24392e1ea3 100644 --- a/android/navi-payments-shared/src/main/java/com/navi/payments/shared/feature/arc/usecase/ArcNudgeSyncUseCase.kt +++ b/android/navi-payments-shared/src/main/java/com/navi/payments/shared/feature/arc/usecase/ArcNudgeSyncUseCase.kt @@ -16,6 +16,7 @@ import com.navi.base.utils.retry import com.navi.common.checkmate.model.MetricInfo import com.navi.common.firebaseremoteconfig.FirebaseRemoteConfigHelper import com.navi.common.firebaseremoteconfig.FirebaseRemoteConfigHelper.NAVI_PAY_DATA_REFRESH_MIN_TIMESTAMP +import com.navi.common.firebaseremoteconfig.FirebaseRemoteConfigHelper.NAVI_PAY_IS_ARC_ENABLED import com.navi.common.network.models.isSuccessWithData import com.navi.common.utils.Constants.ONE_DAY_IN_MILLIS import com.navi.payments.shared.core.di.PaymentsSharedGsonBuilder @@ -34,6 +35,15 @@ constructor( suspend fun execute(skipLastSyncedTimestampCheck: Boolean = false) { + if ( + !FirebaseRemoteConfigHelper.getBoolean( + key = NAVI_PAY_IS_ARC_ENABLED, + defaultValue = false, + ) + ) { + return + } + val arcNudgeCacheResponse = naviCacheRepository.get(key = ARC_NUDGE_RESPONSE_CACHE_KEY) val arcResponseCacheLastSyncTs = arcNudgeCacheResponse?.updatedAt ?: 0