开发App实现Wallet支付卡添加:已获权限,如何正确调用添加界面?
Implementing PKAddPaymentPassRequestController for Wallet Card Provisioning
Hey there! Since you’ve already secured the com.apple.developer.payment-pass-provisioning entitlement and updated your provisioning profile, let’s dive into the concrete steps to get the Wallet card addition flow working smoothly.
Key Implementation Steps
1. Double-Check Prerequisites
Before writing code, quickly confirm these to avoid avoidable headaches:
- Open your Xcode project, go to Signing & Capabilities, ensure the Wallet capability is enabled, and Payment Pass Provisioning is checked.
- Verify your entitlements file includes
com.apple.developer.payment-pass-provisioning(Xcode usually adds this automatically when enabling the capability, but it’s worth a quick check).
2. Core Flow Code
The workflow centers on creating a PKAddPaymentPassRequest, initializing the controller, and handling callbacks. Here’s a practical implementation:
Step 2.1: Fetch Encrypted Card Data from Your Backend
Apple strictly requires sensitive card data to be encrypted by your backend (never generate this client-side!). You’ll need three key pieces of data from your server:
encryptedPassData: The encrypted payment pass payloadactivationData: Optional activation data for the cardephemeralPublicKey: The public key used to encrypt the pass data
Step 2.2: Initialize the Request & Controller
import PassKit class CardProvisioningManager: NSObject, PKAddPaymentPassViewControllerDelegate { func startCardProvisioning() { // First, check if the device supports adding payment passes guard PKAddPaymentPassViewController.canAddPaymentPass() else { // Show user-friendly error: Device doesn't support Apple Pay/Wallet provisioning print("Device cannot add payment passes") return } // Fetch encrypted data from your backend (replace with your actual API call) fetchEncryptedCardData { [weak self] result in guard let self = self else { return } switch result { case .success(let cardData): // Create the provisioning request let request = PKAddPaymentPassRequest() request.encryptedPassData = cardData.encryptedPassData request.activationData = cardData.activationData request.ephemeralPublicKey = cardData.ephemeralPublicKey // Initialize the Wallet add card controller guard let addCardController = PKAddPaymentPassViewController(request: request, delegate: self) else { print("Failed to initialize PKAddPaymentPassViewController") return } // Present the controller (must run on main thread for UI operations) DispatchQueue.main.async { // Replace with your actual presenting view controller UIApplication.shared.keyWindow?.rootViewController?.present(addCardController, animated: true) } case .failure(let error): print("Failed to fetch encrypted card data: \(error.localizedDescription)") } } } // MARK: - PKAddPaymentPassViewControllerDelegate func addPaymentPassViewController(_ controller: PKAddPaymentPassViewController, didFinishAdding pass: PKPaymentPass?, error: Error?) { controller.dismiss(animated: true) { if let error = error { // Handle failure: Show user error message (e.g., "Card addition failed, please retry") print("Failed to add card: \(error.localizedDescription)") } else if let pass = pass { // Handle success: Confirm card was added to Wallet (e.g., show a success alert) print("Successfully added card: \(pass.primaryAccountIdentifier)") } } } // Optional: Use this method if you need to generate the request dynamically (instead of pre-fetching) func addPaymentPassViewController(_ controller: PKAddPaymentPassViewController, generateRequestWithCertificateChain certificates: [Data], nonce: Data, nonceSignature: Data, completionHandler: @escaping (PKAddPaymentPassRequest?) -> Void) { // Call your backend to generate encrypted pass data using the provided certificates/nonce fetchDynamicEncryptedCardData(certificates: certificates, nonce: nonce, nonceSignature: nonceSignature) { request in completionHandler(request) } } // Helper method to simulate backend API call (replace with your real network logic) private func fetchEncryptedCardData(completion: @escaping (Result<(encryptedPassData: Data, activationData: Data?, ephemeralPublicKey: Data), Error>) -> Void) { // In production, this would be a network request to your server let dummyEncryptedData = Data() let dummyEphemeralKey = Data() completion(.success((dummyEncryptedData, nil, dummyEphemeralKey))) } private func fetchDynamicEncryptedCardData(certificates: [Data], nonce: Data, nonceSignature: Data, completion: @escaping (PKAddPaymentPassRequest?) -> Void) { // Implement your dynamic request generation logic here completion(nil) } }
3. Critical Notes & Troubleshooting
- Test on Real Devices Only: The Wallet card provisioning flow doesn’t work on simulators—always use a physical iOS device that supports Apple Pay.
- Backend Encryption Compliance: Your server must use Apple’s root certificates and your payment network’s encryption keys to generate
encryptedPassData. Follow Apple’s Payment Pass Kit documentation closely for encryption specifications. - Main Thread for UI: Always present the
PKAddPaymentPassViewControlleron the main thread—all iOS UI operations require this. - Entitlement Fixes: If the controller fails to initialize, download a fresh provisioning profile from the Apple Developer Portal, restart Xcode, and clean your project build folder.
内容的提问来源于stack exchange,提问作者S P Balu Kommuri
相关产品推荐
相关产品推荐

