# App Switch: Direct API Vaulting without purchase (/limited-release/commerce-platform/accept-payments/standard/customize/app-switch/api-vaulted-payments)



## Overview [#overview]

Vaulting helps securely save a payer's PayPal wallet, with the payer's consent, for future use. PayPal Vault issues a tokenized version of a payment method such as a card or PayPal wallet that you can safely reuse for recurring or future purchases.

You do not need a transaction to save payment methods with the[Payment Method Tokens API](/api/payment-tokens/v3). You can charge payers later, even if they are not present. For example, you can offer a free trial and charge the payer after the trial ends.

With App Switch, payers save their PayPal Wallet for future use on the merchant app or mobile website seamlessly. App Switch streamlines the approval process by allowing payers to authenticate through the PayPal app Long Lived Session (LLS), biometrics, and multi-factor authentication.

When App Switch isn't available, payers use the current approval experience in the merchant app or mobile web.

This is a standalone API integration that manages the payer's interaction between the app or website and the PayPal app. The approval flow depends on where the payer starts and how the integration handles the payer's redirect back from the PayPal app.

> **Info:** This App Switch integration is currently only supported for US merchants and buyers.

## Know before you code [#know-before-you-code]

> **Info:** Before saving payment methods, enable Vault, PayPal, and Venmo in your app settings and ensure the account is approved for saving PayPal payment methods.
>
> 1. Log in to the [PayPal Developer Dashboard](https://www.paypal.com/signin?returnUri=https%3A%2F%2Fdeveloper.paypal.com%2Fdashboard%2F\&intent=developer\&ctxId=ula8d9d6f0e2da4f069ddb4af89ce936a6).
> 2. In the **REST API apps**, select your app name.
> 3. Navigate to **Sandbox App Settings** or **Live App Settings** and enable:
>    * **Vault** under **Accept payments**.
>    * **PayPal** **and Venmo** under **Accept payments**.
> 4. In your PayPal account settings, verify that your account status shows Approved for PayPal Wallet vaulting.

## Prerequisites [#prerequisites]

Before beginning integration, meet these requirements:

**For merchants:**

* Complete the Get Started guide to obtain sandbox test credentials for both payer and business sandbox accounts.
* Obtain approval for billing agreements if you plan to use reference transactions.
* Configure API credentials for client ID and secret.
* Enable the Vault feature in PayPal Developer Dashboard.
* Set up HTTPS-enabled return and cancel URLs.

**For payers:**

* Active PayPal account

## App Switch Vault flow [#app-switch-vault-flow]

Payers can initiate App Switch in a couple of ways:

* The payer is on the native merchant app and switches to the PayPal consumer app to review and approve saving of the PayPal Wallet in merchant's vault.
* The payer is on the merchant website on a mobile browser and switches to the PayPal consumer app to review and approve saving of the PayPal Wallet in merchant's vault.

When the payer takes an action in the PayPal app, PayPal returns the payer to the merchant in one of the following ways:

* If the payer starts in the merchant app, the PayPal app automatically redirects them back to the merchant app.
* If the payer starts on the merchant website, the merchant can choose to redirect the payer automatically or require a manual redirect. For a manual redirect, the payer follows instructions in the PayPal app to return to the merchant website.

## Best practices [#best-practices]

* If your merchant encounters a server-side timeout during the Create Setup Token call and need to retry, use the same idempotency key. This ensures the setup token remains the same. Avoid creating a new Setup Token.
* Configure App Switch for native apps and mobile web browsers. Validate and set up context-specific options as described in the Platform flow requirements section.
* Post the Create Setup Token call after the payer selects the PayPal button. Apply this guidance to all PayPal-bound vault approval calls, including those that use App Switch.

When the payer selects the PayPal button on your merchant's page:

* Disable the button to prevent multiple clicks while the user stays on the same screen.
* Re-enable the button if the user navigates away from the app and then returns.

## Unsupported integrations [#unsupported-integrations]

Avoid opting in for App Switch in the following situations:

* App Switch supports only mobile devices, including mobile apps and mobile websites. App Switch does not support desktop devices and tablets.
* The merchant app approval experience 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.
* The approval experience is embedded in an iFrame. Like web views, iFrames handle links differently and can result in an undesired payer experience when using App Switch.
* The approval experience is hosted with 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.

## Integrate App Switch [#integrate-app-switch]

Enable App Switch for successful integration to support the payer's intent to save PayPal as the payment method in the vault.

1. Configure App Switch using the server-side Create Setup Token API request.
2. Gather specific information from your merchant's system and include it in the Create Setup Token API as part of the App Switch request to enable seamless payer approval flow.
3. To successfully handle App Switch flows originating from either a native app or a mobile web browser, implement careful validation and context-specific configuration steps, as detailed in the guide below.
4. Redirect the payer to approve, and send the payer to the PayPal approval URL from the response.
5. After they approve, convert that setup token to a reusable payment method.
6. Use a vaulted payment method token for a purchase. Payers can complete purchases using their saved PayPal Wallet without re-entering payment details.

## Integrate merchant native mobile app [#integrate-merchant-native-mobile-app]

The payer starts in the merchant's native app and switches to the PayPal app to complete the approval flow. After completing the approval, PayPal redirects the payer back to the merchant app.

### App Switch scenarios [#app-switch-scenarios]

* When the merchant passes `app_switch_context`, the payer starts in the native merchant app, switches to the PayPal app to complete the approval flow, and is automatically redirected back to the merchant app.
* If the merchant passes `app_switch_context` but the app switch to the PayPal app fails, the payer switches to their default browser, where the PayPal pay sheet loads. After completing the approval, the payer returns to the merchant app.
* If the merchant does not pass `app_switch_context` or PayPal sets `app_switch_eligibility` to false, the payer completes the approval using the current experience without switching to the PayPal app.

### Native app prerequisites [#native-app-prerequisites]

For traffic from the native app, complete several checks to pass the `app_switch_context` parameter to PayPal.

> **Note:** Passing `app_switch_context` means your merchant is opting in to App Switch behavior.

* Register for [Universal Links](https://developer.apple.com/documentation/xcode/supporting-universal-links-in-your-app) (iOS) and [App Links](https://developer.android.com/studio/write/app-link-indexing) (Android) before your merchant starts integrating with the Direct API.
* For Android devices, ensure the payer has enabled **Open supported links** for your merchant app. When PayPal redirects the payer to the app using app links, the payer lands on your merchant's mobile website instead of the app if **Open supported links** is not enabled.
* If supported links are not enabled, we recommend not to pass the `app_switch_context` parameter. In the following code, the app checks whether supported links are enabled for your merchant's Android app.

The following code sample determines whether to opt in or out of App Switch behavior.

```text lineNumbers
// 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 the payer has the PayPal app installed on their mobile device. Enabling App Switch without this check prevents the switch to the PayPal app. Instead, the PayPal approval experience opens in a browser, not within the merchant app. We recommend passing the app\_switch\_context parameter only if the PayPal app is installed.

Register the URL scheme in the [Info.plist](https://developer.apple.com/documentation/uikit/uiapplication/canopenurl\(_:\)#Discussion) file. The following sample code checks if the PayPal app is installed on an iOS device. The following sample code checks if the PayPal app is installed on an iOS or Android device.

#### iOS

```text lineNumbers
// Swift
public func isPayPalAppInstalled() -> Bool {
    guard let payPalURL = URL(string: "paypal-app-switch://") else {
        return false
    }
    return UIApplication.shared.canOpenURL(payPalURL)
}
```

#### Android

```text lineNumbers
// Kotlin
fun isPaypPalAppInstalled(context: Context): Boolean {
    val paypalPackageName = "com.paypal.android.p2pmobile"
    return try {
        context.packageManager.getApplicationInfo(paypalPackageName, 0)
        true
    } catch (e: PackageManager.NameNotFoundException) {
        false
    }
}
```

Register the `packageName` in the AndroidManifest file. The following sample checks if the PayPal app is installed on the device.

```text lineNumbers
// Kotlin
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <queries>
        <package android:name="com.paypal.android.p2pmobile" />
    </queries>
...
</manifest>
```

## Payment Method API integration [#payment-method-api-integration]

The Payment Method API enables secure integration with PayPal's vaulting system, allowing merchants to save customer payment methods for future transactions.

## Use cases [#use-cases]

The App Switch vaulting integration supports several key scenarios where users want to save payment methods without immediate purchases. See the primary use cases.

### Use case 1: Save payment method during onboarding [#use-case-1-save-payment-method-during-onboarding]

Let users save their PayPal Wallet during onboarding or account setup without making a purchase, so they don't have to re-enter it later when making a purchase.

Payers trigger this intent when they want to securely store a payment method, even though they're not checking out. This commonly occurs during sign-up flows and when payers create accounts with billing preferences.

|                               |                                                                                                               |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------- |
| Intent                        | Example user phrases                                                                                          |
| `vault_paypal_payment_method` | "Save my PayPal for later.""Set up my wallet for subscriptions.""I don't want to enter this again next time." |

### Use case 2: Purchase with saved payment method [#use-case-2-purchase-with-saved-payment-method]

Payers can complete purchases using their saved PayPal Wallet without re-entering payment details.

|                               |                                                                                                   |
| ----------------------------- | ------------------------------------------------------------------------------------------------- |
| Intent                        | Example user phrases                                                                              |
| `vault_paypal_payment_method` | "Use my saved PayPal for this purchase""Pay with my stored wallet""Use my default payment method" |

## Visual flow summary [#visual-flow-summary]

The App Switch vaulting integration follows a five-step process from token creation to payment processing.

1. Create a setup token → `POST /v3/vault/setup-tokens`
2. Redirect to approval → `links[].rel = approve`
3. Payer approves
4. Create payment token → `POST /v3/vault/payment-tokens`
5. Use the token with purchase → `POST /v2/checkout/orders`

## Integration flow summary [#integration-flow-summary]

This vaulting flow helps payers consent to saving a payment method.

|                               |                                 |                                                          |                                                     |
| ----------------------------- | ------------------------------- | -------------------------------------------------------- | --------------------------------------------------- |
| Intent                        | API endpoint                    | Expected outcome                                         | Next step                                           |
| `vault_paypal_payment_method` | `POST /v3/vault/setup-tokens`   | Creates a `setup_token` and approval URL for App Switch. | Redirect the payer to `links[].approve.href`.       |
| Implicit                      | Off-platform redirect           | Payer grants approval to vault method.                   | Convert the setup token to a payment method token.  |
| `create_payment_token`        | `POST /v3/vault/payment-tokens` | Returns `payment_token` and saves method metadata.       | Store the token securely, and display confirmation. |

## Create setup token [#create-setup-token]

Create a setup token using the `POST /v3/vault/setup-tokens` endpoint. This generates a setup token and approval URL that helps the payer authenticate and approve vaulting their payment method.

This creates a setup token and an approval URL. The payer must authenticate and approve vaulting.

The initial `POST` on `setup-tokens` completes the following actions:

* Returns a `PAYER_ACTION_REQUIRED` status.
* Creates a temporary setup token.
* Redirects to the `approve` URL.
* Update `app_switch_context` for the native app.

When saving a payer's PayPal Wallet for the first time, the response returns the PayPal-generated `customer.id` and `setup_token_id`.

For payers with previously stored `payment_sources`, pass the `customer.id` in the `setup-token` request. This links additional `payment_sources` to the payer.

Store a `merchant_customer_id` aligned with the system to simplify the mapping customer information between the system and PayPal when creating a setup token. This optional field returns the value shared in the response.

Include the following parameters in the Create Setup Token API request: `payment_source.paypal` and `payment_source.paypal.experience_context`.

|                                                        |                                                                                                                                                                                                                                                                                                                                                             |           |
| ------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- |
| Parameter                                              | Description                                                                                                                                                                                                                                                                                                                                                 | Priority  |
| `payment_source.paypal.email_address`                  | Merchants can pass the payer's email in the setup token-creation request, which can be the same email used in the merchant's application.Including the email address helps us determine if the payer has a PayPal account and whether the app is installed, allowing for a quicker assessment of App Switch eligibility and enhancing the payer experience. | Optional  |
| `payment_source.paypal.experience_context.user_action` | The `user_action` configures the PayPal Approval flow and uses the `SETUP_NOW` flow\.App Switch currently only supports `SETUP_NOW` flows.`SETUP_NOW` is a PayPal feature that supports payers to approve and save the agreement on the PayPal review page.                                                                                                 | Mandatory |

### Return and cancel URLs [#return-and-cancel-urls]

PayPal expects the merchant to pass `payment_source.paypal.experience_context.return_url` and `payment_source.paypal.experience_context.cancel_url`:

* `return_url` is the URL that tells PayPal where to send the payer after completing the approval on the PayPal app. Set the URL to the page where the payer selects the PayPal button. (Mandatory)
* `cancel_url` is the URL that tells PayPal where to send the payer when the payer cancels or doesn't complete the approval on the PayPal app. Set the URL to the page where the payer should be redirected when they cancel the approval flow. (Mandatory)

|                      |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Use                  | Mandatory requirements                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| Merchant native apps | The `return_url` and `cancel_url` must be associated with the merchant app.Pass deep links in the `return_url` and `cancel_url` fields. Otherwise, the payer can't return to the merchant app as expected.Include the `app_url` parameter. (Mandatory) This takes precedence over `return_url` and `cancel_url`.If PayPal determines the App Switch is eligible, we use the `app_url` to return the payer to the merchant app. If not, we use the `return_url` and `cancel_url`. |

### Opt into App Switch [#opt-into-app-switch]

PayPal considers passing `payment_source.paypal.experience_context.app_switch_context` as the merchant opting in to App Switch.

**Native app parameters**

If a payer starts from the native app, use `payment_source.paypal.experience_context.app_switch_context.native_app`.

|              |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |           |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------- |
| Field        | Requirement                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              | Priority  |
| `os_type`    | The payer's mobile operating system (OS) family. PayPal uses this value to assess App Switch eligibility. Accepted values: `ANDROID`, `IOS`, and `OTHER`.                                                                                                                                                                                                                                                                                                                                                                | Mandatory |
| `os_version` | The payer's mobile OS version used for telemetry                                                                                                                                                                                                                                                                                                                                                                                                                                                                         | Optional  |
| `app_url`    | Pass `app_url` as the universal link or app link associated with the merchant app. After the payer uses App Switch to approve or cancel the approval in the PayPal app, PayPal uses this URL to redirect the payer back to the merchant app.During an App Switch flow, `app_url` takes precedence over `return_url` and `cancel_url`.PayPal appends either `/success` or `/cancel` to the value of `app_url` when redirecting the payer back to your app, depending on whether the transaction was approved or canceled. | Mandatory |

The following code sample shows how to derive the payer's `os_type` and `os_version` needed to include in the Create Setup Token API request.

```text lineNumbers
function parseMobileOS(userAgent) {
  const os = {
    name: "Unknown",
    version: "Unknown"
  };

  const osPatterns = [
    // iOS
    { regex: /iPhone|iPad|iPod.*OS (\d+[_\.]\d+)/, name: "iOS", versionIndex: 1 },

    // Android
    { regex: /Android (\d+\.\d+)/, name: "Android", versionIndex: 1 },

    // Windows Phone (older devices)
    { regex: /Windows Phone (\d+\.\d+)/, name: "Windows Phone", versionIndex: 1 },

    // Windows 10 Mobile
    { regex: /Windows NT 10.0.*Mobile/, name: "Windows 10 Mobile", version: "10" },

    // Other mobile OS
    { regex: /Linux.*(Ubuntu)/, name: "Ubuntu", version: "Unknown" },
    { regex: /Linux/, name: "Linux", version: "Unknown" }
  ];

  for (const pattern of osPatterns) {
    const match = userAgent.match(pattern.regex);
    if (match) {
      os.name = pattern.name;
      if (pattern.version) {
        os.version = pattern.version;
      } else if (pattern.versionIndex && match[pattern.versionIndex]) {
        os.version = match[pattern.versionIndex].replace("_", ".");
      }
      break;  // Once we match, no need to check further
    }
  }

  return os;
}

// Example usage:
const userAgent = navigator.userAgent;  // Automatically gets the user's device info
const os = parseMobileOS(userAgent);
console.log(`OS: ${os.name}, Version: ${os.version}`);
```

### Sample setup token API request [#sample-setup-token-api-request]

The following sample code shows a create setup token request with App Switch opt-in for a merchant native app.

```text lineNumbers
# x-function-name": "vault_paypal_payment_method"
# x-intent": "vault.save_paypal_payment_method"
curl -v -k -X POST 'https://api-m.sandbox.paypal.com/v3/vault/setup-tokens' \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ACCESS-TOKEN" \
  -H "PayPal-Request-Id: REQUEST-ID" \
  -d '{
    "payment_source": {
      "paypal": {
        "description": "Save your PayPal account for faster checkout",
        "shipping": {
          "name": {
            "full_name": "Firstname Lastname"
          },
          "address": {
            "address_line_1": "2211 N First Street",
            "address_line_2": "Building 17",
            "admin_area_2": "San Jose",
            "admin_area_1": "CA",
            "postal_code": "95131",
            "country_code": "US"
          }
        },
        "permit_multiple_payment_tokens": false,
        "usage_pattern": "IMMEDIATE",
        "usage_type": "MERCHANT",
        "customer_type": "CONSUMER",
        "experience_context": {
          "user_action": "SETUP_NOW",
          "shipping_preference": "SET_PROVIDED_ADDRESS",
          "payment_method_preference": "IMMEDIATE_PAYMENT_REQUIRED",
          "brand_name": "EXAMPLE INC",
          "locale": "en-US",
          "return_url": "app://xo/234?clientStatus=success",
          "cancel_url": "app://xo/234?clientStatus=cancel",
          "app_switch_context": {
            "native_app": {
              "os_type": "IOS",
              "os_version": "17.3.1",
              "app_url": "https://example.com/merchant_app_universal_link"
            }
          }
        }
      }
    }
  }'
```

### Modify the code [#modify-the-code]

1. Copy the code sample.
2. Replace `ACCESS-TOKEN` with the sandbox [access token](/api/rest/authentication).
3. Replace `REQUEST-ID` with unique alphanumeric characters, such as a timestamp.
4. Configure PayPal as the `payment_source` and complete the remaining source object properties for the use case.
5. Set the `return_url` value to the URL that redirects users after they approve the flow.
6. Set the `cancel_url` value to the URL that redirects users after they cancel the flow.
7. Configure the `app_switch_context` parameters for native app.

### Setup token response [#setup-token-response]

A successful request returns an HTTP response code of 200 or 201 and a status of `PAYER_ACTION_REQUIRED`. Returns 200 for idempotent requests.

In the Setup Token response, the `app_switch_eligibility` flag is set to `true`. This flag indicates that the information in the Setup Token request is correct and that PayPal has found the payer eligible. The merchant should attempt to App Switch.

> **Note:** `app_switch_eligibility = true` does not guarantee that the payer will switch to the PayPal app. It only indicates that PayPal requests the merchant to attempt App Switch for the payer.

The response includes the following HATEOAS links:

|           |        |                                                                                                            |
| --------- | ------ | ---------------------------------------------------------------------------------------------------------- |
| Rel       | Method | Description                                                                                                |
| `approve` | GET    | Use this link to take the payer through a PayPal-hosted approval flow.                                     |
| `confirm` | POST   | Make a POST request to use an approved setup token to save the PayPal Wallet and generate a payment token. |
| `self`    | GET    | Make a GET request to view the state of the setup token and payment method details.                        |

The following sample code includes the setup token ID, customer information, payment source details, and links to approve the vaulting request.

```text lineNumbers
{
    "id": "5C991763VB2771612",
    "customer": {
        "id": "customer_4029352051",
        "merchant_customer_id": "merchant_cust_ID"
    },
    "status": "PAYER_ACTION_REQUIRED",
    "payment_source": {
        "paypal": {
            "email_address": "customer@example.com",
            "usage_pattern": "RECURRING_PREPAID",
            "shipping": {
                "name": {
                    "full_name": "John Doe"
                },
                "address": {
                    "address_line_1": "2211 N First Street",
                    "address_line_2": "Building 17",
                    "admin_area_2": "San Jose",
                    "admin_area_1": "CA",
                    "postal_code": "95131",
                    "country_code": "US"
                }
            },
            "permit_multiple_payment_tokens": false,
            "usage_type": "MERCHANT",
            "customer_type": "CONSUMER",
            "app_switch_eligibility": true,
            "experience_status": "CANCELED"
        }
    },
    "links": [
        {
            "href": "https://api.sandbox.paypal.com/v3/vault/setup-tokens/1VH10267W9768550E",
            "rel": "self",
            "method": "GET",
            "encType": "application/json"
        },
        {
            "href": "https://www.sandbox.paypal.com/checkoutnow?token=1VH10267W9768550E",
            "rel": "approve",
            "method": "GET",
            "encType": "application/json"
        }
    ]
}
```

## Setup token lifecycle [#setup-token-lifecycle]

|              |                         |                                        |                                            |
| ------------ | ----------------------- | -------------------------------------- | ------------------------------------------ |
| Token type   | Status                  | Description                            | Suggested action                           |
| Setup\_token | `CREATED`               | Initial token created.                 | No action required.                        |
| Setup\_token | `PAYER_ACTION_REQUIRED` | Awaiting payer approval.               | Redirect the payer to PayPal approval URL. |
| Setup\_token | `APPROVED`              | Payer completed approval.              | Call `POST /v3/vault/payment-tokens`.      |
| Setup\_token | `TOKENIZED`             | Payment token created successfully.    | Store the token securely for future use.   |
| Setup\_token | `VAULTED`               | Payment method is stored in the vault. | Ready for future transactions.             |

Redirect the payer to the approve link to begin the approval process.

After the payer completes the approval flow, upgrade the setup token to a payment method token by calling the [create payment tokens](/api/payment-tokens/v3#payment-tokens_create) endpoint. Setup tokens expire after 3 days by default.

### Sample GET response [#sample-get-response]

In the GET setup-token response, PayPal returns the `app_switch_eligibility` flag.

When payers cancel the transaction in the PayPal app before approval, the GET order response includes a cancellation status with `experience_status = CANCELED`. Use this information to control the cancel flow and determine what to display when payers cancel PayPal approval.

The `experience_status` flag indicates the current state of the PayPal checkout. This flag does not replace the setup token status but helps identify if payers canceled the approval process at any point.

After payer cancellation, PayPal returns this information in the `GET /v3/vault/setup-tokens/{id}` response.

The following code sample shows a canceled approval.

```text lineNumbers
{
    "id": "5C991763VB2771612",
    "customer": {
        "id": "customer_4029352051",
        "merchant_customer_id": "merchant_cust_ID"
    },
    "status": "PAYER_ACTION_REQUIRED",
    "payment_source": {
        "paypal": {
            "email_address": "customer@example.com",
            "app_switch_eligibility": true,
            "experience_status": "CANCELED"
        }
    },
    "links": [
        {
            "href": "https://www.sandbox.paypal.com/checkoutnow?token=1VH10267W9768550E",
            "rel": "approve",
            "method": "GET",
            "encType": "application/json"
        },
        {
            "href": "https://api-m.paypal.com/v3/vault/payment-token",
            "rel": "confirm",
            "method": "POST",
            "encType": "application/json"
        },
        {
            "href": "https://api-m.paypal.com/v3/vault/setup-tokens/5C991763VB2771612",
            "rel": "self",
            "method": "GET",
            "encType": "application/json"
        }
    ]
}
```

## App Switch to and from PayPal [#app-switch-to-and-from-paypal]

If the merchant opts in to App Switch and passes the `app_switch_context`, and the PayPal app is installed with `app_switch_eligibility = true`, the merchant should attempt to switch to the PayPal app.

The following code sample shows how to make the switch.

```text lineNumbers
// Swift
UIApplication.shared.open(url, options: [:], completionHandler: nil)
```

If the OS cannot open the PayPal app or the app is not installed, the URL can open in a browser. The following sample code shows how to handle this scenario.

```text lineNumbers
// Kotlin
fun launchUrl(context: Context, url: Uri, launchAsNewTask: Boolean) {
    try {
        val customTabsIntent: CustomTabsIntent = CustomTabsIntent.Builder().build()
        if (launchAsNewTask) {
            customTabsIntent.intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        }
        customTabsIntent.launchUrl(context, url)
    } catch (e: ActivityNotFoundException) {
        if (hasEnabledSupportedLinks(context)) {
            val intent = Intent(Intent.ACTION_VIEW).apply {
                    data = url
                }
            startActivity(intent)
        } else {
            // Cannot switch to PayPal app, cannot open fallback into custom tabs, cannot return from browser
            // Gracefully handle error
        }
    }
}
```

See the following sample code to handle the return to the merchant app.

```text lineNumbers
override fun onNewIntent(intent: Intent) {
    super.onNewIntent(intent)

}
```

Add a custom tabs dependency to your `build.gradle` file if needed.

```text lineNumbers
// kotlin
dependencies {
...
  implementation("androidx.browser:browser:1.8.0")
...
}
```

If the merchant opts in to App Switch, passes the `app_switch_context`, and PayPal returns `app_switch_eligibility = false`, the merchant can open the approval in the native app so the payer can complete the approval. Handle this the same way as the existing non-App Switch integration.

If the payer manually navigates back to the merchant app without taking action in the PayPal app, or if they leave the PayPal app, the merchant should be prepared to handle these situations appropriately.

The following code samples show how to open the approval experience in the merchant's native app.

#### iOS

```text lineNumbers
// Swift
DispatchQueue.main.async {
	let callbackURLScheme = //your app scheme
    let webAuthSession = ASWebAuthenticationSession(url: url, callbackURLScheme: callbackURLScheme) { [weak self] callbackURL, error in
        DispatchQueue.main.async {
            if let callbackURL  {
              // handle return
            } else if let error {
              // error returned, can be cancel or an error
            }
        }
    }
    session.presentationContextProvider = self
    session.start()
}
```

#### Android

```text lineNumbers
androidx.browser:browser
```

## Integrate merchant website mobile browser [#integrate-merchant-website-mobile-browser]

When a payer uses a mobile browser to access the merchant's website and switches to the PayPal app to complete the approval, the app redirects them to the merchant's site or requires them to return manually.

Create a setup token using the PayPal API after completing the native app prerequisites.

### Create a setup token [#create-a-setup-token]

Create a setup token using the `POST /v3/vault/setup-tokens` endpoint. This generates a setup token and approval URL that helps the payer authenticate and approve vaulting their payment method.

The initial `POST` on `setup-tokens` completes the following actions:

* Returns a `PAYER_ACTION_REQUIRED` status.
* Creates a temporary setup token.
* Redirects to the `approve` URL.
* Update `app_switch_context` for the native app.

When your merchant saves a payer's PayPal Wallet for the first time, the response returns the PayPal-generated `customer.id` and `setup_token_id`.

For payers with previously stored `payment_sources`, pass the `customer.id` in the `setup-token` request. This links additional `payment_sources` to the payer.

Your merchant can store a `merchant_customer_id` aligned with the system to simplify the mapping customer information between the system and PayPal when creating a setup token. This optional field returns the value shared in the response.

Include the following parameters in the Create Setup Token API request: `payment_source.paypal` and `payment_source.paypal.experience_context`.

|                                                        |                                                                                                                                                                                                                                                                                                                                                       |           |
| ------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- |
| Parameter                                              | Description                                                                                                                                                                                                                                                                                                                                           | Priority  |
| `payment_source.paypal.email_address`                  | Merchants can pass the payer's email in the order-creation request, which can be the same email used in the merchant's application.Including the email address helps us determine if the payer has a PayPal account and whether the app is installed, allowing for a quicker assessment of App Switch eligibility and enhancing the payer experience. | Optional  |
| `payment_source.paypal.experience_context.user_action` | The user\_action configures the PayPal Approval flow and uses the `SETUP_NOW` flow\.App Switch currently only supports `SETUP_NOW` flows.`SETUP_NOW` is a PayPal feature that supports payers to approve and save the agreement on the PayPal review page.                                                                                            | Mandatory |

### Return and cancel URLs [#return-and-cancel-urls-1]

PayPal expects the merchant to pass `payment_source.paypal.experience_context.return_url` and `payment_source.paypal.experience_context.cancel_url`:

* `return_url` is the URL that tells PayPal where to send the payer after completing the approval on the PayPal app. Set the URL to the page where the payer selects the PayPal button. (Mandatory)
* `cancel_url` is the URL that tells PayPal where to send the payer when the payer cancels or doesn't complete the approval on the PayPal app. Set the URL to the page where the payer should be redirected when they cancel the approval. (Mandatory)

|                         |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Use                     | Mandatory requirements                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| Merchant mobile website | Set the `return_url` and `cancel_url` to the same value. If the URLs are different, PayPal redirects the payer to the merchant website on a different tab or browser.The `return_url` and `cancel_url` must be web URLs linked to the website where the payer clicked the PayPal button.If your merchant wants to pass query parameters to PayPal that expect PayPal to return, add the query parameters to the `return_url` and `cancel_url` as fragments after the `#`.Use a unique identifier for each payer's session to recognize the payer when PayPal redirects them to the website. |

### Opt into App Switch [#opt-into-app-switch-1]

PayPal considers passing `payment_source.paypal.experience_context.app_switch_context` as the merchant opting in to App Switch.

**Mobile web parameters**

If a payer starts from the merchant mobile website, use `payment_source.paypal.experience_context.app_switch_context.mobile_web`.

|                    |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |           |
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- |
| Field              | Requirement                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                | Priority  |
| `buyer_user_agent` | Pass this field as a raw string without any modifications. PayPal derives the payer's device OS type, OS version, and browser details from this value.Do not alter or modify the payer's device user agent string. Send it to PayPal as is.                                                                                                                                                                                                                                                                                                                                                                                                                                | Mandatory |
| `return_flow`      | The `return_flow` field defines how payers return to the merchant's website after approving the agreement in the PayPal app. For approvals that start on the merchant's website in a mobile browser, the return experience to the merchant's site depends on the merchant's preferences and technical setup.<br />                  Accepted Values:<br />                  `AUTO`: After the payer approves the agreement in the PayPal app, PayPal automatically redirects them to the merchant's website.`MANUAL`: After the payer approves the agreement in the PayPal app, they must manually navigate back to the merchant's website where they started the payment. | Mandatory |

### Create setup token request [#create-setup-token-request]

Use the following code sample to create a setup token for PayPal vaulting with App Switch configuration for mobile web.

```text lineNumbers
# x-intent": "vault.save_paypal_payment_method"
curl -v -k -X POST 'https: //api-m.sandbox.paypal.com/v3/vault/setup-tokens' \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ACCESS-TOKEN" \
-H "PayPal-Request-Id: REQUEST-ID" \
-d '{
    "payment_source": {
        "paypal": {
            "description": "Save your PayPal account for faster checkout",
            "shipping": {
                "name": {
                    "full_name": "Firstname Lastname"
                },
                "address": {
                    "address_line_1": "2211 N First Street",
                    "address_line_2": "Building 17",
                    "admin_area_2": "San Jose",
                    "admin_area_1": "CA",
                    "postal_code": "95131",
                    "country_code": "US"
                }
            },
            "permit_multiple_payment_tokens": false,
            "usage_pattern": "IMMEDIATE",
            "usage_type": "MERCHANT",
            "customer_type": "CONSUMER",
            "experience_context": {
                "user_action": "SETUP_NOW",
                "shipping_preference": "SET_PROVIDED_ADDRESS",
                "payment_method_preference": "IMMEDIATE_PAYMENT_REQUIRED",
                "brand_name": "EXAMPLE INC",
                "locale": "en-US",
                "return_url": "https://example.com/merchant_app_universal_link",
                "cancel_url": "https://example.com/merchant_app_universal_link",
                "app_switch_context": {
                    "mobile_web": {
                        "return_flow": "AUTO" or "MANUAL",
                        "buyer_user_agent": "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"
                    }
                }
            }
        }
    }
```

### Create setup token response [#create-setup-token-response]

In the Create setup token response, the `app_switch_eligibility` flag is set to true. This flag indicates that the information in the Create Setup Token request is correct, and that PayPal has found the payer eligible and is attempting to use App Switch.

> **Note:** `app_switch_eligibility = true` does not guarantee that the payer will successfully switch to the PayPal app. It only indicates that PayPal is attempting App Switch for the payer. PayPal returns the `payer_id` in the response when a payer approves saving of the PayPal Wallet.

```text lineNumbers
{
    "id": "5C991763VB2771612",
    "customer": {
        "id": "customer_4029352051",
        "merchant_customer_id": "merchant_cust_ID"
    },
    "status": "PAYER_ACTION_REQUIRED",
    "payment_source": {
        "paypal": {
            "email_address": "customer@example.com",
            "usage_pattern": "RECURRING_PREPAID",
            "shipping": {
                "name": {
                    "full_name": "John Doe"
                },
                "address": {
                    "address_line_1": "2211 N First Street",
                    "address_line_2": "Building 17",
                    "admin_area_2": "San Jose",
                    "admin_area_1": "CA",
                    "postal_code": "95131",
                    "country_code": "US"
                }
            },
            "permit_multiple_payment_tokens": false,
            "usage_type": "MERCHANT",
            "customer_type": "CONSUMER",
            "app_switch_eligibility": true,
            "experience_status": "CANCELED"
        }
    },
    "links": [
        {
            "href": "https://api.sandbox.paypal.com/v3/vault/setup-tokens/1VH10267W9768550E",
            "rel": "self",
            "method": "GET",
            "encType": "application/json"
        },
        {
            "href": "https://www.sandbox.paypal.com/checkoutnow?token=1VH10267W9768550E",
            "rel": "approve",
            "method": "GET",
            "encType": "application/json"
        }
    ]
}
```

## Setup token lifecycle [#setup-token-lifecycle-1]

|              |                         |                                        |                                            |
| ------------ | ----------------------- | -------------------------------------- | ------------------------------------------ |
| Token type   | Status                  | Description                            | Suggested action                           |
| Setup\_token | `CREATED`               | Initial token created.                 | No action required.                        |
| Setup\_token | `PAYER_ACTION_REQUIRED` | Awaiting payer approval.               | Redirect the payer to PayPal approval URL. |
| Setup\_token | `APPROVED`              | Payer completed approval.              | Call `POST /v3/vault/payment-tokens`.      |
| Setup\_token | `TOKENIZED`             | Payment token created successfully.    | Store the token securely for future use.   |
| Setup\_token | `VAULTED`               | Payment method is stored in the vault. | Ready for future transactions.             |

Redirect the payer to the approve link to begin the approval process.

After the payer completes the approval flow, upgrade the setup token to a payment method token by calling the [create payment tokens](/api/payment-tokens/v3#payment-tokens_create) endpoint. Setup tokens expire after 3 days by default.

### Sample GET response [#sample-get-response-1]

In the GET setup-token response, PayPal returns the `app_switch_eligibility` flag.

If the payer cancels in the PayPal app during the approval process, the GET setup-token response includes a cancellation status, indicated by `experience_status = CANCELED`. Use this information to control the cancel flow and determine what to display when the payer cancels PayPal approval. This is relevant when return\_flow is set to `MANUAL`.

The experience\_status flag indicates the current state of the PayPal checkout. It does not replace the setup token status, but helps identify if the payer canceled the approval process at any point.

PayPal returns this information in the `GET /v3/vault/setup-tokens/{id}` response after payer cancellation. The following code sample shows a canceled approval.

```text lineNumbers
{
 "id": "5C991763VB2771612", // setup_token ID
    "customer": {
        "id": "customer_4029352051", //Your PayPal generated reference to the buyer
        "merchant_customer_id": "merchant_cust_ID"
    },
    "status": "PAYER_ACTION_REQUIRED", // enum: CREATED, PAYER_ACTION_REQUIRED, APPROVED, VAULTED,TOKENIZED
  "payment_source": {
    "paypal": {
            "email_address": "customer@example.com",
            "app_switch_eligibility": true,
           "experience_status":"CANCELED"
        }
  },
  "links": [
    {
      "href": "https://www.sandbox.paypal.com/checkoutnow?token=1VH10267W9768550E",
      "rel": "approve",
      "method": "GET",
      "encType": "application/json"
    },
    {
      "href": "https://api-m.paypal.com/v3/vault/payment-token",
      "rel": "confirm",
      "method": "POST",
      "encType": "application/json"
    },
    {
      "href": "https://api-m.paypal.com/v3/vault/setup-tokens/5C991763VB2771612",
      "rel": "self",
      "method": "GET",
      "encType": "application/json"
    }
  ]
}
```

### Redirects to merchant website [#redirects-to-merchant-website]

The payer's App Switch experience may vary based on the device OS, the default browser, the merchant's preferred return flow, and where the merchant website is hosted. There are technical constraints related to the OS:

* When a payer completes the approval flow in the PayPal app and PayPal redirects them back to the merchant's website, the OS opens the site in the payer's default browser.
* For example, the payer may start the approval on the merchant website in a non-default browser, such as Chrome on Android. However, due to OS limitations, when PayPal redirects the payer back, the merchant's website opens in the browser set as default on their device, such as Firefox.

Review the following table to see how App Switch works in different settings. Use this information to verify the integration during testing and identify situations that require handling a fallback.

| Platform | Browser          | Mode            | Availability                                                                                                                                                                                                                             | Notes                                                                                                                                                                                                          |
| -------- | ---------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| iOS      | Safari           | Default browser | <circle r="22" cx="24" cy="24" fill="#2ecc40" /><path d="M14,24L22,32L34,18" strokeLinejoin="round" strokeWidth="4" fill="none" stroke="#ffffff" strokeLinecap="round" />                                                                |                                                                                                                                                                                                                |
| iOS      | Safari           | Private         | <circle r="22" cx="24" cy="24" fill="#2ecc40" /><path d="M14,24L22,32L34,18" strokeLinejoin="round" strokeWidth="4" fill="none" stroke="#ffffff" strokeLinecap="round" />                                                                |                                                                                                                                                                                                                |
| iOS      | Safari           | Not default     | <circle r="22" cx="24" cy="24" fill="#2ecc40" /><path d="M14,24L22,32L34,18" strokeLinejoin="round" strokeWidth="4" fill="none" stroke="#ffffff" strokeLinecap="round" />                                                                | <circle r="10" cx="10" cy="10" fill="blue" /><circle r="1.3" cx="10" cy="7" fill="white" /><rect rx="1" x="9" width="2" y="9" fill="white" height="6" /> App Switch redirects to the default browser           |
| iOS      | Chrome           | Default browser | <circle r="22" cx="24" cy="24" fill="#2ecc40" /><path d="M14,24L22,32L34,18" strokeLinejoin="round" strokeWidth="4" fill="none" stroke="#ffffff" strokeLinecap="round" />                                                                | <circle r="10" cx="10" cy="10" fill="blue" /><circle r="1.3" cx="10" cy="7" fill="white" /><rect rx="1" x="9" width="2" y="9" fill="white" height="6" /> App Switch redirects to a new tab in the same browser |
| Android  | Chrome           | Default browser | <circle r="22" cx="24" cy="24" fill="#2ecc40" /><path d="M14,24L22,32L34,18" strokeLinejoin="round" strokeWidth="4" fill="none" stroke="#ffffff" strokeLinecap="round" />                                                                |                                                                                                                                                                                                                |
| Android  | Chrome           | Incognito       | <circle r="22" cx="24" cy="24" fill="#d50102" /><line y1="16" x1="16" y2="32" x2="32" strokeWidth="4" stroke="#fff" strokeLinecap="round" /><line y1="16" x1="32" y2="32" x2="16" strokeWidth="4" stroke="#fff" strokeLinecap="round" /> | Fallback experience                                                                                                                                                                                            |
| Android  | Chrome           | Not default     | <circle r="22" cx="24" cy="24" fill="#2ecc40" /><path d="M14,24L22,32L34,18" strokeLinejoin="round" strokeWidth="4" fill="none" stroke="#ffffff" strokeLinecap="round" />                                                                | <circle r="10" cx="10" cy="10" fill="blue" /><circle r="1.3" cx="10" cy="7" fill="white" /><rect rx="1" x="9" width="2" y="9" fill="white" height="6" /> App Switch redirects in the default browser           |
| Android  | Firefox or other | Default         | <circle r="22" cx="24" cy="24" fill="#2ecc40" /><path d="M14,24L22,32L34,18" strokeLinejoin="round" strokeWidth="4" fill="none" stroke="#ffffff" strokeLinecap="round" />                                                                | <circle r="10" cx="10" cy="10" fill="blue" /><circle r="1.3" cx="10" cy="7" fill="white" /><rect rx="1" x="9" width="2" y="9" fill="white" height="6" /> App Switch redirects to a new tab in the same browser |

### Handle auto return flow [#handle-auto-return-flow]

When the `return_flow` value is set to `AUTO` and the payer completes or cancels the approval flow in the PayPal app, PayPal automatically redirects the payer back to the merchant website using the `return_url` or `cancel_url`.

To handle the payer's return and account for these scenarios, the merchant needs to implement the following:

**App Switch from a native default browser**

* A payer starts on the merchant website in a native browser, such as Safari on iOS, and sets the native browser as the default. When the payer completes or cancels the approval, the PayPal app automatically redirects the payer back to the same default browser.
* Use the `visibilitychange` event listener to handle scenarios where the payer abandons the approval.
* Include a client-side event listener to identify this event. Use the hashchange listener, and the completion status will be included as additional hash parameters.

The following sample client-side code handles this request.

```text lineNumbers
document.addEventListener('hashchange', (e) => {
	const params = parseHashParams(window.location.hash);
	if(params.approved) {
		// Buyer is returning from app switch with an approved setup token
		// Verify the setup token approval, complete vaulting
		// & redirect to confirmation page to complete flow
	} else if (params.canceled) {
		// Buyer canceled PayPal app switch
	}
})
```

**App Switch from a non-default browser**

When a payer starts on the merchant website in a browser, such as Safari on iOS, but has Chrome set as the default browser, PayPal redirects the payer back to the merchant's website in Chrome after they complete or cancel the approval.

Use the visibilitychange event listener to handle cases when the payer abandons checkout.

In this scenario, the payer starts in a non-default browser, completes an action in the PayPal app, and returns to the default browser. Because browsers do not share cookies, use the token that PayPal returns to look up the setup token details, or include the original setup token ID in the approval URL to keep the session and complete the payment.

The PayPal app sends order approval or cancellation details as URL parameters during the redirect. Client-side code can read these parameters, but server-side routes cannot. When approved, PayPal also returns payer\_id as a URL parameter during the redirect.

The following code handles the redirects and processes approval data from the URL parameters on the client side.

```text lineNumbers
const onLoadHash = () => {
	const hashParams = parseHashParams(window.location.hash);

	if (hashParams.approved) {
		// Buyer is returning from app switch
		// Complete payment method vaulting & redirect to confirmation page
	} else if (hashParams.canceled) {
		// Buyer has canceled app switch and returned
	} else {
		// No hash params, display standard checkout page
	}
}
onLoadHash()
```

### Handle manual return flow [#handle-manual-return-flow]

When the `return_flow` value is set to `MANUAL` and the payer completes or cancels the approval in the PayPal app, the app prompts the payer to manually return to the merchant website. Use the `visibilitychange` event listener to detect when the payer returns to the merchant website.

For additional visibility into payer actions in the PayPal app, check the `experience_status` in the GET setup token response. When this is approved, PayPal removes `experience_status` from the GET setup token response.

* The `visibilitychange` event listener also handles scenarios where the payer abandons approval and the `return_flow` value is `AUTO`.
* If the payer abandons PayPal approval and navigates back to the merchant website, it is not a terminal state. The payer can switch to PayPal again to review and approve the saving of PayPal wallet in the vault.

The following sample code shows how to handle the `visibilitychange` event listener.

```text lineNumbers
document.addEventListener('visibilitychange', (e) => {
	// call your server API to make a request to PayPal to get the setup token status and buyer cancellation status (if applicable)
	//If #change event was triggered then cancel this event (to avoid multiple GET calls from both the listeners)
    const hashParams = parseHashParams(window.location.hash);
	if (hashParams.approved || hashParams.cancelled) {
		//Wil be handled by hashChange. Exit
		return;
	}
	const vaultResponse = getSetupToken(setuptokenId);
	const setupTokenStatus = vaultResponse.status;
	const experiencStatus = vaultResponse?.payment_source?.paypal?.experiencStatus;
	// Extract only "CANCELLED" status
    const cancelledexperiencStatus= paypal.experiencStatus
                                .filter(item => item.experiencStatus && item.experiencStatus.includes("CANCELLED"))
                                .map(item => item.experiencStatus);
	//setupTokenStatus Approved
	if (setupTokenStatus === 'approved') {
		// Create the Payment Token
	}
	//Buyer cancels vaulting
	else if (setupTokenStatus !== 'approved' && cancelledexperiencStatus == 'cancelled') {
		// Buyer app switched to paypal but closed before approving the vaulting.
		// Dont call create payment token & take the appropriate "cancel" action
	}
	else {
		// Continue current buyer approval process
	}
});
```

## Create payment method token [#create-payment-method-token]

Redirect the payer to the approve link to begin the approval process.

After the payer completes the approval flow, upgrade the setup token to a payment method token by calling the create payment tokens endpoint. Setup tokens expire after 3 days by default.

After the payer approves the setup token, redirect them to the approve link to complete the approval process.

Use the `POST /v3/vault/payment-tokens` endpoint to convert the approved setup token into a reusable payment method token, also known as a `vault_id`. Call this endpoint only after the payer has approved the setup token through the PayPal approval flow.

### Request [#request]

The following code exchanges an approved setup token for a payment token stored in the vault.

```text lineNumbers
# x-function-name: convert_payment_token
# x-intent: vault.create_payment_token
# description: Exchanges an approved setup token for a payment token stored in the Vault.
# example_request_comment: |
#   Step 2 of the vaulting flow.
#   Only call this after the payer has approved the setup token.
#   This returns a reusable vault token (e.g., for PayPal Wallet).
curl -v -k -X POST 'https://api-m.sandbox.paypal.com/v3/vault/payment-tokens' \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ACCESS-TOKEN" \
  -H "PayPal-Request-Id: REQUEST-ID-123" \
  -d '{
    "payment_source": {
      "token": {
        "id": "4G4976650J0948357",
        "type": "SETUP_TOKEN"
      }
    }
  }'
```

Use the following parameters in the request.

|                             |        |                                                 |
| --------------------------- | ------ | ----------------------------------------------- |
| Field                       | Type   | Description                                     |
| `payment_source.token.id`   | string | Pass the `setup_token` that the payer approved. |
| `payment_source.token.type` | string | Set this value to `SETUP_TOKEN`.                |

### Expected output [#expected-output]

The following JSON sample shows the response structure returned after successfully creating a vault token for a PayPal payment method.

```text lineNumbers
{
    "id": "VAULT-TOKEN-XYZ789",
    "payment_source": {
        "paypal": {
            "email": "buyer@example.com",
            .......
        }
    },
    ...
}
```

### Next steps [#next-steps]

After receiving the response, follow these steps:

1. Store the returned `id` securely as the reusable vault token.
2. Use the vault token in future transactions by passing it as `vault_id` in `/v2/checkout/orders` or similar API calls.

### Response details [#response-details]

The API response contains the following information.

|             |                                                                                  |
| ----------- | -------------------------------------------------------------------------------- |
| Field       | Value                                                                            |
| Function    | `convert_payment_token`                                                          |
| Intent      | `vault.convert_payment_token`                                                    |
| Outcome     | `payment_token_created_successfully`                                             |
| Description | Convert an approved setup token into a reusable vault token for a PayPal Wallet. |

### Response payload [#response-payload]

This code sample shows the vault token details returned after converting an approved setup token.

```text lineNumbers
{
  "id": "jwgvx42",
  "customer": {
    "id": "customer_4029352051" ,
    "merchant_customer_id": "merchant_cust_ID"
  },
  "payment_source": {
    "paypal": {
      "description": "Description for PayPal to be shown to PayPal payer",
      "usage_pattern": "IMMEDIATE",
      "shipping": {
        "name": {
          "full_name": "Firstname Lastname"
        },
        "address": {
          "address_line_1": "2211 N First Street",
          "address_line_2": "Building 17",
          "admin_area_2": "San Jose",
          "admin_area_1": "CA",
          "postal_code": "95131",
          "country_code": "US"
        }
      },
      "permit_multiple_payment_tokens": false,
      "usage_type": "MERCHANT",
      "customer_type": "CONSUMER",
      "email_address": "email@example.com",
      "payer_id": "AJM9JTWQJCFTA"
    }
  },
  "links": [
    {
      "rel": "self",
      "href": "https://api-m.sandbox.paypal.com/v3/vault/payment-tokens/jwgvx42",
      "method": "GET",
      "encType": "application/json",
    },
    {
      "rel": "delete",
      "href": "https://api-m.sandbox.paypal.com/v3/vault/payment-tokens/jwgvx42",
      "method": "DELETE",
      "encType": "application/json",
    }
  ]
}
```

### Next steps [#next-steps-1]

After creating the vault token, complete the `store_payment_token` and `customer_id` process by following these actions:

1. Store the token ID `jwgvx42` securely for this customer.
2. Display the masked PayPal email for confirmation.
3. Use this token as `vault_id` in `/v2/checkout/orders`.

### Postconditions [#postconditions]

Validate these conditions to ensure successful implementation:

* Persist the token in your backend.
* Prepare the PayPal Wallet token for future checkout.
* Configure the token for use as `payment_source.paypal.vault_id` in checkout.

### Use case 3: Mobile browser purchase with saved payment method [#use-case-3-mobile-browser-purchase-with-saved-payment-method]

After receiving the payment method token ID, use it with the [Orders v2 API](/api/orders/v2) to create the order with the saved payment method.

|                     |                                           |                            |                                                                     |                  |
| ------------------- | ----------------------------------------- | -------------------------- | ------------------------------------------------------------------- | ---------------- |
| Intent              | Function name                             | API endpoint               | Expected outcome                                                    | Next step        |
| `use_saved_payment` | `create_order_with_vaulted_paypal_wallet` | `POST /v2/checkout/orders` | Creates an order using a previously vaulted `payment method token`. | Save `order_id`. |

### Create order with saved payment method token [#create-order-with-saved-payment-method-token]

Use the `POST /v2/checkout/orders` endpoint to create an order with the saved PayPal Vault token. This represents the final step in the vault flow when processing a purchase using a previously saved payment method token.

### Sample API request [#sample-api-request]

Copy and modify the following code to create an order with a payment token associated with a PayPal account.

```text lineNumbers
# x-function-name: create_order_with_vaulted_paypal_wallet
# x-intent: checkout.use_saved_payment
# description: Creates a PayPal checkout order using a previously vaulted PayPal Wallet via `vault_id`.
# example_request_comment:
#   Step 3: This is the final step in the vault flow.
#   Use this when you're ready to make a purchase using a saved PayPal Wallet token (e.g., from Vault v3).
#   `vault_id` comes from the payment_token returned after converting the setup token.
curl -v -k -X POST 'https://api-m.sandbox.paypal.com/v2/checkout/orders' \
  -H "PayPal-Request-Id: unique-request-id-987" \
  -H "Authorization: Bearer ACCESS-TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "intent": "CAPTURE",
    "purchase_units": [
      {
        "amount": {
          "currency_code": "USD",
          "value": "100.00"
        }
      }
    ],
    "payment_source": {
      "paypal": {
        "vault_id": "jwgvx42"
      }
    }
  }'
```

### Expected output [#expected-output-1]

The following JSON sample shows the response structure returned after successfully creating a checkout order using a vaulted PayPal Wallet token.

```text lineNumbers
{
    "id": "ORDER-ID-12345",
    "status": "CREATED",
    "links": [
        {
            "rel": "self",
            "href": "...",
            "method": "GET"
        }
    ]
}
```

### Next steps [#next-steps-2]

Store the order ID and log transaction metadata if needed.

### Modify the code [#modify-the-code-1]

1. Copy the code sample.
2. Replace `ACCESS-TOKEN` with your [sandbox access token](#).
3. Replace `REQUEST-ID` with unique alphanumeric characters, such as a timestamp.
4. Use your payment method token ID as the `vault_id`.

### Sample API response [#sample-api-response]

The following sample API response demonstrates a successful order creation and capture using a vaulted PayPal payment method, showing the completed transaction with fee breakdown and capture details.

```text lineNumbers
{
  "id": "4TH21426N05692944",
  "status": "COMPLETED",
  "payment_source": {
    "paypal": {
      "email_address": "email@example.com",
      "account_id": "AJM9JTWQJCFTA",
      "name": {
        "given_name": "Firstname",
        "surname": "Lastname"
      },
      "address": {
        "country_code": "US"
      }
    }
  },
  "purchase_units": [
    {
      "reference_id": "default",
      "payments": {
        "captures": [
          {
            "id": "3B017991HX624902V",
            "status": "COMPLETED",
            "amount": {
              "currency_code": "USD",
              "value": "100.00"
            },
            "final_capture": true,
            "seller_protection": {
              "status": "ELIGIBLE",
              "dispute_categories": [
                "ITEM_NOT_RECEIVED",
                "UNAUTHORIZED_TRANSACTION"
              ]
            },
            "seller_receivable_breakdown": {
              "gross_amount": {
                "currency_code": "USD",
                  "value": "100.00"
                },
              "paypal_fee": {
                "currency_code": "USD",
                "value": "3.98"
              },
              "net_amount": {
                "currency_code": "USD",
                "value": "96.02"
              }
            },
            "links": [
              {
                "href": "https://api-m.sandbox.paypal.com/v2/payments/captures/3B017991HX624902V",
                "rel": "self",
                "method": "GET"
              },
              {
                "href": "https://api-m.sandbox.paypal.com/v2/payments/captures/3B017991HX624902V/refund",
                "rel": "refund",
                "method": "POST"
              },
              {
                "href": "https://api-m.sandbox.paypal.com/v2/checkout/orders/4TH21426N05692944",
                "rel": "up",
                "method": "GET"
              }
            ],
            "create_time": "2022-08-08T23:13:35Z",
            "update_time": "2022-08-08T23:13:35Z"
          }
        ]
      }
    }
  ],
  "payer": {
    "name": {
      "given_name": "Firstname",
      "surname": "Lastname"
    },
    "email_address": "email@example.com",
    "payer_id": "AJM9JTWQJCFTA",
    "address": {
        "country_code": "US"
    }
  },
  "links": [
    {
      "href": "https://api-m.sandbox.paypal.com/v2/checkout/orders/4TH21426N05692944",
      "rel": "self",
      "method": "GET"
    }
  ]
}
```

### Use payment token on behalf of payer [#use-payment-token-on-behalf-of-payer]

When payers aren't present to checkout, use their payment method token to create an order on their behalf.

If the payment token the payer created on the site is stored, skip this step.

To process a payment on behalf of the payer, retrieve the payment token they created. The process requires the customer ID that was assigned to this payer when they saved their payment method.

### Sample API request [#sample-api-request-1]

Use the [Payment Methods Token API](/api/payment-tokens/v3) endpoint to retrieve stored payment tokens by customer ID.

```text lineNumbers
curl -v -k -X GET 'https://api-m.sandbox.paypal.com/v3/vault/payment-tokens?customer_id=customer_4029352051' \
 -H 'Authorization: Bearer ACCESS-TOKEN' \
 -H 'Content-Type: application/json'
```

### Modify the code [#modify-the-code-2]

1. Copy the code sample.
2. Replace ACCESS-TOKEN with your [sandbox access token](/api/rest/authentication).
3. Pass the PayPal-generated customer\_id to retrieve the payment token details associated with the payer.
4. The response returns the Merchant Customer ID if stored in the payment token

### Sample response [#sample-response]

This sample response returns all stored payment tokens for the specified customer ID, including payer information, shipping details, and links for token management.

```text lineNumbers
{
  "customer": {
    "id": "customer_4029352051"
  },
  "payment_tokens": [
    {
      "id": "jwgvx42",
      "customer": {
        "id": "customer_4029352051",
      },
      "payment_source": {
        "paypal": {
          "description": "Description for PayPal to be shown to PayPal payer",
          "shipping": {
            "name": {
              "full_name": "Firstname Lastname"
            },
            "address": {
              "address_line_1": "2211 N First Street",
              "address_line_2": "Building 17",
              "admin_area_2": "San Jose",
              "admin_area_1": "CA",
              "postal_code": "95131",
              "country_code": "US"
            }
          },
          "usage_type": "MERCHANT",
          "customer_type": "CONSUMER",
          "name": {
            "given_name": "Firstname",
            "surname": "Lastname",
            "full_name": "Firstname Lastname"
          },
          "email_address": "john.doe@example.com",
          "payer_id": "AJM9JTWQJCFTA",
          "phone": {
            "phone_number": {
              "country_code": "US",
              "national_number": "408-208-9263"
            }
          }
        }
      },
      "links": [
        {
          "rel": "self",
          "href": "https://api-m.sandbox.paypal.com/v3/vault/payment-tokens/jwgvx42",
          "method": "GET",
          "encType": "application/json"
        },
        {
          "rel": "delete",
          "href": "https://api-m.sandbox.paypal.com/v3/vault/payment-tokens/jwgvx42",
          "method": "DELETE",
          "encType": "application/json"
        }
      ]
    }
  ],
  "links": [
    {
      "rel": "self",
      "href": "https://api-m.sandbox.paypal.com/v3/vault/payment-tokens?customer_id=customer_4029352051&page=1&page_size=5&total_required=false",
      "method": "GET",
      "encType": "application/json"
    },
    {
      "rel": "first",
      "href": "https://api-m.sandbox.paypal.com/v3/vault/payment-tokens?customer_id=customer_4029352051&page=1&page_size=5&total_required=false",
      "method": "GET",
      "encType": "application/json"
    },
    {
      "rel": "last",
      "href": "https://api-m.sandbox.paypal.com/v3/vault/payment-tokens?customer_id=customer_4029352051&page=1&page_size=5&total_required=false",
      "method": "GET",
      "encType": "application/json"
    }
  ]
}
```

A successful request returns an HTTP response code of 200 OK with payment method details and status for the provided payment token.

The response includes the following HATEOAS links:

|          |        |                                                                                  |
| -------- | ------ | -------------------------------------------------------------------------------- |
| Rel      | Method | Description                                                                      |
| `self`   | GET    | Make a GET request to this link to retrieve data about the saved payment method. |
| `delete` | DELETE | Make a DELETE request to delete the payment token from the vault.                |

After getting the payment method token ID, use a payment method token with checkout to create the order.

### Troubleshooting [#troubleshooting]

|                        |                                    |                                        |
| ---------------------- | ---------------------------------- | -------------------------------------- |
| Error                  | Meaning                            | Solution                               |
| `INVALID_RESOURCE_ID`  | Setup token is invalid or expired. | Restart the vault flow.                |
| `422 Unprocessable`    | Missing field or invalid value.    | Check all required fields.             |
| `DUPLICATE_REQUEST_ID` | You reused an idempotency key.     | Use a new key or retry only if needed. |

For more information, see [Payment Method Tokens](/api/payment-tokens/v3#errors).

## Webhooks [#webhooks]

Use webhooks to receive real-time notifications about payment token events in the PayPal Vault integration. This helps track when tokens are created, deleted, or when deletion requests are initiated.

Subscribe to the `VAULT.PAYMENT-TOKEN.CREATED` event to monitor when payment tokens are successfully created.

### Sample webhook payload [#sample-webhook-payload]

The following payload shows the webhook data sent when a payment token is created, including the token ID, payer information, and available actions.

```text lineNumbers
{
    "event_version": "1.0",
    "event_type": "VAULT.PAYMENT-TOKEN.CREATED",
    "resource_type": "payment_token",
    "resource_version": "3.0",
    "resource": {
        "owner": {
            "merchant_id": "9LADS7VH329CG"
        },
        "owner_id": "9LADS7VH329CG",
        "time_created": "2025-06-27T09:35:35.537PDT",
        "links": [
            {
                "href": "https://msmaster.qa.paypal.com/v3/vault/payment-tokens/1ct24655vx634972d",
                "rel": "self",
                "method": "GET",
                "encType": "application/json"
            },
            {
                "href": "https://msmaster.qa.paypal.com/v3/vault/payment-tokens/1ct24655vx634972d",
                "rel": "delete",
                "method": "DELETE",
                "encType": "application/json"
            }
        ],
        "id": "1ct24655vx634972d",
        "payment_source": {
            "paypal": {
                "description": "Description for PayPal to be shown to PayPal payer",
                "usage_pattern": "IMMEDIATE",
                "shipping": {
                    "name": {
                        "full_name": "Firstname Lastname"
                    },
                    "address": {
                        "address_line_1": "2211 N First Street",
                        "address_line_2": "Building 17",
                        "admin_area_2": "San Jose",
                        "admin_area_1": "CA",
                        "postal_code": "95131",
                        "country_code": "US"
                    }
                },
                "permit_multiple_payment_tokens": false,
                "usage_type": "MERCHANT",
                "customer_type": "CONSUMER",
                "email_address": "_sys_aquarium-7381640844368376@paypal.com",
                "payer_id": "4T7SJAMG62XMC",
                "name": {
                    "given_name": "Raymond",
                    "surname": "Gerald"
                }
            }
        },
        "customer": {
            "id": "test-sample-webhook"
        }
    },
    "summary": "A payment token has been created.",
    "time_created": "2025-06-27T09:35:35.537PDT"
}
```

### Available webhook events [#available-webhook-events]

Configure the webhook endpoint to listen for the following PayPal Vault events.

|                                          |                                                                                                |                  |
| ---------------------------------------- | ---------------------------------------------------------------------------------------------- | ---------------- |
| Event                                    | Trigger                                                                                        | Payment methods  |
| `VAULT.PAYMENT-TOKEN.CREATED`            | A payment token is created to save a payment method.                                           | Cards and PayPal |
| `VAULT.PAYMENT-TOKEN.DELETED`            | A payment token is deleted. The payer's payment method is no longer saved to the PayPal vault. | Cards and PayPal |
| `VAULT.PAYMENT-TOKEN.DELETION-INITIATED` | A request to delete a payment token has been submitted to the Payment Method Tokens API.       | PayPal           |

## Test App Switch [#test-app-switch]

Your merchant can test this feature in both the PayPal production app and sandbox environments. To test App Switch, make sure your integration meets these requirements:

* Complete the PayPal App Switch integration.
* Opt in for App Switch.

## Test your integration using PayPal's sandbox [#test-your-integration-using-paypals-sandbox]

This solution is available for your merchant's development team internationally.

### Preconditions [#preconditions]

* Long-lived session or remember me flow
* Face ID or Biometric

### Enable login methods [#enable-login-methods]

1. Log in to the PayPal app. On the home screen, select the avatar icon.
2. On the **Personal account** screen, select the **Login and security** option.
3. The **Login and security** screen should show the face ID or fingerprint and **Extend your login sessions** options disabled.
4. Select the toggle icon to enable each login method.

### Testable use cases [#testable-use-cases]

|                           |                                                                                                                                                                                   |                                                                                                                                                                                     |
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Flow                      | Scenario                                                                                                                                                                          | Expected behavior                                                                                                                                                                   |
| Native app                | The user starts from the merchant's native app.                                                                                                                                   | The user switches to the PayPal app and, after completing the approval flow, is automatically redirected back to the merchant app.                                                  |
| Mobile web                | The user starts from the native default browser, such as Safari on iOS or Chrome on Android. The user has the PayPal app installed. The merchant sets return\_flow to `AUTO`.     | The user switches to the PayPal app and, after completing the approval flow, is automatically redirected back to the merchant website in the same browser tab.                      |
| Mobile web                | The user starts from a non-native default browser, such as Chrome on iOS or Firefox on Android. The user has the PayPal app installed. The merchant sets `return_flow` to `AUTO`. | The user switches to the PayPal app and, after completing the approval flow, is automatically redirected back to the merchant website in the default browser.                       |
| Mobile web                | The merchant sets `return_flow` to `MANUAL`. The user has the PayPal app installed.                                                                                               | The user switches to the PayPal app and, after completing the approval flow, the app prompts the payer to manually navigate back to the merchant website.                           |
| Mobile web                | The user does not have the PayPal app installed, but the merchant has opted in for App Switch.                                                                                    | The user does not switch to the PayPal app and completes approval using the existing non-App Switch API integration.                                                                |
| Native app and mobile web | After App Switch, the user changes the payment method on the pay sheet and completes the flow.                                                                                    | The user completes approval with the new payment method they selected.                                                                                                              |
| Native app and mobile web | After logging in, the user cancels the operation on the pay sheet screen in the PayPal app.                                                                                       | The user is switched back to the merchant browser or merchant app after canceling the approval flow.                                                                                |
| Native app and mobile web | The user adds a new card.                                                                                                                                                         | When the user selects **Add Card**, they are prompted to log in again. After logging in, the new card form appears. Once the new card is added, the user can proceed with approval. |

### Test on iOS [#test-on-ios]

1. Download [TestFlight](https://apps.apple.com/us/app/testflight/id899247664) from the App Store.
2. Open [Join the PayPal - Pay, Send, Save beta](https://testflight.apple.com/join/GtF5MEaY) on your device.
3. Select **View in TestFlight** and select **Open**.
4. Select **Install** or choose **Update** for an existing build. This build will become unavailable after 90 days.

> **Note:** If you have the production version of the PayPal app installed, you will get a prompt stating that you already have the app installed. Select **Install** to replace it with the TestFlight build.

### Test on Android [#test-on-android]

1. Open the [Firebase App Distribution](https://appdistribution.firebase.dev/i/76ccbaffb35a2586) site to join the tester program.
2. Enter your email address to receive an invite.
3. To confirm your access, open the invite email on a device.
4. Select **Get Started** on your device after opening the invite email.
5. Check the consent box to agree to Firebase testing and select **Accept Invitation**. You now have access to the tester portal.
6. Select **Download** next to the latest build in the list.
7. After the download is complete, open the build. You can also find the file in your device notifications or in your downloads folder in the Files app.
8. Select **Install** when prompted to download the PayPal app.
9. If you see **Update** instead of **Install**, the PayPal app is already installed on your device. Select **Cancel** and uninstall the PayPal app before proceeding.
10. Select **Open** to access the PayPal app.
11. Log in using the email address provided in the invite. Your device now has the sandbox version of the PayPal app.

> **Note:** Select the **Download App Tester** button to ensure you receive notifications when new versions of the sandbox app are available.

### Next steps [#next-steps-3]

To deploy your merchant's PayPal Vault integration to production, complete these steps:

* Verify that all functionality works correctly in the [sandbox environment](/api/rest/production/).
* Complete the [go-live onboarding](https://www.paypal.com/unifiedonboarding/entry?country.x=US\&locale.x=en_US\&products=ppcp) process to enable PayPal Wallet processing with your live PayPal account.
* Replace sandbox credentials and API URLs with production values when you deploy your integration.
* Use the Payment Method Tokens API to [retrieve payment tokens](/api/payment-tokens/v3#payment-tokens_get), [list all payment tokens](/api/payment-tokens/v3#payment-tokens_payment-tokens), and [delete payment tokens](/api/payment-tokens/v3#payment-tokens_payment-tokens).

> **Note:** After you complete the integration, contact your PayPal support team to enable App Switch for production traffic.
