Handle donation-driven 440 errors more gracefully.

This commit is contained in:
Alex Hart
2023-10-11 08:56:09 -04:00
committed by GitHub
parent 157d194cc5
commit 520b3a14bc
7 changed files with 171 additions and 26 deletions

View File

@@ -20,6 +20,7 @@ import org.thoughtcrime.securesms.components.settings.app.subscription.donate.Do
import org.thoughtcrime.securesms.components.settings.app.subscription.donate.gateway.GatewayRequest
import org.thoughtcrime.securesms.components.settings.app.subscription.errors.DonationError
import org.thoughtcrime.securesms.components.settings.app.subscription.errors.DonationErrorSource
import org.thoughtcrime.securesms.components.settings.app.subscription.errors.toDonationError
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.jobs.MultiDeviceSubscriptionSyncRequestJob
import org.thoughtcrime.securesms.keyvalue.SignalStore
@@ -28,6 +29,7 @@ import org.whispersystems.signalservice.api.subscriptions.PayPalCreatePaymentInt
import org.whispersystems.signalservice.api.subscriptions.PayPalCreatePaymentMethodResponse
import org.whispersystems.signalservice.api.util.Preconditions
import org.whispersystems.signalservice.internal.push.DonationProcessor
import org.whispersystems.signalservice.internal.push.exceptions.DonationProcessorError
class PayPalPaymentInProgressViewModel(
private val payPalRepository: PayPalRepository,
@@ -89,10 +91,10 @@ class PayPalPaymentInProgressViewModel(
},
onError = { throwable ->
Log.w(TAG, "Failed to update subscription", throwable, true)
val donationError: DonationError = if (throwable is DonationError) {
throwable
} else {
DonationError.genericBadgeRedemptionFailure(DonationErrorSource.SUBSCRIPTION)
val donationError: DonationError = when (throwable) {
is DonationError -> throwable
is DonationProcessorError -> throwable.toDonationError(DonationErrorSource.SUBSCRIPTION, PaymentSourceType.PayPal)
else -> DonationError.genericBadgeRedemptionFailure(DonationErrorSource.SUBSCRIPTION)
}
DonationError.routeDonationError(ApplicationDependencies.getApplication(), donationError)
@@ -166,10 +168,10 @@ class PayPalPaymentInProgressViewModel(
Log.w(TAG, "Failure in one-time payment pipeline...", throwable, true)
store.update { DonationProcessorStage.FAILED }
val donationError: DonationError = if (throwable is DonationError) {
throwable
} else {
DonationError.genericBadgeRedemptionFailure(DonationErrorSource.BOOST)
val donationError: DonationError = when (throwable) {
is DonationError -> throwable
is DonationProcessorError -> throwable.toDonationError(request.donateToSignalType.toErrorSource(), PaymentSourceType.PayPal)
else -> DonationError.genericBadgeRedemptionFailure(request.donateToSignalType.toErrorSource())
}
DonationError.routeDonationError(ApplicationDependencies.getApplication(), donationError)
},
@@ -196,10 +198,10 @@ class PayPalPaymentInProgressViewModel(
Log.w(TAG, "Failure in monthly payment pipeline...", throwable, true)
store.update { DonationProcessorStage.FAILED }
val donationError: DonationError = if (throwable is DonationError) {
throwable
} else {
DonationError.genericBadgeRedemptionFailure(DonationErrorSource.SUBSCRIPTION)
val donationError: DonationError = when (throwable) {
is DonationError -> throwable
is DonationProcessorError -> throwable.toDonationError(DonationErrorSource.SUBSCRIPTION, PaymentSourceType.PayPal)
else -> DonationError.genericBadgeRedemptionFailure(DonationErrorSource.SUBSCRIPTION)
}
DonationError.routeDonationError(ApplicationDependencies.getApplication(), donationError)
},

View File

@@ -23,12 +23,14 @@ import org.thoughtcrime.securesms.components.settings.app.subscription.donate.Do
import org.thoughtcrime.securesms.components.settings.app.subscription.donate.gateway.GatewayRequest
import org.thoughtcrime.securesms.components.settings.app.subscription.errors.DonationError
import org.thoughtcrime.securesms.components.settings.app.subscription.errors.DonationErrorSource
import org.thoughtcrime.securesms.components.settings.app.subscription.errors.toDonationError
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.jobs.MultiDeviceSubscriptionSyncRequestJob
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.util.rx.RxStore
import org.whispersystems.signalservice.api.util.Preconditions
import org.whispersystems.signalservice.internal.push.DonationProcessor
import org.whispersystems.signalservice.internal.push.exceptions.DonationProcessorError
class StripePaymentInProgressViewModel(
private val stripeRepository: StripeRepository,
@@ -148,10 +150,10 @@ class StripePaymentInProgressViewModel(
}
.flatMapCompletable { stripeRepository.setDefaultPaymentMethod(it, paymentSourceProvider.paymentSourceType) }
.onErrorResumeNext {
if (it is DonationError) {
Completable.error(it)
} else {
Completable.error(DonationError.getPaymentSetupError(DonationErrorSource.SUBSCRIPTION, it, paymentSourceProvider.paymentSourceType))
when {
it is DonationError -> Completable.error(it)
it is DonationProcessorError -> Completable.error(it.toDonationError(DonationErrorSource.SUBSCRIPTION, paymentSourceProvider.paymentSourceType))
else -> Completable.error(DonationError.getPaymentSetupError(DonationErrorSource.SUBSCRIPTION, it, paymentSourceProvider.paymentSourceType))
}
}
@@ -211,10 +213,10 @@ class StripePaymentInProgressViewModel(
Log.w(TAG, "Failure in one-time payment pipeline...", throwable, true)
store.update { DonationProcessorStage.FAILED }
val donationError: DonationError = if (throwable is DonationError) {
throwable
} else {
DonationError.genericBadgeRedemptionFailure(DonationErrorSource.BOOST)
val donationError: DonationError = when (throwable) {
is DonationError -> throwable
is DonationProcessorError -> throwable.toDonationError(request.donateToSignalType.toErrorSource(), paymentSourceProvider.paymentSourceType)
else -> DonationError.genericBadgeRedemptionFailure(request.donateToSignalType.toErrorSource())
}
DonationError.routeDonationError(ApplicationDependencies.getApplication(), donationError)
},
@@ -256,10 +258,10 @@ class StripePaymentInProgressViewModel(
},
onError = { throwable ->
Log.w(TAG, "Failed to update subscription", throwable, true)
val donationError: DonationError = if (throwable is DonationError) {
throwable
} else {
DonationError.genericBadgeRedemptionFailure(DonationErrorSource.SUBSCRIPTION)
val donationError: DonationError = when (throwable) {
is DonationError -> throwable
is DonationProcessorError -> throwable.toDonationError(DonationErrorSource.SUBSCRIPTION, PaymentSourceType.Stripe.GooglePay)
else -> DonationError.genericBadgeRedemptionFailure(DonationErrorSource.SUBSCRIPTION)
}
DonationError.routeDonationError(ApplicationDependencies.getApplication(), donationError)

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.components.settings.app.subscription.errors
import org.signal.donations.PaymentSourceType
import org.signal.donations.StripeDeclineCode
import org.whispersystems.signalservice.api.subscriptions.ActiveSubscription
import org.whispersystems.signalservice.internal.push.exceptions.DonationProcessorError
fun DonationProcessorError.toDonationError(
source: DonationErrorSource,
method: PaymentSourceType
): DonationError {
return when (processor) {
ActiveSubscription.Processor.STRIPE -> {
check(method is PaymentSourceType.Stripe)
val declineCode = StripeDeclineCode.getFromCode(chargeFailure.code)
if (declineCode.isKnown()) {
DonationError.PaymentSetupError.StripeDeclinedError(source, this, declineCode, method)
} else if (chargeFailure.code != null) {
DonationError.PaymentSetupError.StripeCodedError(source, this, chargeFailure.code)
} else {
DonationError.PaymentSetupError.GenericError(source, this)
}
}
ActiveSubscription.Processor.BRAINTREE -> {
check(method is PaymentSourceType.PayPal)
val code = chargeFailure.code
if (code == null) {
DonationError.PaymentSetupError.GenericError(source, this)
} else {
val declineCode = PayPalDeclineCode.KnownCode.fromCode(code.toInt())
if (declineCode != null) {
DonationError.PaymentSetupError.PayPalDeclinedError(source, this, declineCode)
} else {
DonationError.PaymentSetupError.PayPalCodedError(source, this, code.toInt())
}
}
}
}
}