Overview

App Switch enables buyers with the PayPal app installed to start checkout or vaulting in a merchant app or website and seamlessly complete the flow in the PayPal app. This provides a low-friction authentication experience using native PayPal app login methods such as Face ID, biometrics, or passkeys.

When App Switch is enabled, the Braintree iOS SDK attempts to switch to the PayPal app after tokenize is called, provided the PayPal app is installed and the buyer meets eligibility requirements. If App Switch is unavailable, the SDK automatically falls back to the PayPal web experience using ASWebAuthenticationSession.

This guide applies to merchants using the Braintree iOS SDK with the following PayPal flows:

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

Understand the buyer flowAnchorIcon

The following steps describe the buyer experience when App Switch is available and used during checkout or vaulting.

  1. The buyer initiates PayPal checkout or vaulting from the merchant’s native mobile app.
  2. If the PayPal app is installed and the buyer is eligible, the SDK switches context from the merchant app to the PayPal app.
  3. The buyer is authenticated in the PayPal app using low-friction login methods such as Face ID, biometrics, passkeys, or an existing PayPal app session.
  4. The buyer reviews the transaction or vaulting details and approves the action in the PayPal app.
  5. The buyer is redirected back to the merchant app.
  6. The merchant app completes the transaction or successfully vaults PayPal as a payment method.

One-time payments with Pay NowAnchorIcon

image

Vaulted payments without purchaseAnchorIcon

image

Before you beginAnchorIcon

  1. Get the SDK.
    The PayPal App Switch is part of the BraintreePayPal module in the Braintree SDK. This module can be integrated into your app through all currently supported package managers.
  2. Register for universal links.
  3. Register universal link in Control Panel.
  4. Set universal link in SDK.
    Use the following BTPayPalClient initializer to set your Universal Link:
    let apiClient = BTAPIClient(authorization: <#CLIENT_AUTHORIZATION#>)
    let payPalClient = BTPayPalClient(
      apiClient: apiClient, // required
      universalLink: URL(string: "https://my-universal-link.com/braintree-payments")! // required
    )
  5. Handle App context switching.

Integrate App SwitchAnchorIcon

This section outlines the required iOS configuration and SDK usage needed to enable PayPal App Switch when tokenizing PayPal checkout or vault requests using the Braintree iOS SDK.

  1. To allow your app to detect and switch to the PayPal app, add the PayPal URL scheme to the LSApplicationQueriesSchemes allowlist in your app’s Info.plist:
  2. When creating a PayPal request, set enablePayPalAppSwitch to true. App Switch is triggered during tokenization if the PayPal app is installed and the buyer meets eligibility requirements. Include the buyer’s email address and phone number when available to personalize buyer’s experience and improve authentication and approval success rates. Use the request with BTPayPalClient.tokenize(_:completion:)
  3. .

For One-time flowAnchorIcon

  1. Swift
let request = BTPayPalCheckoutRequest(
    enablePayPalAppSwitch: true, // to enable App Switch
    userAuthenticationEmail: "buyer@example.com",
    userPhoneNumber: BTPayPalPhoneNumber(
        countryCode: "1",
        nationalNumber: "2223334444"
    )
)

For Vaulting flowAnchorIcon

  1. Swift
let request = BTPayPalVaultRequest(
    enablePayPalAppSwitch: true, // to enable App Switch
    userAuthenticationEmail: "buyer@example.com",
    userPhoneNumber: BTPayPalPhoneNumber(
        countryCode: "1",
        nationalNumber: "2223334444"
    )
)

Code SampleAnchorIcon

One-time PaymentsAnchorIcon

  1. Kotlin
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: 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 amount = "10.00" // or whatever you need
    let request = BTPayPalCheckoutRequest(
      amount: amount,
      enablePayPalAppSwitch: true,
      userAuthenticationEmail: "buyer@example.com",
      userPhoneNumber: BTPayPalPhoneNumber(
          countryCode: "1",
          nationalNumber: "2223334444"
      )
    )
    
    // enablePayPalAppSwitch is 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
      }
    }
  }
}

Vaulted PaymentsAnchorIcon

  1. Kotlin
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: "buyer@email.com",
      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
      }
    }
  }
}

Best practicesAnchorIcon

These best practices are important to ensure optimal user experience and improved conversion

  1. Show a loading indicator after PayPal button click
  2. When the user taps the PayPal button:
    • Immediately disable the PayPal button to prevent duplicate submissions.
    • Show a loading indicator while the network request is in progress.
    • This prevents accidental double taps and clearly communicates that the checkout flow is in progress.
  3. Provide buyer email and phone number
  4. Although optional, providing the buyer’s email address and phone number whenever available is strongly recommended. Passing this information allows PayPal to:
    • Personalize the buyer experience.
    • Improve risk assessment.
    • Optimize approval rates.
    This often results in higher overall conversion.
    Phone number formatting requirements

    When passing a phone number, ensure it is sanitized into its canonical format:

    • countryCode must contain numeric digits only (do not include a + prefix).
    • nationalNumber must include digits only.
    • Remove spaces, hyphens, parentheses, and other formatting characters.
    • Do not include the country code in the national number.
    • Do not include leading zeros unless they are part of the actual national number.
    • Example:
      userPhoneNumber: BTPayPalPhoneNumber(
                countryCode: "1",
                nationalNumber: "2223334444"
          )
  5. Handling return to your app
  6. When the buyer returns to your app (either automatically or manually):
    • Remove the loading indicator immediately when your app returns to the foreground.
    • Allow the buyer to continue from where they left off, especially if they exited the PayPal app without approving or canceling the transaction (for example, by backgrounding the PayPal app).
    • When the SDK callback returns a nonce (indicating the buyer approved the transaction):
      • Disable the PayPal button again.
      • Display a loading indicator or overlay while finalizing the vault or transaction.
      • Continue processing until completion.

    You may optionally implement a timeout (for example, 10 seconds) for the loading indicator in case processing takes longer than expected.

    This ensures UI consistency and prevents unintended user interactions during payment finalization.

  7. Redirection to mobile browser
  8. In some cases, after approving the transaction in the PayPal app, the OS may redirect the buyer to your website in the default mobile browser instead of returning to your app, even when return and cancel URLs are configured. This typically occurs when:
    • The universal link configuration on the device does not match the registered domain, or
    • The app cannot properly claim the universal link.

    Recommended mitigations:

    • Ensure universal links are correctly configured and associated with the domain.
    • (Optional but recommended) Implement fallback logic on your website to:
      • Detect redirected buyers.
      • Retrieve or confirm the final token or payment status.
      • Guide the buyer back to the correct in-app flow when possible.

Testing and Go LiveAnchorIcon

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