This guide is for merchants who are using GraphQL API integration with:
- Vault Flow: Vaulting without Checkout
- One Time Checkout: One-time payment processing
Overview
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.
Buyer flow
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.
Steps
- The buyer selects the PayPal button from your app or website.
- The PayPal app opens and buyer is authenticated using low friction login mechanisms like Face ID, biometrics, App LLS or passkey.
- The buyer reviews purchase details and approves the transaction in the PayPal app.
- 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).
- 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 integrations
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
Prerequisites
For traffic from your native app, complete the several checks before you pass the appSwitchContext
parameter to PayPal.
appSwitchContext
means you are opting in to app switch behavior.
Setup Requirements
- Register Deep Link Support:
- For iOS, register for Universal Links.
- For Android, register for App Links.
- 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:
- 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:
- Swift
public func isPayPalAppInstalled() -> Bool {
guard let payPalURL = URL(string: "paypal-app-switch-checkout://") else {
return false
}
return canOpenURL(payPalURL)
}
Android check:
Following sample code checks if the PayPal app is installed on an Android device.
- 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.
- Kotlin
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<queries>
<package android:name="com.paypal.android.p2pmobile" />
</queries>
...
</manifest>
GraphQL updates
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 app
Field | Type | Property | Description |
---|---|---|---|
nativeApp | Object | Optional | Indicates if the flow is originated from native app |
osType | ENUM | IOS, ANDROID, OTHER | Required - OS type for the device |
osVersion | String | Required | OS version for the device |
appUrl | URL | Required | PayPal uses this URL to redirect the payer back to your app |
- json
"appSwitchContext": {
"nativeApp": {
"osType": "IOS",
"osVersion": "18.1",
"appURL": "<<some url>>"
}
}
Case 2 - Origin is mobile web browser
Field | Type | Property | Description |
---|---|---|---|
mobileWeb | Object | Optional | Indicates if the flow is originated from mobile web browser |
buyerUserAgent | String | Required | Browser details |
returnFlow | ENUM | Auto, Manual | Required - 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
- 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"
}
}
Recommendation
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 fields
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 & appUrl
For Native Apps:
If your app uses deep links, supports universal links, the payer is on a previous version of iOS, or you use
ASWebAuthenticationSession
, pass those deep links in thereturn_url
andcancel_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
andcancel_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 requests
Vault Flow
- 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 Checkout
- 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>>"
}
}
}
}
}
New queries and billing agreement status enhancements
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 Flow
fetchPayPalBillingAgreementStatus
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.
experienceStatus
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.
- Response
query FetchBillingAgreementStatus ($input: PayPalBillingAgreementDetailsInput!){
payPalBillingAgreementDetails(input:$input){
status
experienceStatus
}
}
{
"input":{
"billingAgreementToken":"BAToken"
}
}
For One Time Checkout
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.
experienceStatus-
Helps identify if the the payer cancelled the the checkout process at any pointResponse 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.
- graphql
- javascript
- 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)
- javascript
- javascript
- 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
- javascript
- javascript
- They could identify the return on the server-side by intercepting the request, reading the query parameters, and completing payment
- They could identify the return on the client-side after the page has been loaded and then completing payment from there
query FetchPayPalOrderDetails ($input: PayPalOrderDetailsInput!){
fetchPayPalOrderDetails(input:$input){
status
experienceStatus
}
}
{
"input":{
"orderId":"PaypalOrderID"
}
}
Response experience best practices
Originating from Native App
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 Web
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:
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 Flow
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 Tab
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:
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/Browser
When PayPal redirects the buyer back to the merchant page, it may open in a new tab or browser due to several reasons:
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:
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:
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 redirect
This can occur when:
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:
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:
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 others
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:
Summary
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:
Feature | Vault Flow | One Time Checkout |
---|---|---|
Mutation | createPayPalBillingAgreement | createPayPalOneTimePayment |
Token | billingAgreementToken | paymentId |
Status Query | payPalBillingAgreementDetails | fetchPayPalOrderDetails |
Final Call | vaultpaymentmethod | chargepaymentmethod |
Both flows share the same prerequisites, app switch context setup, and response handling patterns, making it easy to implement both if needed.