On this page
No Headings
Last updated: June 18, 2026
Allow customers to save their PayPal Wallets and charge them after a set amount of time. For example, you can offer a free trial and charge payers after the trial expires. Payers don't need to be present when charged and no checkout is required.
Customers with a PayPal Wallet can:
See supported countries:
Businesses save payment methods if they want customers to:
PayPal encrypts payment method information and stores it in a digital vault for that custome
The checkout process is now shorter because it uses saved payment information.
Set up your sandbox and live business accounts to save payment methods:
To go live, you'll need to be vetted to save PayPal Wallets. You can start the vetting process from the Developer Dashboard.
Tip: When prompted for data such as a phone number for a sandbox business request, enter any number that fits the required format. Since this is a sandbox request, the data doesn't have to be real.
Add the PayPalWebPayments package dependency for your app using a Gradle dependency:
Add the following dependency to your app's build.gradle file.
dependencies {
implementation 'com.paypal.android:paypal-web-payments:<CURRENT-VERSION>'
}Add a button to your app's UI:
Button("PayPal") {
// Create a setup token server-side (see next step)
}Request a setup token from your server, create a PayPalVaultRequest object, and call the vault() method.
// Client-side code sample
val coreConfig = CoreConfig(CLIENT_ID)
val setupTokenResponse = createVaultSetupToken()
val vaultRequest = PayPalWebVaultRequest(setupTokenResponse.setupTokenId)
val paypalClient = PayPalWebCheckoutClient(activity, coreConfig, URL_SCHEME)
paypalClient.vaultListener = this
paypalClient.vault(vaultRequest)
override fun onPayPalWebVaultSuccess(result: PayPalWebVaultResult) {
// Handle success
}
override fun onPayPalWebVaultFailure(error: PayPalSDKError) {
// Handle failure
}
override fun onPayPalWebVaultCanceled() {
// Handle cancellation
}Copy the code sample and modify it as follows:
CLIENT_ID to your clientId.URL_SCHEME with the custom URL scheme used to return to your app.createPayPalSetupToken method, call the endpoint on your server to create a setup token with the Payment Method Tokens API. createPayPalSetupToken returns the setup token as a string.Set up your server to call the Payment Method Tokens API
The SDK uses the Payment Method Tokens API to save payment methods in the background. Use the following request as a template to create a setup token.
Note: The return_url and cancel_url values are required, but can have filler values such as in the following sample.
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": {
"usage_type": "PLATFORM",
"experience_context": {
"return_url": "https://example.com/returnUrl",
"cancel_url": "https://example.com/cancelUrl"
}
}
}
}'ACCESS-TOKEN to your sandbox app's access token.REQUEST-ID to a set of unique alphanumeric characters such as a timestamp.{
"id": "4G4976650J0948357",
"customer": {
"id": "customer_4029352050"
},
"status": "PAYER_ACTION_REQUIRED",
"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": "123 Main Street",
"address_line_2": "Unit A",
"admin_area_2": "Anytown",
"admin_area_1": "CA",
"postal_code": "12345",
"country_code": "US"
}
},
"permit_multiple_payment_tokens": false,
"usage_type": "PLATFORM",
"customer_type": "CONSUMER"
}
},
"links": [
{
"href": "https://api-m.sandbox.paypal.com/v3/vault/setup-tokens/4G4976650J0948357",
"rel": "self",
"method": "GET",
"encType": "application/json"
},
{
"href": "https://sandbox.paypal.com/agreements/approve?approval_session_id=4G4976650J0948357",
"rel": "approve",
"method": "GET",
"encType": "application/json"
}
]
}Convert the setup token to a payment token that can be used to process a transaction:
Convert the setup token to a payment token that can be used to process a transaction:
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" \
-d '{
"payment_source": {
"token": {
"id": "VAULT-SETUP-TOKEN",
"type": "SETUP_TOKEN"
}
}
}'ACCESS-TOKEN to your sandbox app's access token.REQUEST-ID to a set of unique alphanumeric characters such as a timestamp.VAULT-SETUP-TOKEN to the value passed from the client.payment token returned from the API to use in future transactions.The following sample shows a complete back-end integration to save PayPal for purchase later:
import "dotenv/config";
import express from "express";
const { PORT = 8888 } = process.env;
const app = express();
app.set("view engine", "ejs");
app.use(express.static("public"));
// Create setup token
app.post("/create/setup/token", async (req, res) => {
try {
// Use your access token to securely generate a setup token
// with an empty payment_source
const vaultResponse = await fetch(
"https://api-m.sandbox.paypal.com/v3/vault/setup-tokens",
{
method: "POST",
body: JSON.stringify({ payment_source: { paypal: {} } }),
headers: {
Authorization: "Bearer ${ACCESS-TOKEN}",
"PayPal-Request-Id": Date.now(),
},
},
);
// Return the reponse to the client
res.json(vaultResponse);
} catch (err) {
res.status(500).send(err.message);
}
});
// Create payment token from a setup token
app.post("/create/payment/token/", async (req, res) => {
try {
const paymentTokenResult = await fetch(
"https://api-m.sandbox.paypal.com/v3/vault/payment-tokens",
{
method: "POST",
body: {
payment_source: {
token: {
id: req.body.vaultSetupToken,
type: "SETUP_TOKEN",
},
},
},
headers: {
Authorization: "Bearer ${ACCESS-TOKEN}",
"PayPal-Request-Id": Date.now(),
},
},
);
const paymentMethodToken = paymentTokenResult.id;
const customerId = paymentTokenResult.customer.id;
await save(paymentMethodToken, customerId);
res.json(captureData);
} catch (err) {
res.status(500).send(err.message);
}
});
const save = async function (paymentMethodToken, customerId) {
// Specify where to save the payment method token
};
app.listen(PORT, () => {
console.log("Server listening at http://localhost:${PORT}/");
});Create a view where payers can see their payment methods using the Payment Method Tokens API
Go live with your integration.