diff --git a/stripe/src/main/java/com/stripe/android/FingerprintDataRepository.kt b/stripe/src/main/java/com/stripe/android/FingerprintDataRepository.kt index 4e395b87f9b..1b35567ba9e 100644 --- a/stripe/src/main/java/com/stripe/android/FingerprintDataRepository.kt +++ b/stripe/src/main/java/com/stripe/android/FingerprintDataRepository.kt @@ -32,33 +32,37 @@ internal interface FingerprintDataRepository { ) override fun refresh() { - handler.post { - val liveData = store.get() - // LiveData observation must occur on the main thread - liveData.observeForever(object : Observer { - override fun onChanged(localFingerprintData: FingerprintData) { - if (localFingerprintData.isExpired(timestampSupplier())) { - fingerprintRequestExecutor.execute( - request = fingerprintRequestFactory.create( - localFingerprintData.guid - ) - ) { remoteFingerprintData -> - remoteFingerprintData?.let { - save(it) + if (Stripe.advancedFraudSignalsEnabled) { + handler.post { + val liveData = store.get() + // LiveData observation must occur on the main thread + liveData.observeForever(object : Observer { + override fun onChanged(localFingerprintData: FingerprintData) { + if (localFingerprintData.isExpired(timestampSupplier())) { + fingerprintRequestExecutor.execute( + request = fingerprintRequestFactory.create( + localFingerprintData.guid + ) + ) { remoteFingerprintData -> + remoteFingerprintData?.let { + save(it) + } + liveData.removeObserver(this) } + } else { + cachedFingerprintData = localFingerprintData liveData.removeObserver(this) } - } else { - cachedFingerprintData = localFingerprintData - liveData.removeObserver(this) } - } - }) + }) + } } } override fun get(): FingerprintData? { - return cachedFingerprintData + return cachedFingerprintData.takeIf { + Stripe.advancedFraudSignalsEnabled + } } override fun save(fingerprintData: FingerprintData) { diff --git a/stripe/src/main/java/com/stripe/android/Stripe.kt b/stripe/src/main/java/com/stripe/android/Stripe.kt index 857af5ec311..5d724613349 100644 --- a/stripe/src/main/java/com/stripe/android/Stripe.kt +++ b/stripe/src/main/java/com/stripe/android/Stripe.kt @@ -1612,5 +1612,18 @@ class Stripe internal constructor( */ @JvmStatic var appInfo: AppInfo? = null + + /** + * [advancedFraudSignalsEnabled] determines whether additional device data is sent to Stripe + * for fraud prevention. By default, this property is set to `true`. + * + * Disabling this setting will reduce Stripe's ability to protect your business from + * fraudulent payments. + * + * For more details on the information we collect, visit + * https://stripe.com/docs/disputes/prevention/advanced-fraud-detection + */ + @JvmStatic + var advancedFraudSignalsEnabled: Boolean = true } } diff --git a/stripe/src/test/java/com/stripe/android/FingerprintDataRepositoryTest.kt b/stripe/src/test/java/com/stripe/android/FingerprintDataRepositoryTest.kt index cecb3f0c74f..e990046564b 100644 --- a/stripe/src/test/java/com/stripe/android/FingerprintDataRepositoryTest.kt +++ b/stripe/src/test/java/com/stripe/android/FingerprintDataRepositoryTest.kt @@ -1,6 +1,7 @@ package com.stripe.android import android.content.Context +import android.os.Handler import androidx.test.core.app.ApplicationProvider import com.google.common.truth.Truth.assertThat import com.nhaarman.mockitokotlin2.KArgumentCaptor @@ -8,11 +9,13 @@ import com.nhaarman.mockitokotlin2.any import com.nhaarman.mockitokotlin2.argumentCaptor import com.nhaarman.mockitokotlin2.doNothing import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.never import com.nhaarman.mockitokotlin2.verify import com.nhaarman.mockitokotlin2.whenever import java.util.Calendar import java.util.UUID import java.util.concurrent.TimeUnit +import kotlin.test.AfterTest import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @@ -24,6 +27,11 @@ class FingerprintDataRepositoryTest { private val fingerprintRequestExecutor: FingerprintRequestExecutor = mock() private val requestExecutorCallback: KArgumentCaptor<(FingerprintData?) -> Unit> = argumentCaptor() + @AfterTest + fun after() { + Stripe.advancedFraudSignalsEnabled = true + } + @Test fun roundtrip_shouldReturnOriginalObject() { val expectedFingerprintData = createFingerprintData(elapsedTime = -5L) @@ -77,6 +85,26 @@ class FingerprintDataRepositoryTest { ).isTrue() } + @Test + fun refresh_whenAdvancedFraudSignalsDisabled_shouldNotFetchFingerprintData() { + Stripe.advancedFraudSignalsEnabled = false + + val store: FingerprintDataStore = mock() + val fingerprintRequestFactory: FingerprintRequestFactory = mock() + val handler: Handler = mock() + val repository = FingerprintDataRepository.Default( + store = store, + fingerprintRequestFactory = fingerprintRequestFactory, + fingerprintRequestExecutor = fingerprintRequestExecutor + ) + repository.refresh() + + verify(store, never()).save(any()) + verify(handler, never()).post(any()) + verify(fingerprintRequestFactory, never()).create(any()) + verify(fingerprintRequestExecutor, never()).execute(any(), any()) + } + private companion object { fun createFingerprintData(elapsedTime: Long = 0L): FingerprintData { return FingerprintData(