From f260633c9de646112291cc07c12cce06caf402b5 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Fri, 19 Nov 2021 17:01:34 -0400 Subject: [PATCH] Update payment failure ux. --- .../app/subscription/boost/BoostFragment.kt | 7 +-- .../manage/ActiveSubscriptionPreference.kt | 45 ++++++++++++++----- .../manage/ManageDonationsFragment.kt | 3 +- .../subscribe/SubscribeFragment.kt | 16 ++++++- .../subscription/DonorBadgeNotifications.kt | 4 +- app/src/main/res/values/strings.xml | 9 +++- .../org/signal/core/util/money/FiatMoney.java | 7 +++ .../api/subscriptions/ActiveSubscription.java | 12 +++++ 8 files changed, 81 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/boost/BoostFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/boost/BoostFragment.kt index 0a00f4df7d..5f374f9c7a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/boost/BoostFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/boost/BoostFragment.kt @@ -21,7 +21,6 @@ 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.DonationPaymentComponent @@ -31,7 +30,6 @@ import org.thoughtcrime.securesms.components.settings.app.subscription.models.Ne import org.thoughtcrime.securesms.components.settings.configure import org.thoughtcrime.securesms.components.settings.models.Progress import org.thoughtcrime.securesms.dependencies.ApplicationDependencies -import org.thoughtcrime.securesms.help.HelpFragment import org.thoughtcrime.securesms.keyboard.findListener import org.thoughtcrime.securesms.util.BottomSheetUtil.requireCoordinatorLayout import org.thoughtcrime.securesms.util.CommunicationActions @@ -251,7 +249,7 @@ class BoostFragment : DSLSettingsBottomSheetFragment( } else if (throwable is DonationExceptions.SetupFailed) { Log.w(TAG, "Error occurred while processing payment", throwable, true) MaterialAlertDialogBuilder(requireContext()) - .setTitle(R.string.DonationsErrors__payment_failed) + .setTitle(R.string.DonationsErrors__error_processing_payment) .setMessage(R.string.DonationsErrors__your_payment) .setPositiveButton(android.R.string.ok) { dialog, _ -> dialog.dismiss() @@ -265,8 +263,7 @@ class BoostFragment : DSLSettingsBottomSheetFragment( .setMessage(R.string.DonationsErrors__your_badge_could_not) .setPositiveButton(R.string.Subscription__contact_support) { dialog, _ -> dialog.dismiss() - requireActivity().finish() - requireActivity().startActivity(AppSettingsActivity.help(requireContext(), HelpFragment.DONATION_INDEX)) + findNavController().popBackStack() } .show() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/manage/ActiveSubscriptionPreference.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/manage/ActiveSubscriptionPreference.kt index b9fb769acd..a61fdcf2b5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/manage/ActiveSubscriptionPreference.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/manage/ActiveSubscriptionPreference.kt @@ -1,6 +1,5 @@ package org.thoughtcrime.securesms.components.settings.app.subscription.manage -import android.text.SpannableStringBuilder import android.text.method.LinkMovementMethod import android.view.View import android.widget.ProgressBar @@ -11,6 +10,7 @@ import org.signal.core.util.money.FiatMoney import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.badges.BadgeImageView import org.thoughtcrime.securesms.components.settings.PreferenceModel +import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.payments.FiatMoneyUtil import org.thoughtcrime.securesms.subscription.Subscription import org.thoughtcrime.securesms.util.DateUtils @@ -18,6 +18,7 @@ import org.thoughtcrime.securesms.util.MappingAdapter import org.thoughtcrime.securesms.util.MappingViewHolder import org.thoughtcrime.securesms.util.SpanUtil import org.thoughtcrime.securesms.util.visible +import org.whispersystems.signalservice.api.subscriptions.ActiveSubscription import java.util.Locale /** @@ -32,6 +33,7 @@ object ActiveSubscriptionPreference { val onAddBoostClick: () -> Unit, val renewalTimestamp: Long = -1L, val redemptionState: ManageDonationsState.SubscriptionRedemptionState, + val activeSubscription: ActiveSubscription.Subscription, val onContactSupport: () -> Unit ) : PreferenceModel() { override fun areItemsTheSame(newItem: Model): Boolean { @@ -42,7 +44,9 @@ object ActiveSubscriptionPreference { return super.areContentsTheSame(newItem) && subscription == newItem.subscription && renewalTimestamp == newItem.renewalTimestamp && - redemptionState == newItem.redemptionState + redemptionState == newItem.redemptionState && + FiatMoney.equals(price, newItem.price) && + activeSubscription == newItem.activeSubscription } } @@ -99,14 +103,35 @@ object ActiveSubscriptionPreference { } private fun presentFailureState(model: Model) { - expiry.text = SpannableStringBuilder(context.getString(R.string.MySupportPreference__couldnt_add_badge)) - .append(" ") - .append( - SpanUtil.clickable( - context.getString(R.string.MySupportPreference__please_contact_support), - ContextCompat.getColor(context, R.color.signal_accent_primary) - ) { model.onContactSupport() } - ) + if (model.activeSubscription.isFailedPayment || SignalStore.donationsValues().shouldCancelSubscriptionBeforeNextSubscribeAttempt) { + presentPaymentFailureState(model) + } else { + presentRedemptionFailureState(model) + } + } + + private fun presentPaymentFailureState(model: Model) { + expiry.text = SpanUtil.clickSubstring( + context.getString(R.string.DonationsErrors__error_processing_payment_s), + context.getString(R.string.MySupportPreference__please_contact_support), + { + model.onContactSupport() + }, + ContextCompat.getColor(context, R.color.signal_accent_primary) + ) + badge.alpha = 0.2f + progress.visible = false + } + + private fun presentRedemptionFailureState(model: Model) { + expiry.text = SpanUtil.clickSubstring( + context.getString(R.string.MySupportPreference__couldnt_add_badge_s), + context.getString(R.string.MySupportPreference__please_contact_support), + { + model.onContactSupport() + }, + ContextCompat.getColor(context, R.color.signal_accent_primary) + ) badge.alpha = 0.2f progress.visible = false } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/manage/ManageDonationsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/manage/ManageDonationsFragment.kt index 775ee77568..ed59c287b2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/manage/ManageDonationsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/manage/ManageDonationsFragment.kt @@ -108,7 +108,8 @@ class ManageDonationsFragment : DSLSettingsFragment() { onContactSupport = { requireActivity().finish() requireActivity().startActivity(AppSettingsActivity.help(requireContext(), HelpFragment.DONATION_INDEX)) - } + }, + activeSubscription = activeSubscription ) ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/subscribe/SubscribeFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/subscribe/SubscribeFragment.kt index 671dc00bb4..8de281f7ef 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/subscribe/SubscribeFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/subscribe/SubscribeFragment.kt @@ -31,6 +31,7 @@ import org.thoughtcrime.securesms.components.settings.models.Progress import org.thoughtcrime.securesms.dependencies.ApplicationDependencies import org.thoughtcrime.securesms.help.HelpFragment import org.thoughtcrime.securesms.keyboard.findListener +import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.payments.FiatMoneyUtil import org.thoughtcrime.securesms.subscription.Subscription import org.thoughtcrime.securesms.util.LifecycleDisposable @@ -181,7 +182,7 @@ class SubscribeFragment : DSLSettingsFragment( customPref( Subscription.Model( - activePrice = if (isActive) { activePrice } else null, + activePrice = if (isActive) activePrice else null, subscription = it, isSelected = state.selectedSubscription == it, isEnabled = areFieldsEnabled, @@ -289,12 +290,23 @@ class SubscribeFragment : DSLSettingsFragment( } else if (throwable is DonationExceptions.SetupFailed) { Log.w(TAG, "Error occurred while processing payment", throwable, true) MaterialAlertDialogBuilder(requireContext()) - .setTitle(R.string.DonationsErrors__payment_failed) + .setTitle(R.string.DonationsErrors__error_processing_payment) .setMessage(R.string.DonationsErrors__your_payment) .setPositiveButton(android.R.string.ok) { dialog, _ -> dialog.dismiss() } .show() + } else if (SignalStore.donationsValues().shouldCancelSubscriptionBeforeNextSubscribeAttempt) { + Log.w(TAG, "Stripe failed to process payment", throwable, true) + MaterialAlertDialogBuilder(requireContext()) + .setTitle(R.string.DonationsErrors__error_processing_payment) + .setMessage(R.string.DonationsErrors__your_badge_could_not_be_added) + .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 trying to redeem token", throwable, true) MaterialAlertDialogBuilder(requireContext()) diff --git a/app/src/main/java/org/thoughtcrime/securesms/subscription/DonorBadgeNotifications.kt b/app/src/main/java/org/thoughtcrime/securesms/subscription/DonorBadgeNotifications.kt index 6e1b901f5b..0871959ed3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/subscription/DonorBadgeNotifications.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/subscription/DonorBadgeNotifications.kt @@ -42,8 +42,8 @@ sealed class DonorBadgeNotifications { override fun show(context: Context) { val notification = NotificationCompat.Builder(context, NotificationChannels.FAILURES) .setSmallIcon(R.drawable.ic_notification) - .setContentTitle(context.getString(R.string.DonationsErrors__payment_failed)) - .setContentText(context.getString(R.string.Subscription__please_contact_support_for_more_information)) + .setContentTitle(context.getString(R.string.DonationsErrors__error_processing_payment)) + .setContentText(context.getString(R.string.DonationsErrors__your_badge_could_not_be_added)) .addAction( NotificationCompat.Action.Builder( null, diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d7781837ee..0fc1a031c4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -3983,7 +3983,8 @@ %1$s/month Renews %1$s Processing transaction… - Couldn\'t add badge. + + Couldn\'t add badge. %1$s Please contact support. Your Badge has Expired @@ -4002,7 +4003,11 @@ Earn a %1$s badge Processing payment… - Payment failed + + Error processing payment + + Error processing payment. $1$s + Your badge could not be added to your account, but you may have been charged. Please contact support. Your payment couldn\'t be processed and you have not been charged. Please try again. Still processing Couldn\'t add badge diff --git a/core-util/src/main/java/org/signal/core/util/money/FiatMoney.java b/core-util/src/main/java/org/signal/core/util/money/FiatMoney.java index a53df9d963..cb32a6e1bf 100644 --- a/core-util/src/main/java/org/signal/core/util/money/FiatMoney.java +++ b/core-util/src/main/java/org/signal/core/util/money/FiatMoney.java @@ -6,6 +6,7 @@ import java.math.BigDecimal; import java.text.NumberFormat; import java.util.Currency; import java.util.Locale; +import java.util.Objects; public class FiatMoney { private final BigDecimal amount; @@ -64,4 +65,10 @@ public class FiatMoney { return formatter.format(amount.multiply(multiplicand)); } + + public static boolean equals(FiatMoney left, FiatMoney right) { + return Objects.equals(left.amount, right.amount) && + Objects.equals(left.currency, right.currency) && + Objects.equals(left.timestamp, right.timestamp); + } } diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/subscriptions/ActiveSubscription.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/subscriptions/ActiveSubscription.java index 88b7904b57..730cec3156 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/subscriptions/ActiveSubscription.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/subscriptions/ActiveSubscription.java @@ -178,5 +178,17 @@ public final class ActiveSubscription { public boolean isFailedPayment() { return Status.isPaymentFailed(getStatus()); } + + @Override public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final Subscription that = (Subscription) o; + return level == that.level && endOfCurrentPeriod == that.endOfCurrentPeriod && isActive == that.isActive && billingCycleAnchor == that.billingCycleAnchor && willCancelAtPeriodEnd == that.willCancelAtPeriodEnd && currency + .equals(that.currency) && amount.equals(that.amount) && status.equals(that.status); + } + + @Override public int hashCode() { + return Objects.hash(level, currency, amount, endOfCurrentPeriod, isActive, billingCycleAnchor, willCancelAtPeriodEnd, status); + } } }