Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
signalapp
GitHub Repository: signalapp/Signal-iOS
Path: blob/main/Signal/src/ViewControllers/Donations/DonateViewController+MonthlyApplePayDonation.swift
1 views
//
// Copyright 2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//

import Foundation
import PassKit
import SignalServiceKit
import SignalUI

extension DonateViewController {
    /// Handle Apple Pay authorization for a monthly payment.
    ///
    /// See also: code for other payment methods, such as credit/debit cards.
    func paymentAuthorizationControllerForMonthly(
        _ controller: PKPaymentAuthorizationController,
        didAuthorizePayment payment: PKPayment,
        handler completion: @escaping (PKPaymentAuthorizationResult) -> Void,
    ) {
        guard
            let monthly = state.monthly,
            let selectedSubscriptionLevel = monthly.selectedSubscriptionLevel
        else {
            owsFail("[Donations] Invalid state; cannot pay")
        }

        Logger.info("[Donations] Starting monthly Apple Pay donation")

        // See also: code for other payment methods, such as credit/debit card.

        Task {
            let subscriberId: Data
            do {
                if let existingSubscriberId = monthly.subscriberID {
                    Logger.info("[Donations] Cancelling existing subscription")
                    try await DonationSubscriptionManager.cancelSubscription(for: existingSubscriberId)
                } else {
                    Logger.info("[Donations] No existing subscription to cancel")
                }

                Logger.info("[Donations] Preparing new monthly subscription with Apple Pay")
                subscriberId = try await DonationSubscriptionManager.prepareNewSubscription(
                    currencyCode: monthly.selectedCurrencyCode,
                )

                Logger.info("[Donations] Creating Signal payment method for new monthly subscription with Apple Pay")
                let clientSecret = try await Stripe.createSignalPaymentMethodForSubscription(subscriberId: subscriberId)

                Logger.info("[Donations] Authorizing payment for new monthly subscription with Apple Pay")
                let confirmedIntent = try await Stripe.setupNewSubscription(
                    clientSecret: clientSecret,
                    paymentMethod: .applePay(payment: payment),
                )
                let paymentMethodId = confirmedIntent.paymentMethodId

                Logger.info("[Donations] Finalizing new subscription for Apple Pay donation")
                _ = try await DonationSubscriptionManager.finalizeNewSubscription(
                    forSubscriberId: subscriberId,
                    paymentType: .applePay(paymentMethodId: paymentMethodId),
                    subscription: selectedSubscriptionLevel,
                    currencyCode: monthly.selectedCurrencyCode,
                )

                let authResult = PKPaymentAuthorizationResult(status: .success, errors: nil)
                completion(authResult)
            } catch {
                let authResult = PKPaymentAuthorizationResult(status: .failure, errors: [error])
                completion(authResult)
                owsFailDebug("[Donations] Error setting up subscription, \(error)")
                return
            }

            do {
                Logger.info("[Donations] Redeeming monthly receipt for Apple Pay donation")
                try await DonationViewsUtil.wrapInProgressView(
                    from: self,
                    operation: {
                        try await DonationViewsUtil.waitForRedemption(paymentMethod: .applePay) {
                            try await DonationSubscriptionManager.requestAndRedeemReceipt(
                                subscriberId: subscriberId,
                                subscriptionLevel: selectedSubscriptionLevel.level,
                                priorSubscriptionLevel: monthly.currentSubscriptionLevel?.level,
                                paymentProcessor: .stripe,
                                paymentMethod: .applePay,
                                isNewSubscription: true,
                            )
                        }
                    },
                )
                Logger.info("[Donations] Monthly card donation finished")
                self.didCompleteDonation(
                    receiptCredentialSuccessMode: .recurringSubscriptionInitiation,
                )
            } catch {
                Logger.info("[Donations] Monthly card donation failed")
                self.didFailDonation(
                    error: error,
                    mode: .monthly,
                    badge: selectedSubscriptionLevel.badge,
                    paymentMethod: .applePay,
                )
            }
        }
    }
}