Skip to main content

Push provisioning

To start Apple Wallet push provisioning, use PKAddPaymentPassViewController with PKAddPaymentPassRequestConfiguration.

The configuration should use the .ECC_V2 encryption scheme.

import PassKit

let configuration = PKAddPaymentPassRequestConfiguration(encryptionScheme: .ECC_V2)!

configuration.cardholderName = "John Doe"
configuration.primaryAccountSuffix = "1234" // Last 4 digits of the card number

// Required when provisioning to Apple Watch.
// Use deviceAccountIdentifier from the phone PassItem.
configuration.primaryAccountIdentifier = phonePassItem.deviceAccountIdentifier

let viewController = PKAddPaymentPassViewController(
    requestConfiguration: configuration,
    delegate: delegate
)

If the card is being added to Apple Watch, primaryAccountIdentifier should be set using deviceAccountIdentifier from the phone PassItem.

Delegate Implementation

The application must implement PKAddPaymentPassViewControllerDelegate.

In generateRequestWithCertificateChain, call the TMP API endpoint:

POST /issuer/push-provisioning/signed-cards

For Apple Pay, the request requires certificate, nonce, and nonceSignature. The TMP response returns activationData, ephemeralPublicKey, and encryptedData, which are required to create PKAddPaymentPassRequest.

Swift Example

import PassKit

final class AddPaymentPassDelegate: NSObject, PKAddPaymentPassViewControllerDelegate {

    func addPaymentPassViewController(
        _ controller: PKAddPaymentPassViewController,
        generateRequestWithCertificateChain certificates: [Data],
        nonce: Data,
        nonceSignature: Data,
        completionHandler handler: @escaping (PKAddPaymentPassRequest) -> Void
    ) {
        Task {
            do {
                let signedCard = try await signCard(
                    certificates: certificates,
                    nonce: nonce,
                    nonceSignature: nonceSignature
                )

                let request = PKAddPaymentPassRequest()
                request.activationData = Data(base64Encoded: signedCard.activationData)
                request.ephemeralPublicKey = Data(base64Encoded: signedCard.ephemeralPublicKey)
                request.encryptedPassData = Data(base64Encoded: signedCard.encryptedData)

                handler(request)
            } catch {
                controller.dismiss(animated: true)
            }
        }
    }

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

        if let error {
            // Handle provisioning error
            print("Apple Wallet provisioning failed: \\(error)")
            return
        }

        // Provisioning finished successfully
    }
}

Notes

  • The code above is simplified and should be adapted to the existing TMP integration layer.
  • Sensitive card data should not be handled directly in the mobile application unless explicitly required by the approved architecture.