availability

iOS v3 is deprecated. We recommend migrating to the latest version of our iOS SDK. The best way to start is by making an incremental upgrade from v3 to v4.

Once your Certificate and Merchant ID are set up, you can add Apple Pay to your app.

Apple Pay user experience

Apple Pay has specific user experience and identity guidelines, which Apple may enforce during the App Store review process. Please consult these guidelines as well as Apple's developer information when designing your Apple Pay user experience.

Get the SDK

In your Podfile, include both Braintree and its Braintree/Apple-Pay subspec:

Ruby
pod 'Braintree', '~> 3.9'
pod 'Braintree/Apple-Pay', '~> 3.9'

We believe that dependency management makes iOS developers happier and more productive in the long run. While it is technically possible to pull in our code manually, this setup is not recommended. See our Manual Integration Instructions for more details.

Initialization

Like all Braintree SDK integrations, you will first need to initialize the Braintree SDK client:

@interface MyViewController ()
@property (nonatomic, strong) Braintree *braintree;
@end

// Obtain a client token before the user enters a new checkout flow.
// This should take place early enough in the experience to
// avoid blocking the user during checkout.

self.braintree = [Braintree braintreeWithClientToken:CLIENT_TOKEN_FROM_SERVER];

A fresh client token is recommended for a successful Apple Pay transaction, as it contains configuration information, such as the Braintree environment, your Apple Pay Merchant ID, and supported payment networks.

Your app will submit an Apple Pay authorization to Braintree and receive a payment method nonce in return, which can be used for server-side processing.

Choose an integration style

There are two ways to integrate Apple Pay:

  1. PassKit - Integrate directly with Apple Pay (Recommended)
  2. BTPaymentProvider - Simplified Apple Pay integration
    • Support multiple payment types (e.g. Apple Pay, PayPal, Venmo) in a single code path
    • Configure Apple Pay configuration in your app dynamically from the Braintree Control Panel (via client token)
    • Test directly in sandbox and the iOS Simulator

We recommend Option #1 because it provides better control and configurability of the Apple Pay workflow, including all of its capabilities.

While option #2 simplifies your Apple Pay integration with a unified code path for Apple Pay, PayPal, and Venmo, it does not expose the full power and flexibility of PassKit.

If you are doing a new integration in Swift, we recommend Option #1.

In either case, the UX is the same and you will receive a payment method nonce on success.

Please refer to the headers in our codebase for detailed developer information.

Previous versions of this document recommended BTPaymentProvider over the PassKit integration.

Option 1: PassKit integration and payment tokenization

Set up your Apple Pay button

Apple Pay is only available to a subset of iOS users. Before presenting the Apple Pay option to the current user, you should determine whether Apple Pay is available.

@import PassKit;
#import "BraintreeApplePay.h"

@interface MyCheckoutViewController <PKPaymentAuthorizationViewControllerDelegate>
@end

@implementation MyCheckoutViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    // Conditionally show Apple Pay button based on device availability
    if ([PKPaymentAuthorizationViewController canMakePaymentsUsingNetworks:@[PKPaymentNetworkVisa, PKPaymentNetworkMasterCard, PKPaymentNetworkAmex, PKPaymentNetworkDiscover]]) {
        UIButton *button = [self applePayButton];
        // TODO: Add button to view and set its constraints/frame...
        // [self.view addSubview:button];
    }
}

Since iOS 8.3, Apple has provided an Apple Pay button, PKPaymentButton. If your app targets older versions of iOS, you will need to create your own button using Apple's Human Interface Guidelines and assets.

- (UIButton *)applePayButton {
    UIButton *button;

    if ([PKPaymentButton class]) { // Available in iOS 8.3+
        button = [PKPaymentButton buttonWithType:PKPaymentButtonTypeBuy style:PKPaymentButtonStyleBlack];
    } else {
        // TODO: Create and return your own apple pay button
        // button = ...
    }

    [button addTarget:self action:@selector(tappedApplePay) forControlEvents:UIControlEventTouchUpInside];

    return button;
}

Create a PKPaymentRequest

Before you can initiate a user-facing Apple Pay experience, you will need to initialize a payment request:

