Important

This guide is for merchants who are using GraphQL API integration with:

  • Vault Flow: Vaulting without Checkout
  • One Time Checkout: One-time payment processing

OverviewAnchorIcon

App Switch allows buyers who have the PayPal app installed to start in a merchant app or website and navigate to the PayPal app to complete their transaction or vault PayPal as a payment method. App switch is enabled via strong multi-factor authentication and the buyer can use their Face ID, biometrics and passkey to log in instead of entering their password or OTP.

The buyer experience falls back to the existing PayPal buyer approval web flow in cases where App Switch isn't available.

This is a standalone API integration where the merchant is expected to handle the buyer's interaction between their app/website and PayPal app depending on where the buyer initiates the flow from and how the merchant wants to deal with the redirect scenarios back to their app/website to complete the buyer flow.

Note
The app switch feature is available only on mobile devices and is not supported on desktop platforms.

Buyer flowAnchorIcon

We support App Switch functionality in the following scenarios to streamline the buyer's checkout experience:

  • Native App to PayPal App:
    The buyer initiates checkout within the merchant’s native mobile application and is seamlessly redirected to the PayPal consumer app to review and approve the transaction.

  • Mobile Web to PayPal App:
    The buyer begins the transaction on the merchant’s website via a mobile browser and is then switched to the PayPal consumer app to complete the review and authorization process.


Buyer,is,directed,to,the,PayPal,app,to,complete,the,transaction.

StepsAnchorIcon

  1. The buyer selects the PayPal button from your app or website.
  2. The PayPal app opens and buyer is authenticated using low friction login mechanisms like Face ID, biometrics, App LLS or passkey.
  3. The buyer reviews purchase details and approves the transaction in the PayPal app.
  4. The buyer is redirected to the merchant's app or website. This redirect can either be automatic or manual (buyer is shown a message like 'Head back to Merchant' to return to the source from where they started the transaction).
  5. Complete the transaction using the appropriate API call:
      • Vault Flow: Create vaulted payment method token
      • One Time Checkout: Capture API call

For detailed information on getting started with GraphQL, see the GraphQL integration guide.

Unsupported integrationsAnchorIcon

Avoid opting in for App Switch in the following situations:

  • Desktop/Tablet Support: App Switch supports only mobile devices, including mobile apps and mobile websites. App Switch does not support desktop devices and tablets
  • Web Views: The merchant app checkout is hosted in a web view, or a third-party app hosts the merchant website in a web view. Using App Switch in this scenario may cause unexpected redirects, browser launches, or app installation prompts
  • iFrames: The checkout experience is embedded in an iFrame. Similar to web views, iFrames handle links differently, resulting in an undesired payer experience when you attempt to App Switch
  • Safari View Controller/Chrome Custom Tabs: The merchant checkout is hosted using Safari View Controller or Chrome Custom Tabs. These components open web content in a browser tab that mimics the app's appearance but does not fully support App Switch

PrerequisitesAnchorIcon

For traffic from your native app, complete the several checks before you pass the appSwitchContext parameter to PayPal.

Note
Note: Passing appSwitchContext means you are opting in to app switch behavior.

Setup RequirementsAnchorIcon

  • Register Deep Link Support:
  • Verify Link Handling on Android:
    Ensure that the payer has enabled "Open supported links" for your Android app. If this setting is disabled, PayPal’s redirection via app links may send the user to your mobile website instead of your in-app checkout, disrupting the intended flow.

Recommendation: If supported links are not enabled, do not include theappSwitchContext parameter in your request.

The following code demonstrates how your Android app can verify whether supported links are enabled. This check helps determine whether to opt in or out of App Switch behavior:

  1. Kotlin
fun hasEnabledSupportedLinks(context: Context): Boolean {
    val intent = Intent(Intent.ACTION_VIEW, app_link_return_uri).apply {
        addCategory(Intent.CATEGORY_BROWSABLE)
    }
    val resolvedActivity = context.packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY)
    return if (resolvedActivity?.activityInfo?.packageName == context.packageName) {
        // Open Supported Links for my native app enabled
        // i.e. can return to this app via AppLinks invoked by PayPal
        // opt-in to app switch
        true
    } else {
        // Open Supported Links for my native app disabled
        // i.e. cannot return to this app via AppLinks invoked by PayPal
        // opt-out from app switch
        false
    }
}

