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

import AuthenticationServices
import Foundation
import SignalServiceKit
import SignalUI

extension DonateViewController {
    /// Executes a PayPal one-time donation flow.
    func startPaypalBoost(
        with amount: FiatMoney,
        badge: ProfileBadge,
    ) {
        Logger.info("[Donations] Starting one-time PayPal donation")
        Task {
            await self._startPaypalBoost(amount: amount, badge: badge)
        }
    }

    private func _startPaypalBoost(amount: FiatMoney, badge: ProfileBadge) async {
        do {
            Logger.info("[Donations] Creating one-time PayPal payment")
            let (approvalUrl, paymentId) = try await DonationViewsUtil.Paypal.createPaypalPaymentBehindActivityIndicator(
                amount: amount,
                level: .boostBadge,
                fromViewController: self,
            )

            Logger.info("[Donations] Presenting PayPal web UI for user approval of one-time donation")
            let approvalParams: Paypal.OneTimePaymentWebAuthApprovalParams = try await Paypal.presentExpectingApprovalParams(
                approvalUrl: approvalUrl,
                withPresentationContext: self,
            )

            Logger.info("[Donations] Creating and redeeming one-time boost receipt for PayPal donation")
            try await DonationViewsUtil.wrapInProgressView(
                from: self,
                operation: {
                    try await self.confirmPaypalPaymentAndRedeemBoost(
                        amount: amount,
                        paymentId: paymentId,
                        approvalParams: approvalParams,
                    )
                },
            )

            Logger.info("[Donations] One-time PayPal donation finished")
            self.didCompleteDonation(
                receiptCredentialSuccessMode: .oneTimeBoost,
            )
        } catch {
            if let webAuthError = error as? Paypal.AuthError {
                switch webAuthError {
                case .userCanceled:
                    Logger.info("[Donations] One-time PayPal donation canceled")
                    self.didCancelDonation()
                }
            } else {
                Logger.info("[Donations] One-time PayPal donation failed")
                self.didFailDonation(
                    error: error,
                    mode: .oneTime,
                    badge: badge,
                    paymentMethod: .paypal,
                )
            }
        }
    }

    private func confirmPaypalPaymentAndRedeemBoost(
        amount: FiatMoney,
        paymentId: String,
        approvalParams: Paypal.OneTimePaymentWebAuthApprovalParams,
    ) async throws {
        let paymentIntentId = try await Paypal.confirmOneTimePayment(
            amount: amount,
            level: .boostBadge,
            paymentId: paymentId,
            approvalParams: approvalParams,
        )

        try await DonationViewsUtil.waitForRedemption(paymentMethod: .paypal) {
            try await DonationSubscriptionManager.requestAndRedeemReceipt(
                boostPaymentIntentId: paymentIntentId,
                amount: amount,
                paymentProcessor: .braintree,
                paymentMethod: .paypal,
            )
        }
    }
}

// MARK: - ASWebAuthenticationPresentationContextProviding

extension DonateViewController: ASWebAuthenticationPresentationContextProviding {
    func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
        view.window!
    }
}