Skip to content

Commit

Permalink
Remove deprecated BroadcastReceiver logic from PaymentFlowActivity
Browse files Browse the repository at this point in the history
This is a breaking change.

Implement `PaymentSessionConfig.shippingInformationValidator` and
`PaymentSessionConfig.shippingMethodsFactory` to replace logic
previously handled via broadcast.

The default implementation of `ShippingInformationValidator` will
treat all `ShippingInformation` instances as valid.

See migration guide for more info.
  • Loading branch information
mshafrir-stripe committed Dec 23, 2019
1 parent e9390bf commit e422356
Show file tree
Hide file tree
Showing 13 changed files with 119 additions and 289 deletions.
28 changes: 19 additions & 9 deletions stripe/src/main/java/com/stripe/android/PaymentSessionConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import com.stripe.android.model.ShippingMethod
import com.stripe.android.view.AddPaymentMethodActivity
import com.stripe.android.view.BillingAddressFields
import com.stripe.android.view.PaymentFlowActivity
import com.stripe.android.view.PaymentFlowExtras
import com.stripe.android.view.SelectShippingMethodWidget
import com.stripe.android.view.ShippingInfoWidget
import com.stripe.android.view.ShippingInfoWidget.CustomizableShippingField
Expand All @@ -37,7 +36,7 @@ data class PaymentSessionConfig internal constructor(
val billingAddressFields: BillingAddressFields = BillingAddressFields.None,

internal val shouldPrefetchCustomer: Boolean = true,
internal val shippingInformationValidator: ShippingInformationValidator? = null,
internal val shippingInformationValidator: ShippingInformationValidator = DefaultShippingInfoValidator(),
internal val shippingMethodsFactory: ShippingMethodsFactory? = null,
internal val windowFlags: Int? = null
) : Parcelable {
Expand All @@ -51,11 +50,10 @@ data class PaymentSessionConfig internal constructor(
}
}

if (shippingInformationValidator != null && isShippingMethodRequired) {
if (isShippingMethodRequired) {
requireNotNull(shippingMethodsFactory) {
"""
If isShippingMethodRequired is true and a ShippingInformationValidator is provided,
a ShippingMethodsFactory must also be provided.
If isShippingMethodRequired is true a ShippingMethodsFactory must also be provided.
""".trimIndent()
}
}
Expand Down Expand Up @@ -217,9 +215,7 @@ data class PaymentSessionConfig internal constructor(
}

/**
* @param shippingInformationValidator if specified, will be used to validate
* [ShippingInformation] in [PaymentFlowActivity] instead of sending a broadcast with
* [PaymentFlowExtras.EVENT_SHIPPING_INFO_SUBMITTED].
* @param shippingInformationValidator used to validate [ShippingInformation] in [PaymentFlowActivity]
*
* Note: this instance must be [Serializable].
*/
Expand Down Expand Up @@ -263,12 +259,26 @@ data class PaymentSessionConfig internal constructor(
paymentMethodTypes = paymentMethodTypes,
shouldShowGooglePay = shouldShowGooglePay,
allowedShippingCountryCodes = allowedShippingCountryCodes,
shippingInformationValidator = shippingInformationValidator,
shippingInformationValidator = shippingInformationValidator
?: DefaultShippingInfoValidator(),
shippingMethodsFactory = shippingMethodsFactory,
windowFlags = windowFlags,
billingAddressFields = billingAddressFields,
shouldPrefetchCustomer = shouldPrefetchCustomer
)
}
}

/**
* A [ShippingInformationValidator] that accepts any [ShippingInformation] as valid.
*/
private class DefaultShippingInfoValidator : ShippingInformationValidator {
override fun isValid(shippingInformation: ShippingInformation): Boolean {
return true
}

override fun getErrorMessage(shippingInformation: ShippingInformation): String {
return ""
}
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
package com.stripe.android.view

import android.app.Activity
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Bundle
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.viewpager.widget.ViewPager
import com.stripe.android.CustomerSession
import com.stripe.android.PaymentSession.Companion.EXTRA_PAYMENT_SESSION_DATA
Expand All @@ -32,8 +28,6 @@ class PaymentFlowActivity : StripeActivity() {
private lateinit var customerSession: CustomerSession
private lateinit var viewModel: PaymentFlowViewModel

private var shippingInfoSubmittedBroadcastReceiver: BroadcastReceiver? = null

private val keyboardController: KeyboardController by lazy {
KeyboardController(this)
}
Expand Down Expand Up @@ -84,40 +78,9 @@ class PaymentFlowActivity : StripeActivity() {
}
})

if (paymentSessionConfig.shippingInformationValidator == null) {
shippingInfoSubmittedBroadcastReceiver = createShippingInfoSubmittedBroadcastReceiver()
}

title = paymentFlowPagerAdapter.getPageTitle(shipping_flow_viewpager.currentItem)
}