Ensure that the payer has the PayPal app installed on their mobile device before enabling App Switch. If the app is not installed, the App Switch feature will not work as intended. Instead, the PayPal checkout experience will open in the device’s browser rather than within the merchant app.

We recommend passing the appSwitchContext parameter only if the PayPal app is installed.

Register the URL scheme in you Info.plist file. The following sample code checks if the PayPal app is installed on an iOS device or android

iOS check:AnchorIcon
  1. Swift
public func isPayPalAppInstalled() -> Bool {
    guard let payPalURL = URL(string: "paypal-app-switch-checkout://") else {
      return false
    }
    return canOpenURL(payPalURL)
  }
Android check:AnchorIcon

Following sample code checks if the PayPal app is installed on an Android device.

  1. Kotlin
fun isPayPalAppInstalled(context: Context): Boolean {
    val paypalPackageName = "com.paypal.android.p2pmobile"
    return try {
        context.packageManager.getApplicationInfo(paypalPackageName, 0)
        true
    } catch (e: PackageManager.NameNotFoundException) {
        false
    }
}
You would need to register the packageName in your AndroidManifest.AnchorIcon
  1. Kotlin
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <queries>
        <package android:name="com.paypal.android.p2pmobile" />
    </queries>
...
</manifest>

GraphQL updatesAnchorIcon

Note
The schema of these objects are subject to change.

To support App Switch, a new appSwitchContext field will be added to the inputs of both the createPayPalBillingAgreement mutation (for Vaulted Flow) and the createPayPalOneTimePayment mutation (for One-time Payment).

Case 1 - Origin is merchant native appAnchorIcon

FieldTypePropertyDescription
nativeAppObjectOptionalIndicates if the flow is originated from native app
osTypeENUMIOS, ANDROID, OTHERRequired - OS type for the device
osVersionStringRequiredOS version for the device
appUrlURLRequiredPayPal uses this URL to redirect the payer back to your app
  1. json
"appSwitchContext": {
  "nativeApp": {
    "osType": "IOS", 
    "osVersion": "18.1",
    "appURL": "<<some url>>"
  }
}

Case 2 - Origin is mobile web browserAnchorIcon

FieldTypePropertyDescription
mobileWebObjectOptionalIndicates if the flow is originated from mobile web browser
buyerUserAgentStringRequiredBrowser details
returnFlowENUMAuto, ManualRequired - Indicates the return flow from PayPal app to merchant app
  • buyerUserAgent: Buyer's user agent. This field is a raw string and will be used in mobile web scenarios to derive the buyer's device os information - type, version and browser details. Forward us your buyer's user agent without any modifications.

  • returnFlow - type ENUM:

    • AUTO - (Default) For these transactions, post approval of payment within the PayPal App, PayPal will attempt to automatically redirect the buyer back to the merchant website
    • MANUAL - For these transactions, post approval of payment within the PayPal App, the buyer will be asked to manually navigate back to the merchant website where they started the payment from
  1. json
"appSwitchContext": {
  "mobileWeb": {
    "buyerUserAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.4 Mobile/15E148 Safari/604.1",
    "returnFlow": "AUTO"
  }
}

RecommendationAnchorIcon

When traffic comes from web views in either first-party or third-party apps (such as social media apps), it is recommended to avoid app switching by not including the appSwitch field. Due to the limitations of web view behavior, app switching might not occur, and the buyer will go through the existing PayPal buyer approval web flow. If the merchant still chooses to send the app switch parameters, ensure that the return and cancel URLs are universal links or app links associated with your native app (for first-party web views) or web links related to your mobile browser’s website page where the buyer initiated the flow (for third-party web views).

Other fieldsAnchorIcon

returnURL - URL that tells PayPal where to send the payer after completing checkout on the PayPal app. Set the URL to the page where the payer selects the PayPal button. (Recommended)

cancelURL - URL that tells PayPal where to send the payer when the payer cancels or doesn't complete the transaction on the PayPal app. Set the URL to the page where the payer should be redirected when they cancel the transaction. (Recommended)

