// // Copyright (c) 2021 Open Whisper Systems. All rights reserved. // import Foundation import PassKit import SignalCoreKit public class DonationUtilities: NSObject { public static var isApplePayAvailable: Bool { PKPaymentAuthorizationController.canMakePayments() } public static let supportedNetworks: [PKPaymentNetwork] = [ .visa, .masterCard, .amex, .discover, .JCB, .interac ] public enum Symbol: Equatable { case before(String) case after(String) case currencyCode } public struct Presets { public struct Preset { public let symbol: Symbol public let amounts: [UInt] } public static let presets: [Currency.Code: Preset] = [ "USD": Preset(symbol: .before("$"), amounts: [3, 5, 10, 20, 50, 100]), "AUD": Preset(symbol: .before("A$"), amounts: [5, 10, 15, 25, 65, 125]), "BRL": Preset(symbol: .before("R$"), amounts: [15, 25, 50, 100, 250, 525]), "GBP": Preset(symbol: .before("£"), amounts: [3, 5, 10, 15, 35, 70]), "CAD": Preset(symbol: .before("CA$"), amounts: [5, 10, 15, 25, 60, 125]), "CNY": Preset(symbol: .before("CN¥"), amounts: [20, 35, 65, 130, 320, 650]), "EUR": Preset(symbol: .before("€"), amounts: [3, 5, 10, 15, 40, 80]), "HKD": Preset(symbol: .before("HK$"), amounts: [25, 40, 80, 150, 400, 775]), "INR": Preset(symbol: .before("₹"), amounts: [100, 200, 300, 500, 1_000, 5_000]), "JPY": Preset(symbol: .before("¥"), amounts: [325, 550, 1_000, 2_200, 5_500, 11_000]), "KRW": Preset(symbol: .before("₩"), amounts: [3_500, 5_500, 11_000, 22_500, 55_500, 100_000]), "PLN": Preset(symbol: .after("zł"), amounts: [10, 20, 40, 75, 150, 375]), "SEK": Preset(symbol: .after("kr"), amounts: [25, 50, 75, 150, 400, 800]), "CHF": Preset(symbol: .currencyCode, amounts: [3, 5, 10, 20, 50, 100]) ] public static func symbol(for code: Currency.Code) -> Symbol { presets[code]?.symbol ?? .currencyCode } } private static let currencyFormatter: NumberFormatter = { let currencyFormatter = NumberFormatter() currencyFormatter.numberStyle = .decimal return currencyFormatter }() public static func formatCurrency(_ value: NSDecimalNumber, currencyCode: Currency.Code, includeSymbol: Bool = true) -> String { let isZeroDecimalCurrency = Stripe.zeroDecimalCurrencyCodes.contains(currencyCode) let decimalPlaces: Int if isZeroDecimalCurrency { decimalPlaces = 0 } else if value.doubleValue == Double(value.intValue) { decimalPlaces = 0 } else { decimalPlaces = 2 } currencyFormatter.minimumFractionDigits = decimalPlaces currencyFormatter.maximumFractionDigits = decimalPlaces let valueString = currencyFormatter.string(from: value) ?? value.stringValue guard includeSymbol else { return valueString } switch Presets.symbol(for: currencyCode) { case .before(let symbol): return symbol + valueString case .after(let symbol): return valueString + symbol case .currencyCode: return currencyCode + " " + valueString } } public static func newPaymentRequest(for amount: NSDecimalNumber, currencyCode: String) -> PKPaymentRequest { let request = PKPaymentRequest() request.paymentSummaryItems = [PKPaymentSummaryItem( label: NSLocalizedString( "DONATION_VIEW_DONATION_TO_SIGNAL", comment: "Text describing to the user that they're going to pay a donation to Signal" ), amount: amount, type: .final )] request.merchantIdentifier = "merchant.org.signalfoundation" request.merchantCapabilities = .capability3DS request.countryCode = "US" request.currencyCode = currencyCode request.requiredShippingContactFields = [.emailAddress] request.supportedNetworks = DonationUtilities.supportedNetworks return request } }