Implementation

Sequence Diagram

Sequence Diagram.

Card Data Parameters

Push provisioning flow is started by initializing MppCardDataParameters object with CardId, CardSecret or EncryptedPan. Initialize MppCardDataParams.
Guide for encryptedCardData generation can be found in Card Data Encryption manual.

let cardId = "<value>"
let cardSecret = "<value>"

let cardParams = MppCardDataParameters.init(cardId: cardId, cardSecret: cardSecret)

or

let encryptedCardData = "<value>"
let publicKeyFingerprint = "<value>"
let encryptedKey = "<value>"
let initialVector = "<value>"

let cardParams = MppCardDataParameters.init(encryptedCardData: encryptedCardData,
                                            publicKeyFingerprint: publicKeyFingerprint,
                                            encryptedKey: encryptedKey,
                                            initialVector: initialVector)

Loading Configuration

By default MPP SDK is loading mea_config configuration file, however a custom name can be used. When defining a custom name for the configuration file use MeaPushProvisioning.loadConfig(configFileName: String) to load the configuration. Configuration file can be placed anywhere in application bundle.

import MeaPushProvisioning

...

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

    MeaPushProvisioning.loadConfig("custom_config_name")

    ...

    return true
}

ℹ️

INFO

MPP SDK raises an exception if configuration file is missing, broken or otherwise fails to load.

❗ Double check that you added mea_config (or file with custom name) to specific target:

Target Membership

Initialize Apple Pay In-App Provisioning

MPP SDK provides MeaPushProvisioning class with initializeOemTokenization method to initiate Apple Pay In-App provisioning.

Initiate in-app push provisioning by using MeaPushProvisioning.initializeOemTokenization method and MppCardDataParameters parameter. Check if the payment card can be added to Apple Pay by using primaryAccountIdentifier in response. Go to Show or hide "Add to Apple Wallet" button.

Tokenization receipt in response data MppInitializeOemTokenizationResponseData has a validity determined by tokenizationResponseData.validFor value in milliseconds. During the validity time frame (by default 15 minutes) tokenization receipt can be used in MeaPushProvisioning.completeOemTokenization(tokenizationData), when tokenization receipt expires a new initiation is required.

ℹ️

INFO

primaryAccountIdentifier

  1. Value is always empty for the very first push provisioning of the specific card. Empty value indicates that card can be added to Apple Pay.
  2. Users may have different passes installed on different paired devices (for example, on an iPhone and an Apple Watch). This property allows to filter out the devices that already contain a matching pass.
  3. Once the value is fetched for a specific card app should cache primaryAccountIdentifier to avoid unnecessary MeaPushProvisioning.initializeOemTokenization calls every time when Add to Apple Wallet button should be shown or hidden.
let isPassLibraryAvailable = PKPassLibrary.isPassLibraryAvailable()
let canAddPaymentPass = PKAddPaymentPassViewController.canAddPaymentPass()

if (isPassLibraryAvailable && canAddPaymentPass) {
    MeaPushProvisioning.initializeOemTokenization(mppCardParameters) { (responseData, error) in
        if (responseData?.isValid())! {
            // Field primaryAccountIdentifier is always empty for the very first tokenization of the card.
            var canAddPaymentPassWithPAI = true
            if let primaryAccountIdentifier = responseData?.primaryAccountIdentifier, !primaryAccountIdentifier.isEmpty {
                if #available(iOS 13.4, *) {
                    canAddPaymentPassWithPAI = MeaPushProvisioning.canAddSecureElementPass(primaryAccountIdentifier: primaryAccountIdentifier)
                } else {
                    canAddPaymentPassWithPAI = MeaPushProvisioning.canAddPaymentPass(withPrimaryAccountIdentifier: primaryAccountIdentifier)
                }
            }

            if (canAddPaymentPassWithPAI) {
                self.tokenizationResponseData = responseData;

                // Show "Add to Apple Wallet" button.
                // ...
            }
        }
    }
}

Add to Apple Wallet Button

Use PKAddPassButton class to create an Add to Apple Wallet button. iOS SDK provides a control with the correct appearance.

let addPassButton = PKAddPassButton(addPassButtonStyle: PKAddPassButtonStyle.black)
addPassButton.frame = CGRect(x:45.0, y: 340.0, width: 320, height: 20)
view.addSubview(addPassButton)
PKAddPassButton image

Show or Hide Add to Apple Wallet Button

ℹ️

INFO

associatedApplicationIdentifiers

