Overview

App Switch allows buyers with the PayPal app installed to begin their transactions on a merchant's app or website and then seamlessly navigate to the PayPal app to complete their purchase or vault PayPal as a payment method. This feature is enabled through strong multi-factor authentication, allowing buyers to log in Face ID, biometrics or a passkey, rather than entering a password or one-time password (OTP).

In this flow the SDK will attempt to switch to the PayPal App after calling tokenize if the PayPal App is installed and the user meets eligibility requirements. In cases where App Switch isn't available, the user falls back to the existing PayPal web flow, that is, ASWebAuthenticationSession.

This guide is for merchants who are using the Braintree iOS SDK with the following flows:

  • Vault flow: Vaulting without purchase
  • One-time Checkout: One-time "Pay Now

Buyer flowAnchorIcon

The buyer initiates PayPal checkout within your native mobile application and is seamlessly redirected to the PayPal consumer app to review and approve the Vault or transaction.

  1. The buyer selects the PayPal button from your app.
  2. The PayPal app opens, and the buyer is authenticated using low-friction app login mechanisms, such as Face ID, Fingerprint, App Long-Lived Session, or passkey.
  3. The buyer reviews details and approves the Vault or transaction in the PayPal app.
  4. The buyer is redirected back to your app.
  5. Save PayPal as a payment method, or complete the transaction.

It's important to disable the payment button immediately after it's clicked and show a loading indicator while the network call is being made. This prevents duplicate submissions and improves the user experience by signaling that processing is underway.

EligibilityAnchorIcon

  • Now available in the United States. We will roll out this feature to the EU and other markets.
  • App switch is available with One-time payments and Vaulted payments in Pay Now flows only.

Get the SDKAnchorIcon

The PayPal App Switch is part of the BraintreePayPal module in the Braintree SDK. This module can be pulled into your app via all of the currently supported package managers.

CocoapodsAnchorIcon

In your Podfile, add the dependency for the PayPal Mobile Checkout module:

  1. Swift
pod 'Braintree/PayPal'

Swift Package ManagerAnchorIcon

Include the BraintreeCore, BraintreePayPal, and PayPalDataCollector frameworks.

CarthageAnchorIcon

Include the BraintreeCore, BraintreePayPal and PayPalDataCollector frameworks.

Allowlist PayPal URL SchemeAnchorIcon

You must add the following to the queries schemes allowlist in your app's info.plist:

  1. Swift
<key>LSApplicationQueriesSchemes</key>
<array>
  <string>paypal-app-switch-checkout</string>
</array>

In order to use the PayPal App Switch flow your application must be set up for Universal Links. You will need to use a custom path dedicated to Braintree app switch returns. This URL must also be added to your app association file with wildcards allowed to ensure we receive the expected data on return.

An example apple-app-site-association handle may look like the following:

  1. Swift
{
  "applinks": {
    "details": [
      {
        "appID": "com.your-app-id",
        "paths": [
          "/braintree-payments/*"
        ]
      }
    ]
  }
}

Before using this feature, you must register your Universal Link domain in the Braintree Control Panel:

  • Log into your Control Panel (e.g. Sandbox, or Production).
  • Click on the gear icon in the top right corner. A drop-down menu will open.
  • Click Account Settings from the drop-down menu.
  • Scroll to the Payment Methods section.
  • Next to PayPal, click the Options link. This will take you to your linked PayPal Account(s) page.
  • Click the View Domain Names button.
  • Note: If you have a single PayPal account, it will be at the bottom of the page. If you have multiple PayPal accounts, it will be at the top right of the page.
  • Click the + Add link.
  • Enter your list of domain names separated by commas.
  • Note: The value must match your fully qualified domain name exactly.
  • Click Add Domain Names.
  • If successful, a confirmation banner will appear.
  • If not, the banner will list rejected domains and reasons.

You will need to use the following BTPayPalClient initializer to set your Universal Link:

  1. Swift
