Skip to content

Commit

Permalink
Handle CustomerSession failure in PaymentMethodsActivity (#2836)
Browse files Browse the repository at this point in the history
`CustomerSession.getInstance()` can throw an exception, so this should
be handled.

Fixes #2816
  • Loading branch information
mshafrir-stripe authored Sep 10, 2020
1 parent 296f346 commit 02f0a75
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ internal class DeletePaymentMethodDialogFactory internal constructor(
private val context: Context,
private val adapter: PaymentMethodsAdapter,
private val cardDisplayTextFactory: CardDisplayTextFactory,
private val customerSession: CustomerSession,
private val customerSession: Result<CustomerSession>,
private val productUsage: Set<String>,
private val onDeletedPaymentMethodCallback: (PaymentMethod) -> Unit
) {
Expand Down Expand Up @@ -40,7 +40,7 @@ internal class DeletePaymentMethodDialogFactory internal constructor(
adapter.deletePaymentMethod(paymentMethod)

paymentMethod.id?.let { paymentMethodId ->
customerSession.detachPaymentMethod(
customerSession.getOrNull()?.detachPaymentMethod(
paymentMethodId = paymentMethodId,
productUsage = productUsage,
listener = PaymentMethodDeleteListener()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ class PaymentMethodsActivity : AppCompatActivity() {
args.isPaymentSessionActive
}

private val customerSession: CustomerSession by lazy {
CustomerSession.getInstance()
private val customerSession: Result<CustomerSession> by lazy {
runCatching { CustomerSession.getInstance() }
}
private val cardDisplayTextFactory: CardDisplayTextFactory by lazy {
CardDisplayTextFactory(this)
Expand Down Expand Up @@ -72,6 +72,14 @@ class PaymentMethodsActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (customerSession.isFailure) {
finishWithResult(
null,
Activity.RESULT_CANCELED
)
return
}

setContentView(viewBinding.root)

args.windowFlags?.let {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import com.stripe.android.model.PaymentMethod

internal class PaymentMethodsViewModel(
application: Application,
private val customerSession: CustomerSession,
private val customerSession: Result<CustomerSession>,
internal var selectedPaymentMethodId: String? = null,
private val startedFromPaymentSession: Boolean
) : AndroidViewModel(application) {
Expand Down Expand Up @@ -61,29 +61,38 @@ internal class PaymentMethodsViewModel(
internal fun getPaymentMethods(): LiveData<Result<List<PaymentMethod>>> {
val resultData = MutableLiveData<Result<List<PaymentMethod>>>()
progressData.value = true
customerSession.getPaymentMethods(
paymentMethodType = PaymentMethod.Type.Card,
productUsage = productUsage,
listener = object : CustomerSession.PaymentMethodsRetrievalListener {
override fun onPaymentMethodsRetrieved(paymentMethods: List<PaymentMethod>) {
resultData.value = Result.success(paymentMethods)
progressData.value = false
}

override fun onError(
errorCode: Int,
errorMessage: String,
stripeError: StripeError?
) {
resultData.value = Result.failure(
APIException(
stripeError = stripeError,
statusCode = errorCode,
message = errorMessage
)
)
progressData.value = false
}
customerSession.fold(
onSuccess = {
it.getPaymentMethods(
paymentMethodType = PaymentMethod.Type.Card,
productUsage = productUsage,
listener = object : CustomerSession.PaymentMethodsRetrievalListener {
override fun onPaymentMethodsRetrieved(paymentMethods: List<PaymentMethod>) {
resultData.value = Result.success(paymentMethods)
progressData.value = false
}

override fun onError(
errorCode: Int,
errorMessage: String,
stripeError: StripeError?
) {
resultData.value = Result.failure(
APIException(
stripeError = stripeError,
statusCode = errorCode,
message = errorMessage
)
)
progressData.value = false
}
}
)
},
onFailure = {
resultData.value = Result.failure(it)
progressData.value = false
}
)

Expand All @@ -92,7 +101,7 @@ internal class PaymentMethodsViewModel(

internal class Factory(
private val application: Application,
private val customerSession: CustomerSession,
private val customerSession: Result<CustomerSession>,
private val initialPaymentMethodId: String?,
private val startedFromPaymentSession: Boolean
) : ViewModelProvider.Factory {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import kotlin.test.Test
class DeletePaymentMethodDialogFactoryTest {

private val customerSession: CustomerSession = mock()
private val context: Context = ApplicationProvider.getApplicationContext<Context>()
private val context: Context = ApplicationProvider.getApplicationContext()

@Test
fun onDeletedPaymentMethod_shouldCallDetachPaymentMethodAndCallback() {
Expand All @@ -27,7 +27,7 @@ class DeletePaymentMethodDialogFactoryTest {
context,
mock(),
CardDisplayTextFactory(context),
customerSession,
Result.success(customerSession),
setOf(PaymentMethodsActivity.PRODUCT_TOKEN)
) {
callbackPaymentMethod = it
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class PaymentMethodsViewModelTest {
private val listenerArgumentCaptor: KArgumentCaptor<CustomerSession.PaymentMethodsRetrievalListener> = argumentCaptor()
private val viewModel = PaymentMethodsViewModel(
application = ApplicationProvider.getApplicationContext(),
customerSession = customerSession,
customerSession = Result.success(customerSession),
startedFromPaymentSession = true
)

Expand Down Expand Up @@ -102,6 +102,22 @@ class PaymentMethodsViewModelTest {
.isNull()
}

@Test
fun `getPaymentMethods() with CustomerSession failure should return failure result`() {
var result: Result<List<PaymentMethod>>? = null
PaymentMethodsViewModel(
application = ApplicationProvider.getApplicationContext(),
customerSession = Result.failure(RuntimeException("failure")),
startedFromPaymentSession = true
).getPaymentMethods().observeForever {
result = it
}

requireNotNull(result)
assertThat(result?.isFailure)
.isTrue()
}

private companion object {
private val EXPECTED_PRODUCT_USAGE = setOf(
PaymentSession.PRODUCT_TOKEN,
Expand Down

0 comments on commit 02f0a75

Please sign in to comment.