Correct configuration of associatedApplicationIdentifiers allows the respective app to see, access and activate your payment passes. [[PKPassLibrary new] passes], [[PKPassLibrary new] remotePaymentPasses] / [[PKPassLibrary new] remoteSecureElementPasses] and [passLibrary canAddPaymentPassWithPrimaryAccountIdentifier:] / [passLibrary canAddSecureElementPassWithPrimaryAccountIdentifier:] will ONLY return passes if App ID (TeamID.BundleID) is specified in associatedApplicationIdentifiers on token service provider side.

App ID is constructed by combining the Team ID with the App bundle ID, for example, A1B2C3D4E5.com.thebank.mobileapp.
TeamID is the Apple Developer team which is used to distribute your app via App Store. BundleID is the ID of your app.

App is responsible to check and decide if "Add to Apple Wallet" should be shown or hidden for the user using primaryAccountIdentifier or primaryAccountNumberSuffix. Using primaryAccountNumberSuffix is useful when Customer Service API backend connection is not available. Button should be shown only when card is not added to Apple Pay already. Only one of the following approaches should be used.

Use the following methods to check whether the card with specific primaryAccountNumberSuffix can be added to Apple Wallet:

  • MeaPushProvisioning.canAddPaymentPass(withPrimaryAccountNumberSuffix:) (before iOS 13.4)
  • MeaPushProvisioning.canAddSecureElementPass(withPrimaryAccountNumberSuffix:) (iOS 13.4+)

Use the following methods to check whether the card with specific primaryAccountIdentifier can be added to Apple Wallet:

  • MeaPushProvisioning.canAddPaymentPass(withPrimaryAccountIdentifier:) (before iOS 13.4)
  • MeaPushProvisioning.canAddSecureElementPass(withPrimaryAccountIdentifier:) (iOS 13.4+)

Return value indicates whether the app can add a card to Apple Pay for the provided primary account identifier. This function does not provide specific information about Apple Wallet on iPhone or Apple Watch.

Caching PrimaryAccountIdentifier

App should cache primaryAccountIdentifier value for the specific card to avoid unnecessary MeaPushProvisioning.initializeOemTokenization calls every time when Add to Apple Wallet button should be shown or hidden. The value can be cached permanently.

mpp-ios-apple-pai-usage

Check if Card Can Be Added to Apple Wallet on iPhone or Apple Watch

ℹ️

INFO

Read further only when app needs to detect if PKPaymentPass or PKSecureElementPass can be added specifically to Apple Wallet on iPhone or Apple Watch. Use for Apple Watch only when it is paired to iPhone.

  1. Use one of the methods listed above to check whether the card can be added to Apple Wallet on iPhone or Apple Watch. Return value of the function indicates if card can be added, however it does not provide specific information about Apple Wallet on iPhone or Apple Watch.
  2. Detect if Apple Watch is paired with iPhone.
import WatchConnectivity

...

if WCSession.isSupported() { // Check if the iPhone supports Watch Connectivity session handling.
    let session = WCSession.default()
    session.delegate = self
    session.activate() // Activate the session, asynchronous call to delegate method below.
}

...

func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
    if activationState == .activated && session.isPaired {
        // Apple Watch is paired with iPhone.
    }
}
  1. Check if PKPaymentPass or PKSecureElementPass is already added to Apple Wallet on iPhone or Apple Watch:
  • Apple Wallet on iPhone:
// Before iOS 13.4
let cardExists = MeaPushProvisioning.paymentPassExists(withPrimaryAccountIdentifier: "<primaryAccountIdentifier>")
let cardExists = MeaPushProvisioning.paymentPassExists(withPrimaryAccountNumberSuffix: "4321")

// iOS 13.4+
let cardExists = MeaPushProvisioning.secureElementPassExists(withPrimaryAccountIdentifier: "<primaryAccountIdentifier>")
let cardExists = MeaPushProvisioning.secureElementPassExists(withPrimaryAccountNumberSuffix: "4321")
  • Apple Watch:
// Before iOS 13.4
let cardExists = MeaPushProvisioning.remotePaymentPassExists(withPrimaryAccountIdentifier: "<primaryAccountIdentifier>")
let cardExists = MeaPushProvisioning.remotePaymentPassExists(withPrimaryAccountNumberSuffix: "4321")

// iOS 13.4+
let cardExists = MeaPushProvisioning.remoteSecureElementPassExists(withPrimaryAccountIdentifier: "<primaryAccountIdentifier>")
let cardExists = MeaPushProvisioning.remoteSecureElementPassExists(withPrimaryAccountNumberSuffix: "4321")

