Skip to main content

Implementation Guide

Before starting implementation you should have completed Installation of the MPP SDK.

MPP SDK helps the Issuer with Apple Pay In-App Provisioning implementation. Apple Pay In-App Provisioning provides a credit or debit card issuer the ability to initiate the card provisioning process for Apple Pay directly from the Issuer's iOS app.

Cardholders will find the In-App Provisioning feature an extremely convenient method to provision their payment details into their iOS devices by avoiding the need to input card details manually. Apple Pay Wallet needs to be setup before payment card is provisioned to it. This is done via the PassKit framework. PassKit in Apple Developer Documentation.

info

Adding payment passes requires a special entitlement issued by Apple, see MPP SDK Installation Step 6.

Sequence Diagram

Apple Pay

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.

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

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.

PrimaryAccountIdentifier

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.
}
}

3. 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

When card is added to Apple Wallet, instead of hiding Add to Apple Wallet button the app can show Pay with Apple Pay button, tapping the button can present a specific card for payment. Use PKPassLibrary.present(pass: PKSecureElementPass) to select the card for payment.

Use PKPaymentButton class to create a Pay with Apple Pay button. iOS SDK provides a control with the correct appearance.

let payButton = PKPaymentButton(paymentButtonType: PKPaymentButtonType.inStore, paymentButtonStyle: PKPaymentButtonStyle.black)
payButton.frame = CGRect(x:45.0, y: 340.0, width: 320, height: 20)
view.addSubview(payButton)

PKPaymentButton

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 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.

Issuers can implement In-App Verification using one of the two methods:

  • Obtaining activation data with cryptographic OTP and passing to Apple Wallet
  • Server-side activation

Activation Data with Cryptographic OTP

This method uses a cryptographic value that is generated by the issuer and is validated by TSP.

Sequence Diagram

Apple Pay In-App Verification - Activation Data

Implementation

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

1. Initiate activation process

Apple Wallet application redirects the end user to the issuer application and the issuer application has to check that it is launched from the Apple Wallet. Apple Wallet uses appLaunchURL with appended query parameters that identify the pass to be activated.

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.

For example:

scheme://path?passTypeIdentifier=paymentpass.com.apple&action=verify&serialNumber=pr.prod.pod1_1...

Use passTypeIdentifier and serialNumber to identify selected pass for activation.

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.

2. Authenticate the cardholder

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

3. Find pass with specific serial number

Use method MeaPushProvisioning.secureElementPassWithSerialNumber(serialNumber) to find the selected pass for activation, and determine if activation state of a Secure Element Pass is set to requiresActivation.

4. Get activation data

Find the related card for specific pass, fetch cardId, cardSecret, and use MeaPushProvisioning.getActivationData(...) method to get the activation data:

MeaPushProvisioning.getActivationData(cardParams) { (data, error) in

if let activationData = data {
// Activation data received, proceed to the Pass activation
} else {
// Handle error.
}
}
5. 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.
}
}

Server-side Activation

This method allows to activate the pass using TSP APIs.

Sequence Diagram

Apple Pay In-App Verification - Server-side Activation

Implementation

1. Initiate activation process

This step is identical to other activation method, see Initiate activation process.

2. Authenticate the cardholder

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

3. Find pass with specific serial number

Use method MeaPushProvisioning.secureElementPassWithSerialNumber(serialNumber) to find the selected pass for activation, and determine if activation state of a Secure Element Pass is set to requiresActivation.

4. Complete activation

Use method MeaPushProvisioning.activateSecureElementPass(secureElementPass, paymentNetwork) to activate the pass.

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)

Debugging

Apple Wallet Logging

  1. Install WalletFull.mobileconfig profile by following Wallet Logging Instructions.
  2. Record Sysdiagnose Apple Pay logs by following the official documentation "Getting Started with Apple Pay: In-App Provisioning, Verification, Security, and Wallet Extensions - 9. Troubleshooting".

SDK Logging

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