From 6fa84dd9614177d08041fa07e6ef638b836b3fc1 Mon Sep 17 00:00:00 2001 From: Prakhar Saxena Date: Tue, 17 Dec 2024 22:02:59 +0530 Subject: [PATCH] NTP-14219 | PS | Net Banking list based on vertical and category (#14165) --- .../viewmodel/PennyDropOptionsViewModel.kt | 8 +- .../navi/amc/kyc/viewmodel/BankDetailsVM.kt | 11 ++- .../FirebaseRemoteConfigHelper.kt | 1 + .../PaymentPrefetchMethodRequest.kt | 1 + .../components/NPSNetBankingWidget.kt | 37 ++++---- .../components/NetBankingScreenListSection.kt | 42 ++++---- .../dataprovider/PaymentDataProvider.kt | 9 -- .../db/NaviPaymentAppDatabase.kt | 31 +++++- .../db/converter/BanksEntityConverter.kt | 28 ++++++ .../nativepayment/db/dao/BankListDao.kt | 36 +++++++ .../db/di/NaviPaymentDbModule.kt | 8 ++ .../nativepayment/db/model/BankListEntity.kt | 35 +++++++ .../model/NetBankingPaymentInstrument.kt | 11 +++ .../presentation/reducer/NPSScreenContract.kt | 23 +++-- .../reducer/NetBankingScreenContract.kt | 10 +- .../repository/BanksRepository.kt | 37 ++++++++ .../nativepayment/screens/NPSScreen.kt | 24 +++-- .../nativepayment/screens/NetBankingScreen.kt | 6 +- .../sharedviewmodel/NaviCheckoutViewModel.kt | 45 ++++++++- .../usecase/NetBankingUseCase.kt | 95 +++++++++++++++++++ .../nativepayment/viewmodel/NPSViewModel.kt | 44 ++++++--- .../viewmodel/NetBankingViewModel.kt | 74 ++++++--------- 22 files changed, 490 insertions(+), 126 deletions(-) create mode 100644 android/navi-payment/src/main/java/com/navi/payment/nativepayment/db/converter/BanksEntityConverter.kt create mode 100644 android/navi-payment/src/main/java/com/navi/payment/nativepayment/db/dao/BankListDao.kt create mode 100644 android/navi-payment/src/main/java/com/navi/payment/nativepayment/db/model/BankListEntity.kt create mode 100644 android/navi-payment/src/main/java/com/navi/payment/nativepayment/repository/BanksRepository.kt create mode 100644 android/navi-payment/src/main/java/com/navi/payment/nativepayment/usecase/NetBankingUseCase.kt diff --git a/android/navi-amc/src/main/java/com/navi/amc/common/viewmodel/PennyDropOptionsViewModel.kt b/android/navi-amc/src/main/java/com/navi/amc/common/viewmodel/PennyDropOptionsViewModel.kt index fa42b0ad69..d04b492b40 100644 --- a/android/navi-amc/src/main/java/com/navi/amc/common/viewmodel/PennyDropOptionsViewModel.kt +++ b/android/navi-amc/src/main/java/com/navi/amc/common/viewmodel/PennyDropOptionsViewModel.kt @@ -21,6 +21,8 @@ import com.navi.base.utils.SUCCESS import com.navi.base.utils.isNotNullAndNotEmpty import com.navi.common.model.UploadDataAsyncResponse import com.navi.common.utils.isValidResponse +import com.navi.payment.nativepayment.usecase.NetBankingUseCase +import com.navi.payment.utils.PaymentSource import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject import kotlinx.coroutines.launch @@ -28,7 +30,10 @@ import kotlinx.coroutines.launch @HiltViewModel class PennyDropOptionsViewModel @Inject -constructor(private val repository: PennyDropOptionsRepository) : BaseAmcVM() { +constructor( + private val repository: PennyDropOptionsRepository, + private val pmsNetBankingUseCase: NetBankingUseCase +) : BaseAmcVM() { var requestId: String? = null @@ -84,6 +89,7 @@ constructor(private val repository: PennyDropOptionsRepository) : BaseAmcVM() { } else { setErrorData(response.errors, response.error) } + pmsNetBankingUseCase.clearBankListForVertical(PaymentSource.AMC.name) } } diff --git a/android/navi-amc/src/main/java/com/navi/amc/kyc/viewmodel/BankDetailsVM.kt b/android/navi-amc/src/main/java/com/navi/amc/kyc/viewmodel/BankDetailsVM.kt index 91a55027ea..de1e3480b5 100644 --- a/android/navi-amc/src/main/java/com/navi/amc/kyc/viewmodel/BankDetailsVM.kt +++ b/android/navi-amc/src/main/java/com/navi/amc/kyc/viewmodel/BankDetailsVM.kt @@ -17,13 +17,19 @@ import com.navi.amc.kyc.model.BankDetailsResponse import com.navi.amc.kyc.repository.BankDetailsRepository import com.navi.common.network.models.SuccessResponse import com.navi.common.utils.Constants.AMC_KYC_BANK_DETAILS_URL +import com.navi.payment.nativepayment.usecase.NetBankingUseCase +import com.navi.payment.utils.PaymentSource import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject import kotlinx.coroutines.launch @HiltViewModel -class BankDetailsVM @Inject constructor(private val repository: BankDetailsRepository) : - BaseAmcVM() { +class BankDetailsVM +@Inject +constructor( + private val repository: BankDetailsRepository, + private val pmsNetBankingUseCase: NetBankingUseCase +) : BaseAmcVM() { private val _bankItems = MutableLiveData() val bankItems: LiveData @@ -71,6 +77,7 @@ class BankDetailsVM @Inject constructor(private val repository: BankDetailsRepos } else { setErrorData(response.errors, response.error) } + pmsNetBankingUseCase.clearBankListForVertical(PaymentSource.AMC.name) } } 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 dde54df65f..9e891fb7b5 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_PMT_DISABLE_WEB_PAYMENTS = "NAVI_PMT_DISABLE_WEB_PAYMENTS" const val NAVI_PMT_RETRY_POLICY_ENABLED = "NAVI_PMT_RETRY_POLICY_ENABLED" const val NAVI_PMT_API_CONNECT_TIMEOUT = "NAVI_PMT_API_CONNECT_TIMEOUT" + const val NAVI_PMT_NET_BANKING_CACHE_TTL = "NAVI_PMT_NET_BANKING_CACHE_TTL" // BBPS const val NAVI_BBPS_REWARDS_NUDGE_CACHE_TTL_IN_MILLIS = diff --git a/android/navi-payment/src/main/java/com/navi/payment/model/initiatesdk/PaymentPrefetchMethodRequest.kt b/android/navi-payment/src/main/java/com/navi/payment/model/initiatesdk/PaymentPrefetchMethodRequest.kt index ddc82cb0d1..a007c89d2c 100644 --- a/android/navi-payment/src/main/java/com/navi/payment/model/initiatesdk/PaymentPrefetchMethodRequest.kt +++ b/android/navi-payment/src/main/java/com/navi/payment/model/initiatesdk/PaymentPrefetchMethodRequest.kt @@ -17,6 +17,7 @@ import kotlinx.parcelize.RawValue data class PaymentPrefetchMethodRequest( @SerializedName("prefetchDetails") var prefetchDetails: List? = null, @SerializedName("previousScreenName") val previousScreenName: String? = null, + @SerializedName("shouldFetchBankList") val shouldFetchBankList: Boolean = false, @SerializedName("additionalParam") var additionalParam: AdditionalParam? = AdditionalParam("CLIENT_PAYNOW"), @SerializedName("callSdkExitOnBack") var callSdkExitOnBack: Boolean = true diff --git a/android/navi-payment/src/main/java/com/navi/payment/nativepayment/components/NPSNetBankingWidget.kt b/android/navi-payment/src/main/java/com/navi/payment/nativepayment/components/NPSNetBankingWidget.kt index 1149ecda08..e9d049f325 100644 --- a/android/navi-payment/src/main/java/com/navi/payment/nativepayment/components/NPSNetBankingWidget.kt +++ b/android/navi-payment/src/main/java/com/navi/payment/nativepayment/components/NPSNetBankingWidget.kt @@ -30,6 +30,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import com.navi.base.utils.isNotNullAndNotEmpty import com.navi.common.utils.EMPTY import com.navi.common.utils.clickableDebounce import com.navi.design.font.FontWeightEnum @@ -42,7 +43,7 @@ import com.navi.pay.common.theme.color.NaviPayColor.inputFieldFilled import com.navi.pay.common.ui.ImageWithCircularBackground import com.navi.pay.utils.getMaskedAccountNumber import com.navi.payment.R -import com.navi.payment.nativepayment.model.BankDetails +import com.navi.payment.nativepayment.db.model.BankEntity import com.navi.payment.nativepayment.presentation.reducer.NPSScreenContract @Composable @@ -50,8 +51,8 @@ fun NPSNetBankingWidget( paymentOptionIconUrl: String, onAction: (action: NPSScreenContract.NPSScreenEvent) -> Unit, requireRedirection: Boolean, - bankDetailsList: List, - selectedBankDetails: BankDetails? + banksList: List, + selectedBank: BankEntity? ) { Column(modifier = Modifier.fillMaxWidth()) { NpsPaymentInstrumentHeader(header = stringResource(id = R.string.net_banking_card_header)) @@ -65,8 +66,8 @@ fun NPSNetBankingWidget( ) } else { NpsNetbankingList( - bankDetailsList = bankDetailsList, - selectedBankDetails = selectedBankDetails, + banksList = banksList, + selectedBank = selectedBank, onClick = { bankDetail -> onAction(NPSScreenContract.NPSScreenEvent.OnNetbankingBankSelected(bankDetail)) } @@ -77,15 +78,15 @@ fun NPSNetBankingWidget( @Composable fun NpsNetbankingList( - bankDetailsList: List, - onClick: (bankDetail: BankDetails) -> Unit, - selectedBankDetails: BankDetails? + banksList: List, + onClick: (bankEntity: BankEntity) -> Unit, + selectedBank: BankEntity? ) { Column(modifier = Modifier.padding(horizontal = 16.dp)) { - bankDetailsList.forEach { bankDetail -> + banksList.forEach { bankDetail -> NpsNetbankingBankItem( - bankDetail = bankDetail, - isSelected = selectedBankDetails == bankDetail, + bankEntity = bankDetail, + isSelected = selectedBank == bankDetail, onClick = onClick ) Spacer(modifier = Modifier.height(16.dp)) @@ -95,9 +96,9 @@ fun NpsNetbankingList( @Composable fun NpsNetbankingBankItem( - bankDetail: BankDetails, + bankEntity: BankEntity, isSelected: Boolean, - onClick: (bankDetail: BankDetails) -> Unit + onClick: (bankEntity: BankEntity) -> Unit ) { Row( verticalAlignment = Alignment.CenterVertically, @@ -114,19 +115,19 @@ fun NpsNetbankingBankItem( border = BorderStroke(width = 1.dp, color = NaviPayColor.borderDefault), shape = RoundedCornerShape(4.dp) ) - .clickableDebounce { onClick.invoke(bankDetail) } + .clickableDebounce { onClick.invoke(bankEntity) } .padding(horizontal = 16.dp, vertical = 12.dp), ) { Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.weight(1f)) { ImageWithCircularBackground( boxSize = 40.dp, - imageUrl = bankDetail.bankIcon, + imageUrl = bankEntity.iconUrl, imageSize = 24.dp ) Spacer(modifier = Modifier.width(12.dp)) NaviText( modifier = Modifier.weight(1f, fill = false), - text = bankDetail.bankTitle, + text = bankEntity.name, fontSize = 16.sp, lineHeight = 24.sp, fontFamily = naviFontFamily, @@ -135,9 +136,9 @@ fun NpsNetbankingBankItem( overflow = TextOverflow.Ellipsis, maxLines = 1, ) - if (bankDetail.bankAccountNumber.isNotEmpty()) { + if (bankEntity.accountNumber.isNotNullAndNotEmpty()) { NaviText( - text = " - ${bankDetail.bankAccountNumber.getMaskedAccountNumber()}", + text = " - ${bankEntity.accountNumber?.getMaskedAccountNumber()}", fontSize = 16.sp, lineHeight = 24.sp, fontFamily = naviFontFamily, diff --git a/android/navi-payment/src/main/java/com/navi/payment/nativepayment/components/NetBankingScreenListSection.kt b/android/navi-payment/src/main/java/com/navi/payment/nativepayment/components/NetBankingScreenListSection.kt index 17961552df..5706aca719 100644 --- a/android/navi-payment/src/main/java/com/navi/payment/nativepayment/components/NetBankingScreenListSection.kt +++ b/android/navi-payment/src/main/java/com/navi/payment/nativepayment/components/NetBankingScreenListSection.kt @@ -63,17 +63,17 @@ import com.navi.pay.common.ui.ImageWithCircularBackground import com.navi.pay.utils.NAVI_PAY_PURPLE_CTA_LOADER_LOTTIE import com.navi.payment.R import com.navi.payment.nativepayment.common.theme.NaviPmsColor -import com.navi.payment.nativepayment.model.BankDetails +import com.navi.payment.nativepayment.db.model.BankEntity import kotlin.math.ceil import kotlin.math.max @Composable fun NetBankingScreenListSection( showPopularBanks: Boolean, - popularBanks: List, - allBanks: List, - onBankSelected: (bankDetails: BankDetails, isPopularBank: Boolean) -> Unit, - selectedBank: BankDetails?, + popularBanks: List, + allBanks: List, + onBankSelected: (bankEntity: BankEntity, isPopularBank: Boolean) -> Unit, + selectedBank: BankEntity?, isSelectedFromPopularBanks: Boolean ) { val lazyListState = rememberLazyListState() @@ -154,9 +154,9 @@ fun NetBankingScreenListSection( @Composable fun PopularBanksListSection( - popularBanks: List, - onBankSelected: (bankDetails: BankDetails, isPopularBank: Boolean) -> Unit, - selectedBank: BankDetails?, + popularBanks: List, + onBankSelected: (bankEntity: BankEntity, isPopularBank: Boolean) -> Unit, + selectedBank: BankEntity?, isSelectedFromPopularBanks: Boolean ) { Column { @@ -182,7 +182,7 @@ fun PopularBanksListSection( horizontalArrangement = Arrangement.spacedBy(12.dp), verticalArrangement = Arrangement.spacedBy(12.dp), ) { - items(items = popularBanks, key = { it.bankCode }) { bankEntity -> + items(items = popularBanks, key = { it.code }) { bankEntity -> PopularBanksGridItem( bankEntity = bankEntity, height = 100.dp, @@ -197,10 +197,10 @@ fun PopularBanksListSection( @Composable fun PopularBanksGridItem( - bankEntity: BankDetails, + bankEntity: BankEntity, height: Dp = 100.dp, - onBankSelected: (bankDetails: BankDetails, isPopularBank: Boolean) -> Unit, - selectedBank: BankDetails?, + onBankSelected: (bankEntity: BankEntity, isPopularBank: Boolean) -> Unit, + selectedBank: BankEntity?, isSelectedFromPopularBanks: Boolean ) { Card( @@ -221,16 +221,16 @@ fun PopularBanksGridItem( horizontalAlignment = Alignment.CenterHorizontally ) { ImageWithCircularBackground( - imageUrl = bankEntity.bankIcon, + imageUrl = bankEntity.iconUrl, boxSize = 40.dp, imageSize = 28.dp ) Spacer(modifier = Modifier.weight(1f)) - if (selectedBank?.bankCode == bankEntity.bankCode && isSelectedFromPopularBanks) { + if (selectedBank?.code == bankEntity.code && isSelectedFromPopularBanks) { ButtonLoaderLottie() } else { NaviText( - text = bankEntity.bankTitle, + text = bankEntity.name, fontSize = 12.sp, lineHeight = 18.sp, fontFamily = naviFontFamily, @@ -248,9 +248,9 @@ fun PopularBanksGridItem( @Composable fun RegularBanksListItem( - bankEntity: BankDetails, - onBankSelected: (bankDetails: BankDetails, isPopularBank: Boolean) -> Unit, - selectedBank: BankDetails?, + bankEntity: BankEntity, + onBankSelected: (bankEntity: BankEntity, isPopularBank: Boolean) -> Unit, + selectedBank: BankEntity?, isSelectedFromPopularBanks: Boolean ) { Row( @@ -261,7 +261,7 @@ fun RegularBanksListItem( .padding(top = 8.dp, bottom = 8.dp) ) { ImageWithCircularBackground( - imageUrl = bankEntity.bankIcon, + imageUrl = bankEntity.iconUrl, backgroundColor = NaviPayColor.bgDefault, fallbackIconUrl = CommonR.drawable.ic_upi_bbps_default_bank_logo, boxSize = 40.dp, @@ -273,7 +273,7 @@ fun RegularBanksListItem( Spacer(modifier = Modifier.width(12.dp)) NaviText( - text = bankEntity.bankTitle, + text = bankEntity.name, fontSize = 14.sp, lineHeight = 22.sp, fontFamily = naviFontFamily, @@ -283,7 +283,7 @@ fun RegularBanksListItem( overflow = TextOverflow.Ellipsis, modifier = Modifier.weight(1f) ) - if (selectedBank?.bankCode == bankEntity.bankCode && !isSelectedFromPopularBanks) { + if (selectedBank?.code == bankEntity.code && !isSelectedFromPopularBanks) { Spacer(modifier = Modifier.weight(1f)) ButtonLoaderLottie() } diff --git a/android/navi-payment/src/main/java/com/navi/payment/nativepayment/dataprovider/PaymentDataProvider.kt b/android/navi-payment/src/main/java/com/navi/payment/nativepayment/dataprovider/PaymentDataProvider.kt index 75f4327d77..188f632ab1 100644 --- a/android/navi-payment/src/main/java/com/navi/payment/nativepayment/dataprovider/PaymentDataProvider.kt +++ b/android/navi-payment/src/main/java/com/navi/payment/nativepayment/dataprovider/PaymentDataProvider.kt @@ -8,7 +8,6 @@ package com.navi.payment.nativepayment.dataprovider import com.navi.common.utils.SoftRefLruCache -import com.navi.payment.nativepayment.model.BankDetails import com.navi.payment.nativepayment.model.BasePaymentMethodResponse import javax.inject.Inject import javax.inject.Singleton @@ -26,9 +25,6 @@ class PaymentDataProvider @Inject constructor() { private val _paymentMethodResponse = MutableStateFlow(null) val paymentMethodResponse = _paymentMethodResponse.asStateFlow() - private val _netBankingBankDetails = MutableStateFlow>(value = emptyList()) - val netBankingBankDetails = _netBankingBankDetails.asStateFlow() - private val _isDiscountPreApplied = MutableStateFlow(false) val isDiscountPreApplied = _isDiscountPreApplied.asStateFlow() @@ -60,7 +56,6 @@ class PaymentDataProvider @Inject constructor() { analyticsEventParams.clear() sourceEventProperties.clear() _paymentMethodResponse.update { null } - _netBankingBankDetails.update { emptyList() } _isDiscountPreApplied.update { false } } @@ -91,10 +86,6 @@ class PaymentDataProvider @Inject constructor() { _isDiscountPreApplied.update { isDiscountApplied } } - fun updateNetBankingBanksResponse(banks: List) { - _netBankingBankDetails.update { banks } - } - companion object { private const val MAX_CONSUMPTION_LIMIT = 100 private const val MAX_TTL = 60 * 60 * 1000L diff --git a/android/navi-payment/src/main/java/com/navi/payment/nativepayment/db/NaviPaymentAppDatabase.kt b/android/navi-payment/src/main/java/com/navi/payment/nativepayment/db/NaviPaymentAppDatabase.kt index b16fb2e5a6..df546b00ad 100644 --- a/android/navi-payment/src/main/java/com/navi/payment/nativepayment/db/NaviPaymentAppDatabase.kt +++ b/android/navi-payment/src/main/java/com/navi/payment/nativepayment/db/NaviPaymentAppDatabase.kt @@ -9,16 +9,45 @@ package com.navi.payment.nativepayment.db import androidx.room.Database import androidx.room.RoomDatabase +import androidx.room.TypeConverters +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.dao.BankListDao import com.navi.payment.nativepayment.db.dao.TransactionStatusRequestDao +import com.navi.payment.nativepayment.db.model.BankListEntity import com.navi.payment.nativepayment.db.model.TransactionStatusRequestEntity -@Database(entities = [TransactionStatusRequestEntity::class], version = 1, exportSchema = false) +@Database( + entities = [TransactionStatusRequestEntity::class, BankListEntity::class], + version = 2, + exportSchema = false +) +@TypeConverters(BanksEntityConverter::class) abstract class NaviPaymentAppDatabase : RoomDatabase() { abstract fun transactionStatusRequestDao(): TransactionStatusRequestDao + abstract fun bankListDao(): BankListDao + 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" } } + +val NAVI_PAYMENT_APP_DATABASE_MIGRATION_1_2 = + object : Migration(1, 2) { + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL( + "CREATE TABLE IF NOT EXISTS ${NaviPaymentAppDatabase.BANKS_TABLE} (" + + "`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, " + + "`vertical` TEXT NOT NULL, " + + "`category` TEXT NOT NULL DEFAULT '${DEFAULT}', " + + "`bankList` TEXT NOT NULL, " + + "`updatedAt` INTEGER NOT NULL)" + ) + } + } diff --git a/android/navi-payment/src/main/java/com/navi/payment/nativepayment/db/converter/BanksEntityConverter.kt b/android/navi-payment/src/main/java/com/navi/payment/nativepayment/db/converter/BanksEntityConverter.kt new file mode 100644 index 0000000000..092af7d620 --- /dev/null +++ b/android/navi-payment/src/main/java/com/navi/payment/nativepayment/db/converter/BanksEntityConverter.kt @@ -0,0 +1,28 @@ +/* + * + * * Copyright © 2024 by Navi Technologies Limited + * * All rights reserved. Strictly confidential + * + */ + +package com.navi.payment.nativepayment.db.converter + +import androidx.room.TypeConverter +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.navi.payment.nativepayment.db.model.BankEntity + +class BanksEntityConverter { + private val gson = Gson() + + @TypeConverter + fun fromBankEntityList(bankEntityList: List): String { + return gson.toJson(bankEntityList) + } + + @TypeConverter + fun toPlansEntityList(bankEntityListString: String): List { + val type = object : TypeToken>() {}.type + return gson.fromJson(bankEntityListString, type) + } +} diff --git a/android/navi-payment/src/main/java/com/navi/payment/nativepayment/db/dao/BankListDao.kt b/android/navi-payment/src/main/java/com/navi/payment/nativepayment/db/dao/BankListDao.kt new file mode 100644 index 0000000000..c407cb93f4 --- /dev/null +++ b/android/navi-payment/src/main/java/com/navi/payment/nativepayment/db/dao/BankListDao.kt @@ -0,0 +1,36 @@ +/* + * + * * Copyright © 2024 by Navi Technologies Limited + * * All rights reserved. Strictly confidential + * + */ + +package com.navi.payment.nativepayment.db.dao + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import com.navi.payment.nativepayment.db.NaviPaymentAppDatabase +import com.navi.payment.nativepayment.db.model.BankListEntity + +@Dao +interface BankListDao { + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insert(bankListEntity: BankListEntity) + + @Query( + "SELECT * FROM ${NaviPaymentAppDatabase.BANKS_TABLE} WHERE vertical = :vertical AND category = :category LIMIT 1" + ) + suspend fun getBanksForVerticalAndCategory(vertical: String, category: String): BankListEntity? + + @Query("DELETE FROM ${NaviPaymentAppDatabase.BANKS_TABLE} WHERE vertical = :vertical") + suspend fun deleteBanksForVertical(vertical: String) + + @Query( + "DELETE FROM ${NaviPaymentAppDatabase.BANKS_TABLE} WHERE vertical = :vertical AND category = :category" + ) + suspend fun deleteBanksForVerticalAndCategory(vertical: String, category: String) + + @Query("DELETE FROM ${NaviPaymentAppDatabase.BANKS_TABLE}") suspend fun deleteAll() +} diff --git a/android/navi-payment/src/main/java/com/navi/payment/nativepayment/db/di/NaviPaymentDbModule.kt b/android/navi-payment/src/main/java/com/navi/payment/nativepayment/db/di/NaviPaymentDbModule.kt index 1fd689392f..ef67707012 100644 --- a/android/navi-payment/src/main/java/com/navi/payment/nativepayment/db/di/NaviPaymentDbModule.kt +++ b/android/navi-payment/src/main/java/com/navi/payment/nativepayment/db/di/NaviPaymentDbModule.kt @@ -9,6 +9,7 @@ package com.navi.payment.nativepayment.db.di import android.content.Context import androidx.room.Room +import com.navi.payment.nativepayment.db.NAVI_PAYMENT_APP_DATABASE_MIGRATION_1_2 import com.navi.payment.nativepayment.db.NaviPaymentAppDatabase import com.navi.payment.nativepayment.db.NaviPaymentAppDatabase.Companion.NAVI_PAYMENT_DATABASE_NAME import dagger.Module @@ -30,10 +31,17 @@ object NaviPaymentDbModule { NaviPaymentAppDatabase::class.java, NAVI_PAYMENT_DATABASE_NAME ) + .addMigrations(NAVI_PAYMENT_APP_DATABASE_MIGRATION_1_2) + .fallbackToDestructiveMigration() .build() @Singleton @Provides fun providesTransactionStatusDao(naviPaymentAppDatabase: NaviPaymentAppDatabase) = naviPaymentAppDatabase.transactionStatusRequestDao() + + @Singleton + @Provides + fun providesBankListDao(naviPaymentAppDatabase: NaviPaymentAppDatabase) = + naviPaymentAppDatabase.bankListDao() } diff --git a/android/navi-payment/src/main/java/com/navi/payment/nativepayment/db/model/BankListEntity.kt b/android/navi-payment/src/main/java/com/navi/payment/nativepayment/db/model/BankListEntity.kt new file mode 100644 index 0000000000..c4faf7e57e --- /dev/null +++ b/android/navi-payment/src/main/java/com/navi/payment/nativepayment/db/model/BankListEntity.kt @@ -0,0 +1,35 @@ +/* + * + * * Copyright © 2024 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.navi.common.utils.Constants.DEFAULT +import com.navi.payment.nativepayment.db.NaviPaymentAppDatabase +import com.navi.payment.nativepayment.db.converter.BanksEntityConverter + +@Entity(tableName = NaviPaymentAppDatabase.BANKS_TABLE) +data class BankListEntity( + @PrimaryKey(autoGenerate = true) @ColumnInfo(name = "id") var id: Int = 0, + @ColumnInfo(name = "vertical") val vertical: String, + @ColumnInfo(name = "category") val category: String = DEFAULT, + @TypeConverters(BanksEntityConverter::class) + @ColumnInfo(name = "bankList") + val bankList: List, + @ColumnInfo(name = "updatedAt") val updatedAt: Long +) + +data class BankEntity( + @ColumnInfo(name = "code") val code: String, + @ColumnInfo(name = "name") val name: String, + @ColumnInfo(name = "popular") val popular: Boolean, + @ColumnInfo(name = "iconUrl") val iconUrl: String, + @ColumnInfo(name = "accountNumber") val accountNumber: String? = null +) diff --git a/android/navi-payment/src/main/java/com/navi/payment/nativepayment/model/NetBankingPaymentInstrument.kt b/android/navi-payment/src/main/java/com/navi/payment/nativepayment/model/NetBankingPaymentInstrument.kt index 636ef13d1c..dbab9dbb01 100644 --- a/android/navi-payment/src/main/java/com/navi/payment/nativepayment/model/NetBankingPaymentInstrument.kt +++ b/android/navi-payment/src/main/java/com/navi/payment/nativepayment/model/NetBankingPaymentInstrument.kt @@ -9,6 +9,7 @@ package com.navi.payment.nativepayment.model import com.google.gson.annotations.SerializedName import com.navi.base.utils.EMPTY +import com.navi.payment.nativepayment.db.model.BankEntity data class NetBankingPaymentInstrument( @SerializedName("instrumentName") override val instrumentName: String?, @@ -34,3 +35,13 @@ data class BankDetails( @SerializedName("isBankPopular") val isBankPopular: Boolean = false, @SerializedName("bankAccountNumber") val bankAccountNumber: String = EMPTY ) + +fun BankDetails.toBankEntity(): BankEntity { + return BankEntity( + code = bankCode, + name = bankTitle, + popular = isBankPopular, + iconUrl = bankIcon, + accountNumber = bankAccountNumber + ) +} diff --git a/android/navi-payment/src/main/java/com/navi/payment/nativepayment/presentation/reducer/NPSScreenContract.kt b/android/navi-payment/src/main/java/com/navi/payment/nativepayment/presentation/reducer/NPSScreenContract.kt index 572b23fcb9..97b54925c2 100644 --- a/android/navi-payment/src/main/java/com/navi/payment/nativepayment/presentation/reducer/NPSScreenContract.kt +++ b/android/navi-payment/src/main/java/com/navi/payment/nativepayment/presentation/reducer/NPSScreenContract.kt @@ -13,7 +13,7 @@ import com.navi.pay.management.common.sendmoney.model.view.EligibilityState import com.navi.pay.management.common.sendmoney.model.view.PMSSendMoneyStatus import com.navi.pay.onboarding.account.detail.model.view.LinkedAccountEntity import com.navi.pay.utils.SAVINGS_ONLY_ENABLED_ACCOUNTS -import com.navi.payment.nativepayment.model.BankDetails +import com.navi.payment.nativepayment.db.model.BankEntity import com.navi.payment.nativepayment.model.CoinDetails import com.navi.payment.nativepayment.model.CoinRewards import com.navi.payment.nativepayment.model.FomoBottomSheetCtaAction @@ -59,13 +59,18 @@ interface NPSScreenContract : NPSBaseContract { data class OnFomoSheetCtaClicked(val action: FomoBottomSheetCtaAction) : NPSScreenEvent - data class OnNetbankingBankSelected(val bankDetails: BankDetails) : NPSScreenEvent + data class OnNetbankingBankSelected(val bankEntity: BankEntity) : NPSScreenEvent } interface NPSScreenEffect : NPSBaseContract.Effect { data object NavigateToUpiIntentScreen : NPSScreenEffect - data object NavigateToNetBankingScreen : NPSScreenEffect + data class NavigateToNetBankingScreen( + val paymentAmount: Double, + val token: String, + val vertical: String, + val category: String + ) : NPSScreenEffect data class StartNaviUpiAddAccount( val addAccountType: String = SAVINGS_ONLY_ENABLED_ACCOUNTS @@ -85,7 +90,13 @@ interface NPSScreenContract : NPSBaseContract { data object ScrollToTop : NPSScreenEffect - data class NavigateToNextScreen(val direction: String) : NPSScreenEffect + data class NavigateToNextScreen( + val direction: String, + val paymentAmount: Double, + val token: String, + val vertical: String, + val category: String + ) : NPSScreenEffect } } @@ -135,6 +146,6 @@ data class InstrumentIconDetails( data class NetbankingDetails( val requireRedirection: Boolean, - val availableBanks: List, - val selectedBank: BankDetails? + val availableBanks: List, + val selectedBank: BankEntity? ) diff --git a/android/navi-payment/src/main/java/com/navi/payment/nativepayment/presentation/reducer/NetBankingScreenContract.kt b/android/navi-payment/src/main/java/com/navi/payment/nativepayment/presentation/reducer/NetBankingScreenContract.kt index ae70d22efe..a6dd62daa1 100644 --- a/android/navi-payment/src/main/java/com/navi/payment/nativepayment/presentation/reducer/NetBankingScreenContract.kt +++ b/android/navi-payment/src/main/java/com/navi/payment/nativepayment/presentation/reducer/NetBankingScreenContract.kt @@ -10,7 +10,7 @@ package com.navi.payment.nativepayment.presentation.reducer import androidx.compose.ui.text.input.TextFieldValue import com.navi.common.basemvi.UiEvent import com.navi.common.basemvi.UiState -import com.navi.payment.nativepayment.model.BankDetails +import com.navi.payment.nativepayment.db.model.BankEntity import com.navi.payment.paymentscreen.model.PayNowResponse import org.json.JSONObject @@ -22,18 +22,18 @@ interface NetBankingScreenContract : data class NetBankingScreenState( val searchQuery: TextFieldValue, val isLoading: Boolean, - val allBanksList: List?, - val popularBanksList: List?, + val allBanksList: List?, + val popularBanksList: List?, val showPopularBanks: Boolean, val paymentAmount: Double, - val selectedBank: BankDetails?, + val selectedBank: BankEntity?, val isSelectedFromPopularBanks: Boolean, ) : UiState sealed interface NetBankingScreenEvent : UiEvent { data class OnSearchQueryChange(val query: TextFieldValue) : NetBankingScreenEvent - data class OnBankSelected(val bankDetails: BankDetails, val isPopularBank: Boolean) : + data class OnBankSelected(val bankEntity: BankEntity, val isPopularBank: Boolean) : NetBankingScreenEvent data object OnClearSearchQueryClicked : NetBankingScreenEvent diff --git a/android/navi-payment/src/main/java/com/navi/payment/nativepayment/repository/BanksRepository.kt b/android/navi-payment/src/main/java/com/navi/payment/nativepayment/repository/BanksRepository.kt new file mode 100644 index 0000000000..cfacddd181 --- /dev/null +++ b/android/navi-payment/src/main/java/com/navi/payment/nativepayment/repository/BanksRepository.kt @@ -0,0 +1,37 @@ +/* + * + * * Copyright © 2024 by Navi Technologies Limited + * * All rights reserved. Strictly confidential + * + */ + +package com.navi.payment.nativepayment.repository + +import com.navi.common.network.retrofit.ResponseCallback +import com.navi.payment.nativepayment.db.dao.BankListDao +import com.navi.payment.nativepayment.db.model.BankListEntity +import javax.inject.Inject + +class BanksRepository @Inject constructor(private val bankListDao: BankListDao) : + ResponseCallback() { + + suspend fun insertBanksList(bankListEntity: BankListEntity) { + bankListDao.insert(bankListEntity) + } + + suspend fun getBanksList(vertical: String, category: String): BankListEntity? { + return bankListDao.getBanksForVerticalAndCategory(vertical, category) + } + + suspend fun clearBanksListForVerticalAndCategory(vertical: String, category: String) { + bankListDao.deleteBanksForVerticalAndCategory(vertical, category) + } + + suspend fun clearBanksListForVertical(vertical: String) { + bankListDao.deleteBanksForVertical(vertical) + } + + suspend fun clearBanksList() { + bankListDao.deleteAll() + } +} diff --git a/android/navi-payment/src/main/java/com/navi/payment/nativepayment/screens/NPSScreen.kt b/android/navi-payment/src/main/java/com/navi/payment/nativepayment/screens/NPSScreen.kt index 72661778c3..8721033379 100644 --- a/android/navi-payment/src/main/java/com/navi/payment/nativepayment/screens/NPSScreen.kt +++ b/android/navi-payment/src/main/java/com/navi/payment/nativepayment/screens/NPSScreen.kt @@ -437,10 +437,8 @@ private fun NpsMainScreen( onAction = onEvent, requireRedirection = screenState.netBankingDetails.requireRedirection, - bankDetailsList = - screenState.netBankingDetails.availableBanks, - selectedBankDetails = - screenState.netBankingDetails.selectedBank + banksList = screenState.netBankingDetails.availableBanks, + selectedBank = screenState.netBankingDetails.selectedBank ) Spacer(modifier = Modifier.height(32.dp)) } @@ -825,7 +823,14 @@ private fun EffectsHandler( ) } is NPSScreenContract.NPSScreenEffect.NavigateToNetBankingScreen -> { - navigator.navigate(NetBankingScreenRootDestination) + navigator.navigate( + NetBankingScreenRootDestination( + paymentAmount = effect.paymentAmount, + token = effect.token, + vertical = effect.vertical, + category = effect.category + ) + ) } is NPSScreenContract.NPSScreenEffect.NavigateToNextScreen -> { when (effect.direction) { @@ -836,7 +841,14 @@ private fun EffectsHandler( navigator.navigate(CardDetailScreenRootDestination) } NaviPaymentScreenType.NET_BANKING.name -> { - navigator.navigate(NetBankingScreenRootDestination) + navigator.navigate( + NetBankingScreenRootDestination( + paymentAmount = effect.paymentAmount, + token = effect.token, + vertical = effect.vertical, + category = effect.category + ) + ) } else -> {} } diff --git a/android/navi-payment/src/main/java/com/navi/payment/nativepayment/screens/NetBankingScreen.kt b/android/navi-payment/src/main/java/com/navi/payment/nativepayment/screens/NetBankingScreen.kt index 6271a008aa..db42dc478c 100644 --- a/android/navi-payment/src/main/java/com/navi/payment/nativepayment/screens/NetBankingScreen.kt +++ b/android/navi-payment/src/main/java/com/navi/payment/nativepayment/screens/NetBankingScreen.kt @@ -83,6 +83,10 @@ import org.json.JSONObject fun NetBankingScreenRoot( naviPaymentActivity: NaviPaymentActivity, navigator: DestinationsNavigator, + paymentAmount: Double, + token: String, + vertical: String, + category: String, netBankingViewModel: NetBankingViewModel = hiltViewModel(), naviPaymentAnalytics: NaviPaymentAnalytics.NetBankingScreen = NaviPaymentAnalytics.INSTANCE.NetBankingScreen() @@ -180,7 +184,7 @@ fun NetBankingScreen( onBankSelected = { bankDetails, isPopularBank -> onEvent( NetBankingScreenEvent.OnBankSelected( - bankDetails = bankDetails, + bankEntity = bankDetails, isPopularBank = isPopularBank ) ) diff --git a/android/navi-payment/src/main/java/com/navi/payment/nativepayment/sharedviewmodel/NaviCheckoutViewModel.kt b/android/navi-payment/src/main/java/com/navi/payment/nativepayment/sharedviewmodel/NaviCheckoutViewModel.kt index f3987543b6..d34f793186 100644 --- a/android/navi-payment/src/main/java/com/navi/payment/nativepayment/sharedviewmodel/NaviCheckoutViewModel.kt +++ b/android/navi-payment/src/main/java/com/navi/payment/nativepayment/sharedviewmodel/NaviCheckoutViewModel.kt @@ -10,10 +10,12 @@ package com.navi.payment.nativepayment.sharedviewmodel import androidx.lifecycle.viewModelScope import com.google.gson.Gson import com.navi.base.AppServiceManager +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.pay.common.setup.NaviPayManager import com.navi.payment.model.common.PaymentSdkInitParams import com.navi.payment.model.initiatesdk.PaymentPrefetchDetail @@ -27,9 +29,11 @@ import com.navi.payment.nativepayment.dataprovider.PaymentDataProvider.Companion import com.navi.payment.nativepayment.dataprovider.PaymentDataProvider.Companion.SCREEN_TYPE import com.navi.payment.nativepayment.dataprovider.PaymentDataProvider.Companion.SDK_PARAMS 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.S2sPaymentMethodResponse import com.navi.payment.nativepayment.repository.PaymentRepository +import com.navi.payment.nativepayment.usecase.NetBankingUseCase import com.navi.payment.nativepayment.utils.getPayloadBasedOnType import com.navi.payment.nativepayment.utils.toGenericErrorResponse import com.navi.payment.nativepayment.viewmodel.NaviPaymentBaseVM @@ -57,6 +61,7 @@ constructor( private val naviPayManager: NaviPayManager, private val paymentDataProvider: PaymentDataProvider, @PaymentsSdkRetrofit private val deserializer: Gson, + private val netBankingUseCase: NetBankingUseCase, private val litmusExperimentsUseCase: LitmusExperimentsUseCase ) : NaviPaymentBaseVM(NaviPaymentAnalyticScreenName.CHECKOUT_SCREEN.screenName) { @@ -67,7 +72,7 @@ constructor( paymentSdkInitParams: PaymentSdkInitParams, ) { viewModelScope.safeLaunch { - fetchPmsExperimentsData(paymentSdkInitParams?.paymentSource.orEmpty()) + fetchPmsExperimentsData(paymentSdkInitParams.paymentSource.orEmpty()) clearPaymentData() paymentDataProvider.add(PAYMENT_INITIATE_START_TIME, System.currentTimeMillis()) _paymentResponse.emit(false) @@ -83,6 +88,11 @@ constructor( ) { connectedAccountsData?.isOnboarded = true } + val isBankListAvailable = + netBankingUseCase.isBanksListAvailable( + paymentSdkInitParams.paymentSource.orEmpty(), + paymentSdkInitParams.categoryId.orElse(DEFAULT) + ) val paymentPrefetchMethodRequest = PaymentPrefetchMethodRequest( prefetchDetails = @@ -93,7 +103,8 @@ constructor( ), getInstalledUpiApps() ), - previousScreenName = paymentSdkInitParams.paymentSource + previousScreenName = paymentSdkInitParams.paymentSource, + shouldFetchBankList = isBankListAvailable.not() ) val response = paymentRepository.fetchScreenData( @@ -106,6 +117,12 @@ constructor( ) if (response.isSuccessWithData()) { paymentDataProvider.updatePaymentMethodResponse(response.data) + if (isBankListAvailable.not()) { + saveNetBankingList( + paymentSdkInitParams = paymentSdkInitParams, + response = response.data as? S2sPaymentMethodResponse + ) + } } else { val error = response.toGenericErrorResponse() paymentDataProvider.add( @@ -151,6 +168,30 @@ constructor( } } + private suspend fun saveNetBankingList( + paymentSdkInitParams: PaymentSdkInitParams, + response: S2sPaymentMethodResponse? + ) { + response?.let { + val vertical = paymentSdkInitParams.paymentSource.orEmpty() + val category = paymentSdkInitParams.categoryId.orElse(DEFAULT) + val bankList = + it.methodDetails + ?.availablePaymentInstruments + ?.filterIsInstance() + ?.firstOrNull() + ?.instrument + ?.availableBanks + bankList?.let { list -> + netBankingUseCase.insertBanksList( + vertical = vertical, + category = category, + bankList = list + ) + } + } + } + private fun clearPaymentData() { paymentDataProvider.remove(GET_METHOD_RESPONSE) paymentDataProvider.remove(ERROR_RESPONSE) diff --git a/android/navi-payment/src/main/java/com/navi/payment/nativepayment/usecase/NetBankingUseCase.kt b/android/navi-payment/src/main/java/com/navi/payment/nativepayment/usecase/NetBankingUseCase.kt new file mode 100644 index 0000000000..986088fdc6 --- /dev/null +++ b/android/navi-payment/src/main/java/com/navi/payment/nativepayment/usecase/NetBankingUseCase.kt @@ -0,0 +1,95 @@ +/* + * + * * Copyright © 2024 by Navi Technologies Limited + * * All rights reserved. Strictly confidential + * + */ + +package com.navi.payment.nativepayment.usecase + +import com.navi.common.firebaseremoteconfig.FirebaseRemoteConfigHelper +import com.navi.common.utils.Constants.DEFAULT +import com.navi.payment.nativepayment.db.model.BankEntity +import com.navi.payment.nativepayment.db.model.BankListEntity +import com.navi.payment.nativepayment.model.BankDetails +import com.navi.payment.nativepayment.model.toBankEntity +import com.navi.payment.nativepayment.repository.BanksRepository +import javax.inject.Inject + +class NetBankingUseCase +@Inject +constructor( + private val banksRepository: BanksRepository, +) { + + suspend fun insertBanksList( + vertical: String, + category: String = DEFAULT, + bankList: List + ) { + val verticalCategory = category.ifEmpty { DEFAULT } + + val bankListEntity = + BankListEntity( + vertical = vertical, + category = verticalCategory, + bankList = bankList.map { it.toBankEntity() }, + updatedAt = System.currentTimeMillis() + ) + + banksRepository.insertBanksList(bankListEntity) + } + + suspend fun isBanksListAvailable(vertical: String, category: String = DEFAULT): Boolean { + + val verticalCategory = category.ifEmpty { DEFAULT } + + val bankListEntity = + banksRepository.getBanksList(vertical = vertical, category = verticalCategory) + + val isValidBankList = isValidBankList(bankListEntity) + + if (isValidBankList.not()) { + banksRepository.clearBanksListForVerticalAndCategory( + vertical = vertical, + category = verticalCategory + ) + } + + return isValidBankList + } + + suspend fun getBanksList(vertical: String, category: String = DEFAULT): List? { + val verticalCategory = category.ifEmpty { DEFAULT } + + val bankListEntity = + banksRepository.getBanksList(vertical = vertical, category = verticalCategory) + + return bankListEntity?.bankList + } + + suspend fun clearBankList(vertical: String, category: String = DEFAULT) { + val verticalCategory = category.ifEmpty { DEFAULT } + + banksRepository.clearBanksListForVerticalAndCategory( + vertical = vertical, + category = verticalCategory + ) + } + + suspend fun clearBankListForVertical(vertical: String) { + banksRepository.clearBanksListForVertical(vertical = vertical) + } + + private fun isValidBankList(bankListEntity: BankListEntity?): Boolean { + val netBankingCachingTtl = + FirebaseRemoteConfigHelper.getLong( + key = FirebaseRemoteConfigHelper.NAVI_PMT_NET_BANKING_CACHE_TTL, + defaultValue = 86400000L + ) + + return bankListEntity?.updatedAt?.let { + System.currentTimeMillis() - it < netBankingCachingTtl + } ?: false + } +} diff --git a/android/navi-payment/src/main/java/com/navi/payment/nativepayment/viewmodel/NPSViewModel.kt b/android/navi-payment/src/main/java/com/navi/payment/nativepayment/viewmodel/NPSViewModel.kt index 6abcbcb626..9753d4607f 100644 --- a/android/navi-payment/src/main/java/com/navi/payment/nativepayment/viewmodel/NPSViewModel.kt +++ b/android/navi-payment/src/main/java/com/navi/payment/nativepayment/viewmodel/NPSViewModel.kt @@ -20,6 +20,7 @@ import com.navi.base.utils.ResourceProvider import com.navi.base.utils.isNotNull import com.navi.base.utils.isNotNullAndNotEmpty import com.navi.base.utils.isNull +import com.navi.base.utils.orElse import com.navi.base.utils.orFalse import com.navi.base.utils.orTrue import com.navi.base.utils.orZero @@ -31,6 +32,7 @@ import com.navi.common.upi.NAVI_PAY_RESPONSE import com.navi.common.upi.NaviPayAction import com.navi.common.upi.TYPE import com.navi.common.upi.UpiDataType +import com.navi.common.utils.Constants.DEFAULT import com.navi.common.utils.stringToJsonObject import com.navi.common.utils.toJsonObject import com.navi.pay.common.model.view.NaviPayErrorConfig @@ -54,9 +56,9 @@ import com.navi.payment.nativepayment.dataprovider.PaymentDataProvider.Companion import com.navi.payment.nativepayment.dataprovider.PaymentDataProvider.Companion.SCREEN_TYPE import com.navi.payment.nativepayment.dataprovider.PaymentDataProvider.Companion.TRANSACTION_REFERENCE_ID import com.navi.payment.nativepayment.dataprovider.getMpinSetAction +import com.navi.payment.nativepayment.db.model.BankEntity import com.navi.payment.nativepayment.db.model.TransactionStatusRequestEntity import com.navi.payment.nativepayment.model.AvailableCardTypes -import com.navi.payment.nativepayment.model.BankDetails import com.navi.payment.nativepayment.model.BasePaymentInstrument import com.navi.payment.nativepayment.model.CardPaymentInstrument import com.navi.payment.nativepayment.model.CardPaymentInstrumentDetails @@ -84,6 +86,7 @@ import com.navi.payment.nativepayment.presentation.reducer.NaviUpiPaymentState import com.navi.payment.nativepayment.presentation.reducer.NetbankingDetails import com.navi.payment.nativepayment.presentation.reducer.UpiCollectOptionState import com.navi.payment.nativepayment.repository.PaymentRepository +import com.navi.payment.nativepayment.usecase.NetBankingUseCase import com.navi.payment.nativepayment.usecase.PmsLinkedAccountUseCase import com.navi.payment.nativepayment.utils.getDiscountAdjustedAmount import com.navi.payment.nativepayment.utils.getEligibilityStateForUpiLiteAccount @@ -137,6 +140,7 @@ class NPSViewModel constructor( savedStateHandle: SavedStateHandle, private val naviPayManager: NaviPayManager, + private val netBankingUseCase: NetBankingUseCase, private val transactionStatusUseCase: TransactionStatusUseCase, @PaymentsSdkRetrofit private val deserializer: Gson, resourceProvider: ResourceProvider, @@ -349,7 +353,7 @@ constructor( return updatedState } - private fun processNetBankingPaymentInstrument( + private suspend fun processNetBankingPaymentInstrument( updatedState: NPSScreenContract.NPSScreenState, instrumentDetails: NetBankingPaymentInstumentDetails ): NPSScreenContract.NPSScreenState { @@ -357,11 +361,15 @@ constructor( val requireRedirection = instrumentDetails.requireRedirection val preferredBankList = if (requireRedirection.not()) { - instrumentDetails.availableBanks + netBankingUseCase + .getBanksList( + vertical = paymentSdkInitParams?.paymentSource.orEmpty(), + category = paymentSdkInitParams?.categoryId.orElse(DEFAULT) + ) + .orEmpty() } else { emptyList() } - paymentDataProvider.updateNetBankingBanksResponse(instrumentDetails.availableBanks) return if (iconUrl.isNotNullAndNotEmpty()) updatedState.copy( iconDetails = @@ -604,7 +612,12 @@ constructor( } else { onDiscountApplyLottieComplete() _effect.emit( - NPSScreenContract.NPSScreenEffect.NavigateToNetBankingScreen + NPSScreenContract.NPSScreenEffect.NavigateToNetBankingScreen( + paymentAmount = npsScreenState.npsBaseState.paymentAmount, + token = paymentSdkInitParams?.token.orEmpty(), + vertical = paymentSdkInitParams?.paymentSource.orEmpty(), + category = paymentSdkInitParams?.categoryId.orElse(DEFAULT) + ) ) } } @@ -616,7 +629,7 @@ constructor( } is NPSScreenContract.NPSScreenEvent.OnNetbankingBankSelected -> { if (isInteractionAllowed) { - handleOnNetBankingSelected(event.bankDetails) + handleOnNetBankingSelected(event.bankEntity) } } else -> { @@ -626,11 +639,10 @@ constructor( } } - private suspend fun handleOnNetBankingSelected(bankDetails: BankDetails) { + private suspend fun handleOnNetBankingSelected(bankEntity: BankEntity) { _state.update { npsScreenState.copy( - netBankingDetails = - npsScreenState.netBankingDetails.copy(selectedBank = bankDetails) + netBankingDetails = npsScreenState.netBankingDetails.copy(selectedBank = bankEntity) ) } handlePayNowClicked() @@ -664,7 +676,11 @@ constructor( onDiscountApplyLottieComplete() _effect.emit( NPSScreenContract.NPSScreenEffect.NavigateToNextScreen( - fomoBottomSheetRedirection.orEmpty() + direction = fomoBottomSheetRedirection.orEmpty(), + paymentAmount = npsScreenState.npsBaseState.paymentAmount, + token = paymentSdkInitParams?.token.orEmpty(), + vertical = paymentSdkInitParams?.paymentSource.orEmpty(), + category = paymentSdkInitParams?.categoryId.orElse(DEFAULT) ) ) } @@ -869,7 +885,7 @@ constructor( methodName = NetBankingPaymentInstrument.PAYMENT_INSTRUMENT_TYPE, selectedMethodDetails = SelectedMethodDetails( - bankName = npsScreenState.netBankingDetails.selectedBank?.bankCode + bankName = npsScreenState.netBankingDetails.selectedBank?.code ) ) } else if (npsScreenState.npsBaseState.selectedUpiApp != null) { @@ -1028,6 +1044,12 @@ constructor( } } else { showLoader(false) + _state.update { + npsScreenState.copy( + netBankingDetails = + npsScreenState.netBankingDetails.copy(selectedBank = null) + ) + } updateBottomSheetUIState(false) handleError(response = response, errorReason = PMSErrorReason.PayNowError) } diff --git a/android/navi-payment/src/main/java/com/navi/payment/nativepayment/viewmodel/NetBankingViewModel.kt b/android/navi-payment/src/main/java/com/navi/payment/nativepayment/viewmodel/NetBankingViewModel.kt index 4f59b5fae9..7dba809712 100644 --- a/android/navi-payment/src/main/java/com/navi/payment/nativepayment/viewmodel/NetBankingViewModel.kt +++ b/android/navi-payment/src/main/java/com/navi/payment/nativepayment/viewmodel/NetBankingViewModel.kt @@ -8,26 +8,26 @@ package com.navi.payment.nativepayment.viewmodel import androidx.compose.ui.text.input.TextFieldValue +import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import com.google.gson.Gson import com.navi.base.utils.EMPTY +import com.navi.base.utils.orElse +import com.navi.base.utils.orZero import com.navi.common.network.models.RepoResult import com.navi.common.network.models.isSuccessWithData -import com.navi.payment.model.common.PaymentSdkInitParams +import com.navi.common.utils.Constants.DEFAULT import com.navi.payment.model.common.SignalPaymentData import com.navi.payment.nativepayment.NaviPaymentAnalyticScreenName import com.navi.payment.nativepayment.NaviPaymentAnalytics -import com.navi.payment.nativepayment.dataprovider.PaymentDataProvider -import com.navi.payment.nativepayment.dataprovider.PaymentDataProvider.Companion.PAYMENT_ORDER_REFERENCE_ID -import com.navi.payment.nativepayment.dataprovider.PaymentDataProvider.Companion.TRANSACTION_REFERENCE_ID -import com.navi.payment.nativepayment.model.BankDetails +import com.navi.payment.nativepayment.db.model.BankEntity import com.navi.payment.nativepayment.model.NetBankingPaymentInstrument -import com.navi.payment.nativepayment.model.S2sPaymentMethodResponse import com.navi.payment.nativepayment.presentation.reducer.NetBankingScreenContract import com.navi.payment.nativepayment.presentation.reducer.NetBankingScreenEffect import com.navi.payment.nativepayment.presentation.reducer.NetBankingScreenEvent import com.navi.payment.nativepayment.presentation.reducer.NetBankingScreenState import com.navi.payment.nativepayment.repository.PaymentRepository +import com.navi.payment.nativepayment.usecase.NetBankingUseCase import com.navi.payment.network.util.PaymentsSdkRetrofit import com.navi.payment.paymentscreen.model.ExternalPayNowResponse import com.navi.payment.paymentscreen.model.PMSErrorReason @@ -58,8 +58,9 @@ import org.json.JSONObject class NetBankingViewModel @Inject constructor( - private val paymentDataProvider: PaymentDataProvider, + savedStateHandle: SavedStateHandle, private val paymentRepository: PaymentRepository, + private val netBankingUseCase: NetBankingUseCase, @PaymentsSdkRetrofit private val deserializer: Gson, ) : NaviPaymentBaseVM(NaviPaymentAnalyticScreenName.NET_BANKING_SCREEN.screenName), @@ -78,22 +79,24 @@ constructor( override val effect: SharedFlow get() = _effect + private val paymentAmount = savedStateHandle.get("paymentAmount").orZero() + private val token = savedStateHandle.get("token").orEmpty() + private val vertical = savedStateHandle.get("vertical").orEmpty() + private val category = savedStateHandle.get("category").orElse(DEFAULT) private var payNowResponse: PayNowResponse? = null - var paymentSdkInitParams: PaymentSdkInitParams? = null private val baseAnalyticsParams = mutableMapOf() private val netBankingAnalytics = NaviPaymentAnalytics.INSTANCE.NetBankingScreen() - private var sortedAllBanksList: List = emptyList() + private var sortedAllBanksList: List = emptyList() init { recordScreenLandTime(screenName) viewModelScope.safeLaunch(Dispatchers.IO) { - paymentSdkInitParams = getSDKInitParams() baseAnalyticsParams.apply { - put("token", paymentSdkInitParams?.token.orEmpty()) - put("source", paymentSdkInitParams?.paymentSource.orEmpty()) - put("category_id", paymentSdkInitParams?.categoryId.orEmpty()) + put("token", token) + put("source", vertical) + put("category_id", category) } fetchBanksList() handleOnSearchQueryChange() @@ -115,11 +118,11 @@ constructor( private fun fetchBanksList() { viewModelScope.safeLaunch { - paymentDataProvider.netBankingBankDetails.collectLatest { bankDetails -> - if (bankDetails.isEmpty()) { + netBankingUseCase.getBanksList(vertical, category)?.let { banksList -> + if (banksList.isEmpty()) { handleEmptyBanksList() } else { - handleBankDetails(bankDetails) + handleBankDetails(banksList) } } } @@ -129,27 +132,20 @@ constructor( netBankingAnalytics.emptyBanksList() } - private fun handleBankDetails(bankList: List) { - val popularBanksList = bankList.filter { it.isBankPopular } - sortedAllBanksList = bankList.sortedBy { it.bankTitle } - - val paymentMethodResponse = - paymentDataProvider.paymentMethodResponse.value as? S2sPaymentMethodResponse? + private fun handleBankDetails(bankList: List) { + val popularBanksList = bankList.filter { it.popular } + sortedAllBanksList = bankList.sortedBy { it.name } updateState( currentState.copy( isLoading = false, allBanksList = sortedAllBanksList, popularBanksList = popularBanksList, - paymentAmount = paymentMethodResponse?.methodDetails?.amount?.value ?: 0.0 + paymentAmount = paymentAmount ) ) recordScreenLatency(NaviPaymentAnalyticScreenName.NET_BANKING_SCREEN.screenName) } - private fun getSDKInitParams(): PaymentSdkInitParams? { - return (paymentDataProvider.get(PaymentDataProvider.SDK_PARAMS) as? PaymentSdkInitParams) - } - override fun onEvent(event: NetBankingScreenEvent) { when (event) { is NetBankingScreenEvent.OnSearchQueryChange -> { @@ -157,7 +153,7 @@ constructor( } is NetBankingScreenEvent.OnBankSelected -> { netBankingAnalytics.onBankSelected( - selectedBank = event.bankDetails.bankTitle, + selectedBank = event.bankEntity.name, isSelectedFromPopularBanks = event.isPopularBank, isSelectedFromFilteredList = state.value.searchQuery.text.isNotEmpty(), baseAttributes = getBaseAnalyticsParams() @@ -166,7 +162,7 @@ constructor( if (state.value.selectedBank == null) { _state.update { it.copy( - selectedBank = event.bankDetails, + selectedBank = event.bankEntity, isSelectedFromPopularBanks = event.isPopularBank ) } @@ -189,9 +185,9 @@ constructor( if (PaymentScreenUtil.isValidJuspayResponse(payload)) { _effect.emit( NetBankingScreenEffect.NavigateToLoaderScreen( - token = paymentSdkInitParams?.token.orEmpty(), + token = token, paymentAmount = state.value.paymentAmount, - source = paymentSdkInitParams?.paymentSource.orEmpty(), + source = vertical, eventPayload = payload.toString(), payNowResponse = payNowResponse!! ) @@ -220,7 +216,7 @@ constructor( private suspend fun handleFeedbackSubmit() { paymentRepository.postSdkExitSignal( - token = paymentSdkInitParams?.token.orEmpty(), + token = token, data = SignalPaymentData(), metricInfo = getPMSMetricInfo( @@ -252,7 +248,7 @@ constructor( ) val filteredBanksList = sortedAllBanksList.filter { - it.bankTitle.contains(query, ignoreCase = true) + it.name.contains(query, ignoreCase = true) } if (filteredBanksList.isEmpty()) { netBankingAnalytics.onNoBankFound( @@ -273,12 +269,12 @@ constructor( NetBankingPayNowRequest( methodName = NetBankingPaymentInstrument.PAYMENT_INSTRUMENT_TYPE, selectedMethodDetails = - SelectedMethodDetails(bankName = state.value.selectedBank?.bankCode) + SelectedMethodDetails(bankName = state.value.selectedBank?.code) ) val response = paymentRepository.postPayNow( - token = paymentSdkInitParams?.token, + token = token, data = data, metricInfo = getPMSMetricInfo( @@ -291,14 +287,6 @@ constructor( private suspend fun handlePayNowResponse(response: RepoResult) { if (response.isSuccessWithData()) { payNowResponse = response.data - paymentDataProvider.add( - TRANSACTION_REFERENCE_ID, - response.data?.transactionReferenceId.orEmpty() - ) - paymentDataProvider.add( - PAYMENT_ORDER_REFERENCE_ID, - response.data?.paymentOrderReferenceId.orEmpty() - ) when (val payNowResponseData = response.data) { is ExternalPayNowResponse -> { payNowResponseData.providerPayload?.let {