Add better application error handling for badges and token redemption.

This commit is contained in:
Alex Hart
2021-11-05 12:28:07 -03:00
committed by Greyson Parrelli
parent 2b6190bf34
commit b8dc541fc5
10 changed files with 160 additions and 45 deletions

View File

@@ -2,4 +2,5 @@ package org.thoughtcrime.securesms.components.settings.app.subscription
class DonationExceptions {
object TimedOutWaitingForTokenRedemption : Exception()
object RedemptionFailed : Exception()
}

View File

@@ -12,6 +12,7 @@ import org.signal.donations.GooglePayApi
import org.signal.donations.GooglePayPaymentSource
import org.signal.donations.StripeApi
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.jobmanager.JobTracker
import org.thoughtcrime.securesms.jobs.BoostReceiptRequestResponseJob
import org.thoughtcrime.securesms.jobs.SubscriptionReceiptRequestResponseJob
import org.thoughtcrime.securesms.keyvalue.SignalStore
@@ -114,15 +115,30 @@ class DonationPaymentRepository(activity: Activity) : StripeApi.PaymentIntentFet
val jobId = BoostReceiptRequestResponseJob.enqueueChain(paymentIntent)
val countDownLatch = CountDownLatch(1)
var finalJobState: JobTracker.JobState? = null
ApplicationDependencies.getJobManager().addListener(jobId) { _, jobState ->
if (jobState.isComplete) {
finalJobState = jobState
countDownLatch.countDown()
}
}
try {
if (countDownLatch.await(10, TimeUnit.SECONDS)) {
it.onComplete()
when (finalJobState) {
JobTracker.JobState.SUCCESS -> {
Log.d(TAG, "Request response job chain succeeded.", true)
it.onComplete()
}
JobTracker.JobState.FAILURE -> {
Log.d(TAG, "Request response job chain failed permanently.", true)
it.onError(DonationExceptions.RedemptionFailed)
}
else -> {
Log.d(TAG, "Request response job chain ignored due to in-progress jobs.", true)
it.onError(DonationExceptions.TimedOutWaitingForTokenRedemption)
}
}
} else {
it.onError(DonationExceptions.TimedOutWaitingForTokenRedemption)
}
@@ -137,7 +153,7 @@ class DonationPaymentRepository(activity: Activity) : StripeApi.PaymentIntentFet
.flatMapCompletable { levelUpdateOperation ->
val subscriber = SignalStore.donationsValues().requireSubscriber()
Log.d(TAG, "Attempting to set user subscription level to $subscriptionLevel")
Log.d(TAG, "Attempting to set user subscription level to $subscriptionLevel", true)
ApplicationDependencies.getDonationsService().updateSubscriptionLevel(
subscriber.subscriberId,
@@ -145,27 +161,46 @@ class DonationPaymentRepository(activity: Activity) : StripeApi.PaymentIntentFet
subscriber.currencyCode,
levelUpdateOperation.idempotencyKey.serialize()
).flatMap(ServiceResponse<EmptyResponse>::flattenResult).ignoreElement().andThen {
Log.d(TAG, "Successfully set user subscription to level $subscriptionLevel", true)
SignalStore.donationsValues().clearUserManuallyCancelled()
SignalStore.donationsValues().clearLevelOperation()
LevelUpdate.updateProcessingState(false)
it.onComplete()
}.andThen {
Log.d(TAG, "Enqueuing request response job chain.", true)
val jobId = SubscriptionReceiptRequestResponseJob.enqueueSubscriptionContinuation()
val countDownLatch = CountDownLatch(1)
var finalJobState: JobTracker.JobState? = null
ApplicationDependencies.getJobManager().addListener(jobId) { _, jobState ->
if (jobState.isComplete) {
finalJobState = jobState
countDownLatch.countDown()
}
}
try {
if (countDownLatch.await(10, TimeUnit.SECONDS)) {
it.onComplete()
when (finalJobState) {
JobTracker.JobState.SUCCESS -> {
Log.d(TAG, "Request response job chain succeeded.", true)
it.onComplete()
}
JobTracker.JobState.FAILURE -> {
Log.d(TAG, "Request response job chain failed permanently.", true)
it.onError(DonationExceptions.RedemptionFailed)
}
else -> {
Log.d(TAG, "Request response job chain ignored due to in-progress jobs.", true)
it.onError(DonationExceptions.TimedOutWaitingForTokenRedemption)
}
}
} else {
Log.d(TAG, "Request response job timed out.", true)
it.onError(DonationExceptions.TimedOutWaitingForTokenRedemption)
}
} catch (e: InterruptedException) {
Log.w(TAG, "Request response interrupted.", e, true)
it.onError(DonationExceptions.TimedOutWaitingForTokenRedemption)
}
}

View File

@@ -21,11 +21,13 @@ import org.thoughtcrime.securesms.components.settings.DSLSettingsAdapter
import org.thoughtcrime.securesms.components.settings.DSLSettingsBottomSheetFragment
import org.thoughtcrime.securesms.components.settings.DSLSettingsIcon
import org.thoughtcrime.securesms.components.settings.DSLSettingsText
import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity
import org.thoughtcrime.securesms.components.settings.app.subscription.DonationEvent
import org.thoughtcrime.securesms.components.settings.app.subscription.DonationExceptions
import org.thoughtcrime.securesms.components.settings.app.subscription.models.CurrencySelection
import org.thoughtcrime.securesms.components.settings.app.subscription.models.GooglePayButton
import org.thoughtcrime.securesms.components.settings.configure
import org.thoughtcrime.securesms.help.HelpFragment
import org.thoughtcrime.securesms.util.BottomSheetUtil.requireCoordinatorLayout
import org.thoughtcrime.securesms.util.CommunicationActions
import org.thoughtcrime.securesms.util.LifecycleDisposable
@@ -201,7 +203,7 @@ class BoostFragment : DSLSettingsBottomSheetFragment(
private fun onPaymentError(throwable: Throwable?) {
if (throwable is DonationExceptions.TimedOutWaitingForTokenRedemption) {
Log.w(TAG, "Error occurred while redeeming token", throwable)
Log.w(TAG, "Timed out while redeeming token", throwable, true)
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.DonationsErrors__redemption_still_pending)
.setMessage(R.string.DonationsErrors__you_might_not_see_your_badge_right_away)
@@ -210,8 +212,19 @@ class BoostFragment : DSLSettingsBottomSheetFragment(
findNavController().popBackStack()
}
.show()
} else if (throwable is DonationExceptions.RedemptionFailed) {
Log.w(TAG, "Error occurred while trying to redeem token", throwable, true)
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.DonationsErrors__redemption_failed)
.setMessage(R.string.DonationsErrors__please_contact_support)
.setPositiveButton(R.string.Subscription__contact_support) { dialog, _ ->
dialog.dismiss()
requireActivity().finish()
requireActivity().startActivity(AppSettingsActivity.help(requireContext(), HelpFragment.DONATION_INDEX))
}
.show()
} else {
Log.w(TAG, "Error occurred while processing payment", throwable)
Log.w(TAG, "Error occurred while processing payment", throwable, true)
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.DonationsErrors__payment_failed)
.setMessage(R.string.DonationsErrors__your_payment)

View File

@@ -24,6 +24,7 @@ import org.thoughtcrime.securesms.components.settings.app.subscription.DonationE
import org.thoughtcrime.securesms.components.settings.app.subscription.models.CurrencySelection
import org.thoughtcrime.securesms.components.settings.app.subscription.models.GooglePayButton
import org.thoughtcrime.securesms.components.settings.configure
import org.thoughtcrime.securesms.help.HelpFragment
import org.thoughtcrime.securesms.payments.FiatMoneyUtil
import org.thoughtcrime.securesms.subscription.Subscription
import org.thoughtcrime.securesms.util.CommunicationActions
@@ -235,7 +236,7 @@ class SubscribeFragment : DSLSettingsFragment(
private fun onPaymentError(throwable: Throwable?) {
if (throwable is DonationExceptions.TimedOutWaitingForTokenRedemption) {
Log.w(TAG, "Error occurred while redeeming token", throwable)
Log.w(TAG, "Timeout occurred while redeeming token", throwable, true)
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.DonationsErrors__redemption_still_pending)
.setMessage(R.string.DonationsErrors__you_might_not_see_your_badge_right_away)
@@ -245,8 +246,19 @@ class SubscribeFragment : DSLSettingsFragment(
requireActivity().startActivity(AppSettingsActivity.subscriptions(requireContext()))
}
.show()
} else if (throwable is DonationExceptions.RedemptionFailed) {
Log.w(TAG, "Error occurred while trying to redeem token", throwable, true)
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.DonationsErrors__redemption_failed)
.setMessage(R.string.DonationsErrors__please_contact_support)
.setPositiveButton(R.string.Subscription__contact_support) { dialog, _ ->
dialog.dismiss()
requireActivity().finish()
requireActivity().startActivity(AppSettingsActivity.help(requireContext(), HelpFragment.DONATION_INDEX))
}
.show()
} else {
Log.w(TAG, "Error occurred while processing payment", throwable)
Log.w(TAG, "Error occurred while processing payment", throwable, true)
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.DonationsErrors__payment_failed)
.setMessage(R.string.DonationsErrors__your_payment)
@@ -267,7 +279,7 @@ class SubscribeFragment : DSLSettingsFragment(
}
private fun onSubscriptionFailedToCancel(throwable: Throwable) {
Log.w(TAG, "Failed to cancel subscription", throwable)
Log.w(TAG, "Failed to cancel subscription", throwable, true)
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.DonationsErrors__failed_to_cancel_subscription)
.setMessage(R.string.DonationsErrors__subscription_cancellation_requires_an_internet_connection)