diff --git a/Signal/src/ViewControllers/AppSettings/Donations/DonationSettingsViewController+MySupport.swift b/Signal/src/ViewControllers/AppSettings/Donations/DonationSettingsViewController+MySupport.swift index 49cf3d18d6..22f3fa9918 100644 --- a/Signal/src/ViewControllers/AppSettings/Donations/DonationSettingsViewController+MySupport.swift +++ b/Signal/src/ViewControllers/AppSettings/Donations/DonationSettingsViewController+MySupport.swift @@ -471,10 +471,12 @@ extension DonationSettingsViewController { message: actionSheetMessage ) - actionSheet.addAction(showDonateAndClearErrorAction( - title: .tryAgain, - preferredDonateMode: preferredDonateMode - )) + switch preferredDonateMode { + case .monthly: + actionSheet.addAction(showDonateAndCancelSubscriptionAction(title: .tryAgain)) + case .oneTime: + actionSheet.addAction(showOneTimeDonateAndClearErrorAction(title: .tryAgain)) + } actionSheet.addAction(OWSActionSheets.cancelAction) self.presentActionSheet(actionSheet, animated: true) @@ -506,10 +508,7 @@ extension DonationSettingsViewController { message: actionSheetMessage ) - actionSheet.addAction(showDonateAndClearErrorAction( - title: .renewSubscription, - preferredDonateMode: .monthly - )) + actionSheet.addAction(showDonateAndCancelSubscriptionAction(title: .renewSubscription)) actionSheet.addAction(OWSActionSheets.cancelAction) self.presentActionSheet(actionSheet, animated: true) @@ -535,20 +534,13 @@ extension DonationSettingsViewController { } } - private func showDonateAndClearErrorAction( - title: ShowDonateActionTitle, - preferredDonateMode: DonateViewController.DonateMode + private func showOneTimeDonateAndClearErrorAction( + title: ShowDonateActionTitle ) -> ActionSheetAction { return ActionSheetAction(title: title.localizedTitle) { _ in self.databaseStorage.write { tx in - switch preferredDonateMode { - case .oneTime: - DependenciesBridge.shared.receiptCredentialResultStore - .clearRequestError(errorMode: .oneTimeBoost, tx: tx.asV2Write) - case .monthly: - DependenciesBridge.shared.receiptCredentialResultStore - .clearRequestErrorForAnyRecurringSubscription(tx: tx.asV2Write) - } + DependenciesBridge.shared.receiptCredentialResultStore + .clearRequestError(errorMode: .oneTimeBoost, tx: tx.asV2Write) } // Not ideal, because this makes network requests. However, this @@ -556,11 +548,31 @@ extension DonationSettingsViewController { // methods for updating the state outside the normal loading flow. self.loadAndUpdateState().done(on: DispatchQueue.main) { [weak self] in guard let self else { return } - self.showDonateViewController(preferredDonateMode: preferredDonateMode) + self.showDonateViewController(preferredDonateMode: .oneTime) } } } + private func showDonateAndCancelSubscriptionAction( + title: ShowDonateActionTitle + ) -> ActionSheetAction { + return ActionSheetAction(title: title.localizedTitle) { _ in + firstly(on: DispatchQueue.global()) { + self.databaseStorage.read { tx in + SubscriptionManagerImpl.getSubscriberID(transaction: tx) + } + }.then(on: DispatchQueue.global()) { subscriberID in + guard let subscriberID else { return Promise.value(())} + return SubscriptionManagerImpl.cancelSubscription(for: subscriberID) + }.then(on: DispatchQueue.main) { + self.loadAndUpdateState() + }.done(on: DispatchQueue.main) { [weak self] in + guard let self else { return } + self.showDonateViewController(preferredDonateMode: .monthly) + }.cauterize() + } + } + private func mySupportErrorIconView() -> UIView { let imageView = UIImageView.withTemplateImageName( "error-circle", diff --git a/Signal/src/ViewControllers/Donations/DonateViewController.swift b/Signal/src/ViewControllers/Donations/DonateViewController.swift index aac13a0e84..4f252bf939 100644 --- a/Signal/src/ViewControllers/Donations/DonateViewController.swift +++ b/Signal/src/ViewControllers/Donations/DonateViewController.swift @@ -1414,11 +1414,11 @@ class DonateViewController: OWSViewController, OWSNavigationChildController { isEnabled: isDifferentSubscriptionLevelSelected(monthly.currentSubscription) ) buttons.append(doomedContinueButton) - } else if - let currentSubscription = monthly.currentSubscription, - currentSubscription.active - { - if Self.canMakeNewDonations(forDonateMode: .monthly) { + } else if let currentSubscription = monthly.currentSubscription { + if + currentSubscription.active, + Self.canMakeNewDonations(forDonateMode: .monthly) + { let updateTitle = OWSLocalizedString( "DONATE_SCREEN_UPDATE_MONTHLY_SUBSCRIPTION_BUTTON", comment: "On the donation screen, if you already have a subscription, you'll see a button to update your subscription. This is the text on that button." diff --git a/SignalMessaging/Subscriptions/SubscriptionManagerImpl.swift b/SignalMessaging/Subscriptions/SubscriptionManagerImpl.swift index 6490be77eb..21668e4e4e 100644 --- a/SignalMessaging/Subscriptions/SubscriptionManagerImpl.swift +++ b/SignalMessaging/Subscriptions/SubscriptionManagerImpl.swift @@ -404,17 +404,39 @@ public class SubscriptionManagerImpl: NSObject { public class func cancelSubscription(for subscriberID: Data) -> Promise { Logger.info("[Donations] Cancelling subscription") - let request = OWSRequestFactory.deleteSubscriberID(subscriberID) - return firstly { - networkManager.makePromise(request: request) - }.map(on: DispatchQueue.global()) { response in - switch response.responseStatusCode { - case 200, 404: - break - default: - throw OWSAssertionError("Got bad response code \(response.responseStatusCode).") + return firstly(on: DispatchQueue.global()) { + // Fetch the latest subscription state + self.getCurrentSubscriptionStatus(for: subscriberID) + }.then(on: DispatchQueue.global()) { subscription in + guard let subscription else { + return Promise.value(()) } + // Check the subscription is in a state that can be cancelled + // If the state isn't in active or pastDue, skip deleting the + // subscription on the backend, and continue to clearing out the + // local subscription information. + switch subscription.status { + case .active, .pastDue: + break + case .canceled, .incomplete, .incompleteExpired, .trialing, .unpaid, .unknown: + return Promise.value(()) + } + + let request = OWSRequestFactory.deleteSubscriberID(subscriberID) + return firstly(on: DispatchQueue.global()) { + networkManager.makePromise(request: request) + }.map(on: DispatchQueue.global()) { response in + switch response.responseStatusCode { + case 200, 404: + break + default: + throw OWSAssertionError("Got bad response code \(response.responseStatusCode).") + } + }.done(on: DispatchQueue.global()) { + Logger.info("[Donations] Deleted remote subscription.") + } + }.done(on: DispatchQueue.global()) { databaseStorage.write { transaction in self.setSubscriberID(nil, transaction: transaction) self.setSubscriberCurrencyCode(nil, transaction: transaction) @@ -429,6 +451,7 @@ public class SubscriptionManagerImpl: NSObject { } self.storageServiceManager.recordPendingLocalAccountUpdates() + Logger.info("[Donations] Deleted local subscription.") } }