App Switch
Upgrade immediately to:
- iOS: SDK version 6.17.0+
- Android: SDK version 4.45.0+ or version 5.0.0+
Customers on affected app versions will see SSL/TLS connection failures when processing payments.
App Switch enables buyers with the PayPal app installed to start checkout or save a payment method 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 biometrics or passkeys.
When App Switch is enabled, the Braintree Android SDK attempts to switch to the PayPal app after
PayPalLauncher().launch
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.
This guide is for merchants who are using the Braintree Android SDK. App Switch is eligible for the following checkout options, all with the Pay Now consumer experience:
One-time Checkout and Save Payment Method with Purchase also support PayPal Pay Later — set
shouldOfferPayLater = true on the request to prioritize installment options on the
PayPal screen.
- App Switch does not currently support the One-time Continue flow.
- App Switch is currently only supported for US merchants
Understand the buyer flow
The following steps describe the buyer experience when App Switch is available and used during checkout or when saving a payment method.
- The buyer initiates PayPal checkout or saves a payment method from the merchant's native mobile app.
- If the PayPal app is installed and the buyer is eligible, the SDK switches context from the merchant app to the PayPal app.
- The buyer is authenticated in the PayPal app using low-friction login methods such as biometrics, passkeys, or an existing PayPal app session.
- The buyer reviews the transaction or save-payment details and approves the action in the PayPal app.
- The buyer is redirected back to the merchant app.
- The merchant app completes the transaction or successfully saves PayPal as a payment method.
Before you begin
- Get the SDK
- Set up App Links
- Register App Link in Control Panel
- Set App Link in SDK
during PayPalClient initialization (if your App Link contains a path, see Best Practice #5 for
the required
pathPrefixconfiguration)
Integrate App Switch
Enable App Switch on the PayPal request and supply the buyer's email and phone number when
available. When
enablePayPalAppSwitch
is true, the Braintree Android SDK attempts to switch to the PayPal app (after calling
PayPalLauncher().launch(...))
if the PayPal app is installed and the buyer is eligible. Otherwise, it falls back to the web
flow.
- Kotlin
request.userAuthenticationEmail = "[email protected]"
request.userPhoneNumber = PayPalPhoneNumber("1", "2223334444")
request.enablePayPalAppSwitch = trueCode Sample
Each sample uses the Pay Now consumer experience
(userAction = PayPalPaymentUserAction.USER_ACTION_PAY_NOW). For One-time Checkout and
Save Payment Method with Purchase, the request includes shouldOfferPayLater with an
inline comment showing how to prioritize PayPal Pay Later installment options on the PayPal screen.
One-time Payments
- Kotlin
// One-time Checkout — Pay Now.
// USER_ACTION_PAY_NOW presents the "Pay Now" experience: the buyer commits to the payment
// in the PayPal app, and you present the final order status when they return.
val request = PayPalCheckoutRequest(
hasUserLocationConsent = true,
amount = "59.00"
).apply {
enablePayPalAppSwitch = true
userAuthenticationEmail = "[email protected]" // Strongly recommended — improves App Switch eligibility
userAction = PayPalPaymentUserAction.USER_ACTION_PAY_NOW
shouldOfferPayLater = false // Set to true to prioritize PayPal Pay Later installment options on the PayPal screen
}
// Use the official Braintree PayPal launcher to present the PayPal button flow
payPalClient.createPaymentAuthRequest(this, request) { authRequest ->
when (authRequest) {
is PayPalPaymentAuthRequest.ReadyToLaunch -> {
pendingRequest = payPalLauncher.launch(this, authRequest)
}
is PayPalPaymentAuthRequest.Failure -> handleError(authRequest.error)
}
}
// In your tokenize callback:
// PayPalResult.Success(nonce) -> createTransaction(nonce.string, "59.00")Saved Payments
- Kotlin
class VaultedPaymentActivity : AppCompatActivity() {
private lateinit var payPalLauncher: PayPalLauncher
private lateinit var payPalClient: PayPalClient
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// PayPalLauncher must be initialized in onCreate
payPalLauncher = PayPalLauncher()
// can initialize the PayPalClient outside of onCreate if desired
payPalClient = PayPalClient(
context = this,
authorization = "[TOKENIZATION_KEY or CLIENT_TOKEN]",
appLinkReturnUrl = Uri.parse("https://merchant-app.com"), // Merchant App Link
"com.merchant.app.deeplink.braintree" // Fallback deep link return url scheme
)
}
// ONLY REQUIRED IF YOUR ACTIVITY LAUNCH MODE IS SINGLE_TOP
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
handleReturnToApp(intent)
}
// ALL OTHER ACTIVITY LAUNCH MODES
override fun onResume() {
super.onResume()
handleReturnToApp(intent)
}
private fun handleReturnToApp(intent: Intent) {
// fetch stored PayPalPendingRequest.Success
val pendingRequest: String = fetchPendingRequestFromPersistantStore()
when (val paymentAuthResult = payPalLauncher.handleReturnToApp(
pendingRequest = PayPalPendingRequest.Started(pendingRequest),
intent = intent
)) {
is PayPalPaymentAuthResult.Success -> {
completePayPalFlow(paymentAuthResult)
// clear stored PayPalPendingRequest.Success
}
is PayPalPaymentAuthResult.NoResult -> {
// user returned to app without completing PayPal flow, handle accordingly
}
is PayPalPaymentAuthResult.Failure -> {
// handle error case
}
}
}
private fun completePayPalFlow(paymentAuthResult: PayPalPaymentAuthResult.Success) {
payPalClient.tokenize(paymentAuthResult) { result ->
when (result) {
is PayPalResult.Success -> { /* handle result.nonce */ }
is PayPalResult.Failure -> { /* handle result.error */ }
is PayPalResult.Cancel -> { /* handle user canceled */ }
}
}
}
private fun onPayPalButtonClick() {
val payPalVaultRequest = PayPalVaultRequest(
hasUserLocationConsent = true,
billingAgreementDescription = "Your agreement description"
)
payPalVaultRequest.userAuthenticationEmail = "[email protected]"
request.userPhoneNumber = PayPalPhoneNumber("1", "2223334444")
payPalVaultRequest.enablePayPalAppSwitch = true
payPalClient.createPaymentAuthRequest(this, payPalVaultRequest) { paymentAuthRequest ->
when (paymentAuthRequest) {
is PayPalPaymentAuthRequest.ReadyToLaunch -> {
val pendingRequest = payPalLauncher.launch(this, paymentAuthRequest)
when (pendingRequest) {
is PayPalPendingRequest.Started -> {/* store pending request */ }
is PayPalPendingRequest.Failure -> { /* handle error */ }
}
}
is PayPalPaymentAuthRequest.Failure -> { /* handle paymentAuthRequest.error */ }
}
}
}
}Save Payment Method with Purchase
The Save Payment Method with Purchase flow charges the buyer and saves their PayPal account as a payment method for future use — in a single checkout step. The buyer approves both the payment and the save consent together in the PayPal app.
- Kotlin
// Save Payment Method with Purchase — the buyer pays and saves their PayPal account in one step.
// shouldRequestBillingAgreement = true — buyer consents to save their PayPal account for future use.
// USER_ACTION_PAY_NOW — the buyer commits to the payment in the PayPal app (no review step on your side).
val request = PayPalCheckoutRequest(
hasUserLocationConsent = true,
amount = "64.00"
).apply {
enablePayPalAppSwitch = true
userAuthenticationEmail = "[email protected]" // Strongly recommended — improves App Switch eligibility
userAction = PayPalPaymentUserAction.USER_ACTION_PAY_NOW
shouldOfferPayLater = false // Set to true to prioritize PayPal Pay Later installment options on the PayPal screen
shouldRequestBillingAgreement = true // Buyer consents to save their PayPal account
billingAgreementDescription = "Save your PayPal for future purchases"
}
// Use the official Braintree PayPal launcher to present the PayPal button flow
payPalClient.createPaymentAuthRequest(this, request) { authRequest ->
when (authRequest) {
is PayPalPaymentAuthRequest.ReadyToLaunch -> {
pendingRequest = payPalLauncher.launch(this, authRequest)
}
is PayPalPaymentAuthRequest.Failure -> handleError(authRequest.error)
}
}
// In your tokenize callback:
// PayPalResult.Success(nonce) -> createTransactionAndSave(nonce.string, "64.00")
// Server-side: gateway.transaction.sale with storeInVaultOnSuccess: true
// Returns transaction ID + a reusable payment method token for future charges
On your server, call gateway.transaction.sale with
storeInVaultOnSuccess: true. The response includes both the transaction ID and a
reusable payment method token for future charges.
Best Practices
These best practices are crucial for ensuring an optimal user experience and improved conversion rates.
1. Show a loading indicator after PayPal button click
When the user taps the PayPal button:
- Turn off the payment button to prevent duplicate submissions
- Display a loading indicator while the network request is in progress
The loading indicator not only protects against accidental double-clicks but also reassures the user that processing is underway.
2. Provide buyer email and phone number
Although optional, passing the buyer's email and phone number whenever available is strongly recommended.
Providing this data helps PayPal to:
- Personalize the buyer experience
- Improve risk assessment
- Optimize approval rates
Buyers' email and phone numbers often result in higher overall conversion.
Phone number formatting requirements
When passing a phone number, ensure it is sanitized into its canonical format:
- Country code should be numeric only (no "+" prefix).
-
National number 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 that are not part of the actual national number.
Example: request.userPhoneNumber = PayPalPhoneNumber("1", "2223334444")
3. Handling return to your app
When the buyer returns to your app (either automatically or manually):
- Remove the loading indicator when your app returns to the foreground.
- This allows the user to continue where they left off, especially when they return 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):
- Show the loading indicator again.
- Turn off the PayPal button.
- Continue processing until the transaction completes.
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 unexpected user interactions during the payment finalization process.
4. Set fallback deep link return URL
A buyer may uncheck the "Open supported links" setting on their Android device, which prevents them from returning to your app from the PayPal app through app links. You can provide a fallback deep link return URL scheme that will be used to complete the session. The regular Chrome Custom Tab flow will be displayed when the deep link fallback return URL is used, and App Switch will not occur in this scenario. Follow these steps to configure the deep link return URL scheme in your AndroidManifest.xml and pass the deep link return URL in your PayPalClient constructor.
- Kotlin
payPalClient = PayPalClient(
context = this,
authorization = "[TOKENIZATION_KEY or CLIENT_TOKEN]",
appLinkReturnUrl = Uri.parse("https://merchant-app.com"), // Merchant App Link
"com.merchant.app.deeplink.braintree" // Fallback deep link return url scheme
)5. Set App Link path prefix
If your App Link URL contains a path (for example,
https://merchant-app.com/paypal), you must register that path in your
AndroidManifest.xml intent-filter using the pathPrefix attribute.
PayPal uses your App Link domain and path to return buyers to your app after completing payment. This includes both App Switch sessions and non-App Switch sessions (OneTouch, long-lived sessions). If the path prefix is not registered, your app will not handle the return URL for non-App Switch sessions, and those buyers will see a failed checkout.
<data android:pathPrefix="/[path]" /> to your intent-filter. Without this,
buyers returning via OneTouch or long-lived sessions will receive a NoResult error
and their transactions will not complete.
Add the pathPrefix to your intent-filter in AndroidManifest.xml:
- XML
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" />
<data android:host="merchant-app.com" />
<!-- Required if your App Link URL contains a path -->
<data android:pathPrefix="/your-app-switch-path" />
</intent-filter><data android:pathPrefix="..." /> element within the same intent-filter.
Also, after you complete the integration, contact your PayPal support team to enable App Switch
for production traffic.
For detailed testing procedures and production deployment guidance, see the Testing and Go Live section.

