From 0b896e7dc5a70a2fd1f87841fb62b1d20ea0652d Mon Sep 17 00:00:00 2001 From: Tony Teate <4317686+tonyteate@users.noreply.github.com> Date: Thu, 24 Sep 2026 05:55:41 -0400 Subject: [PATCH] Enable Google Pay support in Stripe PaymentSheet behind a feature gate --- app/build.gradle | 4 +- .../java/com/kickstarter/KSApplication.kt | 8 +++ .../libs/featureflag/StatsigClient.kt | 3 +- .../libs/utils/extensions/ContextExt.kt | 39 +++++++++++- .../PaymentMethodsSettingsActivity.kt | 5 +- .../ui/activities/ProjectPageActivity.kt | 10 +++- .../ui/fragments/CrowdfundCheckoutFragment.kt | 10 +++- .../utils/GooglePayAvailabilityUtil.kt | 30 ++++++++++ .../viewmodels/PaymentMethodsViewModel.kt | 8 +++ .../projectpage/CrowdfundCheckoutViewModel.kt | 5 ++ .../LatePledgeCheckoutViewModel.kt | 10 +++- .../projectpage/RewardsSelectionViewModel.kt | 2 +- .../libs/utils/extensions/ContextExtTest.kt | 15 +++++ .../CrowdfundCheckoutViewModelTest.kt | 24 ++++++++ .../LatePledgeCheckoutViewModelTest.kt | 59 +++++++++++++++++++ .../viewmodels/PaymentMethodsViewModelTest.kt | 24 ++++++++ 16 files changed, 243 insertions(+), 13 deletions(-) create mode 100644 app/src/main/java/com/kickstarter/utils/GooglePayAvailabilityUtil.kt create mode 100644 app/src/test/java/com/kickstarter/libs/utils/extensions/ContextExtTest.kt diff --git a/app/build.gradle b/app/build.gradle index c01070ca0b..77670ffbb0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -249,7 +249,9 @@ dependencies { kapt "com.google.dagger:dagger-compiler:$dagger_version" implementation 'com.jakewharton:process-phoenix:3.0.0' implementation "com.jakewharton.timber:timber:5.0.1" - implementation 'com.stripe:stripe-android:23.17.1' + implementation 'com.stripe:stripe-android:23.20.0' + // Google Pay - to access and override `GooglePayAvailabilityClient` + implementation 'com.google.android.gms:play-services-wallet:20.0.0' final okhttp_version = '5.1.0' implementation "com.squareup.okhttp3:logging-interceptor:$okhttp_version" diff --git a/app/src/main/java/com/kickstarter/KSApplication.kt b/app/src/main/java/com/kickstarter/KSApplication.kt index 14166406eb..7ab8be1350 100644 --- a/app/src/main/java/com/kickstarter/KSApplication.kt +++ b/app/src/main/java/com/kickstarter/KSApplication.kt @@ -23,6 +23,7 @@ import com.kickstarter.libs.featureflag.StatsigClient import com.kickstarter.libs.featureflag.StatsigException import com.kickstarter.libs.utils.ApplicationLifecycleUtil import com.kickstarter.libs.utils.Secrets +import com.kickstarter.utils.GooglePayAvailabilityUtil import com.kickstarter.viewmodels.InitializationState import io.reactivex.exceptions.OnErrorNotImplementedException import io.reactivex.exceptions.UndeliverableException @@ -70,6 +71,9 @@ open class KSApplication : MultiDexApplication(), IKSApplicationComponent, Image @Inject lateinit var statsigClient: StatsigClient + @Inject + lateinit var apiEndpoint: ApiEndpoint + /** * - A CoroutineScope tied to the Application lifecycle * used to initialize dependencies that require coroutines and early on network calls. @@ -112,6 +116,10 @@ open class KSApplication : MultiDexApplication(), IKSApplicationComponent, Image plant(Timber.DebugTree()) } + if (apiEndpoint != ApiEndpoint.PRODUCTION) { + GooglePayAvailabilityUtil.overrideGooglePayAvailabilityClient() + } + createErrorHandler() initialize(applicationContext, ffClient) { this.initializeDependencies() } } diff --git a/app/src/main/java/com/kickstarter/libs/featureflag/StatsigClient.kt b/app/src/main/java/com/kickstarter/libs/featureflag/StatsigClient.kt index ebc78e56a4..2f1e33afb2 100644 --- a/app/src/main/java/com/kickstarter/libs/featureflag/StatsigClient.kt +++ b/app/src/main/java/com/kickstarter/libs/featureflag/StatsigClient.kt @@ -36,7 +36,8 @@ enum class StatsigGateKey(val key: String) { ANDROID_PRELAUNCH_SOCIAL_SHARE("android_social_share"), ANDROID_PRELAUNCH_PROJECT_STORY("android_pre-launch_project_story"), ANDROID_OPEN_CALLS("android_open_calls"), - ANDROID_REPORT_PROJECT("android_report_project") + ANDROID_REPORT_PROJECT("android_report_project"), + ANDROID_PAYMENTSHEET_GOOGLE_PAY("android_paymentsheet_google_pay"), } object StatsigExperiments { diff --git a/app/src/main/java/com/kickstarter/libs/utils/extensions/ContextExt.kt b/app/src/main/java/com/kickstarter/libs/utils/extensions/ContextExt.kt index 153668986c..a024c151d2 100644 --- a/app/src/main/java/com/kickstarter/libs/utils/extensions/ContextExt.kt +++ b/app/src/main/java/com/kickstarter/libs/utils/extensions/ContextExt.kt @@ -7,6 +7,7 @@ import android.content.Context import android.content.pm.PackageManager import android.os.Build import androidx.annotation.OptIn +import androidx.annotation.VisibleForTesting import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.runtime.Composable import androidx.core.content.ContextCompat @@ -22,6 +23,7 @@ import com.kickstarter.KSApplication import com.kickstarter.R import com.kickstarter.libs.Environment import com.kickstarter.libs.featureflag.FlagKey +import com.kickstarter.libs.utils.Secrets import com.kickstarter.libs.utils.WebUtils import com.kickstarter.ui.SharedPreferenceKey import com.kickstarter.ui.activities.AppThemes @@ -195,19 +197,52 @@ fun Context.showAlertDialog( /** * Provides the configuration for the PaymentSheet, following the specs * @see [link](https://stripe.com/docs/payments/accept-a-payment?platform=android&ui=elements#android-flowcontroller) + * + * @param googlePayCurrencyCode The three-letter ISO 4217 alphabetic currency code, e.g. "USD" or "EUR". + * Required in order to support Google Pay when processing a Setup Intent. */ -fun Context.getPaymentSheetConfiguration(userEmail: String): PaymentSheet.Configuration { +fun Context.getPaymentSheetConfiguration( + userEmail: String, + googlePayEnabled: Boolean = false, + googlePayCurrencyCode: String? = null +): PaymentSheet.Configuration { val stripeLinkEnabled = this.getEnvironment()?.featureFlagClient()?.getBoolean(FlagKey.ANDROID_STRIPE_LINK) ?: false // TODO: Wait for stripe to devise a client-side option for turning off link + val googlePayConfiguration = this.getGooglePayConfiguration(googlePayEnabled, googlePayCurrencyCode) return PaymentSheet.Configuration( merchantDisplayName = getString(R.string.app_name), allowsDelayedPaymentMethods = true, appearance = this.getPaymentSheetAppearance(), - defaultBillingDetails = PaymentSheet.BillingDetails(email = userEmail) + defaultBillingDetails = PaymentSheet.BillingDetails(email = userEmail), + googlePay = googlePayConfiguration, ) } +@VisibleForTesting +fun Context.getGooglePayConfiguration( + enabled: Boolean, + currencyCode: String? +): PaymentSheet.GooglePayConfiguration? { + val currencyCode = currencyCode?.trim()?.takeIf { it.isNotEmpty() } + + val googlePayEnvironment = + if (this.getEnvironment()?.webEndpoint() == Secrets.WebEndpoint.PRODUCTION) { + PaymentSheet.GooglePayConfiguration.Environment.Production + } else { + PaymentSheet.GooglePayConfiguration.Environment.Test + } + + // `currencyCode` is required to use Google Pay with Setup Intents + return if (enabled && currencyCode != null) { + PaymentSheet.GooglePayConfiguration( + environment = googlePayEnvironment, + countryCode = "US", + currencyCode = currencyCode + ) + } else null +} + /** * Provides the color configuration for the PaymentSheet, following the specs * @see [link](https://stripe.com/docs/elements/appearance-api?platform=android#colors-android) diff --git a/app/src/main/java/com/kickstarter/ui/activities/PaymentMethodsSettingsActivity.kt b/app/src/main/java/com/kickstarter/ui/activities/PaymentMethodsSettingsActivity.kt index 332da92a1a..09f7a35517 100644 --- a/app/src/main/java/com/kickstarter/ui/activities/PaymentMethodsSettingsActivity.kt +++ b/app/src/main/java/com/kickstarter/ui/activities/PaymentMethodsSettingsActivity.kt @@ -25,6 +25,7 @@ import com.stripe.android.paymentsheet.model.PaymentOption import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable import io.reactivex.schedulers.Schedulers +import timber.log.Timber class PaymentMethodsSettingsActivity : AppCompatActivity() { @@ -149,9 +150,11 @@ class PaymentMethodsSettingsActivity : AppCompatActivity() { } private fun flowControllerPresentPaymentOption(clientSecret: String, userEmail: String) { + val googlePayEnabled = viewModel.outputs.isPaymentSheetGooglePayEnabled() + Timber.d("googlePayEnabled: $googlePayEnabled") flowController.configureWithSetupIntent( setupIntentClientSecret = clientSecret, - configuration = this.getPaymentSheetConfiguration(userEmail), + configuration = this.getPaymentSheetConfiguration(userEmail, googlePayEnabled, "USD"), callback = ::onConfigured ) } diff --git a/app/src/main/java/com/kickstarter/ui/activities/ProjectPageActivity.kt b/app/src/main/java/com/kickstarter/ui/activities/ProjectPageActivity.kt index 02715a3289..83ce9bb183 100644 --- a/app/src/main/java/com/kickstarter/ui/activities/ProjectPageActivity.kt +++ b/app/src/main/java/com/kickstarter/ui/activities/ProjectPageActivity.kt @@ -123,6 +123,7 @@ import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable import io.reactivex.schedulers.Schedulers import kotlinx.coroutines.launch +import timber.log.Timber const val REFRESH = "refresh" @@ -718,7 +719,8 @@ class ProjectPageActivity : latePledgeCheckoutViewModel.clientSecretForNewPaymentMethod.collect { flowControllerPresentPaymentOption( it, - latePledgeCheckoutUIState.userEmail + latePledgeCheckoutUIState.userEmail, + latePledgeCheckoutUIState.projectCurrency ) } } @@ -1439,10 +1441,12 @@ class ProjectPageActivity : } } - private fun flowControllerPresentPaymentOption(clientSecret: String, userEmail: String) { + private fun flowControllerPresentPaymentOption(clientSecret: String, userEmail: String, googlePayCurrencyCode: String? = null) { + val googlePayEnabled = latePledgeCheckoutViewModel.isPaymentSheetGooglePayEnabled() + Timber.d("googlePayEnabled: $googlePayEnabled, googlePayCurrencyCode: $googlePayCurrencyCode") flowController.configureWithSetupIntent( setupIntentClientSecret = clientSecret, - configuration = getPaymentSheetConfiguration(userEmail), + configuration = getPaymentSheetConfiguration(userEmail, googlePayEnabled, googlePayCurrencyCode), callback = ::onConfigured ) } diff --git a/app/src/main/java/com/kickstarter/ui/fragments/CrowdfundCheckoutFragment.kt b/app/src/main/java/com/kickstarter/ui/fragments/CrowdfundCheckoutFragment.kt index 8f93cefb83..923b57dbbb 100644 --- a/app/src/main/java/com/kickstarter/ui/fragments/CrowdfundCheckoutFragment.kt +++ b/app/src/main/java/com/kickstarter/ui/fragments/CrowdfundCheckoutFragment.kt @@ -117,10 +117,12 @@ class CrowdfundCheckoutFragment : Fragment() { viewModel.presentPaymentSheetStates.collectAsStateWithLifecycle().value val setUpIntent = paymentSheetPresenter.setupClientId + val projectCurrencyCode = pledgeData?.projectData()?.project()?.currency() + configurePaymentSheet(paymentSheetPresenter.setupClientId) LaunchedEffect(key1 = setUpIntent) { if (setUpIntent.isNotEmpty() && email.isNotEmpty()) { - flowControllerPresentPaymentOption(setUpIntent, email) + flowControllerPresentPaymentOption(setUpIntent, email, projectCurrencyCode) } } @@ -205,11 +207,13 @@ class CrowdfundCheckoutFragment : Fragment() { return view } - private fun flowControllerPresentPaymentOption(clientSecret: String, userEmail: String) { + private fun flowControllerPresentPaymentOption(clientSecret: String, userEmail: String, googlePayCurrencyCode: String? = null) { context?.let { + val googlePayEnabled = viewModel.isPaymentSheetGooglePayEnabled() + Timber.d("googlePayEnabled: $googlePayEnabled, googlePayCurrencyCode: $googlePayCurrencyCode") flowController.configureWithSetupIntent( setupIntentClientSecret = clientSecret, - configuration = it.getPaymentSheetConfiguration(userEmail), + configuration = it.getPaymentSheetConfiguration(userEmail, googlePayEnabled, googlePayCurrencyCode), callback = ::onConfigured ) } diff --git a/app/src/main/java/com/kickstarter/utils/GooglePayAvailabilityUtil.kt b/app/src/main/java/com/kickstarter/utils/GooglePayAvailabilityUtil.kt new file mode 100644 index 0000000000..9af2f88482 --- /dev/null +++ b/app/src/main/java/com/kickstarter/utils/GooglePayAvailabilityUtil.kt @@ -0,0 +1,30 @@ +package com.kickstarter.utils + +import android.annotation.SuppressLint +import com.google.android.gms.wallet.IsReadyToPayRequest +import com.google.android.gms.wallet.PaymentsClient +import com.stripe.android.googlepaylauncher.GooglePayAvailabilityClient +import com.stripe.android.googlepaylauncher.GooglePayRepository +import kotlinx.coroutines.tasks.await +import org.json.JSONObject + +object GooglePayAvailabilityUtil { + /* This is a workaround that allows Google Pay to work in `Test` mode, pulled directly from + * the associated Issue in the Stripe SDK: https://github.com/stripe/stripe-android/issues/13968 */ + fun overrideGooglePayAvailabilityClient() { + @SuppressLint("RestrictedApi") + GooglePayRepository.googlePayAvailabilityClientFactory = + object : GooglePayAvailabilityClient.Factory { + override fun create(paymentsClient: PaymentsClient) = + object : GooglePayAvailabilityClient { + override suspend fun isReady(request: IsReadyToPayRequest): Boolean { + val relaxed = JSONObject(request.toJson()) + .apply { remove("existingPaymentMethodRequired") } + return paymentsClient + .isReadyToPay(IsReadyToPayRequest.fromJson(relaxed.toString())) + .await() + } + } + } + } +} diff --git a/app/src/main/java/com/kickstarter/viewmodels/PaymentMethodsViewModel.kt b/app/src/main/java/com/kickstarter/viewmodels/PaymentMethodsViewModel.kt index 6f0bc329ac..ba216c9842 100644 --- a/app/src/main/java/com/kickstarter/viewmodels/PaymentMethodsViewModel.kt +++ b/app/src/main/java/com/kickstarter/viewmodels/PaymentMethodsViewModel.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import com.kickstarter.DeletePaymentSourceMutation import com.kickstarter.libs.Environment +import com.kickstarter.libs.featureflag.StatsigGateKey import com.kickstarter.libs.rx.transformers.Transformers.combineLatestPair import com.kickstarter.libs.rx.transformers.Transformers.errorsV2 import com.kickstarter.libs.rx.transformers.Transformers.neverErrorV2 @@ -67,6 +68,8 @@ interface Outputs { /** Emits in case SavePaymentMethod returns success output */ fun successSaving(): Observable + + fun isPaymentSheetGooglePayEnabled(): Boolean } class PaymentMethodsViewModel(environment: Environment) : ViewModel(), PaymentMethodsAdapter.Delegate, Inputs, Outputs { @@ -89,6 +92,8 @@ class PaymentMethodsViewModel(environment: Environment) : ViewModel(), PaymentMe private val loadingConfirmed = PublishSubject.create() private val apolloClient = requireNotNull(environment.apolloClientV2()) + + private val statsigClient = requireNotNull(environment.statsigClient()) private val compositeDisposable = CompositeDisposable() val inputs: Inputs = this @@ -298,6 +303,9 @@ class PaymentMethodsViewModel(environment: Environment) : ViewModel(), PaymentMe override fun successSaving(): Observable = this.successSaving + override fun isPaymentSheetGooglePayEnabled(): Boolean = + statsigClient.configReady.value && statsigClient.checkGate(StatsigGateKey.ANDROID_PAYMENTSHEET_GOOGLE_PAY.key) + class Factory(private val environment: Environment) : ViewModelProvider.Factory { override fun create(modelClass: Class): T { return PaymentMethodsViewModel(environment) as T diff --git a/app/src/main/java/com/kickstarter/viewmodels/projectpage/CrowdfundCheckoutViewModel.kt b/app/src/main/java/com/kickstarter/viewmodels/projectpage/CrowdfundCheckoutViewModel.kt index 3661e74bef..1d1a4942ed 100644 --- a/app/src/main/java/com/kickstarter/viewmodels/projectpage/CrowdfundCheckoutViewModel.kt +++ b/app/src/main/java/com/kickstarter/viewmodels/projectpage/CrowdfundCheckoutViewModel.kt @@ -8,6 +8,7 @@ import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope import com.kickstarter.libs.Environment import com.kickstarter.libs.RefTag +import com.kickstarter.libs.featureflag.StatsigGateKey import com.kickstarter.libs.utils.RefTagUtils import com.kickstarter.libs.utils.RewardUtils import com.kickstarter.libs.utils.ThirdPartyEventValues @@ -78,6 +79,7 @@ class CrowdfundCheckoutViewModel(val environment: Environment, bundle: Bundle? = val cookieManager = requireNotNull(environment.cookieManager()) val sharedPreferences = requireNotNull(environment.sharedPreferences()) val ffClient = requireNotNull(environment.featureFlagClient()) + val statsigClient = requireNotNull(environment.statsigClient()) private var pledgeData: PledgeData? = null private var checkoutData: CheckoutData? = null // TOD potentially needs to change with user card input @@ -581,6 +583,9 @@ class CrowdfundCheckoutViewModel(val environment: Environment, bundle: Bundle? = } } + fun isPaymentSheetGooglePayEnabled(): Boolean = + statsigClient.configReady.value && statsigClient.checkGate(StatsigGateKey.ANDROID_PAYMENTSHEET_GOOGLE_PAY.key) + /** * PaymentSheet has been presented to the user, stop loading until * a new payment method is received. Will cover as well the case of diff --git a/app/src/main/java/com/kickstarter/viewmodels/projectpage/LatePledgeCheckoutViewModel.kt b/app/src/main/java/com/kickstarter/viewmodels/projectpage/LatePledgeCheckoutViewModel.kt index b4f6c4d7ee..5c368b7cc9 100644 --- a/app/src/main/java/com/kickstarter/viewmodels/projectpage/LatePledgeCheckoutViewModel.kt +++ b/app/src/main/java/com/kickstarter/viewmodels/projectpage/LatePledgeCheckoutViewModel.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope import com.kickstarter.libs.Environment +import com.kickstarter.libs.featureflag.StatsigGateKey import com.kickstarter.libs.utils.RewardUtils import com.kickstarter.libs.utils.extensions.checkoutTotalAmount import com.kickstarter.libs.utils.extensions.isNotNull @@ -60,7 +61,8 @@ data class LatePledgeCheckoutUIState( val selectedRewards: List = emptyList(), val shippingAmount: Double = 0.0, val checkoutTotal: Double = 0.0, - val isPledgeButtonEnabled: Boolean = true + val isPledgeButtonEnabled: Boolean = true, + val projectCurrency: String? = null ) class LatePledgeCheckoutViewModel(val environment: Environment) : ViewModel() { @@ -79,6 +81,8 @@ class LatePledgeCheckoutViewModel(val environment: Environment) : ViewModel() { private var stripe: Stripe = requireNotNull(environment.stripe()) + private val statsigClient = requireNotNull(environment.statsigClient()) + private var clientSecretForNewCard: String = "" private var newStoredCard: StoredCard? = null private var errorAction: (message: String?) -> Unit = {} @@ -434,6 +438,7 @@ class LatePledgeCheckoutViewModel(val environment: Environment) : ViewModel() { shippingAmount = this.pledgeData?.shippingCostIfShipping() ?: 0.0, checkoutTotal = this.pledgeData?.checkoutTotalAmount() ?: 0.0, isPledgeButtonEnabled = buttonEnabled && !isLoading, + projectCurrency = this.pledgeData?.projectData()?.project()?.currency() ) ) } @@ -549,6 +554,9 @@ class LatePledgeCheckoutViewModel(val environment: Environment) : ViewModel() { } } + fun isPaymentSheetGooglePayEnabled(): Boolean = + statsigClient.configReady.value && statsigClient.checkGate(StatsigGateKey.ANDROID_PAYMENTSHEET_GOOGLE_PAY.key) + class Factory(private val environment: Environment) : ViewModelProvider.Factory { override fun create(modelClass: Class): T { diff --git a/app/src/main/java/com/kickstarter/viewmodels/projectpage/RewardsSelectionViewModel.kt b/app/src/main/java/com/kickstarter/viewmodels/projectpage/RewardsSelectionViewModel.kt index 12e68f1d6c..3f27209cdd 100644 --- a/app/src/main/java/com/kickstarter/viewmodels/projectpage/RewardsSelectionViewModel.kt +++ b/app/src/main/java/com/kickstarter/viewmodels/projectpage/RewardsSelectionViewModel.kt @@ -110,7 +110,7 @@ class RewardsSelectionViewModel(private val environment: Environment, private va emitCurrentState() } - if (!refreshData) return +// if (!refreshData) return viewModelScope.launch(CoroutineExceptionHandler { _, throwable -> Timber.e(throwable, "CoroutineExceptionHandler") }) { mutableShippingUIState.update { previous -> diff --git a/app/src/test/java/com/kickstarter/libs/utils/extensions/ContextExtTest.kt b/app/src/test/java/com/kickstarter/libs/utils/extensions/ContextExtTest.kt new file mode 100644 index 0000000000..efa2acc45c --- /dev/null +++ b/app/src/test/java/com/kickstarter/libs/utils/extensions/ContextExtTest.kt @@ -0,0 +1,15 @@ +package com.kickstarter.libs.utils.extensions + +import com.kickstarter.KSRobolectricTestCase +import org.junit.Test + +class ContextExtTest : KSRobolectricTestCase() { + @Test + fun `test getGooglePayConfiguration()`() { + val context = application() + assertNull(context.getGooglePayConfiguration(false, "USD")) + assertNull(context.getGooglePayConfiguration(true, null)) + assertNull(context.getGooglePayConfiguration(true, " ")) + assertNotNull(context.getGooglePayConfiguration(true, "EUR")) + } +} diff --git a/app/src/test/java/com/kickstarter/viewmodels/CrowdfundCheckoutViewModelTest.kt b/app/src/test/java/com/kickstarter/viewmodels/CrowdfundCheckoutViewModelTest.kt index 8dc672717c..ba45dc6f14 100644 --- a/app/src/test/java/com/kickstarter/viewmodels/CrowdfundCheckoutViewModelTest.kt +++ b/app/src/test/java/com/kickstarter/viewmodels/CrowdfundCheckoutViewModelTest.kt @@ -6,7 +6,9 @@ import android.util.Pair import com.kickstarter.KSRobolectricTestCase import com.kickstarter.libs.Environment import com.kickstarter.libs.MockCurrentUserV2 +import com.kickstarter.libs.MockStatsigClient import com.kickstarter.libs.featureflag.FlagKey +import com.kickstarter.libs.featureflag.StatsigGateKey import com.kickstarter.libs.utils.EventName import com.kickstarter.libs.utils.extensions.checkoutTotalAmount import com.kickstarter.libs.utils.extensions.pledgeAmountTotal @@ -1605,4 +1607,26 @@ class CrowdfundCheckoutViewModelTest : KSRobolectricTestCase() { assertNull(updateData.locationId) assertNull(updateData.rewardsIds) } + + @Test + fun testIsPaymentSheetGooglePayEnabled_whenFlagIsOn() { + val statsigClient = MockStatsigClient( + context = application(), + gateMap = mapOf(StatsigGateKey.ANDROID_PAYMENTSHEET_GOOGLE_PAY.key to true) + ) + setUpEnvironment(environment().toBuilder().statsigClient(statsigClient).build()) + + assertTrue(viewModel.isPaymentSheetGooglePayEnabled()) + } + + @Test + fun testIsPaymentSheetGooglePayEnabled_whenFlagIsOff() { + val statsigClient = MockStatsigClient( + context = application(), + gateMap = mapOf(StatsigGateKey.ANDROID_PAYMENTSHEET_GOOGLE_PAY.key to false) + ) + setUpEnvironment(environment().toBuilder().statsigClient(statsigClient).build()) + + assertFalse(viewModel.isPaymentSheetGooglePayEnabled()) + } } diff --git a/app/src/test/java/com/kickstarter/viewmodels/LatePledgeCheckoutViewModelTest.kt b/app/src/test/java/com/kickstarter/viewmodels/LatePledgeCheckoutViewModelTest.kt index 6cd1177a23..c2167304cd 100644 --- a/app/src/test/java/com/kickstarter/viewmodels/LatePledgeCheckoutViewModelTest.kt +++ b/app/src/test/java/com/kickstarter/viewmodels/LatePledgeCheckoutViewModelTest.kt @@ -4,6 +4,8 @@ import android.util.Pair import com.kickstarter.KSRobolectricTestCase import com.kickstarter.libs.Environment import com.kickstarter.libs.MockCurrentUserV2 +import com.kickstarter.libs.MockStatsigClient +import com.kickstarter.libs.featureflag.StatsigGateKey import com.kickstarter.libs.utils.EventName import com.kickstarter.mock.factories.BackingFactory import com.kickstarter.mock.factories.ProjectDataFactory @@ -841,4 +843,61 @@ class LatePledgeCheckoutViewModelTest : KSRobolectricTestCase() { assertEquals(state.last().isPledgeButtonEnabled, true) assertEquals(state.last().isLoading, false) } + + @Test + fun `test isPaymentSheetGooglePayEnabled when flag is on`() { + val statsigClient = MockStatsigClient( + context = application(), + gateMap = mapOf( + StatsigGateKey.ANDROID_PAYMENTSHEET_GOOGLE_PAY.key to true + ) + ) + + setUpEnvironment(environment().toBuilder().statsigClient(statsigClient).build()) + + assertTrue(viewModel.isPaymentSheetGooglePayEnabled()) + } + + @Test + fun `test isPaymentSheetGooglePayEnabled when flag is off`() { + val statsigClient = MockStatsigClient( + context = application(), + gateMap = mapOf( + StatsigGateKey.ANDROID_PAYMENTSHEET_GOOGLE_PAY.key to false + ) + ) + + setUpEnvironment(environment().toBuilder().statsigClient(statsigClient).build()) + + assertFalse(viewModel.isPaymentSheetGooglePayEnabled()) + } + + @OptIn(ExperimentalCoroutinesApi::class) + @Test + fun `test project currency derived from pledge data`() = runTest { + val rw = RewardFactory.rewardWithShipping().toBuilder().latePledgeAmount(34.0).build() + val project = ProjectFactory.project().toBuilder() + .isInPostCampaignPledgingPhase(true) + .postCampaignPledgingEnabled(true) + .currency("EUR") + .build() + + val projectData = ProjectDataFactory.project(project = project) + val pledgeData = PledgeData.with(PledgeFlowContext.LATE_PLEDGES, projectData, rw) + + setUpEnvironment(environment()) + + val dispatcher = UnconfinedTestDispatcher(testScheduler) + val state = mutableListOf() + + backgroundScope.launch(dispatcher) { + viewModel.provideScopeAndDispatcher(this, dispatcher) + viewModel.providePledgeData(pledgeData) + viewModel.latePledgeCheckoutUIState.toList(state) + } + + advanceUntilIdle() + + assertEquals("EUR", state.last().projectCurrency) + } } diff --git a/app/src/test/java/com/kickstarter/viewmodels/PaymentMethodsViewModelTest.kt b/app/src/test/java/com/kickstarter/viewmodels/PaymentMethodsViewModelTest.kt index 2b0134ce76..bd6323d50c 100644 --- a/app/src/test/java/com/kickstarter/viewmodels/PaymentMethodsViewModelTest.kt +++ b/app/src/test/java/com/kickstarter/viewmodels/PaymentMethodsViewModelTest.kt @@ -4,6 +4,8 @@ import android.util.Pair import com.kickstarter.DeletePaymentSourceMutation import com.kickstarter.KSRobolectricTestCase import com.kickstarter.libs.Environment +import com.kickstarter.libs.MockStatsigClient +import com.kickstarter.libs.featureflag.StatsigGateKey import com.kickstarter.mock.factories.StoredCardFactory import com.kickstarter.mock.services.MockApolloClientV2 import com.kickstarter.models.Project @@ -274,4 +276,26 @@ class PaymentMethodsViewModelTest : KSRobolectricTestCase() { this.showError.assertValues(errorString) this.successSaving.assertNoValues() } + + @Test + fun testIsPaymentSheetGooglePayEnabled_whenFlagIsOn() { + val statsigClient = MockStatsigClient( + context = application(), + gateMap = mapOf(StatsigGateKey.ANDROID_PAYMENTSHEET_GOOGLE_PAY.key to true) + ) + setUpEnvironment(environment().toBuilder().statsigClient(statsigClient).build()) + + assertTrue(vm.outputs.isPaymentSheetGooglePayEnabled()) + } + + @Test + fun testIsPaymentSheetGooglePayEnabled_whenFlagIsOff() { + val statsigClient = MockStatsigClient( + context = application(), + gateMap = mapOf(StatsigGateKey.ANDROID_PAYMENTSHEET_GOOGLE_PAY.key to false) + ) + setUpEnvironment(environment().toBuilder().statsigClient(statsigClient).build()) + + assertFalse(vm.outputs.isPaymentSheetGooglePayEnabled()) + } }