In-App Payments SDK

Enable Apple Pay

This document provides step-by-step instructions for integrating Apple Pay with the In-App Payments SDK on an iOS device.

iOS
Swift (iOS)
iOS
Save

Limitations

  • Apple Pay integrations cannot be tested in a simulator. You must use an iOS 10.0+ device with at least one verified payment card in your Apple Wallet.
  • Square sandbox does not support digital wallets. Use the production Transactions API endpoint for testing these wallets. For more information, see Testing Mobile Apps.

Prerequisites

To enable Apple Pay with In-App Payments SDK on iOS, the following must be true:

  • You have followed the instructions in the In-App Payments: Build on iOS guide. This guide does not cover the general setup of In-App Payments SDK.
  • You have added your Apple developer account to Xcode.
  • The deployment target for your app is iOS 10.0 or newer.

Step 1: Upload an Apple Pay certificate to the Square Developer Portal

To add an Apple Pay payment processing certificate in the Apple Developer Portal, you must first obtain a Certificate Signing Request (CSR) from Square. The Square Developer Portal provides a CSR file that can be submitted to Apple:

  1. Select the application you created for your In-App Payments SDK app.
  2. Click the Apple Pay tab.
  3. Click Add Certificate and follow the instructions to generate an Apple Pay certificate from Apple and upload it to your Square account.
Applepay cert

Step 2: Configure your app for Apple Pay

  1. Open your project in Xcode.
  2. In the Navigator Area, click your project file and select the SDK integration target in the editor area.
  3. Click the Capabilities tab and enable Apple Pay by sliding the capability toggle to ON.
  4. Check the box adjacent to the merchant ID you added to trigger an automatic Xcode process that completes the 3 Apple Pay configuration steps.

Step 3: Display the Apple Pay payment authorization sheet

@import PassKit;
@import SquareInAppPaymentsSDK;

@implementation <#YourViewController#>
- (void)requestApplePayAuthorization;
{
    if (!SQIPInAppPaymentsSDK.canUseApplePay) {
        return;
    }
    
    PKPaymentRequest *paymentRequest =
        [PKPaymentRequest squarePaymentRequestWithMerchantIdentifier:@"<#REPLACE_ME#>"
                                                         countryCode:@"US"
                                                        currencyCode:@"USD"];

    // Payment summary information will be displayed on the Apple Pay sheet.
    NSDecimalNumber *amount = [NSDecimalNumber decimalNumberWithString:@"1.00"];

    paymentRequest.paymentSummaryItems = @[
                                           [PKPaymentSummaryItem summaryItemWithLabel:@"My Company Name"
                                                                               amount:amount],
                                          ];
    
    PKPaymentAuthorizationViewController *paymentAuthorizationViewController =
        [[PKPaymentAuthorizationViewController alloc] initWithPaymentRequest:paymentRequest];

    paymentAuthorizationViewController.delegate = self;
    [self presentViewController:paymentAuthorizationViewController animated:YES completion:nil];
}

@end
import PassKit
import SquareInAppPaymentsSDK

extension <#YourViewController#> { 
    func requestApplePayAuthorization() {
        guard SQIPInAppPaymentsSDK.canUseApplePay else {
            return;
        }
        let paymentRequest = PKPaymentRequest.squarePaymentRequest(
            // Set to your Apple merchant ID
            merchantIdentifier:"<#REPLACE_ME#>",
            countryCode: "US",   
            currencyCode: "USD") 
        
        // Payment summary information will be displayed on the Apple Pay sheet.
        paymentRequest.paymentSummaryItems = [
            PKPaymentSummaryItem(label: "Testing Apple Pay", amount: 1.00),
        ]
        
        let paymentAuthorizationViewController =
            PKPaymentAuthorizationViewController(paymentRequest: paymentRequest)
        
        paymentAuthorizationViewController!.delegate = self
        
        present(paymentAuthorizationViewController!, animated: true, completion: nil)
    }

}

Step 4: Implement PKPaymentAuthorizationViewControllerDelegate methods

The 2 delegate methods that you implement let you handle the following events:

  • didAuthorizePayment: Customer submitted payment card information and the payment authorization sheet created a payment. This method is invoked by the SDK while the payment authorization sheet is still open.
  • didFinish: Payment authorization sheet closed. This method is invoked after the didAuthorizePayment completion handler is invoked by your code or after the customer cancels payment authorization.

Objective-C iOS 11.0+

@import PassKit;

@interface <#YourViewController#> () <PKPaymentAuthorizationViewControllerDelegate>
@end

@implementation <#YourViewController#>

