# Push Provisioning

## Step 1: Showing the **Add to Wallet** button <a href="#step-1-showing-the-add-to-wallet-button" id="step-1-showing-the-add-to-wallet-button"></a>

When the end user first launches the issuer application, it must determine whether to show or hide the **Add to Apple Wallet** button for a specific card.

As early as possible, determine whether the end user has an iPhone or an iPhone paired with an Apple Watch.

Then combine this pairing status with the Push Provisioning SDK `getToken` API result to decide whether to show or hide the **Add to Apple Wallet** button.

{% tabs %}
{% tab title="Swift" %}

```swift
import WatchConnectivity

class ViewController: UIViewController, TPCSDKProvisionDelegate, WCSessionDelegate {    
    // To store the isWatchPaired value before calling TPC function
    var isWatchPaired = false

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        if WCSession.isSupported() {
            let session = WCSession.default
            session.delegate = self
            session.activate()
        }
    }
    
    // MARK: WCSessionDelegate
    func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
        if error != nil {
            //handle error
        } else if activationState == .activated && session.isPaired {
            isWatchPaired = true
        }
    }
    
    func sessionDidBecomeInactive(_ session: WCSession) {
        // handle sessionDidBecomeInactive
        isWatchPaired = false
    }
    
    func sessionDidDeactivate(_ session: WCSession) {
        // handle sessionDidDeactivate
        isWatchPaired = false
    }
}
```

{% endtab %}

{% tab title="Objective-C" %}

```objective-c
#import <WatchConnectivity/WatchConnectivity.h>

@interface ViewController : UIViewController <TPCSDKProvisionDelegate, WCSessionDelegate>
@property BOOL isWatchPaired;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    if ([WCSession isSupported]) {
        WCSession* session = [WCSession defaultSession];
        session.delegate = self;
        [session activateSession];
    }
}

// MARK: WCSessionDelegate
- (void)session:(nonnull WCSession *)session activationDidCompleteWithState:(WCSessionActivationState)activationState error:(nullable NSError *)error { 
    if (error != nil) {
        // handle error
    } else if (activationState == WCSessionActivationStateActivated && session.isPaired == TRUE)  {
        _isWatchPaired = TRUE;
    }
}

- (void)sessionDidBecomeInactive:(nonnull WCSession *)session { 
    // handle sessionDidBecomeInactive
    _isWatchPaired = FALSE;
}

- (void)sessionDidDeactivate:(nonnull WCSession *)session { 
    // handle sessionDidDeactivate
    _isWatchPaired = FALSE;
}
```

{% endtab %}
{% endtabs %}

Then call the `getToken` API to update the result.

