Apple Pay

Client-Side Implementationanchor

important

The SSL certificates for all Braintree SDKs are set to expire by June 30, 2025. This will impact existing versions of the SDK in published versions of your app. To reduce the impact, upgrade the iOS SDK to version 6.17.0+ for the new SSL certifications.

If you do not decommission your app versions that include the older SDK versions or force upgrade your app with the updated certificates by the expiration date, 100% of your customer traffic will fail.

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

Apple Pay user experienceanchor

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 SDKanchor

Custom UI Integrationanchor

CocoaPodsanchor

Include Braintree/ApplePay in your podfile:

  1. Ruby
pod 'Braintree/ApplePay'

Carthageanchor

Include the BraintreeApplePay and BraintreeCore frameworks.

Drop-in UI Integrationanchor

No additional steps are required to fetch the BraintreeApplePay module once BraintreeDropIn has been included in your project.

Initializationanchor

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

  1. Swift
var braintreeClient: BTAPIClient?

// BTAPIClient can be initialized in a couple different ways, here's one example:
braintreeClient = BTAPIClient(authorization: CLIENT_AUTHORIZATION)

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.

PassKit integration and payment tokenizationanchor

Set up your Apple Pay buttonanchor

Apple provides an Apple Pay button, PKPaymentButton. See their brand guidelines for more information.

  1. Swift
func applePayButton() -> UIButton {
    let button = PKPaymentButton(type: PKPaymentButtonType.buy, style: PKPaymentButtonStyle.black)

    button?.addTarget(self, action: #selector(tappedApplePay), for: UIControlEvents.touchUpInside)

    return button!
}

Custom UIanchor

Apple Pay is only available on certain iOS devices. Before presenting the Apple Pay option to the current user, you should determine whether Apple Pay is available.

  1. Swift
import PassKit
// Add BraintreeApplePay.h to bridging header

class MyCheckoutViewController: UIViewController, PKPaymentAuthorizationViewControllerDelegate {
  // ...

  override func viewDidLoad() {
    super.viewDidLoad()

    // Conditionally show Apple Pay button based on device availability
    if PKPaymentAuthorizationViewController.canMakePayments(usingNetworks: [PKPaymentNetwork.visa, PKPaymentNetwork.masterCard, PKPaymentNetwork.amex, PKPaymentNetwork.discover]) {
      let button = self.applePayButton()
      // TODO: Set button constraints/frame...
      self.view.addSubview(button)
    }
  }

  // ...
}

Drop-in UIanchor

First, you must properly configure the Drop-in UI.

If your customer selects Apple Pay via the Drop-in, the result.paymentMethodType == .applePay and result.paymentMethod will be nil. If Apple Pay was selected, you must now show a PKPaymentButton.

  1. Swift
let dropIn = BTDropInController(authorization: clientTokenOrTokenizationKey, request: request) {
	 (controller, result, error) in
	 	if (result?.paymentMethodType == .applePay) {
	 		// Display PKPaymentButton
	 		let button = self.applePayButton()

	 		// TODO: Set button constraints/frame...
	 		// self.view.addSubview(button)
	 	}
}
note

The Drop-in UI will not display the Apple Pay sheet or create a nonce. The Drop-in also handles the canMakePayments check to see if a customer's device has Apple Pay available.

Create a PKPaymentRequestanchor

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

  1. Swift
func setupPaymentRequest(completion: @escaping (PKPaymentRequest?, Error?) -> Void) {
    let applePayClient = BTApplePayClient(apiClient: self.braintreeApiClient)
    // 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 { (paymentRequest, error) in
        guard let paymentRequest = paymentRequest else {
            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 = [.postalAddress]

        // Set other PKPaymentRequest properties here
        paymentRequest.merchantCapabilities = .capability3DS
        paymentRequest.paymentSummaryItems =
        [
            PKPaymentSummaryItem(label: "<#ITEM_NAME#>", amount: NSDecimalNumber(string: "<#PRICE#>")),
            // Add add'l payment summary items...
            PKPaymentSummaryItem(label: "<#COMPANY NAME#>", amount: NSDecimalNumber(string: "<#GRAND TOTAL#>")),
        ]
        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 PKPaymentAuthorizationViewControlleranchor

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

  1. Swift
func tappedApplePay() {
    self.setupPaymentRequest { (paymentRequest, error)
        guard error == nil else {
           // Handle error
           return
        }

        // Example: Promote PKPaymentAuthorizationViewController to optional so that we can verify
        // that our paymentRequest is valid. Otherwise, an invalid paymentRequest would crash our app.
        if let vc = PKPaymentAuthorizationViewController(paymentRequest: paymentRequest)
            as PKPaymentAuthorizationViewController?
        {
            vc.delegate = self
            present(vc, animated: true, completion: nil)
        } else {
            print("Error: Payment request is invalid.")
        }
    }
}

Implement PKPaymentAuthorizationViewControllerDelegateanchor

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

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

    // Tokenize the Apple Pay payment
    braintree!.tokenizeApplePay(payment) { (nonce, error) in
        if error != nil {
            // Received an error from Braintree.
            // Indicate failure via the completion callback.
            completion(PKPaymentAuthorizationResult(status: .failure, errors: nil)
            return
        }

        // 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(status: .success, errors: nil))
    }
}

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

  1. Swift
func paymentAuthorizationViewControllerDidFinish(_ controller: PKPaymentAuthorizationViewController) {
    // Apple Payment finished
    dismiss(animated: true)
}

Next Page: Server-side