- (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller
                       didAuthorizePayment:(PKPayment *)payment
                                   handler:(void (^)(PKPaymentAuthorizationResult * _Nonnull))completion;
{
    // TODO: Add payment->nonce exchange logic. See Step 5: Request a Square nonce
}

- (void)paymentAuthorizationViewControllerDidFinish:(nonnull PKPaymentAuthorizationViewController *)controller;
{
    [self dismissViewControllerAnimated:YES completion:nil];
}
@end

Objective-C iOS 10.0+

@import PassKit;

@interface <#YourViewController#> () <PKPaymentAuthorizationViewControllerDelegate>
@end

@implementation <#YourViewController#>

- (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller
                       didAuthorizePayment:(PKPayment *)payment
                                completion:(void (^)(PKPaymentAuthorizationStatus))completion;
{
    // TODO: Add payment->nonce exchange logic. See Step 5: Request a Square nonce
}

- (void)paymentAuthorizationViewControllerDidFinish:(nonnull PKPaymentAuthorizationViewController *)controller;
{
    [self dismissViewControllerAnimated:YES completion:nil];
}
@end

Swift iOS 11.0+

import PassKit

extension <#YourViewController#>: PKPaymentAuthorizationViewControllerDelegate {
    func paymentAuthorizationViewController(_ controller: PKPaymentAuthorizationViewController,
                                            didAuthorizePayment payment: PKPayment,
                                            handler completion: @escaping (PKPaymentAuthorizationResult) -> Void){

        // TODO: Add payment->nonce exchange logic. See Step 5: Request a Square nonce
    }

    func paymentAuthorizationViewControllerDidFinish(_ controller: PKPaymentAuthorizationViewController) {
        dismiss(animated: true, completion: nil)
    }
}

Swift iOS 10.0+

import PassKit

extension <#YourViewController#>: PKPaymentAuthorizationViewControllerDelegate {
   func paymentAuthorizationViewController(_ controller: PKPaymentAuthorizationViewController,
                                            didAuthorizePayment payment: PKPayment,
                                            completion: @escaping (PKPaymentAuthorizationStatus) -> Void) {

        // TODO: Add payment->nonce exchange logic. See Step 5: Request a Square nonce
    }

    func paymentAuthorizationViewControllerDidFinish(_ controller: PKPaymentAuthorizationViewController) {
        dismiss(animated: true, completion: nil)
    }
}

If the user authorizes payment, a PKPayment object is returned and can be used in Step 5 to get a Square nonce.

Step 5: Request a Square nonce