private fun createShippingInfoSubmittedBroadcastReceiver(): BroadcastReceiver {
return object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val isShippingInfoValid = intent.getBooleanExtra(
PaymentFlowExtras.EXTRA_IS_SHIPPING_INFO_VALID,
false)
if (isShippingInfoValid) {
val shippingMethods: List<ShippingMethod>? =
intent.getParcelableArrayListExtra(
PaymentFlowExtras.EXTRA_VALID_SHIPPING_METHODS
)
val defaultShippingMethod: ShippingMethod? = intent
.getParcelableExtra(PaymentFlowExtras.EXTRA_DEFAULT_SHIPPING_METHOD)

onShippingInfoValidated(
shippingMethods.orEmpty(),
defaultShippingMethod
)
} else {
val errorMessage =
intent.getStringExtra(PaymentFlowExtras.EXTRA_SHIPPING_INFO_ERROR)
onShippingInfoError(errorMessage)
}
}
}
}

override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
shipping_flow_viewpager.currentItem = savedInstanceState.getInt(STATE_CURRENT_ITEM, 0)
Expand All @@ -132,25 +95,6 @@ class PaymentFlowActivity : StripeActivity() {
}
}

override fun onPause() {
super.onPause()

shippingInfoSubmittedBroadcastReceiver?.let {
LocalBroadcastManager.getInstance(this).unregisterReceiver(it)
}
}

override fun onResume() {
super.onResume()

shippingInfoSubmittedBroadcastReceiver?.let {
LocalBroadcastManager.getInstance(this).registerReceiver(
it,
IntentFilter(PaymentFlowExtras.EVENT_SHIPPING_INFO_PROCESSED)
)
}
}

override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putParcelable(STATE_SHIPPING_INFO, shippingInfo)
Expand Down Expand Up @@ -216,17 +160,11 @@ class PaymentFlowActivity : StripeActivity() {
)
setCommunicatingProgress(true)

val shippingInfoValidator =
paymentSessionConfig.shippingInformationValidator
if (shippingInfoValidator != null) {
validateShippingInformation(
shippingInfoValidator,
paymentSessionConfig.shippingMethodsFactory,
shippingInfo
)
} else {
broadcastShippingInfoSubmitted(shippingInfo)
}
validateShippingInformation(
paymentSessionConfig.shippingInformationValidator,
paymentSessionConfig.shippingMethodsFactory,
shippingInfo
)
}
}

Expand All @@ -247,14 +185,6 @@ class PaymentFlowActivity : StripeActivity() {
}
}

private fun broadcastShippingInfoSubmitted(shippingInformation: ShippingInformation) {
LocalBroadcastManager.getInstance(this)
.sendBroadcast(
Intent(PaymentFlowExtras.EVENT_SHIPPING_INFO_SUBMITTED)
.putExtra(PaymentFlowExtras.EXTRA_SHIPPING_INFO_DATA, shippingInformation)
)
}

