Не удалось внедрить Apple Pay в SwiftUI

Я пытаюсь внедрить Apple Pay в свое приложение SwiftUI и не могу показать кнопку.

Я сделал это, используя UIViewRepresentable

import SwiftUI
import UIKit
import PassKit
import Foundation

struct ApplePayButton: UIViewRepresentable {
    func makeCoordinator() -> Coordinator {
        Coordinator()
    }
    
    func updateUIView(_ uiView: PKPaymentButton, context: Context) {

    }
    
    func makeUIView(context: Context) -> PKPaymentButton {
        let paymentButton = PKPaymentButton(paymentButtonType: .plain, paymentButtonStyle: .black)
        return paymentButton
    }
    
    class Coordinator: NSObject, PKPaymentAuthorizationViewControllerDelegate  {
        func paymentAuthorizationViewControllerDidFinish(_ controller: PKPaymentAuthorizationViewController) {
            //
        }

        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 = "EUR",
           countryCode: String = "AT",
           merchantID: String = "c.c.c",
           paymentNetworks: [PKPaymentNetwork] = [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
    }
}

Я не хочу использовать PKPaymentAuthorizationController, потому что хочу использовать родную кнопку.

Когда я нажимаю на кнопку, я получаю эту ошибку:

[General] Payment request is invalid: Error Domain=PKPassKitErrorDomain Code=1 "Invalid in-app payment request" UserInfo={NSLocalizedDescription=Invalid in-app payment request, NSUnderlyingError=0x600003aeebb0 {Error Domain=PKPassKitErrorDomain Code=1 "PKPaymentRequest must contain an NSArray property 'paymentSummaryItems' of at least 1 valid objects of class PKPaymentSummaryItem" UserInfo={NSLocalizedDescription=PKPaymentRequest must contain an NSArray property 'paymentSummaryItems' of at least 1 valid objects of class PKPaymentSummaryItem}}}

Вид:

struct PaymentView: View {
    @Environment(\.presentationMode) private var presentationMode
    @ObservedObject var requestViewModel: RequestViewModel
    
    var applePayManager = ApplePayManager(items: [
        PKPaymentSummaryItem(label: "Some Product", amount: 9.99)
    ])
    
    var body: some View {
        NavigationView {
            VStack {
                Text("By paying you agree to give the package to transporter.")
                // requestViewModel.respondToRequest(status: button.status)
                
                ApplePayButton()
                    .frame(width: 228, height: 40, alignment: .center)
                    .onTapGesture {
                        applePayManager.paymentViewController()
                    }
                
            }
            .navigationBarTitle("Payment")
            .navigationBarItems(trailing: Button(action: {
                presentationMode.wrappedValue.dismiss()
            }) {
                Text("Done")
            })
        }
    }
}

Что я здесь делаю неправильно?


person Rexhin    schedule 22.12.2020    source источник
comment
Как вы передаете предметы? Сообщение об ошибке говорит, что у вас нет элементов   -  person Paulw11    schedule 23.12.2020
comment
@Paulw11 Paulw11 Я добавил код просмотра в вопрос. var applePayManager = ApplePayManager(items: [ PKPaymentSummaryItem(label: "Some Product", amount: 9.99) ]) Нужно ли здесь устанавливать делегата? Потому что я пытался, но не смог   -  person Rexhin    schedule 23.12.2020
comment
Я не большой поклонник размещения объектов, как ваш ApplePayManager в представлениях, поскольку представления эфемерны. Передайте экземпляр из среды. Используйте отладчик, чтобы увидеть, действительно ли items пусто.   -  person Paulw11    schedule 23.12.2020
comment
Я сделал это request.paymentSummaryItems = [ PKPaymentSummaryItem(label: "Some Product", amount: 9.99) ] и теперь ничего не происходит. Итак, paymentViewController должен открыть платежный лист, верно? Что с делегатом? Мне нужно установить его в makeUIView(context: Context) -> PKPaymentButton Я пытался, но не смог, так как нет PKPaymentButtonDelegate @Paulw11   -  person Rexhin    schedule 23.12.2020
comment
Я также получаю это предупреждение: Result of call to 'paymentViewController()' is unused здесь .onTapGesture { applePayManager.paymentViewController() }   -  person Rexhin    schedule 23.12.2020
comment
Это правильно, потому что вы ничего не делаете с этим. Вам нужно фактически представить контроллер представления.   -  person Paulw11    schedule 23.12.2020
comment
Проверьте репозиторий Apple-Pay-SwiftUI-2.0 от @nelglez на GitHub и создайте свой сервис на его основе — это единственное работающее решение для SwiftUI 2.0.   -  person Christopher Lowiec    schedule 15.05.2021


Ответы (1)


На всякий случай, если кто-то еще борется, как я: вот полный код.

import Foundation
import PassKit

class PaymentHandler: NSObject, ObservableObject {
    func startPayment(paymentSummaryItems: [PKPaymentSummaryItem]) {
        
        // Create our payment request
        let paymentRequest = PKPaymentRequest()
        paymentRequest.paymentSummaryItems = paymentSummaryItems
        paymentRequest.merchantIdentifier = "merchant.de.xxx"
        paymentRequest.merchantCapabilities = .capability3DS
        paymentRequest.countryCode = "AT"
        paymentRequest.currencyCode = "EUR"
        paymentRequest.requiredShippingContactFields = [.phoneNumber, .emailAddress]
        paymentRequest.supportedNetworks = [.masterCard, .visa]
        
        // Display our payment request
        let paymentController = PKPaymentAuthorizationController(paymentRequest: paymentRequest)
        paymentController.delegate = self
        paymentController.present(completion: { (presented: Bool) in })
    }
}


/**
 PKPaymentAuthorizationControllerDelegate conformance.
 */
extension PaymentHandler: PKPaymentAuthorizationControllerDelegate {

    func paymentAuthorizationController(_ controller: PKPaymentAuthorizationController, didAuthorizePayment payment: PKPayment, completion: @escaping (PKPaymentAuthorizationStatus) -> Void) {
        completion(.success)
        print("paymentAuthorizationController completion(.success)")
    }

    func paymentAuthorizationControllerDidFinish(_ controller: PKPaymentAuthorizationController) {
        print("DidFinish")
    }
    
    func paymentAuthorizationControllerWillAuthorizePayment(_ controller: PKPaymentAuthorizationController) {
        print("WillAuthorizePayment")
    }

}

struct PaymentButton: UIViewRepresentable {
    func updateUIView(_ uiView: PKPaymentButton, context: Context) { }
    
    func makeUIView(context: Context) -> PKPaymentButton {
        return PKPaymentButton(paymentButtonType: .plain, paymentButtonStyle: .automatic)
    }
}

Используйте его в представлении:

PaymentButton()
    .frame(width: 228, height: 40, alignment: .center)
    .onTapGesture {
        paymentHandler.startPayment(paymentSummaryItems: paymentSummaryItems)
    }
person Rexhin    schedule 23.12.2020