- (void)setupPaymentRequest:(void (^)(PKPaymentRequest * _Nullable, NSError * _Nullable))completion {
    BTApplePayClient *applePayClient = [[BTApplePayClient alloc] initWithAPIClient:self.apiClient];
    // You can use the following helper method to create a PKPaymentRequest which will set the `countryCode`,
    // `currencyCode`, `merchantIdentifier`, and `supportedNetworks` properties.
    // You can also create the PKPaymentRequest manually. Be aware that you'll need to keep these in
    // sync with the gateway settings if you go this route.
    [applePayClient paymentRequest:^(PKPaymentRequest * _Nullable paymentRequest, NSError * _Nullable error) {
        if (error) {
            completion(nil, error);
            return;
        }

        // We recommend collecting billing address information, at minimum
        // billing postal code, and passing that billing postal code with all
        // Apple Pay transactions as a best practice.
        paymentRequest.requiredBillingContactFields = [NSSet setWithObject:PKContactFieldPostalAddress];

        // Set other PKPaymentRequest properties here
        paymentRequest.merchantCapabilities = PKMerchantCapability3DS;
        paymentRequest.paymentSummaryItems =
        @[
            [PKPaymentSummaryItem summaryItemWithLabel:@"<#ITEM_NAME#>" amount:[NSDecimalNumber decimalNumberWithString:@"<#PRICE#>"]],
            // Add add'l payment summary items...
            [PKPaymentSummaryItem summaryItemWithLabel:@"<#COMPANY_NAME#>" amount:[NSDecimalNumber decimalNumberWithString:@"<#GRAND_TOTAL#>"]]
        ];

        // Save the PKPaymentRequest or start the payment flow
        completion(paymentRequest, nil);
    }];
}

Here are some Braintree-specific recommendations about the various fields in the PKPaymentRequest:

  • countryCode: We suggest using the country where your business is located
  • currencyCode: This value must correspond to your Braintree merchant account's currency
  • merchantCapabilities: Please use PKMerchantCapability3DS (PKMerchantCapabilityEMV corresponds to in-store purchasing only)
  • merchantIdentifier: This value must match up with the merchantIdentifier and certificate on file in both Apple's developer center and Braintree's Control Panel
  • paymentSummaryItems: This value is required, and the grand total amount should not exceed the amount that is authorized or submitted for settlement
  • supportedNetworks: This value must match up with your merchant account's accepted payment methods
  • requiredBillingContactFields: We recommend including PKContactFieldPostalAddress as a best practice

In order to be prepared to make changes without re-releasing your app, you should consider setting these values dynamically based on a response from your server.

In particular, you should be careful about keeping the PKPaymentRequest in sync with server environment (production vs. sandbox), as well as with your merchant account configuration and Apple Pay configuration. Self-service certificate management can be found in the Control Panel by clicking the gear icon in the top right corner, selecting Processing from the drop-down menu, scrolling to Apple Pay, and clicking the Options link.

Present a PKPaymentAuthorizationViewController

When your user taps on your Apple Pay UI, present a PKPaymentAuthorizationViewController to initiate Apple Pay:

- (IBAction)tappedApplePay {
    [self setupPaymentRequest:^(PKPaymentRequest * _Nullable paymentRequest, NSError * _Nullable error) {
        if (error) {
            // Handle error
            return;
        }
        PKPaymentAuthorizationViewController *vc = [[PKPaymentAuthorizationViewController alloc] initWithPaymentRequest:paymentRequest];
        vc.delegate = self;
        [self presentViewController:vc animated:YES completion:nil];
    }];
}

Implement PKPaymentAuthorizationViewControllerDelegate

Implement the PKPaymentAuthorizationViewControllerDelegate protocol methods. In your implementation of paymentAuthorizationViewController:didAuthorizePayment:handler:, create a nonce by tokenizing the PKPayment via the Braintree SDK:

