Last active
March 14, 2021 16:21
-
-
Save Lavmint/fe6399f4ac6b1eb22ff5e263c1cf53fe to your computer and use it in GitHub Desktop.
Apple pay template (not tested)
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
import Foundation | |
import Combine | |
import PassKit | |
enum Request { | |
static var applePay: PaymentRequestBuilder { | |
return PaymentRequestBuilder() | |
} | |
} | |
enum ApplePayError: Error { | |
case noSupportedCards | |
case paymentUnavailable | |
case paymentFailed | |
case controllerPresentationFailed | |
} | |
class PaymentRequestBuilder { | |
private var _paymentRequest = PKPaymentRequest() | |
init() { | |
_paymentRequest.merchantIdentifier = "merchant.ru.angelsit.parking" | |
_paymentRequest.merchantCapabilities = .capability3DS | |
// _paymentRequest.supportedCountries = ["RU"] //testing cards won't work if uncommented | |
_paymentRequest.countryCode = "RU" | |
_paymentRequest.currencyCode = "RUB" | |
_paymentRequest.supportedNetworks = [.visa, .masterCard] | |
} | |
func balance(amount: Float) -> Self { | |
_paymentRequest.paymentSummaryItems = [ | |
PKPaymentSummaryItem(label: "Пополнение баланса", amount: NSDecimalNumber(value: amount)) | |
] | |
return self | |
} | |
func paymentProvider() -> ApplePayProvider { | |
return ApplePayProvider(request: _paymentRequest) | |
} | |
} | |
class ApplePayProvider: NSObject, PKPaymentAuthorizationControllerDelegate { | |
struct PaymentPublisher: Publisher { | |
typealias Output = Void | |
typealias Failure = Error | |
fileprivate var provider: ApplePayProvider | |
func receive<S: Subscriber>(subscriber: S) where S.Input == Output, S.Failure == Failure { | |
do { | |
try provider.start() | |
_ = subscriber.receive() | |
} catch { | |
subscriber.receive(completion: .failure(error)) | |
} | |
} | |
} | |
public func publisher() -> AnyPublisher<PKPayment, Error> { | |
PaymentPublisher(provider: self) | |
.flatMap({ | |
self._delegate.eraseToAnyPublisher() | |
}) | |
.eraseToAnyPublisher() | |
} | |
private let _delegate = PassthroughSubject<PKPayment, Error>() | |
private let _request: PKPaymentRequest | |
private let _controller: PKPaymentAuthorizationController | |
init(request: PKPaymentRequest) { | |
_request = request | |
_controller = PKPaymentAuthorizationController(paymentRequest: _request) | |
super.init() | |
} | |
func start() throws { | |
let canMakePayments = PKPaymentAuthorizationController.canMakePayments() | |
let canSetupCards = PKPaymentAuthorizationController.canMakePayments( | |
usingNetworks: _request.supportedNetworks | |
) | |
if canMakePayments { | |
//passthrought | |
} else if canSetupCards { | |
throw ApplePayError.noSupportedCards | |
} else { | |
throw ApplePayError.paymentUnavailable | |
} | |
_controller.delegate = self | |
_controller.present { (isPresented) in | |
if !isPresented { | |
self._delegate.send(completion: .failure(ApplePayError.controllerPresentationFailed)) | |
} | |
} | |
} | |
func paymentAuthorizationControllerDidFinish(_ controller: PKPaymentAuthorizationController) { | |
controller.dismiss { | |
self._delegate.send(completion: .finished) | |
} | |
} | |
func paymentAuthorizationController(_ controller: PKPaymentAuthorizationController, didAuthorizePayment payment: PKPayment, handler completion: @escaping (PKPaymentAuthorizationResult) -> Void) { | |
// Perform some very basic validation on the provided contact information | |
let errors = [Error]() | |
let status = PKPaymentAuthorizationStatus.success | |
// if payment.shippingContact?.postalAddress?.isoCountryCode != "US" { | |
// let pickupError = PKPaymentRequest.paymentShippingAddressUnserviceableError( | |
// withLocalizedDescription: "Sample App only picks up in the United States" | |
// ) | |
// let countryError = PKPaymentRequest.paymentShippingAddressInvalidError( | |
// withKey: CNPostalAddressCountryKey, | |
// localizedDescription: "Invalid country" | |
// ) | |
// errors.append(pickupError) | |
// errors.append(countryError) | |
// status = .failure | |
// } else { | |
// // Here you would send the payment token to your server or payment provider to process | |
// // Once processed, return an appropriate status in the completion handler (success, failure, etc) | |
// } | |
switch status { | |
case .success: | |
_delegate.send(payment) | |
default: | |
let error = errors.first ?? ApplePayError.paymentFailed | |
_delegate.send(completion: .failure(error)) | |
} | |
completion(PKPaymentAuthorizationResult(status: status, errors: errors)) | |
} | |
func paymentAuthorizationController(_ controller: PKPaymentAuthorizationController, didSelectPaymentMethod paymentMethod: PKPaymentMethod, handler completion: @escaping (PKPaymentRequestPaymentMethodUpdate) -> Void) { | |
let paymentSummaryItems = _request.paymentSummaryItems | |
// The didSelectPaymentMethod delegate method allows you to make changes when the user updates their payment card | |
// Here we're applying a $2 discount when a debit card is selected | |
// guard paymentMethod.type == .debit else { | |
// completion(PKPaymentRequestPaymentMethodUpdate(paymentSummaryItems: paymentSummaryItems)) | |
// return | |
// } | |
// | |
// var discountedSummaryItems = paymentSummaryItems | |
// let discount = PKPaymentSummaryItem( | |
// label: "Debit Card Discount", | |
// amount: NSDecimalNumber(string: "-2.00") | |
// ) | |
// discountedSummaryItems.insert(discount, at: paymentSummaryItems.count - 1) | |
// | |
// if let total = paymentSummaryItems.last { | |
// total.amount = total.amount.subtracting(2.0) | |
// } | |
completion(PKPaymentRequestPaymentMethodUpdate(paymentSummaryItems: paymentSummaryItems)) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment