NTP-50498 | PS | Payments kill switch (#15664)

This commit is contained in:
Prakhar Saxena
2025-04-09 18:02:24 +05:30
committed by GitHub
parent 02891ac979
commit 9b285659c4
17 changed files with 432 additions and 8 deletions

View File

@@ -174,6 +174,8 @@ object FirebaseRemoteConfigHelper {
"NAVI_PMT_TRANSACTION_STORE_EXPIRY_TIME_MILLIS"
const val NAVI_PMT_ONE_CLICK_CHECKOUT_API_TIMEOUT_MILLIS =
"NAVI_PMT_ONE_CLICK_CHECKOUT_API_TIMEOUT_MILLIS"
const val ENABLE_NAVI_PAYMENT_KILL_SWITCH_MECHANISM =
"ENABLE_NAVI_PAYMENT_KILL_SWITCH_MECHANISM"
// BBPS
const val NAVI_BBPS_REWARDS_NUDGE_CACHE_TTL_IN_MILLIS =

View File

@@ -23,6 +23,7 @@ import com.navi.payment.nativepayment.model.RewardsInfoV2
import com.navi.payment.nativepayment.model.WebPaymentAction
import com.navi.payment.nativepayment.model.transactionStatusRequest.TransactionStatusRequest
import com.navi.payment.nativepayment.presentation.reducer.ImageSource
import com.navi.payment.nativepayment.utils.NaviPaymentErrorConfig
import com.navi.payment.nativepayment.viewmodel.ScanCardResult
import com.navi.payment.paymentscreen.model.ErrorReason
import com.navi.payment.utils.putIfNotNullAndNotEmpty
@@ -180,6 +181,32 @@ class NaviPaymentAnalytics private constructor() {
eventValues = eventAttributes,
)
}
fun onErrorOccurredInGetQuerySnapshot(exception: Exception) {
NaviTrackEvent.trackEventOnClickStream(
"NaviPmt_UptimePoller_ErrorOccurredInGetQuerySnapshot",
mapOf(
"exception" to exception.toString(),
"exceptionMessage" to exception.message.toString(),
),
)
}
fun onErrorOccurredInProcessingQuerySnapshot(exception: Exception) {
NaviTrackEvent.trackEventOnClickStream(
"NaviPmt_UptimePoller_ErrorOccurredInProcessingQuerySnapshot",
mapOf(
"exception" to exception.toString(),
"exceptionMessage" to exception.message.toString(),
),
)
}
fun onPaymentsUptimePollerSyncSuccess() {
NaviTrackEvent.trackEventOnClickStream(
"NaviPmt_UptimePoller_PaymentsUptimePollerSyncSuccess"
)
}
}
inner class NPSMainScreen {
@@ -1503,6 +1530,16 @@ class NaviPaymentAnalytics private constructor() {
)
}
fun onBottomSheetCloseClicked(
bottomSheetState: String,
error: NaviPaymentErrorConfig? = null,
) {
NaviTrackEvent.trackEvent(
"NaviPMT_OneClickCheckoutScreen_BottomSheetCloseClicked",
mapOf("bottom_sheet_state" to bottomSheetState, "error" to error.toString()),
)
}
fun onRedirectingBottomSheetClosed(selectedBankAccountId: String) {
NaviTrackEvent.trackEvent(
"NaviPMT_OneClickCheckoutScreen_RedirectingBottomSheetClosed",

View File

@@ -0,0 +1,78 @@
/*
*
* * Copyright © 2025 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.payment.nativepayment.common.usecase
import com.google.firebase.firestore.QuerySnapshot
import com.navi.base.utils.FirestoreDataProvider
import com.navi.common.utils.NaviApiPoller
import com.navi.pay.utils.parallelMap
import com.navi.payment.nativepayment.NaviPaymentAnalytics
import com.navi.payment.nativepayment.db.model.toPaymentsUptimeEntity
import com.navi.payment.nativepayment.repository.PaymentsUptimeRepository
import com.navi.payment.utils.Constants.FIRESTORE_PAYMENTS_UPTIME_COLLECTION_PATH
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.time.Duration.Companion.minutes
@Singleton
class PaymentsUptimePollerUseCase
@Inject
constructor(
private val firestoreDataProvider: FirestoreDataProvider,
private val paymentsUptimeRepository: PaymentsUptimeRepository,
) {
private val PAYMENTS_UPTIME_POLLING_INTERVAL = 5.minutes
private val paymentsUptimePoller by lazy {
NaviApiPoller(repeatInterval = PAYMENTS_UPTIME_POLLING_INTERVAL)
}
private val naviPaymentAnalytics = NaviPaymentAnalytics.INSTANCE.CommonAnalytics()
suspend fun executePollerForPaymentsUptime() {
paymentsUptimePoller.stopPolling()
paymentsUptimePoller
.startPolling {
try {
firestoreDataProvider.getQuerySnapShotForCollectionPath(
collectionPath = FIRESTORE_PAYMENTS_UPTIME_COLLECTION_PATH
)
} catch (exception: Exception) {
naviPaymentAnalytics.onErrorOccurredInGetQuerySnapshot(exception = exception)
paymentsUptimePoller.stopPolling()
}
}
.collect {
try {
val paymentsUptimeQuerySnapShot = it as? QuerySnapshot?
processQuerySnapShotForPaymentsUptime(
paymentsUptimeQuerySnapShot = paymentsUptimeQuerySnapShot
)
naviPaymentAnalytics.onPaymentsUptimePollerSyncSuccess()
} catch (exception: Exception) {
naviPaymentAnalytics.onErrorOccurredInProcessingQuerySnapshot(
exception = exception
)
paymentsUptimePoller.stopPolling()
}
}
}
private suspend fun processQuerySnapShotForPaymentsUptime(
paymentsUptimeQuerySnapShot: QuerySnapshot?
) {
paymentsUptimeQuerySnapShot?.let { querySnapShot ->
val paymentsUptimeEntityList =
querySnapShot.documents.parallelMap { it.toPaymentsUptimeEntity() }
if (paymentsUptimeEntityList.isNotEmpty()) {
paymentsUptimeRepository.deleteAllExistingDataAndInsertAll(
uptimeEntityList = paymentsUptimeEntityList
)
}
}
}
}

View File

@@ -69,6 +69,7 @@ import com.navi.pay.R
import com.navi.pay.common.arc.ui.ArcNudgeBottomSheetContent
import com.navi.pay.common.model.view.CheckBalanceState
import com.navi.pay.common.theme.color.NaviPayColor
import com.navi.pay.common.ui.BottomSheetContentWithVerticalPrimarySecondaryButton
import com.navi.pay.common.ui.ImageWithCircularBackground
import com.navi.pay.common.ui.LoaderRoundedButton
import com.navi.pay.common.ui.NaviPayModalBottomSheet
@@ -260,6 +261,39 @@ fun PaymentCheckoutFooter(
is OneClickCheckoutFooterBottomSheetUiState.RedirectingBottomSheet -> {
RedirectingBottomSheetContent(title = bottomSheetType.titleText)
}
is OneClickCheckoutFooterBottomSheetUiState.Error -> {
BottomSheetContentWithVerticalPrimarySecondaryButton(
iconId = bottomSheetType.errorConfig.iconResId,
headerText = bottomSheetType.errorConfig.title,
descriptionText = bottomSheetType.errorConfig.description,
primaryButtonText =
bottomSheetType.errorConfig.firstPrimaryButtonConfig.text,
secondaryButtonText =
bottomSheetType.errorConfig.firstSecondaryButtonConfig?.text,
onPrimaryButtonClicked = {
coroutineScope
.launch { sheetState.hide() }
.invokeOnCompletion {
naviCheckoutViewModel.onBottomSheetCloseClicked()
}
},
onSecondaryButtonClicked = {
coroutineScope
.launch { sheetState.hide() }
.invokeOnCompletion {
naviCheckoutViewModel.onBottomSheetCloseClicked()
}
},
onDismissClicked = {
coroutineScope
.launch { sheetState.hide() }
.invokeOnCompletion {
naviCheckoutViewModel.onBottomSheetCloseClicked()
}
},
shouldShowCloseIcon = bottomSheetType.errorConfig.showDismissIcon,
)
}
else -> {}
}
}

View File

@@ -14,28 +14,35 @@ import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
import com.navi.common.utils.Constants.DEFAULT
import com.navi.payment.nativepayment.db.converter.BanksEntityConverter
import com.navi.payment.nativepayment.db.converter.PaymentsUptimeStatusConverter
import com.navi.payment.nativepayment.db.dao.BankListDao
import com.navi.payment.nativepayment.db.dao.PaymentsUptimeDao
import com.navi.payment.nativepayment.db.dao.TransactionStatusRequestDao
import com.navi.payment.nativepayment.db.model.BankListEntity
import com.navi.payment.nativepayment.db.model.PaymentsUptimeEntity
import com.navi.payment.nativepayment.db.model.TransactionStatusRequestEntity
import com.navi.payment.nativepayment.model.transactionStatusRequest.TransactionStatus
@Database(
entities = [TransactionStatusRequestEntity::class, BankListEntity::class],
version = 3,
entities =
[TransactionStatusRequestEntity::class, BankListEntity::class, PaymentsUptimeEntity::class],
version = 4,
exportSchema = false,
)
@TypeConverters(BanksEntityConverter::class)
@TypeConverters(BanksEntityConverter::class, PaymentsUptimeStatusConverter::class)
abstract class NaviPaymentAppDatabase : RoomDatabase() {
abstract fun transactionStatusRequestDao(): TransactionStatusRequestDao
abstract fun bankListDao(): BankListDao
abstract fun paymentUptimeDao(): PaymentsUptimeDao
companion object {
const val NAVI_PAYMENT_DATABASE_NAME = "navi-payment.db"
const val TRANSACTION_STATUS_REQUEST_TABLE = "transaction_status_request"
const val BANKS_TABLE = "banks"
const val UPTIME_STATUS_TABLE = "uptime_status"
}
}
@@ -105,3 +112,16 @@ val NAVI_PAYMENT_APP_DATABASE_MIGRATION_2_3 =
}
}
}
val NAVI_PAYMENT_APP_DATABASE_MIGRATION_3_4 =
object : Migration(3, 4) {
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL(
"CREATE TABLE IF NOT EXISTS ${NaviPaymentAppDatabase.UPTIME_STATUS_TABLE} (" +
"`vertical` TEXT PRIMARY KEY NOT NULL, " +
"`downtimeTitle` TEXT, " +
"`downtimeDescription` TEXT, " +
"`status` TEXT NOT NULL, "
)
}
}

View File

@@ -23,6 +23,9 @@ constructor(private val naviPaymentAppDatabase: NaviPaymentAppDatabase) : NaviMo
NaviPaymentAppDatabase.BANKS_TABLE -> {
runWithCatching { naviPaymentAppDatabase.bankListDao().deleteAll() }
}
NaviPaymentAppDatabase.UPTIME_STATUS_TABLE -> {
runWithCatching { naviPaymentAppDatabase.paymentUptimeDao().deleteAll() }
}
}
}

View File

@@ -0,0 +1,24 @@
/*
*
* * Copyright © 2025 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.payment.nativepayment.db.converter
import androidx.room.TypeConverter
import com.navi.payment.nativepayment.db.model.PaymentsUptimeStatus
class PaymentsUptimeStatusConverter {
@TypeConverter
fun fromPaymentsUptimeStatus(uptimeStatus: PaymentsUptimeStatus): String {
return uptimeStatus.name
}
@TypeConverter
fun toPaymentsUptimeStatus(uptimeStatus: String): PaymentsUptimeStatus {
return PaymentsUptimeStatus.valueOf(uptimeStatus)
}
}

View File

@@ -0,0 +1,35 @@
/*
*
* * Copyright © 2025 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.payment.nativepayment.db.dao
import androidx.room.Dao
import androidx.room.Query
import androidx.room.RawQuery
import androidx.room.Transaction
import androidx.room.Upsert
import androidx.sqlite.db.SupportSQLiteQuery
import com.navi.payment.nativepayment.db.NaviPaymentAppDatabase.Companion.UPTIME_STATUS_TABLE
import com.navi.payment.nativepayment.db.model.PaymentsUptimeEntity
@Dao
interface PaymentsUptimeDao {
@Upsert suspend fun insertAll(paymentsUptimeEntityList: List<PaymentsUptimeEntity>)
@Query("DELETE FROM $UPTIME_STATUS_TABLE") suspend fun deleteAll()
@RawQuery suspend fun deleteRows(query: SupportSQLiteQuery): Int
@Query("SELECT * FROM $UPTIME_STATUS_TABLE WHERE vertical = :vertical")
suspend fun getPaymentsUptimeEntityForVertical(vertical: String): List<PaymentsUptimeEntity>
@Transaction
suspend fun deleteAllExistingDataAndInsertAll(uptimeEntityList: List<PaymentsUptimeEntity>) {
deleteAll()
insertAll(uptimeEntityList)
}
}

View File

@@ -48,4 +48,9 @@ object NaviPaymentDbModule {
@Provides
fun providesBankListDao(naviPaymentAppDatabase: NaviPaymentAppDatabase) =
naviPaymentAppDatabase.bankListDao()
@Singleton
@Provides
fun providesPaymentsUptimeDao(naviPaymentAppDatabase: NaviPaymentAppDatabase) =
naviPaymentAppDatabase.paymentUptimeDao()
}

View File

@@ -0,0 +1,39 @@
/*
*
* * Copyright © 2025 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.payment.nativepayment.db.model
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import androidx.room.TypeConverters
import com.google.firebase.firestore.DocumentSnapshot
import com.navi.pay.utils.getIfscForBankUptime
import com.navi.payment.nativepayment.db.NaviPaymentAppDatabase.Companion.UPTIME_STATUS_TABLE
import com.navi.payment.nativepayment.db.converter.PaymentsUptimeStatusConverter
@Entity(tableName = UPTIME_STATUS_TABLE)
data class PaymentsUptimeEntity(
@PrimaryKey @ColumnInfo("vertical") val vertical: String,
@ColumnInfo("downtimeTitle") val downtimeTitle: String? = null,
@ColumnInfo("downtimeDescription") val downtimeDescription: String? = null,
@ColumnInfo("status")
@TypeConverters(PaymentsUptimeStatusConverter::class)
val status: PaymentsUptimeStatus,
)
fun DocumentSnapshot.toPaymentsUptimeEntity(): PaymentsUptimeEntity {
return PaymentsUptimeEntity(
vertical = this.id.getIfscForBankUptime(),
downtimeTitle = this.getString("downtimeTitle"),
downtimeDescription = this.getString("downtimeDescription"),
status =
PaymentsUptimeStatus.getBankUptimeStatusFromString(
bankUptimeStatus = this.getString("status") ?: ""
),
)
}

View File

@@ -0,0 +1,21 @@
/*
*
* * Copyright © 2025 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.payment.nativepayment.db.model
enum class PaymentsUptimeStatus {
UP,
WARNING,
DOWN;
companion object {
fun getBankUptimeStatusFromString(bankUptimeStatus: String): PaymentsUptimeStatus {
return entries.singleOrNull { it.name.equals(bankUptimeStatus, ignoreCase = true) }
?: UP
}
}
}

View File

@@ -7,6 +7,8 @@
package com.navi.payment.nativepayment.model
import com.navi.payment.nativepayment.utils.NaviPaymentErrorConfig
data class OneClickCheckoutFooterBottomSheetStateHolder(
val showBottomSheet: Boolean,
val bottomSheetUIState: OneClickCheckoutFooterBottomSheetUiState,
@@ -25,4 +27,7 @@ sealed class OneClickCheckoutFooterBottomSheetUiState {
data class RedirectingBottomSheet(val titleText: String) :
OneClickCheckoutFooterBottomSheetUiState()
data class Error(val errorConfig: NaviPaymentErrorConfig) :
OneClickCheckoutFooterBottomSheetUiState()
}

View File

@@ -0,0 +1,35 @@
/*
*
* * Copyright © 2025 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.payment.nativepayment.repository
import com.navi.common.firebaseremoteconfig.FirebaseRemoteConfigHelper
import com.navi.common.firebaseremoteconfig.FirebaseRemoteConfigHelper.ENABLE_NAVI_PAYMENT_KILL_SWITCH_MECHANISM
import com.navi.payment.nativepayment.db.dao.PaymentsUptimeDao
import com.navi.payment.nativepayment.db.model.PaymentsUptimeEntity
import com.navi.payment.nativepayment.db.model.PaymentsUptimeStatus
import javax.inject.Inject
class PaymentsUptimeRepository
@Inject
constructor(private val paymentsUptimeDao: PaymentsUptimeDao) {
suspend fun getPaymentsUptimeEntityForVertical(vertical: String): PaymentsUptimeEntity {
if (!FirebaseRemoteConfigHelper.getBoolean(ENABLE_NAVI_PAYMENT_KILL_SWITCH_MECHANISM)) {
return PaymentsUptimeEntity(vertical = vertical, status = PaymentsUptimeStatus.UP)
}
val paymentsUptimeEntity = paymentsUptimeDao.getPaymentsUptimeEntityForVertical(vertical)
return paymentsUptimeEntity.firstOrNull()
?: PaymentsUptimeEntity(vertical = vertical, status = PaymentsUptimeStatus.UP)
}
suspend fun deleteAllExistingDataAndInsertAll(uptimeEntityList: List<PaymentsUptimeEntity>) =
paymentsUptimeDao.deleteAllExistingDataAndInsertAll(uptimeEntityList = uptimeEntityList)
}

View File

@@ -12,20 +12,26 @@ import androidx.lifecycle.viewModelScope
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.navi.base.AppServiceManager
import com.navi.base.utils.ResourceProvider
import com.navi.base.utils.orElse
import com.navi.base.utils.orFalse
import com.navi.common.network.models.isSuccessWithData
import com.navi.common.upi.UpiDataType
import com.navi.common.usecase.LitmusExperimentsUseCase
import com.navi.common.utils.Constants.DEFAULT
import com.navi.naviwidgets.R as WidgetsR
import com.navi.pay.R as NaviPayR
import com.navi.pay.common.setup.NaviPayManager
import com.navi.pay.common.usecase.NaviPayConfigUseCase
import com.navi.pay.common.viewmodel.NaviPayBaseVM.Companion.ERROR_DEFAULT_TAG
import com.navi.pay.management.lite.models.NaviPayUpiLiteConfig
import com.navi.pay.utils.UPI_LITE_CONFIG
import com.navi.payment.R
import com.navi.payment.model.common.PaymentSdkInitParams
import com.navi.payment.model.initiatesdk.PaymentPrefetchDetail
import com.navi.payment.model.initiatesdk.PaymentPrefetchMethodRequest
import com.navi.payment.nativepayment.NaviPaymentAnalyticScreenName
import com.navi.payment.nativepayment.common.usecase.PaymentsUptimePollerUseCase
import com.navi.payment.nativepayment.common.usecase.TransactionStatusUseCase
import com.navi.payment.nativepayment.dataprovider.PaymentDataProvider
import com.navi.payment.nativepayment.dataprovider.PaymentDataProvider.Companion.ACTION_TYPE
@@ -33,14 +39,20 @@ import com.navi.payment.nativepayment.dataprovider.PaymentDataProvider.Companion
import com.navi.payment.nativepayment.dataprovider.PaymentDataProvider.Companion.PAYMENT_INITIATE_START_TIME
import com.navi.payment.nativepayment.dataprovider.PaymentDataProvider.Companion.SCREEN_TYPE
import com.navi.payment.nativepayment.dataprovider.PaymentDataProvider.Companion.UPI_LITE_MAX_PAYABLE_AMOUNT_PER_TRANSACTION
import com.navi.payment.nativepayment.db.model.PaymentsUptimeStatus
import com.navi.payment.nativepayment.model.NaviPaymentScreenType
import com.navi.payment.nativepayment.model.NetBankingPaymentInstrument
import com.navi.payment.nativepayment.model.PaymentActionType
import com.navi.payment.nativepayment.model.PaymentInitResponse
import com.navi.payment.nativepayment.model.S2sPaymentMethodResponse
import com.navi.payment.nativepayment.repository.PaymentRepository
import com.navi.payment.nativepayment.repository.PaymentsUptimeRepository
import com.navi.payment.nativepayment.usecase.NetBankingUseCase
import com.navi.payment.nativepayment.usecase.PmsLinkedAccountUseCase
import com.navi.payment.nativepayment.utils.NaviPaymentBottomSheetButtonAction
import com.navi.payment.nativepayment.utils.NaviPaymentButtonTheme
import com.navi.payment.nativepayment.utils.NaviPaymentErrorButtonConfig
import com.navi.payment.nativepayment.utils.NaviPaymentErrorConfig
import com.navi.payment.nativepayment.utils.getPayloadBasedOnType
import com.navi.payment.nativepayment.utils.toGenericErrorResponse
import com.navi.payment.nativepayment.viewmodel.NaviPaymentBaseVM
@@ -74,11 +86,21 @@ constructor(
protected val transactionStatusUseCase: TransactionStatusUseCase,
protected val litmusExperimentsUseCase: LitmusExperimentsUseCase,
private val naviPayConfigUseCase: NaviPayConfigUseCase,
private val paymentsUptimePollerUseCase: PaymentsUptimePollerUseCase,
protected val paymentsUptimeRepository: PaymentsUptimeRepository,
protected val resourceProvider: ResourceProvider,
) : NaviPaymentBaseVM(screenName = NaviPaymentAnalyticScreenName.CHECKOUT_SCREEN.screenName) {
protected val _paymentResponse = MutableSharedFlow<PaymentInitResponse>(replay = 0)
val paymentResponse = _paymentResponse.asSharedFlow()
init {
updateUpiLiteConfig()
viewModelScope.safeLaunch(Dispatchers.IO) {
paymentsUptimePollerUseCase.executePollerForPaymentsUptime()
}
}
fun initiatePayment(paymentSdkInitParams: PaymentSdkInitParams) {
viewModelScope.safeLaunch {
initializePaymentSession(paymentSdkInitParams.paymentSource.orEmpty())
@@ -211,7 +233,6 @@ constructor(
protected suspend fun initializePaymentSession(source: String) {
clearPaymentData()
fetchPmsExperimentsData(source)
updateUpiLiteConfig()
paymentDataProvider.add(PAYMENT_INITIATE_START_TIME, System.currentTimeMillis())
_paymentResponse.emit(PaymentInitResponse(isSuccess = false))
}
@@ -230,4 +251,35 @@ constructor(
)
}
}
protected suspend fun isPaymentsServiceDown(source: String): NaviPaymentErrorConfig? {
val paymentsUptimeEntity =
paymentsUptimeRepository.getPaymentsUptimeEntityForVertical(source)
return if (paymentsUptimeEntity.status == PaymentsUptimeStatus.DOWN) {
NaviPaymentErrorConfig(
iconResId = WidgetsR.drawable.ic_error_outlined,
title =
paymentsUptimeEntity.downtimeTitle.orElse(
resourceProvider.getString(NaviPayR.string.np_psp_down_message_title)
),
description =
paymentsUptimeEntity.downtimeDescription.orElse(
resourceProvider.getString(NaviPayR.string.np_psp_down_message_desc)
),
buttonConfigs =
listOf(
NaviPaymentErrorButtonConfig(
text = resourceProvider.getString(R.string.okay_got_it),
type = NaviPaymentButtonTheme.Primary,
action = NaviPaymentBottomSheetButtonAction.Dismiss,
)
),
code = UNKNOWN_ERROR_CODE,
tag = ERROR_DEFAULT_TAG,
showDismissIcon = false,
)
} else {
null
}
}
}

View File

@@ -53,6 +53,7 @@ import com.navi.payment.R
import com.navi.payment.model.common.PaymentSdkInitParams
import com.navi.payment.nativepayment.NaviPaymentAnalyticScreenName
import com.navi.payment.nativepayment.NaviPaymentAnalytics
import com.navi.payment.nativepayment.common.usecase.PaymentsUptimePollerUseCase
import com.navi.payment.nativepayment.common.usecase.TransactionStatusUseCase
import com.navi.payment.nativepayment.dataprovider.PaymentDataProvider
import com.navi.payment.nativepayment.dataprovider.PaymentDataProvider.Companion.ACTION_TYPE
@@ -81,9 +82,11 @@ import com.navi.payment.nativepayment.presentation.reducer.OneClickScreenContrac
import com.navi.payment.nativepayment.presentation.reducer.OneClickScreenEffect
import com.navi.payment.nativepayment.presentation.reducer.OneClickScreenEvent
import com.navi.payment.nativepayment.repository.PaymentRepository
import com.navi.payment.nativepayment.repository.PaymentsUptimeRepository
import com.navi.payment.nativepayment.usecase.NetBankingUseCase
import com.navi.payment.nativepayment.usecase.PmsLinkedAccountUseCase
import com.navi.payment.nativepayment.utils.Constants.NAVI_PAY_ACTION
import com.navi.payment.nativepayment.utils.NaviPaymentErrorConfig
import com.navi.payment.nativepayment.utils.getDiscountAdjustedAmount
import com.navi.payment.nativepayment.utils.getPreferredSelectedAccount
import com.navi.payment.nativepayment.utils.mapToLinkedAccountEntity
@@ -143,7 +146,9 @@ constructor(
transactionStatusUseCase: TransactionStatusUseCase,
litmusExperimentsUseCase: LitmusExperimentsUseCase,
naviPayConfigUseCase: NaviPayConfigUseCase,
private val resourceProvider: ResourceProvider,
paymentsUptimePollerUseCase: PaymentsUptimePollerUseCase,
paymentsUptimeRepository: PaymentsUptimeRepository,
resourceProvider: ResourceProvider,
private val accountListCheckBalanceUseCase: AccountListCheckBalanceUseCase,
private val naviPayPspManager: NaviPayPspManager,
private val arcNudgeUseCase: ArcNudgeUseCase,
@@ -159,6 +164,9 @@ constructor(
transactionStatusUseCase = transactionStatusUseCase,
litmusExperimentsUseCase = litmusExperimentsUseCase,
naviPayConfigUseCase = naviPayConfigUseCase,
paymentsUptimePollerUseCase = paymentsUptimePollerUseCase,
paymentsUptimeRepository = paymentsUptimeRepository,
resourceProvider = resourceProvider,
),
OneClickScreenContract {
@@ -246,6 +254,10 @@ constructor(
}
is OneClickScreenEvent.OnBankAccountItemClicked -> {
isPaymentsServiceDown(paymentSource?.name.orEmpty())?.let {
handlePaymentsDowntime(it)
return@safeLaunch
}
navigationScreen = NaviPaymentScreenType.FULL_PAYMENT_SCREEN
naviPaymentAnalytics.onBankAccountClicked(
selectedBankAccountId = state.value.selectedAccount?.accountId.orEmpty()
@@ -617,6 +629,10 @@ constructor(
state.value.selectedAccount?.eligibilityState?.isAccountEligible?.not().orFalse()
private suspend fun handleCtaClicked(generateToken: () -> Unit) {
isPaymentsServiceDown(paymentSource?.name.orEmpty())?.let {
handlePaymentsDowntime(it)
return
}
when (ctaAction) {
CheckoutCtaAction.SEND_MONEY_NAVIGATION -> {
navigationScreen = NaviPaymentScreenType.ONE_CLICK_CHECKOUT_SCREEN
@@ -792,8 +808,14 @@ constructor(
selectedBankAccountId = state.value.selectedAccount?.accountId.orEmpty()
)
} else {
naviPaymentAnalytics.onRedirectingBottomSheetClosed(
selectedBankAccountId = state.value.selectedAccount?.accountId.orEmpty()
val error =
(bottomSheetStateHolder.value.bottomSheetUIState
as? OneClickCheckoutFooterBottomSheetUiState.Error)
?.errorConfig
naviPaymentAnalytics.onBottomSheetCloseClicked(
bottomSheetState =
bottomSheetStateHolder.value.bottomSheetUIState::class.java.simpleName,
error = error,
)
}
updateBottomSheetUIState(
@@ -1108,4 +1130,11 @@ constructor(
it <= AppServiceManager.appVersionCode
} ?: false)
}
private fun handlePaymentsDowntime(errorConfig: NaviPaymentErrorConfig) {
updateBottomSheetUIState(
showBottomSheet = true,
bottomSheetUIState = OneClickCheckoutFooterBottomSheetUiState.Error(errorConfig),
)
}
}

View File

@@ -1,6 +1,6 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * Copyright © 2024-2025 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
@@ -27,4 +27,6 @@ sealed class PMSErrorReason : Parcelable, ErrorReason {
data object TokenNotFoundError : PMSErrorReason()
data object Unknown : PMSErrorReason()
data object PaymentsServiceDown : PMSErrorReason()
}

View File

@@ -170,6 +170,9 @@ object Constants {
const val SCREEN_NAME = "screen_name"
const val IS_AMOUNT_READ_ONLY = "is_amount_read_only"
// Firestore
const val FIRESTORE_PAYMENTS_UPTIME_COLLECTION_PATH = "navipmt-uptime-status"
// CARDS
const val CARD_VALIDATION_DEBOUNCE_TIME_IN_MILLIS = 50L
const val DEFAULT_MIN_CVV_LENGTH = 3