let apiClient = BTAPIClient(authorization: <#CLIENT_AUTHORIZATION#>)
let payPalClient = BTPayPalClient(
  apiClient: apiClient, // required
  universalLink: URL(string: "https://my-universal-link.com/braintree-payments")! // required
)

Handle App Context SwitchingAnchorIcon

Then pass the URL to Braintree for completion of the flow.

If you're using SwiftUI, add the onOpenURL(perform:) modifier to your ContentView, and call BTAppContextSwitcher.handleOpen(_:).

  1. Swift
.onOpenURL { url in
  BTAppContextSwitcher.sharedInstance.handleOpen(url)
}

If you're using UISceneDelegate, call handleOpen(_:) inside scene(_:continue:).

  1. Swift
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
  if let returnURL = userActivity.webpageURL, returnURL.path.contains("/my-dedicated-braintree-path") {
    BTAppContextSwitcher.sharedInstance.handleOpen(returnURL)
  }
}

If not using UISceneDelegate, call handleOpen(_:) within application(_:continue:restorationHandler:).

  1. Swift
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
  if let returnURL = userActivity.webpageURL, returnURL.path.contains("/my-dedicated-braintree-path") {
    BTAppContextSwitcher.sharedInstance.handleOpen(returnURL)
  }
}

Invoking the PayPal App Switch FlowAnchorIcon

Opt in to the App Switch flowAnchorIcon

Construct a BTPayPalVaultRequest or a BTPayPalCheckoutRequest with enablePayPalAppSwitch set to true and a userAuthenticationEmail included. Use this with your BTPayPalClient to call BTPayPalClient.tokenize(_:completion:).

An example integration might look like this:

  1. Swift
import UIKit
import BraintreePayPal

class MyViewController: UIViewController {
  var apiClient: BTAPIClient!
  var payPalClient: BTPayPalClient!
  let payPalButton = UIButton(type: .system)

  override func viewDidLoad() {
    super.viewDidLoad()
    apiClient = BTAPIClient(authorization: <#CLIENT_AUTHORIZATION#>)
    payPalClient = BTPayPalClient(
      apiClient: apiClient, 
      universalLink: universalLink: URL(string: "https://my-universal-link.com/braintree-payments")! // required for this flow
    )
    setUpButton()
  }

   private func setUpButton() {
    payPalButton.translatesAutoresizingMaskIntoConstraints = false
    payPalButton.setTitle("PayPal Checkout", for: .normal)
    payPalButton.setTitleColor(.blue, for: .normal)
    payPalButton.addTarget(self, action: #selector(payPalButtonTapped), for: .touchUpInside)
    view.addSubview(payPalButton)
  }

  @objc func payPalButtonTapped(_ sender: UIButton) {
    sender.isEnabled = false

    // one time checkout flows
    let request = BTPayPalCheckoutRequest(
      amount: amount
      userAuthenticationEmail: "[email protected]",
      enablePayPalAppSwitch: true
    )
    
    // userAuthenticationEmail and enablePayPalAppSwitch are required for this flow
    // Configure other values on 'request' as needed.

    payPalClient.tokenize(request) { payPalNonce, error in
      sender.isEnabled = true
      if let payPalNonce { 
        // send payPalNonce.nonce to server 
      } else { 
        // handle error
      }
    }
  }
}

Complete Example - Vaulted Payments with App SwitchAnchorIcon

  1. Swift
import UIKit
import BraintreePayPal

class VaultedPaymentViewController: UIViewController {
    var apiClient: BTAPIClient!
    var payPalClient: BTPayPalClient!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        apiClient = BTAPIClient(authorization: "<#CLIENT_AUTHORIZATION#>")
        payPalClient = BTPayPalClient(
            apiClient: apiClient,
            universalLink: URL(string: "https://my-universal-link.com/braintree-payments")!
        )
    }
    private func payPalButtonTapped() {
    // vault flows
    let request = BTPayPalVaultRequest(
      enablePayPalAppSwitch: true,
      userAuthenticationEmail: "[email protected]",
      userPhoneNumber: BTPayPalPhoneNumber(
          countryCode: "1",
          nationalNumber: "2223334444"
      )
    )
    
    // enablePayPalAppSwitch are required for this flow
    // Configure other values on 'request' as needed.

    payPalClient.tokenize(request) { payPalNonce, error in
      if let payPalNonce { 
        // send payPalNonce.nonce to server 
      } else { 
        // handle error
      }
    }
  }
}

For detailed testing procedures and production deployment guidance, see the Testing and Go Live section.