-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #57 from AppSci/feature/ApplePay
Feature/apple pay
- Loading branch information
Showing
13 changed files
with
673 additions
and
48 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
// | ||
// File.swift | ||
// | ||
// | ||
// Created by Yegor Kyrylov on 08.09.2022. | ||
// | ||
|
||
import Foundation | ||
|
||
public extension Float { | ||
|
||
/// Returns rounded number that will have 1 or 2 digits after the dot. Example: 13.21 | ||
/// | ||
/// - Returns: maths rounded number to hundredths | ||
func roundedToHundredths() -> Float { | ||
return Float((100 * self).rounded() / 100) | ||
} | ||
|
||
} | ||
|
||
public struct MonetaryAmount: Equatable { | ||
public let amountCents: Int | ||
public let amountDollars: Float | ||
|
||
public init(amountCents: Int) { | ||
self.init(amountDollars: Float(amountCents) / 100) | ||
} | ||
|
||
public init(amountDollars: Float) { | ||
let adjustedAmount = amountDollars.roundedToHundredths() | ||
self.amountCents = Int((adjustedAmount * 100).rounded()) | ||
self.amountDollars = adjustedAmount | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
// | ||
// File.swift | ||
// | ||
// | ||
// Created by Yegor Kyrylov on 09.09.2022. | ||
// | ||
|
||
import Foundation | ||
|
||
enum CurrencyHelper { | ||
static func getSymbolForCurrencyCode(code: String) -> String { | ||
var candidates: [String] = [] | ||
let locales: [String] = Locale.availableIdentifiers | ||
for localeID in locales { | ||
guard let symbol = findMatchingSymbol(localeID: localeID, currencyCode: code) else { | ||
continue | ||
} | ||
if symbol.count == 1 { | ||
return symbol | ||
} | ||
candidates.append(symbol) | ||
} | ||
let sorted = sortAscByLength(list: candidates) | ||
if sorted.count < 1 { | ||
return "" | ||
} | ||
return sorted[0] | ||
} | ||
|
||
private static func findMatchingSymbol(localeID: String, currencyCode: String) -> String? { | ||
let locale = Locale(identifier: localeID as String) | ||
guard let code = locale.currencyCode else { | ||
return nil | ||
} | ||
if code != currencyCode { | ||
return nil | ||
} | ||
guard let symbol = locale.currencySymbol else { | ||
return nil | ||
} | ||
return symbol | ||
} | ||
|
||
private static func sortAscByLength(list: [String]) -> [String] { | ||
return list.sorted(by: { $0.count < $1.count }) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// | ||
// ApplePayConfiguration.swift | ||
// | ||
// | ||
// Created by Roman Mishchenko on 02.06.2022. | ||
// | ||
|
||
import Foundation | ||
|
||
public struct ApplePayConfiguration { | ||
let merchantIdentifier: String | ||
|
||
public init(merchantIdentifier: String) { | ||
self.merchantIdentifier = merchantIdentifier | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
// | ||
// ApplePayPaymentHandler.swift | ||
// | ||
// | ||
// Created by Roman Mishchenko on 02.06.2022. | ||
// | ||
|
||
import Foundation | ||
import PassKit | ||
import Combine | ||
|
||
public enum ApplePayPaymentHandlerOutputMessage { | ||
case failedToPresentPayment | ||
case paymentFinished(_ status: PKPaymentAuthorizationStatus, _ productId: String, _ paymentData: Data) | ||
} | ||
|
||
final class ApplePayPaymentHandler: NSObject { | ||
|
||
private var paymentController: PKPaymentAuthorizationController? | ||
private var paymentSummaryItems = [PKPaymentSummaryItem]() | ||
private var paymentStatus = PKPaymentAuthorizationStatus.failure | ||
private var productId: String? = nil | ||
private var paymentData: Data? = nil | ||
private let configuration: ApplePayConfiguration | ||
|
||
let outputPublisher: AnyPublisher<ApplePayPaymentHandlerOutputMessage, Error> | ||
private let outputSubject = PassthroughSubject<ApplePayPaymentHandlerOutputMessage, Error>() | ||
|
||
init(configuration: ApplePayConfiguration) { | ||
self.configuration = configuration | ||
self.outputPublisher = outputSubject.eraseToAnyPublisher() | ||
} | ||
|
||
static let supportedNetworks: [PKPaymentNetwork] = [ | ||
.amex, | ||
.masterCard, | ||
.visa, | ||
.discover | ||
] | ||
|
||
class func applePayStatus() -> (canMakePayments: Bool, canSetupCards: Bool) { | ||
return (PKPaymentAuthorizationController.canMakePayments(), | ||
PKPaymentAuthorizationController.canMakePayments(usingNetworks: supportedNetworks)) | ||
} | ||
|
||
func startPayment(with label: String, price: String?, currency: String?, productId: String?, countryCode: String?) { | ||
guard | ||
let price = price, | ||
let currency = currency, | ||
let productId = productId, | ||
let countryCode = countryCode | ||
else { | ||
self.outputSubject.send(.failedToPresentPayment) | ||
return | ||
} | ||
|
||
let product = PKPaymentSummaryItem(label: label, amount: NSDecimalNumber(string: price), type: .final) | ||
|
||
paymentSummaryItems = [product] | ||
|
||
// Create a payment request. | ||
let paymentRequest = PKPaymentRequest() | ||
paymentRequest.paymentSummaryItems = paymentSummaryItems | ||
paymentRequest.merchantIdentifier = configuration.merchantIdentifier | ||
paymentRequest.merchantCapabilities = .capability3DS | ||
|
||
paymentRequest.countryCode = countryCode | ||
paymentRequest.currencyCode = currency | ||
paymentRequest.supportedNetworks = ApplePayPaymentHandler.supportedNetworks | ||
|
||
// Display the payment request. | ||
paymentController = PKPaymentAuthorizationController(paymentRequest: paymentRequest) | ||
paymentController?.delegate = self | ||
|
||
paymentController?.present(completion: { presented in | ||
if !presented { | ||
self.outputSubject.send(.failedToPresentPayment) | ||
} else { | ||
self.productId = productId | ||
} | ||
}) | ||
} | ||
} | ||
|
||
extension ApplePayPaymentHandler: PKPaymentAuthorizationControllerDelegate { | ||
func paymentAuthorizationController( | ||
_ controller: PKPaymentAuthorizationController, | ||
didAuthorizePayment payment: PKPayment, | ||
handler completion: @escaping (PKPaymentAuthorizationResult) -> Void | ||
) { | ||
paymentStatus = .success | ||
paymentData = payment.token.paymentData | ||
completion(PKPaymentAuthorizationResult(status: .success, errors: [])) | ||
} | ||
|
||
func paymentAuthorizationControllerDidFinish(_ controller: PKPaymentAuthorizationController) { | ||
controller.dismiss { | ||
// The payment sheet doesn't automatically dismiss once it has finished. Dismiss the payment sheet. | ||
DispatchQueue.main.async { | ||
guard | ||
let paymentData = self.paymentData, | ||
let productId = self.productId | ||
else { | ||
return | ||
} | ||
self.outputSubject.send(.paymentFinished(self.paymentStatus, productId, paymentData)) | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
// | ||
// ApplePayPayment.swift | ||
// | ||
// | ||
// Created by Roman Mishchenko on 02.06.2022. | ||
// | ||
|
||
import Foundation | ||
|
||
struct ApplePayPaymentInfo: Codable { | ||
let header: PaymnetHeader | ||
let data: String | ||
let signature: String | ||
let version: String | ||
} | ||
|
||
struct PaymnetHeader: Codable { | ||
let ephemeralPublicKey: String | ||
let publicKeyHash: String | ||
let transactionId: String | ||
} | ||
|
||
struct ApplePayPayment: Codable { | ||
let data: String | ||
let ephemeralPublicKey: String | ||
let publicKeyHash: String | ||
let transactionId: String | ||
let signature: String | ||
let version: String | ||
let sandbox: Bool | ||
let webAppId: String | ||
let productId: String | ||
let userId: String | ||
|
||
enum CodingKeys: String, CodingKey { | ||
case data | ||
case ephemeralPublicKey = "ephemeral_public_key" | ||
case publicKeyHash = "public_key_hash" | ||
case transactionId = "transaction_id" | ||
case signature | ||
case version | ||
case sandbox | ||
case webAppId = "web_app_id" | ||
case productId = "product_id" | ||
case userId = "user_id" | ||
} | ||
} |
Oops, something went wrong.