Skip to content

Instantly share code, notes, and snippets.

@Lavmint
Last active March 14, 2021 16:21
Show Gist options
  • Save Lavmint/fe6399f4ac6b1eb22ff5e263c1cf53fe to your computer and use it in GitHub Desktop.
Save Lavmint/fe6399f4ac6b1eb22ff5e263c1cf53fe to your computer and use it in GitHub Desktop.
Apple pay template (not tested)
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