private fun hasNextPage(): Boolean {
return shipping_flow_viewpager.currentItem + 1 < paymentFlowPagerAdapter.count
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,9 @@ class PaymentFlowActivityStarter :

override fun build(): Args {
return Args(
paymentSessionConfig = paymentSessionConfig
?: PaymentSessionConfig.Builder().build(),
paymentSessionConfig = requireNotNull(paymentSessionConfig) {
"PaymentFlowActivity launched without PaymentSessionConfig"
},
paymentSessionData = requireNotNull(paymentSessionData) {
"PaymentFlowActivity launched without PaymentSessionData"
},
Expand Down
51 changes: 0 additions & 51 deletions stripe/src/main/java/com/stripe/android/view/PaymentFlowExtras.kt

This file was deleted.

54 changes: 31 additions & 23 deletions stripe/src/test/java/com/stripe/android/CustomerSessionTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -53,36 +53,38 @@ class CustomerSessionTest {
@Mock
private lateinit var threadPoolExecutor: ThreadPoolExecutor

private lateinit var productUsageArgumentCaptor: KArgumentCaptor<Set<String>>
private lateinit var sourceArgumentCaptor: KArgumentCaptor<Source>
private lateinit var paymentMethodArgumentCaptor: KArgumentCaptor<PaymentMethod>
private lateinit var paymentMethodsArgumentCaptor: KArgumentCaptor<List<PaymentMethod>>
private lateinit var customerArgumentCaptor: KArgumentCaptor<Customer>
private lateinit var intentArgumentCaptor: KArgumentCaptor<Intent>
private lateinit var requestOptionsArgumentCaptor: KArgumentCaptor<ApiRequest.Options>
private val productUsageArgumentCaptor: KArgumentCaptor<Set<String>> by lazy {
argumentCaptor<Set<String>>()
}
private val sourceArgumentCaptor: KArgumentCaptor<Source> by lazy {
argumentCaptor<Source>()
}
private val paymentMethodArgumentCaptor: KArgumentCaptor<PaymentMethod> by lazy {
argumentCaptor<PaymentMethod>()
}
private val paymentMethodsArgumentCaptor: KArgumentCaptor<List<PaymentMethod>> by lazy {
argumentCaptor<List<PaymentMethod>>()
}
private val customerArgumentCaptor: KArgumentCaptor<Customer> by lazy {
argumentCaptor<Customer>()
}
private val intentArgumentCaptor: KArgumentCaptor<Intent> by lazy {
argumentCaptor<Intent>()
}
private val requestOptionsArgumentCaptor: KArgumentCaptor<ApiRequest.Options> by lazy {
argumentCaptor<ApiRequest.Options>()
}

private val ephemeralKeyProvider: TestEphemeralKeyProvider = TestEphemeralKeyProvider()

private val activityScenarioFactory: ActivityScenarioFactory by lazy {
ActivityScenarioFactory(ApplicationProvider.getApplicationContext())
}

private fun getCustomerEphemeralKey(key: String): EphemeralKey {
return EphemeralKeyJsonParser().parse(JSONObject(key))
}

@BeforeTest
fun setup() {
MockitoAnnotations.initMocks(this)

productUsageArgumentCaptor = argumentCaptor()
sourceArgumentCaptor = argumentCaptor()
paymentMethodArgumentCaptor = argumentCaptor()
paymentMethodsArgumentCaptor = argumentCaptor()
customerArgumentCaptor = argumentCaptor()
intentArgumentCaptor = argumentCaptor()
requestOptionsArgumentCaptor = argumentCaptor()

`when`<Customer>(stripeRepository.retrieveCustomer(any(), any()))
.thenReturn(FIRST_CUSTOMER, SECOND_CUSTOMER)

Expand Down Expand Up @@ -586,6 +588,9 @@ class CustomerSessionTest {
val customerSession = createCustomerSession(null)
CustomerSession.instance = customerSession

val config = PaymentSessionFixtures.CONFIG.copy(
allowedShippingCountryCodes = emptySet()
)
activityScenarioFactory.create<PaymentFlowActivity>(
PaymentSessionFixtures.PAYMENT_FLOW_ARGS
).use { activityScenario ->
Expand All @@ -600,10 +605,9 @@ class CustomerSessionTest {
val customerSession = createCustomerSession(null)
CustomerSession.instance = customerSession

val config = PaymentSessionConfig.Builder()
.setShippingInfoRequired(false)
.build()

val config = PaymentSessionFixtures.CONFIG.copy(
isShippingInfoRequired = false
)
activityScenarioFactory.create<PaymentFlowActivity>(
PaymentFlowActivityStarter.Args(
paymentSessionConfig = config,
Expand Down Expand Up @@ -909,5 +913,9 @@ class CustomerSessionTest {
private val SECOND_CUSTOMER = CustomerFixtures.OTHER_CUSTOMER

private val PAYMENT_METHOD = PaymentMethodFixtures.CARD_PAYMENT_METHOD

private fun getCustomerEphemeralKey(key: String): EphemeralKey {
return EphemeralKeyJsonParser().parse(JSONObject(key))
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.stripe.android

import com.stripe.android.PaymentSessionFixtures.PAYMENT_SESSION_CONFIG
import com.stripe.android.PaymentSessionFixtures.CONFIG
import com.stripe.android.model.ShippingInformation
import com.stripe.android.utils.ParcelUtils
import kotlin.test.Test
Expand All @@ -15,21 +15,21 @@ class PaymentSessionConfigTest {

@Test
fun testParcel() {
assertEquals(PAYMENT_SESSION_CONFIG, ParcelUtils.create(PAYMENT_SESSION_CONFIG))
assertEquals(CONFIG, ParcelUtils.create(CONFIG))
}

@Test
fun create_withValidCountryCode_succeeds() {
val allowedShippingCountryCodes = setOf("us", "CA")
val config = PAYMENT_SESSION_CONFIG.copy(
val config = CONFIG.copy(
allowedShippingCountryCodes = allowedShippingCountryCodes
)
assertEquals(allowedShippingCountryCodes, config.allowedShippingCountryCodes)
}

@Test
fun create_withEmptyCountryCodesList_succeeds() {
val config = PAYMENT_SESSION_CONFIG.copy(
val config = CONFIG.copy(
allowedShippingCountryCodes = emptySet()
)
assertTrue(config.allowedShippingCountryCodes.isEmpty())
Expand All @@ -38,7 +38,7 @@ class PaymentSessionConfigTest {
@Test
fun create_withInvalidCountryCode_throwsException() {
val exception: IllegalArgumentException = assertFailsWith {
PAYMENT_SESSION_CONFIG.copy(
CONFIG.copy(
allowedShippingCountryCodes = setOf("invalid_country_code")
)
}
Expand Down
Loading

0 comments on commit e422356

Please sign in to comment.