> <i class="fa-info-circle">:info-circle:</i>
>
> #### Note <a href="#note" id="note"></a>
>
> Refer to the [PKAddPassButton](https://developer.apple.com/documentation/passkit/pkaddpassbutton) guidelines for using the **Add to Apple Wallet** button in your issuer application.

The following applies to iOS SDK 2.0.0 and earlier.

{% tabs %}
{% tab title="Swift" %}

```swift
func getCardDigitizationResultWithFundingCardWithSchemeAndPayload() {
    let scheme = CardScheme.Mastercard
    let payload = "PKCS7 encrypted PAN"
    
    let card = FundingCard(scheme: scheme,
                            encryptedPayload: payload)
    
    TPCSDK.getCardDigitizationResult(card: card) { [weak self]
        (primaryAccountIdentifier, sdkResult, error) in
        self?.updateUI(for: sdkResult, sdkError: error)
    }
  }

  func getCardDigitizationResultWithPrimaryAccountIdentifier() {
    let primaryAccountIdentifier = "primaryAccountIdentifier"

    TPCSDK.getCardDigitizationResult(primaryAccountIdentifier: primaryAccountIdentifier) { [weak self]
        (primaryAccountIdentifier, sdkResult, error) in
        self?.updateUI(for: sdkResult, sdkError: error)
    }
  }

  func getCardDigitizationResultWithPrimaryAccountNumberSuffix() {
      let scheme = "SCHEME" // VISA, MASTERCARD, AMEX, DISCOVER
      let encryptedPayload = "PKCS7 encrypted PAN"
      let primaryAccountNumberSuffix = "Last 4 Digits of PAN"

      let card = FundingCard(schemeString: scheme,
              encryptedPayload: encryptedPayload)

      TPCSDK.getCardDigitizationResult(card: card, primaryAccountNumberSuffix: primaryAccountNumberSuffix) { [weak self]
          (primaryAccountIdentifier, sdkResult, error) in
          self?.updateUI(for: sdkResult, sdkError: error)
      }
  }

func updateUI(for sdkResult: CardDigitizationResult?, sdkError: Error?) {
    if let error = sdkError {
      // handle error
    } else if let sdkResult = sdkResult {
      if sdkResult.localPKPass?.secureElementPass?.passActivationState == .requiresActivation {
        // Refer to Token life cycle management for Activate action. 
        // There is pending card activation on iPhone.
      } else if sdkResult.remotePKPass?.secureElementPass?.passActivationState == .requiresActivation {
        // Refer to Token life cycle management for Activate action. 
        // There is pending card activation on Apple Watch.
      } else if sdkResult.localPKPass != nil && sdkResult.remotePKPass == nil {
        if isWatchPaired {
          // display "Add to Apple Wallet" button for Apple watch.
        } else {
          // Hide "Add to Apple Wallet" button.
        }
      } else if sdkResult.localPKPass == nil {
        // display "Add to Apple Wallet" button.
      } else if sdkResult.localPKPass != nil && sdkResult.remotePKPass != nil {
        // card is already provisioned. Hide "Add to Apple Wallet" button.
      }
    }
  }
```

{% endtab %}

{% tab title="Objective-C" %}

```objective-c
- (void)getCardDigitizationResultWithFundingCardWithSchemeAndPayload {
    CardScheme scheme = CardSchemeMastercard;
    NSString* payload = @"PKCS7 encrypted PAN";

    FundingCard* card = [[FundingCard alloc] initWithScheme:scheme
                                            encryptedPayload:payload];

    [TPCSDK getCardDigitizationResultWithCard:card
                     primaryAccountIdentifier:nil
                   primaryAccountNumberSuffix:nil
                                   completion:^(NSString* primaryAccountIdentifier, CardDigitizationResult* _Nullable sdkResult, NSError* _Nullable error) {
        [self updateUIForResult:sdkResult sdkError:error];
    }];
}

- (void)getCardDigitizationResultWithPrimaryAccountIdentifier {
    NSString* primaryAccountIdentifier = @"primaryAccountIdentifier";

    [TPCSDK getCardDigitizationResultWithCard:nil
                     primaryAccountIdentifier:primaryAccountIdentifier
                   primaryAccountNumberSuffix:nil
                                   completion:^(NSString* primaryAccountIdentifier, CardDigitizationResult* _Nullable sdkResult, NSError* _Nullable error) {
        [self updateUIForResult:sdkResult sdkError:error];
    }];
}

- (void)getCardDigitizationResultWithPrimaryAccountNumberSuffix {
    CardScheme scheme = CardSchemeMastercard;
    NSString* payload = @"PKCS7 encrypted PAN";
    NSString* primaryAccountNumberSuffix = @"Last 4 Digits of PAN";

    FundingCard * card = [[FundingCard alloc] initWithScheme:scheme
                                            encryptedPayload:payload];

    [TPCSDK getCardDigitizationResultWithCard:nil
                     primaryAccountIdentifier:nil
                   primaryAccountNumberSuffix:primaryAccountNumberSuffix
                                   completion:^(NSString* primaryAccountIdentifier, CardDigitizationResult* _Nullable sdkResult, NSError* _Nullable error) {
        [self updateUIForResult:sdkResult sdkError:error];
    }];
}

- (void)updateUIForResult:(CardDigitizationResult* _Nullable)sdkResult sdkError:(NSError * _Nullable) error {
    if (error != nil ) {
        // handle the error
    } else if(sdkResult != nil){
        if ( sdkResult.localPKPass.secureElementPass.passActivationState == PKSecureElementPassActivationStateRequiresActivation ) {
            // Refer to Token life cycle management for Activate action. 
            // There is pending card activation on iPhone.
        } else if ( sdkResult.remotePKPass.secureElementPass.passActivationState == PKSecureElementPassActivationStateRequiresActivation ) {
            // Refer to Token life cycle management for Activate action. 
            // There is pending card activation on Apple Watch.
        } else if ( sdkResult.localPKPass != nil && sdkResult.remotePKPass == nil ) {
            if ( [self isWatchPaired] ) {
                // display "Add to Apple Wallet" button for Apple watch.
            } else {
                // Hide "Add to Apple Wallet" button.
            }
        } else if ( sdkResult.localPKPass == nil ) {
            // display "Add to Apple Wallet" button.
        } else if ( sdkResult.localPKPass != nil && sdkResult.remotePKPass != nil ) {
            // card is already provisioned. Hide "Add to Apple Wallet" button.
        }
    }
}
```

{% endtab %}
{% endtabs %}

The following applies to iOS SDK 2.1.0 and later.

{% tabs %}
{% tab title="Swift" %}

```swift
func getTokenWithInputByLast4() {
    // Last four digits of the physical card (FPAN).
    let last4 = "1234" 
    let tokenInput = GetTokenInput(last4: last4)
    TPCSDK.getToken(input: tokenInput) { [weak self]
        (localPass, remotePass, error) in
        self?.updateUI(for: localPass, remotePass: remotePass, sdkError: error)
    }
}

// - Since: 2.3.0
func getTokenWithInputBySerialNumber() {
    // Serial number provided by Apple Pay.
    let serialNumber = "Serial number"
    let tokenInput = GetTokenInput(serialNumber: serialNumber)
    TPCSDK.getToken(input: tokenInput) { [weak self]
        (localPass, remotePass, error) in
        self?.updateUI(for: localPass, remotePass: remotePass, sdkError: error)
    }
}

func updateUI(for localPass: PKPass?, remotePass: PKPass?, sdkError: Error?) {
    if let error = sdkError {
        // Handles the error.
    } else {
        if localPass?.secureElementPass?.passActivationState == .requiresActivation {
            // Refer to Token life cycle management for Activate action. 
            // There is a pending card activation on iPhone.
        } else if remotePass?.secureElementPass?.passActivationState == .requiresActivation {
            // Refer to Token life cycle management for Activate action. 
            // There is pending card activation on Apple Watch.
        } else if localPass != nil && remotePass == nil {
            if isWatchPaired {
                // Displays the "Add to Apple Wallet" button for Apple watch.
            } else {
                // Hides the "Add to Apple Wallet" button.
            }
        } else if localPass == nil {
            // Displays the "Add to Apple Wallet" button.
        } else if localPass != nil && remotePass != nil {
            // Card is already provisioned. Hide the "Add to Apple Wallet" button.
        }
    }
}
```

{% endtab %}

{% tab title="Objective-C" %}

```objective-c
- (void)getTokenWithInputByLast4 {
    // Last four digits of the physical card (FPAN).
    NSString *last4 = @"1234";

    GetTokenInput *tokenInput = [[GetTokenInput alloc] initWithLast4:last4];

    [TPCSDK getTokenWithInput:tokenInput completion:^(PKPass *_Nullable localPass, PKPass *_Nullable remotePass, NSError *_Nullable error) {
        [self updateUIForLocalPass:localPass remotePass:remotePass sdkError:error];
    }];
}

// - Since: 2.3.0
- (void)getTokenWithInputBySerialNumber {
    // Serial number provided by Apple Pay.
    NSString *serialNumber = @"Serial number";

    GetTokenInput *tokenInput = [[GetTokenInput alloc] initWithSerialNumber:serialNumber];

    [TPCSDK getTokenWithInput:tokenInput completion:^(PKPass *_Nullable localPass, PKPass *_Nullable remotePass, NSError *_Nullable error) {
        [self updateUIForLocalPass:localPass remotePass:remotePass sdkError:error];
    }];
}

- (void)updateUIForLocalPass:(PKPass *_Nullable)localPass remotePass:(PKPass *_Nullable)remotePass sdkError:(NSError *_Nullable)error {
    if (error != nil) {
        // Handles the error.
    } else {
        if (localPass != nil && localPass.secureElementPass.passActivationState == PKSecureElementPassActivationStateRequiresActivation) {
            // Refer to Token life cycle management for Activate action. 
            // There is a pending card activation on iPhone.
        } else if (remotePass != nil && remotePass.secureElementPass.passActivationState == PKSecureElementPassActivationStateRequiresActivation) {
            // Refer to Token life cycle management for Activate action. 
            // There is pending card activation on Apple Watch.
        } else if (localPass != nil && remotePass == nil) {
            if ([self isWatchPaired]) {
                // Displays the "Add to Apple Wallet" button for Apple watch.
            } else {
                // Hides the "Add to Apple Wallet" button.
            }
        } else if (localPass == nil) {
            // Displays the "Add to Apple Wallet" button.
        } else if (localPass != nil && remotePass != nil) {
            // Card is already provisioned. Hide the "Add to Apple Wallet" button.
        }
    }
}
```

{% endtab %}
{% endtabs %}

> <i class="fa-exclamation-circle">:exclamation-circle:</i>
>
> #### Caution <a href="#caution" id="caution"></a>
>
> * If the card still appears as "NOT\_DIGITIZED" after provisioning, verify that the issuer application is configured correctly in the TSP portal.
> * The `TPCSDK.isCardDigitized` API is deprecated and not recommended. Use the `getToken` API instead. The `isCardDigitized` API does not indicate whether the card is pending activation or whether it is on an iPhone or Apple Watch.

## Step 2: Provision a card <a href="#step-2-provisioning-a-request" id="step-2-provisioning-a-request"></a>

The provisioning delegate returns a [PKPass](https://developer.apple.com/documentation/passkit/pkpass) after provisioning completes. Use it to check the card digitization status. If the delegate returns `nil`, the SDK also returns an error.

Before using the PKPass class, make sure to add the PassKit.framework to your project.

> <i class="fa-exclamation-circle">:exclamation-circle:</i>
>
> #### Warning <a href="#warning" id="warning"></a>
>
> You must add the Push Provisioning entitlement to the issuer application before calling this API. Otherwise, the API returns an error. To get the entitlement, follow the [Push Provisioning Access guidelines](https://docs.payments.thalescloud.io/classic-push-provisioning/developer-guide/sdk-configuration/ios).

> <i class="fa-info-circle">:info-circle:</i>
>
> Use the `getToken` API to obtain `localPKPass` or `remotePKPass`. If either `localPKPass` or `remotePKPass` is not `nil`, access the `primaryAccountIdentifier` property of the [`PKSecureElementPass`](https://developer.apple.com/documentation/passkit/pksecureelementpass) object. Pass the retrieved `primaryAccountIdentifier` to the `provision` API.

### Provisioning with standard scheme enum values for Visa, Mastercard, Amex, and Discover <a href="#provisioning-with-standard-scheme-enum-types-for-visa-mastercard-amex-and-discover" id="provisioning-with-standard-scheme-enum-types-for-visa-mastercard-amex-and-discover"></a>

{% tabs %}
{% tab title="Swift" %}

```swift
func onProvisionCompletion(pass: PKPaymentPass?, error: Error?) {
    if let error = error {
      if let tpcError = error as? TPCError {
          // handle TPCError
      } else {
          let provisionError = error as NSError
          if provisionError.domain == PKPassKitErrorDomain  {
              // error from Apple
              // https://developer.apple.com/documentation/passkit/pkaddpaymentpasserror
              switch provisionError.code {
                  case PKAddPaymentPassError.unsupported.rawValue:
                      print("The app cannot add cards to Apple Pay.")
                  case PKAddPaymentPassError.userCancelled.rawValue:
                      print("The user canceled the request to add a card to Apple Pay.")
                  case PKAddPaymentPassError.systemCancelled.rawValue:
                      print("The system canceled the request to add a card to Apple Pay.")
                  default:
                      break
              }
          }
      }
    } else if let pass = pass {
        // handle successful result with pass object
    }
  }
  
  func provision() {
    let suffix = "1234"
    let cardholderName = "John Doe"
    let scheme = CardScheme.Mastercard
    let payload = "PKCS7 encrypted PAN"
    let code = "code"
    
    TPCSDK.provision(fromController: self,
                     primaryAccountSuffix: suffix,
                     cardholderName: cardholderName,
                     scheme: scheme,
                     encryptedPayload: payload,
                     authorizationCode: code,
                     provisionDelegate: self)
  }
```

{% endtab %}

{% tab title="Objective-C" %}

```objective-c
- (void)onProvisionCompletionWithPass:(PKPaymentPass * _Nullable)pass
                                error:(NSError * _Nullable)error {
    if (error != nil) {
        // TODO: problem with provisioning
        // Refer to Swift code for more information
    }
}

- (void)provision {
  NSString * suffix = @"1234";
  NSString * cardholderName = @"John Doe";
  NSString * scheme = CardSchemeMastercard;
  NSString * payload = @"PKCS7 encrypted PAN";
  NSString * code = @"code";

  [TPCSDK provisionFromController:self
             primaryAccountSuffix:suffix
                   cardholderName:cardholderName
                           scheme:scheme
                 encryptedPayload:payload
                authorizationCode:code
                provisionDelegate:self];
}
```

{% endtab %}
{% endtabs %}

### Provisioning with a scheme string (standard) <a href="#provisioning-with-a-scheme-string---standard" id="provisioning-with-a-scheme-string---standard"></a>

{% tabs %}
{% tab title="Swift" %}

```swift
func onProvisionCompletion(pass: PKPaymentPass?, error: Error?) {
    if let error = error {
      if let tpcError = error as? TPCError {
          // handle TPCError
      } else {
          let provisionError = error as NSError
          if provisionError.domain == PKPassKitErrorDomain  {
              // error from Apple
              // https://developer.apple.com/documentation/passkit/pkaddpaymentpasserror
              switch provisionError.code {
                  case PKAddPaymentPassError.unsupported.rawValue:
                      print("The app cannot add cards to Apple Pay.")
                  case PKAddPaymentPassError.userCancelled.rawValue:
                      print("The user canceled the request to add a card to Apple Pay.")
                  case PKAddPaymentPassError.systemCancelled.rawValue:
                      print("The system canceled the request to add a card to Apple Pay.")
                  default:
                      break
              }
          }
      }
    } else if let pass = pass {
        // handle successful result with pass object
    }
  }
  
  func provision() {
    let suffix = "1234"
    let cardholderName = "John Doe"
    let scheme = "SCHEME" // VISA, MASTERCARD, AMEX, DISCOVER
    let payload = "PKCS7 encrypted PAN"
    let code = "code"
    let productId = "productId" // Only applicable for domestic schemes. This can be retrieved from Bank's Card Management System.
    let primaryAccountIdentifier = "primaryAccountIdentifier"
    
    TPCSDK.provision(fromController: self,
                     primaryAccountSuffix: suffix,
                     cardholderName: cardholderName,
                     schemeString: scheme,
                     encryptedPayload: payload,
                     authorizationCode: code,
                     productId: productId,
                     primaryAccountIdentifier:primaryAccountIdentifier,
                     provisionDelegate: self)
  }
```

{% endtab %}

{% tab title="Objective-C" %}

```objective-c
- (void)onProvisionCompletionWithPass:(PKPaymentPass * _Nullable)pass
                                error:(NSError * _Nullable)error {
    if (error != nil) {
        // TODO: problem with provisioning
        // Refer to Swift code for more information
    }
}

- (void)provision {
  NSString * suffix = @"1234";
  NSString * cardholderName = @"John Doe";
  NSString * scheme = @"SCHEME"; // VISA, MASTERCARD, AMEX, DISCOVER
  NSString * payload = @"PKCS7 encrypted PAN";
  NSString * code = @"code";
  NSString * productId = @"productId"; // Only applicable for domestic schemes. This can be retrieved from Bank's Card Management System.
  NSString * primaryAccountIdentifier = @"primaryAccountIdentifier";

  [TPCSDK provisionFromController:self
             primaryAccountSuffix:suffix
                   cardholderName:cardholderName
                     schemeString:scheme
                 encryptedPayload:payload
                authorizationCode:code
                        productId:productId
         primaryAccountIdentifier:primaryAccountIdentifier                
                provisionDelegate:self];
}
```

{% endtab %}
{% endtabs %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.payments.thalescloud.io/classic-push-provisioning/use-cases/push-provisioning-to-xpay-wallets/ios/push-provisioning.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