Best practices for returnURL, cancelURL & appUrlAnchorIcon

  • For Native Apps:

    • If your app uses deep links, supports universal links, the payer is on a previous version of iOS, or you useASWebAuthenticationSession, pass those deep links in the return_url and cancel_url fields. Otherwise, the payer may not return to your app as expected.

    • Include the app_url parameter (recommended). This takes precedence over return_url and cancel_url.

    • If PayPal determines the app switch is eligible, we will use app_url to return the payer to your app. If not, we will use return_url and cancel_url as fallbacks.
  • For Mobile Web

    • return_url and cancel_url must be the same.
    • Should be a web link associated with your website on a mobile browser from where the buyer initiated the payment.
    • Match the URL of the page where the buyer selected the PayPal button.
    • If the merchant wants to send query parameters back, append them after the hash (#) in the URL for app switch.
    • The URLs must contain a unique identifier for the buyer's session to identify them when they return from App Switch.
    • The merchant-passed URLs should exactly match the checkout URL from where the buyer starts their app switch experience.
    • If merchant wish to send query parameters that they need PayPal return, the params need to be appended after the hash (#) in the returnURL

launchPayPalApp is new Boolean field, that's added to help merchants with a decision to launch PayPal App. If the response is false, that means, we don't have all the necessary information to help with the flow.

Sample requestsAnchorIcon

Vault FlowAnchorIcon

  1. graphql
mutation CreatePayPalBillingAgreement($input: CreatePayPalBillingAgreementInput!) {
      createPayPalBillingAgreement(input: $input) {
        clientMutationId
        approvalUrl
        billingAgreementToken
        launchPayPalApp
      }
    }

{
    "input": {
      "merchantAccountId": "MERCHANT_ACCOUNT_ID",
      "returnUrl":"",
      "cancelUrl":"",
      "appSwitchContext":
          {
            "nativeApp": {
            "device": { "osType":"IOS", "osVersion":"18.1","appURL":"<<someURL>>"}
            }
          }
    }
}

One Time CheckoutAnchorIcon

  1. json
mutation CreatePayPalOneTimePayment($input: CreatePayPalOneTimePaymentInput!) {
    createPayPalOneTimePayment(input: $input) {
      approvalUrl
      clientMutationId
      paymentId
      launchPayPalApp
    }
  }
  
{
  "input": {
          "amount": { "value": "10", currencyCode: "USD" },
          "merchantAccountId": "MERCHANT_ACCOUNT_ID",
          "intent": "AUTHORIZE",
          "payerEmail": "janedoe@paypal.com"
          "returnUrl": "returnUrl", 
          "cancelUrl": "cancelUrl",
          "appSwitchContext":
          {
            "nativeApp": {
            "osType":"IOS",
            "osVersion":"18.1",
            "appURL":"<<someURL>>"
            }
          }
        }
    }
}

Note
Passing payerEmail ensures a better App Switch experience.

New queries and billing agreement status enhancementsAnchorIcon

To provide merchants with more visibility into the status of a Billing Agreement, we’ve introduced a new GraphQL query along with two supporting fields:

For Vault FlowAnchorIcon

fetchPayPalBillingAgreementStatusAnchorIcon

This new query allows merchants to retrieve the current status of a PayPal Billing Agreement token.

Response Values:

  • APPROVAL_REQUIRED – The token has been created but is still awaiting approval from the payer.
  • APPROVED – The token has been successfully approved by the payer.

experienceStatusAnchorIcon

This field helps identify the payer’s progress through the checkout experience, particularly if the approval process was interrupted or abandoned.

Response Values:

  • APPROVED – The payer has successfully approved saving PayPal as a payment method. You can proceed to create a vaulted payment method token.
  • CANCELED – The payer actively canceled the approval process (e.g., by closing the window or clicking "Cancel").
  • IN_PROGRESS – The payer has initiated the vaulting process and is currently on the PayPal review page, but has not yet approved.
  • NOT_STARTED – The payer has not yet logged in to PayPal or begun the approval process. Approval is still pending.
  1. Response
query FetchBillingAgreementStatus ($input: PayPalBillingAgreementDetailsInput!){
    payPalBillingAgreementDetails(input:$input){
    status
    experienceStatus
  }
}
{
    "input":{
        "billingAgreementToken":"BAToken"
    }
}

For One Time CheckoutAnchorIcon

  1. fetchPayPalOrderStatus- Helps identify the status of the Order. Response Values
    • APPROVED:The customer approved the payment through the PayPal wallet or another guest or unbranded payment form.
    • COMPLETED: The intent of the order was completed and a payments resource was created.
    • CREATED: The Order was created.
    • PARTIALLY_COMPLETED: Some of the lineitems within the Order could not be successfully authorized or captured.
    • PAYER_ACTION_REQUIRED: The Order requires an action from the payer.
    • PENDING_APPROVAL: The Order was confirmed and payer action completed, but order approval processing from PayPal is still pending.
    • SAVED: The order was saved and persisted.
    • VOIDED: All purchase units in the order are voided.
  2. experienceStatus- Helps identify if the the payer cancelled the the checkout process at any point

    Response Values

    • APPROVED: The order is approved. The user has completed the checkout process.
    • CANCELED: PayPal checkout was canceled (e.g., by closing the checkout window or clicking cancel) before the order was approved.
    • IN_PROGRESS: PayPal checkout was initiated. The user is on the checkout page reviewing the order before approval.
    • NOT_STARTED: The PayPal checkout process has not yet begun.
    1. graphql
    query FetchPayPalOrderDetails ($input: PayPalOrderDetailsInput!){
        fetchPayPalOrderDetails(input:$input){
        status
        experienceStatus
      }
    }
    {
        "input":{
            "orderId":"PaypalOrderID"
        }
    }

    Response experience best practicesAnchorIcon

    Originating from Native AppAnchorIcon

    PayPal will handle the return back to merchant app utilizing the universal link/app link provided by merchants in returnUrl and cancelUrl.

    After buyer approves the transaction/token on the PayPal app when buyer is redirected back to merchant app, the OS may redirect the buyer to merchant website instead of app.

    This could be due to mismatch in universal link/app link shared by merchant and universal link/app link associated with buyer's merchant app on their device. Please ensure you handle such scenarios by making necessary configurations to handle this and take the appropriate actions to get final payment/token status, as buyers may not always land on your app despite being redirected using the return or cancel URLs provided.

    Originating from Mobile WebAnchorIcon

    When a buyer approves transaction/token on PayPal, the process of returning them to the merchant's website may not be uniform, leading to varied experiences.

    OS technical constraints:AnchorIcon

    Note
    After buyer approves transaction/token on PayPal App, when we attempt to return the buyer back to the merchant website using the return/cancel URL shared by merchant - the OS will always attempt to open the merchant website in the buyer's default browser. If the buyer's default browser (e.g, chrome) is different from the device native browser (e.g, safari on iOS), this may result in the buyer originating from browser A and returning back to browser B.

    Buyer can start the transaction from a non-default browser or incognito mode. Due to OS limitations, while returning back, merchant website will open in the default browser.

    1. Resume FlowAnchorIcon

    After completing the transaction/approval, the buyer will be redirected back to the merchant website based on the return URL provided during the order/agreement creation process.

    In this case "returnFlow":"AUTO"

    a. Same TabAnchorIcon

    In the case that a buyer is navigating via their mobile device default browser (i.e, if they use Safari on iOS, with Safari as default), when they complete transaction on PayPal App they are successfully redirected back to the merchant mobile browser website automatically.

    Please include a client-side event listener to identify this event. The hashchange listener can be used and the status of the completion will be included as additional hash params.

    For both flows:

    1. javascript
    document.addEventListener('hashchange', (e) => {
    	const params = parseHashParams(window.location.hash); 
    	if(params.approved) {
    		// Buyer is returning from app switch with an approved setup token/order
    		// Verify the setup token/order approval, create the payment method token/complete payment
    	} else if (params.canceled) {
    		// Buyer canceled PayPal app switch
    	}
    })

    b. Different Tab/BrowserAnchorIcon

    When PayPal redirects the buyer back to the merchant page, it may open in a new tab or browser due to several reasons:

    • The buyer initiated the transaction using a non-default browser (i.e, if they use Chrome on iOS, with Safari as default)
    • The return URL provided does not match the page URL where the buyer started the transaction on the merchant site
    • The buyer started the transaction from merchant website within a SFVC/CCT of a third-party app
    • The buyer started the transaction from merchant website within a web-view of a third-party app (App switch not recommended)

    Important
    You must be aware that there are no cookies that can be used to maintain state across browsers, so rely strictly on the token/order status that is returned.

    Since the redirect occurred from the PayPal App, the indicators that specify this approval flow was completed only via hash parameters in the url. These can only be read via client-side code and cannot be parsed via server-side routes.

    Vault Flow example:

    1. javascript
    document.addEventListener('hashchange', (e) => {
    	const params = parseHashParams(window.location.hash); 
    	if(params.approved) {
    		// Buyer is returning from app switch with an approved order
    		// Verify the order approval, complete payment
    		// & redirect to confirmation page to complete flow
    		const response = tokenizePayPalOneTimePayment(orderId);
    		if (response.Success) {
    			call chargePaymentMethod
    		}
    	} else if (params.canceled) {
    		// Buyer canceled PayPal app switch
    	}
    })

    One Time Checkout example:

    1. javascript
    document.addEventListener('hashchange', (e) => {
    	const params = parseHashParams(window.location.hash); 
    	if(params.approved) {
    		// Buyer is returning from app switch with an approved order
    		// Verify the order approval, complete payment
    		// & redirect to confirmation page to complete flow
    		const response = tokenizePayPalOneTimePayment(orderId);
    		if (response.Success) {
    			call chargePaymentMethod
    		}
    	} else if (params.canceled) {
    		// Buyer canceled PayPal app switch
    	}
    })

    2. Manual redirectAnchorIcon

    This can occur when:

    • When a buyer completes their payment through the PayPal App, they may need to manually return to the merchant's website. In this case "returnFlow": "MANUAL"
    • This situation can also arise if the buyer navigates away from the PayPal app, effectively abandoning their checkout process

    To detect when a buyer returns to the merchant's website, you can utilize the visibilitychange event. It's important to note that this event will trigger each time a buyer comes back to your page from another app or switches from a different browser window or tab.

    Vault Flow example:

    1. javascript
    document.addEventListener('visibilitychange', (e) => {
        // If #change event was triggered then cancel this event.
        const hashParams = parseHashParams(window.location.hash);
    	if (hashParams.approved || hashParams.cancelled) {
    		// Will be handled by hashChange. Exit
    		return;
    	}
    	// Merchant to call fetchPayPalBillingAgreementStatus
    	const response = getStatus(billingAgreementToken);
    	if (response.status = 'Approved') {
    		// call tokenizePayPalBillingAgreement API
    		const response = tokenizePayPalBillingAgreement(BAToken);
    		call vaultPaymentMethod(paymentMethodId)
    	} else if (response.error = 'ORDER_NOT_APPROVED') {
    		// Display a modal to complete payment on the PayPal App
    	} else {
    		// Current as is
    	}
    });

    One Time Checkout Example:

    1. javascript
    document.addEventListener('visibilitychange', (e) => {
        //If #change event was triggered then cancel this event.
        const hashParams = parseHashParams(window.location.hash);
    	if (hashParams.approved || hashParams.cancelled) {
    		//Wil be handled by hashChange. Exit
    		return;
    	}
    	//Merchant to Call fetchPayPalOrderStatus API
    	const response = getStatus(orderId);
    	if (response.status = 'Approved') {
    	//call tokenizePayment call to get the nonce for payment
    		const response = tokenizePayPalOneTimePayment(orderId);
    		call chargePaymentMethod(paymentId)
    	} else if (response.error = 'ORDER_NOT_APPROVED') {
    		// Display a modal to complete payment on the PayPal App
    	} else {
    		//Current as is
    	}
    });

    3. Fallback when PayPal app NOT installed or othersAnchorIcon

    Note
    This integration needs to be done irrespective of the returnFlow selected by merchant.

    When a buyer has met the app switch eligibility criteria but does not have the PayPal app installed or in a situation where the transient user activation timeout is hit prior to the app switch occurring, instead of app switch the buyer will fallback on existing PayPal experience. This is mostly similar to the existing API-only integration that exists today, but with the exception that the merchant must have the return_url and cancel_url be the exact same as the merchant website url from where this flow started.

    Merchants have two options to detect a return flow in this situation:

    1. They could identify the return on the server-side by intercepting the request, reading the query parameters, and completing payment
    2. They could identify the return on the client-side after the page has been loaded and then completing payment from there

    SummaryAnchorIcon

    This unified guide covers both Vault Flow and One Time Checkout implementations for App Switch with BT GraphQL. The key differences between the flows are:

    FeatureVault FlowOne Time Checkout
    MutationcreatePayPalBillingAgreementcreatePayPalOneTimePayment
    TokenbillingAgreementTokenpaymentId
    Status QuerypayPalBillingAgreementDetailsfetchPayPalOrderDetails
    Final Callvaultpaymentmethodchargepaymentmethod

    Both flows share the same prerequisites, app switch context setup, and response handling patterns, making it easy to implement both if needed.