问题描述
这是我第一次使用PassKit和SwiftUI处理大型项目.我正在尝试实现Apple Pay SwiftUI,并且由于尚无本地方法,因此我尝试将PKPaymentAuthorizationViewController包装在UIViewControllerRepresentable中,但是我不确定自己是否做得正确.
This is my first time working with PassKit and with SwiftUI on a big project. I'm trying to implement Apple Pay SwiftUI and since there isn't a native way to do so yet, I tried wrapped the PKPaymentAuthorizationViewController in UIViewControllerRepresentable, but I'm not sure if I'm doing it properly.
该视图正确显示,并且在单击以进行支付时似乎可以使用.我通过将视图绑定到isPresentingApplePay bool来控制显示窗口(请参见下文).问题应该在关闭窗口时发生.轻按cancel
按钮不会关闭视图.有时甚至不调用paymentAuthorizationViewControllerDidFinish委托函数.提交付款后 也会发生同样的事情.有时,didFinish委托会被调用,但视图不会关闭.我尝试传递绑定变量isPresentingApplePay并将其从didFinish设置为 false ,但它没有任何作用.使视图消失的唯一方法是点击苹果付款窗口的外部的任何部分.
The view displays properly and seems to work when clicking on it to pay. I control showing the window by binding the view to an isPresentingApplePay bool (see below). The issues happen when the window should be dismissed. Tapping on the cancel
button does not dismiss the view; sometimes it doesn't even call the paymentAuthorizationViewControllerDidFinish delegate function. The same thing happens after submitting the payment. Sometimes the didFinish delegate gets called but the view is not dismissed. I tried passing the binding variable isPresentingApplePay and setting it to false from the didFinish but it doesn't do anything. The only way to get the view to disappear is to tap on any part outside of the apple pay window.
有人知道我在做什么错吗?我有什么想念的吗?
Does anyone know what I'm doing wrong? Is there anything I'm totally missing?
通过将视图绑定到if statement
这是我的PKPaymentAuthorizationViewController包装器:
Here is my PKPaymentAuthorizationViewController wrapper:
import Foundation
import PassKit
import SwiftUI
struct ApplePayController: UIViewControllerRepresentable {
@Environment(\.presentationMode) var presentationMode
@EnvironmentObject var userData: UserData
@Binding var purchase: Purchase
@Binding var isPresenting: Bool
let items: [PKPaymentSummaryItem]
func updateUIViewController(_ uiViewController: PKPaymentAuthorizationViewController, context: Context) {
}
typealias UIViewControllerType = PKPaymentAuthorizationViewController
func makeUIViewController(context: Context) -> PKPaymentAuthorizationViewController {
let applePayManager = ApplePayManager(items: items)
let apm = applePayManager.paymentViewController()!
apm.delegate = context.coordinator
return apm
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, PKPaymentAuthorizationViewControllerDelegate {
var parent: ApplePayController
init(_ parent: ApplePayController) {
self.parent = parent
}
func paymentAuthorizationViewControllerDidFinish(_ controller: PKPaymentAuthorizationViewController) {
controller.dismiss(animated: true) {
self.parent.isPresenting = false
}
}
func paymentAuthorizationViewController(_ controller: PKPaymentAuthorizationViewController, didAuthorizePayment payment: PKPayment, handler completion: @escaping (PKPaymentAuthorizationResult) -> Void) {
print("did authorize payment")
}
func paymentAuthorizationViewControllerWillAuthorizePayment(_ controller: PKPaymentAuthorizationViewController) {
print("Will authorize payment")
}
}
class ApplePayManager: NSObject {
let currencyCode: String
let countryCode: String
let merchantID: String
let paymentNetworks: [PKPaymentNetwork]
let items: [PKPaymentSummaryItem]
init(items: [PKPaymentSummaryItem],
currencyCode: String = "USD",
countryCode: String = "US",
merchantID: String = "xxx.merchant.xxx",
paymentNetworks: [PKPaymentNetwork] = [PKPaymentNetwork.amex, PKPaymentNetwork.masterCard, PKPaymentNetwork.visa]) {
self.items = items
self.currencyCode = currencyCode
self.countryCode = countryCode
self.merchantID = merchantID
self.paymentNetworks = paymentNetworks
}
func paymentViewController() -> PKPaymentAuthorizationViewController? {
if PKPaymentAuthorizationViewController.canMakePayments(usingNetworks: paymentNetworks) {
let request = PKPaymentRequest()
request.currencyCode = self.currencyCode
request.countryCode = self.countryCode
request.supportedNetworks = paymentNetworks
request.merchantIdentifier = self.merchantID
request.paymentSummaryItems = items
request.merchantCapabilities = [.capabilityCredit, .capabilityDebit]
return PKPaymentAuthorizationViewController(paymentRequest: request)
}
return nil
}
}
}
这就是我在UIView中显示它的方式:
and this is how I show it in my UIView:
if isPresentingApplePay {
ApplePayController(purchase: self.$currentOrder.purchase, isPresenting: $isPresentingApplePay, items: self.createOrder(with: self.currentOrder.purchase)).environmentObject(self.userData)
}
推荐答案
我遇到了类似的问题,然后通过使用不与UIViewControllerRepresentable接口的另一种方法解决了.相反,我创建了一个单独的类来处理Apple Pay,如下所示
I have faced similar problem then I have solved by using another approach not interfacing UIViewControllerRepresentable. Instead I created a separate class for handling Apple Pay like following
import PassKit
typealias PaymentCompletionHandler = (Bool) -> Void
class PaymentHandler: NSObject {
static let supportedNetworks: [PKPaymentNetwork] = [
.amex,
.masterCard,
.visa
]
var paymentController: PKPaymentAuthorizationController?
var paymentSummaryItems = [PKPaymentSummaryItem]()
var paymentStatus = PKPaymentAuthorizationStatus.failure
var completionHandler: PaymentCompletionHandler?
func startPayment(completion: @escaping PaymentCompletionHandler) {
let amount = PKPaymentSummaryItem(label: "Ammount", amount: NSDecimalNumber(string: "8.88"), type: .final)
let tax = PKPaymentSummaryItem(label: "Tax", amount: NSDecimalNumber(string: "1.12"), type: .final)
let total = PKPaymentSummaryItem(label: "ToTal", amount: NSDecimalNumber(string: "10.00"), type: .pending)
paymentSummaryItems = [amount, tax, total];
completionHandler = completion
// Create our payment request
let paymentRequest = PKPaymentRequest()
paymentRequest.paymentSummaryItems = paymentSummaryItems
paymentRequest.merchantIdentifier = "merchant.com.YOURDOMAIN.YOURAPPNAME"
paymentRequest.merchantCapabilities = .capability3DS
paymentRequest.countryCode = "US"
paymentRequest.currencyCode = "USD"
paymentRequest.requiredShippingContactFields = [.phoneNumber, .emailAddress]
paymentRequest.supportedNetworks = PaymentHandler.supportedNetworks
// Display our payment request
paymentController = PKPaymentAuthorizationController(paymentRequest: paymentRequest)
paymentController?.delegate = self
paymentController?.present(completion: { (presented: Bool) in
if presented {
NSLog("Presented payment controller")
} else {
NSLog("Failed to present payment controller")
self.completionHandler!(false)
}
})
}
}
/*
PKPaymentAuthorizationControllerDelegate conformance.
*/
extension PaymentHandler: PKPaymentAuthorizationControllerDelegate {
func paymentAuthorizationController(_ controller: PKPaymentAuthorizationController, didAuthorizePayment payment: PKPayment, completion: @escaping (PKPaymentAuthorizationStatus) -> Void) {
// Perform some very basic validation on the provided contact information
if payment.shippingContact?.emailAddress == nil || payment.shippingContact?.phoneNumber == nil {
paymentStatus = .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)
paymentStatus = .success
}
completion(paymentStatus)
}
func paymentAuthorizationControllerDidFinish(_ controller: PKPaymentAuthorizationController) {
controller.dismiss {
DispatchQueue.main.async {
if self.paymentStatus == .success {
self.completionHandler!(true)
} else {
self.completionHandler!(false)
}
}
}
}
}
并为Apple Pay弹出窗口使用了以下按钮代码.
And used the following Button code for Apple Pay popup.
struct ContentView: View {
let paymentHandler = PaymentHandler()
var body: some View {
Button(action: {
self.paymentHandler.startPayment { (success) in
if success {
print("Success")
} else {
print("Failed")
}
}
}, label: {
Text("PAY WITH APPLE")
.font(Font.custom("HelveticaNeue-Bold", size: 16))
.padding(10)
.foregroundColor(.white)
}
}
}
最后,它工作正常
这篇关于使用SwiftUI实施Apple Pay的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!