Pay with Apple Pay Button

Add Payment Pass View Controller

Mobile application have to create an instance of PKAddPaymentPassViewController class, which lets your app prompt the user to add pass to the pass library.

PKAddPaymentPassViewController have to be initialized using data retrieved in initializeOemTokenization:completionHandler: handler and a delegate that implements PKAddPaymentPassViewControllerDelegate protocol.

let addPaymentPassRequestConfiguration = self.tokenizationResponseData?.addPaymentPassRequestConfiguration
addPaymentPassRequestConfiguration?.cardholderName = "Cardholder Name"

let paymentPassController =
    PKAddPaymentPassViewController.init(requestConfiguration: addPaymentPassRequestConfiguration!, delegate: self)

self.present(paymentPassController!, animated: true, completion: nil)

Complete Apple Pay In-App Provisioning

For completion of provisioning MeaPushProvisioning class provides completeOemTokenization method. This method exchanges Apple certificates and signature with Issuer Host.

Delegate should implement PKAddPaymentPassViewControllerDelegate protocol to call completeOemTokenization:completionHandler: method, once the data is exchanged PKAddPaymentPassRequest is passed to the handler to add the payment card to Apple Wallet. In the end and delegate method is invoked to inform you if request has succeeded or failed.

func addPaymentPassViewController(_ controller: PKAddPaymentPassViewController,
                                generateRequestWithCertificateChain certificates: [Data],
                                nonce: Data, nonceSignature: Data,
                                completionHandler handler: @escaping (PKAddPaymentPassRequest) -> Void) {

        let tokenizationData =
            MppCompleteOemTokenizationData(tokenizationReceipt: self.tokenizationResponseData.tokenizationReceipt,
                                            certificates: certificates,
                                            nonce: nonce,
                                            nonceSignature: nonceSignature)

        MeaPushProvisioning.completeOemTokenization(tokenizationData){ (responseData, error) in

            if  (responseData?.isValid())! {
                handler((responseData?.addPaymentPassRequest)!)
            }
        }
    }

func addPaymentPassViewController(_ controller: PKAddPaymentPassViewController,
                                didFinishAdding pass: PKPaymentPass?,
                                error: Error?) {

    self.presentedViewController?.dismiss(animated: true, completion: nil)
}

In-App Verification

Apple Pay In-App Verification provides a credit or debit card issuer the means to utilize its iOS mobile app for the verification of users and activation of a payment pass previously provisioned via Apple Wallet.

Users will find the In-App Verification feature a convenient method to activate their recently provisioned payment passes in addition to existing SMS/E-mail OTP and call center ID&V methods.

In-App Verification Sequence Diagram

In-App Verification Sequence Diagram

Adding In-App Verification

Issuer's app must do the following to provide app-to-app verification functionality:

1. Initiate activation process

Prompt the user immediately after initial login to activate a pass that is awaiting activation. Alternatively, the app can trigger a different experience if the user is deep linked from Apple Wallet for activation via the ‘appLaunchURL’.

ℹ️

INFO

"appLaunchURL":["scheme://path"]
This key is passed to the issuer’s app when the user clicks on the issuer’s app icon from the back of the pass in Apple Wallet or when the issuer’s app is opened during In-App Verification.
Defining a custom URL scheme for your app.

2. Authenticate the cardholder

Use the standard issuer's app authentication to verify the user.

3. Get activation code

Application should determine if activation state of a Secure Element Pass is set to requiresActivation.

Use MeaPushProvisioning.getActivationData(...) method to get activation code:

MeaPushProvisioning.getActivationData(cardParams) { (data, error) in
    if let activationData = data {
        // Activation data received, proceed to the Pass activation
    } else {
        // Handle error.
    }
}

4. Complete activation

Activate secureElementPass using the activation data received in previous step:

MeaPushProvisioning.activate(secureElementPass, withActivationData: activationData) { (activated, error) in
    if (activated) {
        // Pass activated successfully.
    } else {
        // Handle error.
    }
}

Handle Apple Wallet Change Notifications

PassKit posts a PKPassLibraryDidChangeNotification notification when a pass is added or removed on iPhone device, and a PKPassLibraryRemotePaymentPassesDidChangeNotification notification is posted when a pass is added or removed on a paired Watch.

Listen to these notifications to refresh the state of Add to Apple Wallet button. These notification are received and can be handled when app is in foreground.

Start listening to change notifications.