- (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller
                       didAuthorizePayment:(PKPayment *)payment 
                                   handler:(void (^)(PKPaymentAuthorizationResult *result))completion {

    // Tokenize the Apple Pay payment
    [self.braintree tokenizeApplePayPayment:payment
       completion:^(NSString *nonce, NSError *error) {
           if (error) {
               // Received an error from Braintree.
               // Indicate failure via the completion callback.
               completion([[PKPaymentAuthorizationResult alloc] initWithStatus:PKPaymentAuthorizationStatusFailure errors:nil);
               return;
           }

           [NSException raise:@"Not yet implemented" format:@"Send nonce (%@) to your server", nonce];

           // TODO: On success, send nonce to your server for processing.
           // If requested, address information is accessible in payment and may
           // also be sent to your server.

           // Then indicate success or failure based on the server side result of Transaction.sale
           // via the completion callback.
           // e.g. If the Transaction.sale was successful
           completion([[PKPaymentAuthorizationResult alloc] initWithStatus:PKPaymentAuthorizationStatusSuccess errors:nil]);
       }];
}

@end

The delegate method paymentAuthorizationViewController:didAuthorizePayment:handler: is only available in iOS 11+. If you are targeting apps using iOS 10 or below, you must also implement paymentAuthorizationViewController:didAuthorizePayment:completion:.

- (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller
     didAuthorizePayment:(PKPayment *)payment
        completion:(void (^)(PKPaymentAuthorizationStatus))completion {

    // Tokenize the Apple Pay payment
    [self.braintree tokenizeApplePayPayment:payment
       completion:^(NSString *nonce, NSError *error) {
           if (error) {
               // Received an error from Braintree.
               // Indicate failure via the completion callback.
               completion(PKPaymentAuthorizationStatusFailure);
               return;
           }

           [NSException raise:@"Not yet implemented" format:@"Send nonce (%@) to your server", nonce];

           // TODO: On success, send nonce to your server for processing.
           // If requested, address information is accessible in payment and may
           // also be sent to your server. 

           // Then indicate success or failure based on the server side result of Transaction.sale
           // via the completion callback.
           // e.g. If the Transaction.sale was successful
           completion(PKPaymentAuthorizationStatusSuccess);
       }];
}

@end

You must also implement the paymentAuthorizationViewControllerDidFinish(_:) delegate method to handle the dismissal of the Apple Pay sheet upon Apple Payment finishing.

- (void)paymentAuthorizationViewControllerDidFinish:(PKPaymentAuthorizationViewController *)controller {
    // Apple Payment finished
    [self dismissViewControllerAnimated:YES completion:nil];
}

Option 2: BTPaymentProvider integration

This is the preferred integration method if you intend to support all payment methods, like PayPal, Venmo, and cards, with the same code path. This type of integration also supports testing transactions in a "mock" mode in iOS Simulator, which enables Apple Pay testing in sandbox.

Implement BTPaymentMethodCreationDelegate

First, implement methods that conform to the BTPaymentMethodCreationDelegate protocol. Your implementation of paymentMethodCreator:didCreatePaymentMethod: will be called when we successfully receive and decrypt your user's Apple Pay authorization.

// MyViewController.m
#import <Braintree/Braintree.h>

@interface MyViewController () <BTPaymentMethodCreationDelegate>

// Note: The caller is responsible for retaining the BTPaymentProvider instances in order to receive delegate callbacks.
@property (nonatomic, strong) BTPaymentProvider *provider;
@end

@implementation MyViewController

- (void)paymentMethodCreator:(id)sender requestsPresentationOfViewController:(UIViewController *)viewController {
    [self presentViewController:viewController animated:YES completion:nil];
}

- (void)paymentMethodCreator:(id)sender requestsDismissalOfViewController:(UIViewController *)viewController {
    [self dismissViewControllerAnimated:YES completion:nil];
}

- (void)paymentMethodCreator:(id)sender didCreatePaymentMethod:(BTPaymentMethod *)paymentMethod {
    if ([paymentMethod isKindOfClass:[BTApplePayPaymentMethod class]]) {
        BTApplePayPaymentMethod *applePayPaymentMethod = (BTApplePayPaymentMethod *)paymentMethod;
        // Send payment information to your server:
        //   - applePayPaymentMethod.nonce
        //   - applePayPaymentMethod.shippingAddress
        //   - applePayPaymentMethod.billingAddress
        //   - applePayPaymentMethod.shippingMethod
        // Clean up any UI now that the payment is complete
    }
}

// TODO: Implement other BTPaymentCreationDelegate methods
@end

At a minimum, you should also implement paymentMethodCreator:didFailWithError:, paymentMethodCreator:requestsPresentationOfViewController:, paymentMethodCreator:requestsDismisalOfViewController:. See BTPaymentMethodCreationDelegate.h for full details.

Create a BTPaymentProvider

Instantiate BTPaymentProvider, setting the delegate to the class that conforms to BTPaymentMethodCreationDelegate. We will use the provider to start Apple Pay, but it can also be used to start PayPal and Venmo.

self.provider = [self.braintree paymentProviderWithDelegate:self];

You can specify details about the current payment on the provider:

self.provider.paymentSummaryItems = @[
    [PKPaymentSummaryItem summaryItemWithLabel:@"COMPANY NAME" amount:[NSDecimalNumber decimalNumberWithString:@"1"]]
];

Present the Apple Pay button

Apple Pay is only available to a subset of iOS users. Before presenting the Apple Pay option to the current user, you should ask BTPaymentProvider whether Apple Pay is available.

- (void)viewDidLoad {
    [super viewDidLoad];

    if ([self.provider canCreatePaymentMethodWithProviderType:BTPaymentProviderTypeApplePay]) {
        // Present Apple Pay as an option in your UI based on Apple's recommendations at https://developer.apple.com/apple-pay/
    }
}

This response is distinct from -[PKPaymentAuthorizationViewController canMakePaymentsUsingNetworks:] because it also depends on server side values provided by the Gateway. Contact us if you are not receiving the value you expect.

Initiate payment

When your user taps on your Apple Pay UI, use the BTPaymentProvider to initiate Apple Pay:

- (IBAction)tappedApplePay {
    [self.provider createPaymentMethod:BTPaymentProviderTypeApplePay];
}

The provider's delegate will then receive an Apple Pay View Controller via paymentMethodCreator:requestsPresentationOfViewController:.

On the simulator, you will receive an instance of BTMockApplePayPaymentAuthorizationViewController; on real devices that support Apple Pay, you will receive an instance of PKPaymentAuthorizationViewController.

Next Page: Server-side →