If the user authorizes payment, the PassKit framework calls the paymentAuthorizationViewController(_:didAuthorizePayment:handler:) delegate method, where the authorized PKPayment can be exchanged for a nonce.

  1. Send the nonce to your backend and call the relevant completion handler to notify the Apple Pay sheet of success or failure when the backend returns a response.
  2. Request a nonce within the didAuthorizePayment callback by calling SQIPApplePayNonceRequest(payment: payment) and handling the result of the request to get the nonce.
  3. Before invoking completion(PKPaymentAuthorizationResult(), send the nonce to your backend and await results. The returned results determine which completion handler argument is used:
    • Success: If payment is accepted, pass the nil argument in the completion handler. nil tells the card entry view controller to show a success animation to the buyer and call cardEntryViewController:didCompleteWithStatus:.
    • Failure: If payment fails, your backend should return actionable error information to the mobile app. Pass an error object in the completion handler. The error localizedDescription is displayed so the buyer can re enter their payment information and submit payment again.

Objective-C iOS 11.0+

- (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller
                       didAuthorizePayment:(PKPayment *)payment
                                   handler:(void (^)(PKPaymentAuthorizationResult * _Nonnull))completion;
{
    SQIPApplePayNonceRequest *nonceRequest = [[SQIPApplePayNonceRequest alloc] initWithPayment:payment];

    [nonceRequest performWithCompletionHandler:^(SQIPCardDetails * _Nullable cardDetails, NSError * _Nullable error) {
        if (error) {
            PKPaymentAuthorizationResult *errorResult =
                [[PKPaymentAuthorizationResult alloc] initWithStatus:PKPaymentAuthorizationStatusFailure errors:@[error]];

            completion(errorResult);
            return;
        }

        // Send the card nonce to your server to charge the card or store the card on file.

        /*
        [MyAPIClient.shared chargeCardWithNonce:cardDetails.nonce completion:^(id transaction, NSError *chargeError) {
            if (chargeError) {
                PKPaymentAuthorizationResult *errorResult =
                    [[PKPaymentAuthorizationResult alloc] initWithStatus:PKPaymentAuthorizationStatusFailure errors:@[chargeError]];

                completion(errorResult)
            } else {
                PKPaymentAuthorizationResult *successResult =
                    [[PKPaymentAuthorizationResult alloc] initWithStatus:PKPaymentAuthorizationStatusSuccess errors:nil];

                completion(successResult);
            }
        }];
        */

        PKPaymentAuthorizationResult *successResult =
            [[PKPaymentAuthorizationResult alloc] initWithStatus:PKPaymentAuthorizationStatusSuccess errors:nil];

        completion(successResult);
    }];
}

Objective-C iOS 10.0+

- (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller
                       didAuthorizePayment:(PKPayment *)payment
                                completion:(void (^)(PKPaymentAuthorizationStatus))completion;
{
    SQIPApplePayNonceRequest *nonceRequest = [
      [SQIPApplePayNonceRequest alloc] initWithPayment:payment
    ];
    
    [nonceRequest performWithCompletionHandler:^(
        SQIPCardDetails * _Nullable cardDetails, NSError * _Nullable error) {
        if (error) {
            completion(PKPaymentAuthorizationStatusFailure);
            return;
        }
        
        // Send the card nonce to your server to charge the card or store the card on file.

        /*
        [MyAPIClient.shared chargeCardWithNonce:cardDetails.nonce completion:^(id transaction, NSError *chargeError) {
            if (chargeError) {
                completion(PKPaymentAuthorizationStatusFailure);
            } else {
                completion(PKPaymentAuthorizationStatusSuccess);
            }
        }];
        */

        completion(PKPaymentAuthorizationStatusSuccess);
    }];
}

Swift iOS 11.0+

func paymentAuthorizationViewController(_ controller: PKPaymentAuthorizationViewController,
                                        didAuthorizePayment payment: PKPayment,
                                        handler completion: @escaping (PKPaymentAuthorizationResult) -> Void) {

     // Exchange the authorized PKPayment for a nonce.
     let nonceRequest = SQIPApplePayNonceRequest(payment: payment)
     nonceRequest.perform { cardDetails, error in
         if let cardDetails = cardDetails {
             // Send the card nonce to your server to charge the card or store the card on file.

             /*
             MyAPIClient.shared.chargeCard(withNonce: cardDetails.nonce) { transaction, chargeEerror in
            
                 if let chargeError = chargeError {
                     completion(PKPaymentAuthorizationResult(status: .failure, errors: [chargeError]))
                 }
                 else {
                     completion(PKPaymentAuthorizationResult(status: .success, errors: nil))
                 }
             }
             */

             completion(PKPaymentAuthorizationResult(status: .success, errors: nil))
         }
         else if let error = error {
             print(error)
             completion(PKPaymentAuthorizationResult(status: .failure, errors: [error]))
         }
     }
 }

Swift iOS 10.0+

func paymentAuthorizationViewController(_ controller: PKPaymentAuthorizationViewController,
                                         didAuthorizePayment payment: PKPayment,
                                         completion: @escaping (PKPaymentAuthorizationStatus) -> Void) {
     // Exchange the authorized PKPayment for a nonce.
     let nonceRequest = SQIPApplePayNonceRequest(payment: payment)
     nonceRequest.perform { cardDetails, error in
         if let cardDetails = cardDetails {
             // Send the card nonce to your server to charge the card or store the card on file.

             /*
             MyAPIClient.shared.chargeCard(withNonce: cardDetails.nonce) { transaction, chargeError in

                 if chargeError == nil {
                     completion(.success)
                 }
                 else {
                     completion(.failure)
                 }
             }
             */

             completion(.success)
         } else if let error = error {
             print(error)
             completion(.failure)
         }
     }
}

Troubleshooting

I get an apple_pay_nonce_request_invalid_certificate when requesting a nonce

Cause:

There is a problem with your Apple Pay payment processing certificate or Xcode has cached an older (invalid) certificate.

Solution:

  1. Ensure that your Apple Pay certificate in the Apple Developer Portal is active.
  2. Verify that you have uploaded the certificate to the Square Developer Portal.

If you continue receiving the error, you may need to clear a cached certificate:

  1. Sign in to the Apple Developer Portal and follow the steps in the Create a merchant identifier Apple Pay help article to create a new merchant identifier.
  2. In the Apple Developer Portal, create a new Apple Pay payment processing certificate for your new merchant ID using your original Square CSR.
  3. Activate the new certificate in the Apple Developer Portal.
  4. Upload the new Apple Pay certificate to the Square Developer Application Dashboard.
  5. Update your Apple Pay merchant ID on the Capabilities tab in Xcode.
  6. Update the code that creates your PKPaymentRequest to use the new merchant ID.

Contact Developer Support, join our Slack channel, or ask for help on Stack Overflow