let notificationCenter = NotificationCenter.default
notificationCenter.addObserver(self, selector: #selector(self.handleChangeNotification(notification:)), name: NSNotification.Name.PKPassLibraryDidChange, object: nil)
notificationCenter.addObserver(self, selector: #selector(self.handleChangeNotification(notification:)), name: NSNotification.Name.PKPassLibraryRemotePaymentPassesDidChange, object: nil)

...

@objc func handleChangeNotification(notification: Notification) {
    // The notification’s user info dictionary describes the changes.
    // Refresh the state of the Add to Apple Wallet button here.
}

Stop listening to change notifications.

NotificationCenter.default.removeObserver(self, name: NSNotification.Name.PKPassLibraryDidChange, object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.PKPassLibraryRemotePaymentPassesDidChangeNotification, object: nil)

Wallet Extensions

ℹ️

INFO

Support for Wallet Extensions (In-App Provisioning Extensions) is mandatory.

References

  • Refer to section "Wallet Extensions" in "Getting Started with Apple Pay In-App Provisioning, Verification, and Security v4" guide shared by Apple to Issuer when signing the Apple Pay contract.
  • See Apple WWDC 2020 - Adding Cards to Apple Pay to learn about Apple introducing In-App Provisioning Extensions.
  • See Creating an App Extension to learn about App Extensions.

Overview

Wallet Extensions (In-App Provisioning or Issuer Extensions) make it easier for users to know that they can add a payment pass to Apple Pay by improving discoverability from right within Apple Wallet.

This functionality provides users the ability to have the in-app experience of adding a payment pass, but it is initiated directly inside of Apple Wallet. The process starts and finishes within Apple Wallet and is a convenient method for users to provision their payment passes. Apple introduced Wallet Extensions in iOS 14. Issuer application needs to provide special App Extensions to Apple Wallet to be able to request and provision available cards.

ℹ️

INFO

  1. Issuer app needs to be installed and the user needs to have opened the app at least once so that Apple Wallet knows that there are payment passes available. Apple Wallet can also prompt for user authentication when adding a payment pass.
  2. Use of the Wallet Extension requires the same entitlement file used for In-App Provisioning: com.apple.developer.payment-pass-provisioning.
  3. In order for Wallet to hide passes that user already has added to the device, passes should include the extension's bundle identifier in associatedApplicationIdentifiers on token service provider side.

Issuer Provisioning Extensions Sequence Diagram

Issuer Provisioning Extensions Sequence Diagram

Adding Wallet Extensions

Wallet Extensions feature is app extension based and lets the issuer app extend custom functionality and content, and make it available in Apple Wallet. All of the methods are triggered by Apple Wallet and the app extensions ability to enable behavior is solely based on the completion handlers and return values. This feature relies on two extensions:

  1. Issuer app should provide a non-UI extension (subclass of PKIssuerProvisioningExtensionHandler) to report on the status of the extension and the payment passes available for provisioning like when adding payment passes to Apple Pay from within the issuer app.
  2. Issuer app should provide a UI extension (UIViewController that conforms to PKIssuerProvisioningExtensionAuthorizationProviding) to perform authentication of the user if the non-UI extension reports in its status that authentication is required. The UI extension is a separate screen that uses the same issuer app login credentials. UI extension is not a redirect to the issuer app.

❗❗❗ Wallet Extensions do not use a specific extension type template.

So this means you need to create some extension (for example Intents Extension) and remove all unnecessary auto-generated files. Leave only Info.plist and Handler class file.

1. Creating App Extensions

Select Intents Extension as the base. Create two app extensions IssuerNonUIExtension and IssuerUIExtension following the steps below.

  • Add an app extension to Xcode app project, choose File > New > Target, select iOS > Application Extension > Intents Extension.
iOS > Application Extension > Intents Extension
  • Set options for the new target. Create a unique bundle ID for the extension and add App IDs to associatedApplicationIdentifiers in PNO metadata.

For example,

A1B2C3D4E5.com.bank.app
A1B2C3D4E5.com.bank.app.IssuerNonUIExtension
A1B2C3D4E5.com.bank.app.IssuerUIExtension
mpp-ios-extensions-create-02
  • Activate the created scheme, if asked.
  • Add PassKit.framework to Frameworks and Libraries of the IssuerNonUIExtension and IssuerUIExtension targets, and remove Intents.framework which is not needed.
  • Remove Intents.framework of the IssuerUIExtension target.
mpp-ios-extensions-create-03

2. App Extensions Configuration

  • Register both app extension bundle identifiers in Apple Developer portal Identifiers section. Create provisioning profiles accordingly.
  • Create a new App Group and add the main app and both app extension bundle identifiers to the app group, so data can be shared between them.
  • Add mea_config to IssuerNonUIExtension which is consuming MPP SDK. App extension is run separately from the main app process, so SDK is initialized separately.

3. Implementing Issuer Authorization Provider Extension

  • IssuerAuthorizationExtensionHandler UI extension should conform to PKIssuerProvisioningExtensionAuthorizationProviding protocol. Apple Wallet interrogates the issuer app to determine the user's authorization status, and the authorization UI extension performs user authentication.
  • App UI extension has a memory limit of 60 MB, developers are responsible to optimize the code and libraries to fit this requirement.

IssuerAuthorizationExtensionHandler.swift


import PassKit
import UIKit

@available(iOS 14.0, *)
class IssuerAuthorizationExtensionHandler: UIViewController, PKIssuerProvisioningExtensionAuthorizationProviding {
    var completionHandler: ((PKIssuerProvisioningExtensionAuthorizationResult) -> Void)?

    override func viewDidLoad() {
        super.viewDidLoad()
        // Set up view and authenticate user.
    }

    func authenticateUser() {
        let userAuthenticated = true // User authentication outcome.

        let authorizationResult: PKIssuerProvisioningExtensionAuthorizationResult = userAuthenticated ? .authorized : .canceled

        self.completionHandler?(authorizationResult)
    }
}

4. Implementing Issuer Extension Handler

  • IssuerExtensionHandler non-UI class must be a subclass of PKIssuerProvisioningExtensionHandler. Issuer app must be installed and the user must open the issuer app at least once for the system to call the issuer extension handler.
  • App Non-UI extension has a memory limit of 55 MB, developers are responsible to optimize the code and libraries to fit this requirement.

❗❗❗ System will create new instance of PKIssuerProvisioningExtensionHandler for every method call

So this means you can not share data between method calls via private class properties!!!
But you may use another approaches, like global variables outside of the class.

IssuerExtensionHandler.swift

import PassKit
import MeaPushProvisioning

@available(iOS 14.0, *)
class IssuerExtensionHandler: PKIssuerProvisioningExtensionHandler {

    func status(completion: @escaping (PKIssuerProvisioningExtensionStatus) -> Void) {
        // Determines if there is a pass available and if adding the pass requires authentication.
        // The completion handler takes a parameter status of type PKIssuerProvisioningExtensionStatus that indicates
        // whether there are any payment cards available to add as Wallet passes.

        // PKIssuerProvisioningExtensionStatus has the following properties:
        // requiresAuthentication: Bool - authorization required before passes can be added.
        // passEntriesAvailable: Bool - passes will be available to add (at least one).
        // remotePassEntriesAvailable: Bool - passes will be available to add on the remote device (at least one).

        // The handler should be invoked within 100ms. The extension is not displayed to the user in Wallet if this criteria is not met.
    }

    func passEntries(completion: @escaping ([PKIssuerProvisioningExtensionPassEntry]) -> Void) {
        // Finds the list of passes available to add to an iPhone.
        // The completion handler takes a parameter entries of type Array<PKIssuerProvisioningExtensionPassEntry> representing
        // the passes that are available to add to Wallet.

        // Call MeaPushProvisioning.initializeOemTokenization(cardParams, completionHandler: { (data: MppInitializeOemTokenizationResponseData, error: Error?) in ... }) and initialize PKIssuerProvisioningExtensionPaymentPassEntry for each card that can be added to Wallet and add to the array.
        // Use addPaymentPassRequestConfiguration of MppInitializeOemTokenizationResponseData object to set addRequestConfiguration.

        // PKIssuerProvisioningExtensionPaymentPassEntry has the following properties:
        // art: CGImage - image representing the card displayed to the user. The image must have square corners and should not include personally identifiable information like user name or account number.
        // title: String - a name for the pass that the system displays to the user when they add or select the card.
        // identifier: String - an internal value the issuer uses to identify the card. This identifier must be stable.
        // addRequestConfiguration: PKAddPaymentPassRequestConfiguration - the configuration data used for setting up and displaying a view controller that lets the user add a payment pass.

        // Do not return payment passes that are already present in the user’s pass library.
        // The handler should be invoked within 20 seconds or will be treated as a failure and the attempt halted.
    }

    func remotePassEntries(completion: @escaping ([PKIssuerProvisioningExtensionPassEntry]) -> Void) {
        // Finds the list of passes available to add to an Apple Watch.
        // The completion handler takes a parameter entries of type Array<PKIssuerProvisioningExtensionPassEntry> representing
        // the passes that are available to add to Apple Watch.

        // Call MeaPushProvisioning.initializeOemTokenization(cardParams, completionHandler: { (data: MppInitializeOemTokenizationResponseData, error: Error?) in ... }) and initialize PKIssuerProvisioningExtensionPaymentPassEntry for each card that can be added to Wallet and add to the array.
        // Use addPaymentPassRequestConfiguration of MppInitializeOemTokenizationResponseData object to set addRequestConfiguration.

        // PKIssuerProvisioningExtensionPaymentPassEntry has the following properties:
        // art: CGImage - image representing the card displayed to the user. The image must have square corners and should not include personally identifiable information like user name or account number.
        // title: String - a name for the pass that the system displays to the user when they add or select the card.
        // identifier: String - an internal value the issuer uses to identify the card. This identifier must be stable.
        // addRequestConfiguration: PKAddPaymentPassRequestConfiguration - the configuration data used for setting up and displaying a view controller that lets the user add a payment pass.

        // Do not return payment passes that are already present in the user’s pass library.
        // The handler should be invoked within 20 seconds or will be treated as a failure and the attempt halted.
    }

    func generateAddPaymentPassRequestForPassEntryWithIdentifier(
        _ identifier: String,
        configuration: PKAddPaymentPassRequestConfiguration,
        certificateChain certificates: [Data],
        nonce: Data,
        nonceSignature: Data,
        completionHandler completion: @escaping (PKAddPaymentPassRequest?) -> Void) {

        // Creates an object with the data the system needs to add a card to Apple Pay.

        // identifier: String - an internal value the issuer uses to identify the card.
        // configuration: PKAddPaymentPassRequestConfiguration - the configuration the system uses to add a secure pass. This configuration is prepared in methods passEntriesWithCompletion: and remotePassEntriesWithCompletion:.
        // certificates, nonce, nonceSignature - parameters are generated by Apple Pay identically to PKAddPaymentPassViewControllerDelegate methods.

        // The completion handler is called by the system for the data needed to add a card to Apple Pay.
        // This handler takes a parameter request of type PKAddPaymentPassRequestConfiguration that contains the card data the system needs to add a card to Apple Pay.

        // Call MeaPushProvisioning.completeOemTokenization(tokenizationData, completionHandler: { (data: MppCompleteOemTokenizationResponseData, error: Error?) in ... }), and
        // use addPaymentPassRequest of MppCompleteOemTokenizationResponseData to set request in completion handler.

        // The continuation handler must be called within 20 seconds or an error is displayed.
        // Subsequent to timeout, the continuation handler is invalid and invocations is ignored.
    }
}

5. Updating Extension's Info.plist

  • Modify NSExtension dictionary in extension's Info.plist, delete NSExtensionAttributes entry.

NSExtensionPointIdentifier and NSExtensionPrincipalClass should be specified in the extension Info.plist properties dictionary:

Non-UI App Extension

Values:

KeyTypeValue
NSExtensionPointIdentifierStringcom.apple.PassKit.issuer-provisioning
NSExtensionPrincipalClassString$(PRODUCT_MODULE_NAME).IssuerExtensionHandler
Info.plist
UI App Extension

Values:

KeyTypeValue
NSExtensionPointIdentifierStringcom.apple.PassKit.issuer-provisioning.authorization
NSExtensionPrincipalClassString$(PRODUCT_MODULE_NAME).IssuerAuthorizationExtensionHandler
Info.plist

6. Setting Code Signing Entitlements

  • Issuer Extensions use the same entitlement file used for issuer app In-App Provisioning.
entitlement file

Debugging

Use 'setDebugLoggingEnabled' method to enable/disable logging to console. Use 'versionName' and 'versionCode' methods to check the version of the SDK, when necessary.

import MeaPushProvisioning

...

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

    MeaPushProvisioning.setDebugLoggingEnabled(true)

    print(String(format: "SDK version: %@, code: %@", MeaPushProvisioning.versionName(), MeaPushProvisioning.versionCode()))

    ...

    return true
}

Testing in Sandbox Mode

Apple Pay In-App Provisioning entitlement com.apple.developer.payment-pass-provisioning only works with distribution provisioning profiles, which means that even after you obtain it, the only way to test the end-to-end push provisioning flow is by first distributing the app via TestFlight or the App Store.

Apple Pay Sandbox Testing: Sandbox Testing

References