# Save PayPal with the JavaScript SDK

After customers save their PayPal Wallet, they can select it for faster checkout. Customers won't have to enter payment details for future transactions.

To save PayPal Wallets, payers need to log in to your site, make a purchase, and remain on your site when transactions take place.

Customers with a PayPal Wallet can:
- Review PayPal transactions and transaction history
- Review, add, or remove funding sources
- Review and cancel recurring payments
- Hold a balance in their PayPal account
- Use PayPal to send and receive money
- Withdraw money to a linked bank account
- Use PayPal to transact with merchants

**Warning**: Use the customer identifier generated by PayPal and not the identifier that you use to identify the customer in your system.

## 1. Set up sandbox to save payment methods

Set up your sandbox and live business accounts to save payment methods:

- Log in to the Developer Dashboard.
- Under **REST API apps**, select your app name.
- Under **Sandbox App Settings**, check **Accept payments**.
- Expand **Advanced options**. Confirm that **Vault** is selected.

To go live, you'll need to be vetted for PayPal Wallet. You can start the vetting process from the Merchant Servicing Dashboard.

- Only your sandbox business account is enabled for vaulting payment methods. Your developer account remains unaffected.
- You'll complete production onboarding when you're ready to go live.

**Tip**: When prompted for data such as a phone number for the sandbox business request, enter any number that fits the required format. Since this is a sandbox request, the data doesn't have to be factual.

## 2. Generate user ID token for payer

The OAuth 2.0 API to retrieve an **access_token** has an additional parameter, **response_type**, that can be set to **id_token**. Include the **id_token** and **access_token** in the response.

### First-time payer

A payer wants to save a payment method for the first time. Modify the following code to generate a user ID token for the payer:

### Sample server-side user ID token request with a customer ID

```curl
curl -v -X POST https://api-m.sandbox.paypal.com/v2/checkout/orders/ \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ACCESS-TOKEN" \
-H "PayPal-Request-Id: REQUEST-ID" \
-d '{"\
    "intent": "CAPTURE",
    "purchase_units": [{"\
      "amount": {"\
        "currency_code": "USD",
        "value": "100.00"
      }"],
    "payment_source": {
      "paypal": {
        "attributes": {
          "vault": {
            "store_in_vault": "ON_SUCCESS",
            "usage_type": "MERCHANT",
            "customer_type": "CONSUMER"
          }
        },
      }
    }
  }'
```

A successful request returns fields including an **access_token**, **id_token**, and the number of seconds the **access_token** token is valid.

The **id_token**:
- Uniquely identifies each payer.
- Expires in a few minutes because it's meant to be used during checkout. Generate new tokens if the current tokens expire.

**Tip**: Each buyer session is unique. Set up your server to generate a new client token each time payment fields render on your page.

## 3. Pass user ID token to JavaScript SDK

Pass the **id_token** from your server into the JavaScript SDK using the **data-user-id-token**.

```javascript
<script src="https://www.paypal.com/sdk/js?client-id=YOUR_CLIENT_ID" data-user-id-token="YOUR-ID-TOKEN">
```

Include client-side callbacks to:
- Manage interactions with APIs
- Manage payer approval flows
- Handle any events that lead to cancellation or error during payer approval

### Client-side code snippet

```javascript
paypal.Buttons({
    // Call your server to set up the transaction
    createOrder: function(data, actions) {
        return fetch('/yourserver.com/createOrder', {
            method: 'post',
            body: JSON.stringify({
              source: data.paymentSource, //paypal / venmo / etc.
            }),
            // Here for product info
        }).then(function(res) {
            return res.json();
        }).then(function(orderData) {
            return orderData.id;
        });
    },
    // Authorize or capture the transaction after payer approves
    onApprove: (data, actions) => {
        return fetch('/yourserver.com/order/' + data.orderID + '/capture', {
            method: 'post'
        });
    },
    onCancel(data, actions) {
        console.log(`Order Canceled - ID: ${data.orderID}`);
    },
    onError(err) {
        console.error(err);
    }
}).render('#paypal-button-container');
```

### Server-side code snippet

Set up your server to call the Create Order API. The button that the payer selects determines the **payment_source** sent in the following sample.

This SDK uses the Orders v2 API to save payment methods in the background. Use the following request to add attributes needed to save a PayPal Wallet.

#### Request

In the following request, the **payment_source.paypal.attributes.vault.store_in_vault** with the value **ON_SUCCESS** means the PayPal button is saved with a successful authorization or capture.

Pass the **payment_source.paypal.experience_context** and include the **return_url** and **cancel_url** to redirect the payer after they approve or cancel the order.

Create an order with PayPal as a payment source and store the PayPal Wallet in vault:

#### **Code**

```javascript
{
    "id": "5O190127TN364715T",
    "status": "PAYER_ACTION_REQUIRED",
    "payment_source": {
        "paypal": {
            "attribute": {
                "vault": {
                    "store_in_vault": "ON_SUCCESS",
                    "usage_type": "MERCHANT",
                    "customer_type": "CONSUMER"
                }
            },
            "name": {
                "given_name": "Firstname",
                "surname": "Lastname"
            },
            "email_address": "customer@example.com",
            "phone_number": {
                "national_number": "2025212022"
            },
            "account_id": "QYR5Z8XDVJNXQ",
            "address": {
                "country_code": "US"
            },
            "payer": {
                "name": {
                    "given_name": "Firstname",
                    "surname": "Lastname"
                },
                "email_address": "customer@example.com",
                "phone_number": {
                    "national_number": "2025212022"
                },
                "payer_id": "QYR5Z8XDVJNXQ",
                "address": {
                    "country_code": "US"
                }
            }
        },
        "customer": {
            "id": "208743798"
        }
    },
    "payer": {
        "name": {
            "given_name": "Firstname",
            "surname": "Lastname"
        },
        "email_address": "customer@example.com",
        "phone_number": {
            "national_number": "2025212022"
        },
        "payer_id": "QYR5Z8XDVJNXQ",
        "address": {
            "country_code": "US"
        }
    },
    "purchase_units": [{
        "reference_id": "d9f80740-38f0-11e8-b467-0ed5f89f718b",
        "payments": {
            "captures": [{
                "id": "3C679366HH908993F",
                "status": "COMPLETED",
                "amount": {
                  "currency_code": "USD",
                  "value": "100.00"
                },
                "seller_protection": {
                  "status": "ELIGIBLE",
                  "dispute_categories": [
                    "ITEM_NOT_RECEIVED",
                    "UNAUTHORIZED_TRANSACTION"
                  ]
                },
                "final_capture": true,
                "seller_receivable_breakdown": {
                  "gross_amount": {
                    "currency_code": "USD",
                    "value": "100.00"
                  },
                  "paypal_fee": {
                    "currency_code": "USD",
                    "value": "3.00"
                  },
                  "net_amount": {
                    "currency_code": "USD",
                    "value": "97.00"
                  }
                },
                "create_time": "2022-01-01T21:20:49Z",
                "update_time": "2022-01-01T21:20:49Z",
                "links": [{
                    "href": "https://api-m.sandbox.paypal.com/v2/payments/captures/3C679366HH908993F",
                    "rel": "self",
                    "method": "GET"
                  },{
                    "href": "https://api-m.sandbox.paypal.com/v2/payments/captures/3C679366HH908993F/refund",
                    "rel": "refund",
                    "method": "POST"
                  }]
                }
            }]
        }
    }],
    "links": [{
        "href": "https://api-m.sandbox.paypal.com/v2/checkout/orders/5O190127TN364715T",
        "rel": "self",
        "method": "GET"
      }]
}
```

## 4. Add PayPal button

Include the JavaScript SDK code to show the PayPal button on your product and checkout pages.

- Determine where the SDK should show the PayPal button. Use [configuration attributes](https://www.paypal.com/docs/sdk/js/reference/#buttons) to control the layout of the button.

HTML

### Code

```javascript
<!-- Set up a container element for the button -->
<div id="paypal-button-container"></div>

<!-- Include the PayPal JavaScript SDK. Replace YOUR_CLIENT_ID with your client ID.-->
<script src="https://www.paypal.com/sdk/js?client-id=YOUR_CLIENT_ID" data-user-id-token="YOUR-ID-TOKEN">
</script>

<script>
  // Render the eligible PayPal buttons within the #paypal-button-container
  paypal.Buttons().render('#paypal-button-container')
</script>
```

Include client-side callbacks to:
- Manage interactions with APIs
- Manage payer approval flows
- Handle any events that lead to cancellation or error during payer approval

### Client-side code snippet

```javascript
<script>
  paypal.Buttons({
      // Call your server to set up the transaction
      createOrder: function(data, actions) {
        return fetch('/yourserver.com/createOrder', {
          method: 'post',
          body: JSON.stringify({
            source: data.paymentSource, //paypal / venmo / etc.
          }),
          // Here for product info
        }).then(function(res) {
          return res.json();
        }).then(function(orderData) {
          return orderData.id;
        });
      },
      // Authorize or capture the transaction after payer approves
      onApprove: (data, actions) => {
        return fetch('/yourserver.com/order/' + data.orderID + '/capture', {
          method: 'post'
        });
      },
      onCancel(data, actions) {
        console.log(`Order Canceled - ID: ${data.orderID}`);
      },
      onError(err) {
        console.error(err);
      }
  }).render('#paypal-button-container');
</script>
```

### Server-side code snippet

Set up your server to call the Create Order API. The button that the payer selects determines the **payment_source** sent in the following sample.

This SDK uses the Orders v2 API to save payment methods in the background. Use the following request to add attributes needed to save a PayPal Wallet.

#### Request

In the following request, the **payment_source.paypal.attributes.vault.store_in_vault** with the value **ON_SUCCESS** means the PayPal button is saved with a successful authorization or capture.

Pass the **payment_source.paypal.experience_context** and include the **return_url** and **cancel_url** to redirect the payer after they approve or cancel the order.

Create an order with PayPal as a payment source and store the PayPal Wallet in vault:

#### **Code**

```javascript
curl -v -X POST https://api-m.sandbox.paypal.com/v2/checkout/orders/ \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ACCESS-TOKEN" \
-H "PayPal-Request-Id: REQUEST-ID" \
-d '{"\
    "intent": "CAPTURE",
    "purchase_units": [{"\
      "amount": {"\
        "currency_code": "USD",
        "value": "100.00"
      }],
    "payment_source": {
      "paypal": {
        "attributes": {
          "vault": {
            "store_in_vault": "ON_SUCCESS",
            "usage_type": "MERCHANT",
            "customer_type": "CONSUMER"
          }
        },
      }
    }
  }'
```

#### Response

Return the **id** to your client to call the payer approval flow if the **payment_source** needs payer approval.

**Note**: The request to save the PayPal button is made when the order is created through **payment_source.attributes.vault.store_in_vault**. Vault details are available only after an order is authorized or captured.

#### **code**

```javascript
{
    "id": "5O190127TN364715T",
    "status": "COMPLETED",
    "payment_source": {
        "paypal": {}
    },
    "links": [{
        "href": "https://api-m.sandbox.paypal.com/v2/checkout/orders/5O190127TN364715T",
        "rel": "up",
        "method": "GET"
      },{
        "href": "https://www.paypal.com/checkoutnow?token=5O190127TN364715T",
        "rel": "payer-action",
        "method": "GET"
      }
    ]
}
```

## 5. Create Order

Include client-side callbacks to:
- Manage interactions with APIs
- Manage payer approval flows
- Handle any events that lead to cancellation or error during payer approval

### After the payer approves, the **onApprove** function is called in the JavaScript SDK. Depending on the intent passed, the server calls the following APIs:
- Capture Order API if the **intent** passed was **CAPTURE**
- Authorize Order API if the **intent** passed was **AUTHORIZE** as part of your Create Order call.
- If authorization or capture is successful, a **vault.id** is also created.

#### **Code**

```curl
curl -v -X POST https://api-m.sandbox.paypal.com/v2/checkout/orders/5O190127TN364715T/authorize \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ACCESS-TOKEN" \
-d '{"'
    "intent": "CAPTURE",
    "purchase_units": [{""
      "amount": {""
        "currency_code": "USD",
        "value": "100.00"
      }}
    ],
    "payment_source": {
      "paypal": {
        "attributes": {
          "vault": {
            "store_in_vault": "ON_SUCCESS",
            "usage_type": "MERCHANT",
            "customer_type": "CONSUMER"
          }
        },
      }
    }
  }'
```

#### Capture order request

```curl
curl -v -X POST https://api-m.sandbox.paypal.com/v2/checkout/orders/5O190127TN364715T/capture \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ACCESS-TOKEN" \
-d '{"'
    "intent": "CAPTURE",
    "purchase_units": [{""
      "amount": {""
        "currency_code": "USD",
        "value": "100.00"
      }}
    ],
    "payment_source": {
      "paypal": {
        "attributes": {
          "vault": {
            "store_in_vault": "ON_SUCCESS",
            "usage_type": "MERCHANT",
            "customer_type": "CONSUMER"
          }
        },
      }
    }
  }'
```

### Capture order response

Return the **id** to your client to call the payer approval flow if the **payment_source** needs payer approval.

**Note**: The request to save the PayPal button is made when the order is created through **payment_source.attributes.vault.store_in_vault**. Vault details are available only after an order is authorized or captured.

#### **code**

```javascript
{
    "id": "5O190127TN364715T",
    "status": "COMPLETED",
    "payment_source": {
        "paypal": {}
    },
    "links": [{
        "href": "https://api-m.sandbox.paypal.com/v2/checkout/orders/5O190127TN364715T",
        "rel": "up",
        "method": "GET"
      },{
        "href": "https://www.paypal.com/checkoutnow?token=5O190127TN364715T",
        "rel": "payer-action",
        "method": "GET"
      }
    ]
}
```

## 6. Payer approval

If payer approval is required, the client SDK calls the payer approval flow. The approval flow takes the payer through PayPal Checkout.

- On your checkout page, click the PayPal button.
- Log in to the payer account and approve the payment and billing agreement.
- Capture the transaction.
- Store the PayPal-generated customer ID in your system.
- Log in to [sandbox](https://www.sandbox.paypal.com/) with your merchant account and verify the transaction.
- Refresh the page that contains the PayPal button. Ensure the JavaScript SDK is initialized with the PayPal-generated customer ID.
- Ensure the PayPal button displays the payer's preferred payment method.
- Ensure that the payment method you just saved is visible with the other buttons.
- Select the PayPal button again to test the return payer flow.

## 7. Authorize or capture order

Include the **id_token** from your server into the JavaScript SDK using the **data-user-id-token**.

```javascript
<script>
  paypal.Buttons({
      // Call your server to set up the transaction
      createOrder: function(data, actions) {
        return fetch('/yourserver.com/createOrder', {
          method: 'post',
          body: JSON.stringify({
            source: data.paymentSource, //paypal / venmo / etc.
          }),
          // Here for product info
        }).then(function(res) {
          return res.json();
        }).then(function(orderData) {
          return orderData.id;
        });
      },
      // Authorize or capture the transaction after payer approves
      onApprove: (data, actions) => {
        return fetch('/yourserver.com/order/' + data.orderID + '/capture', {
          method: 'post'
        });
      },
      onCancel(data, actions) {
        console.log(`Order Canceled - ID: ${data.orderID}`);
      },
      onError(err) {
        console.error(err);
      }
  }).render('#paypal-button-container');
</script>
```

### Server-side code snippet

Set up your server to call the Create Order API. The button that the payer selects determines the **payment_source** sent in the following sample.

This SDK uses the Orders v2 API to save payment methods in the background. Use the following request to add attributes needed to save a PayPal Wallet.

#### Request

In the following request, the **payment_source.paypal.attributes.vault.store_in_vault** with the value **ON_SUCCESS** means the PayPal button is saved with a successful authorization or capture.

Pass the **payment_source.paypal.experience_context** and include the **return_url** and **cancel_url** to redirect the payer after they approve or cancel the order.

Create an order with PayPal as a payment source and store the PayPal Wallet in vault:

#### **Code**

```javascript
curl -v -X POST https://api-m.sandbox.paypal.com/v2/checkout/orders/ \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ACCESS-TOKEN" \
-H "PayPal-Request-Id: REQUEST-ID" \
-d '{"\
    "intent": "CAPTURE",
    "purchase_units": [{""
      "amount": {""
        "currency_code": "USD",
        "value": "100.00"
      }}
    ],
    "payment_source": {
      "paypal": {
        "attributes": {
          "vault": {
            "store_in_vault": "ON_SUCCESS",
            "usage_type": "MERCHANT",
            "customer_type": "CONSUMER"
          }
        },
        "name": {
          "given_name": "Firstname",
          "surname": "Lastname"
        },
        "email_address": "customer@example.com",
        "phone_number": {
          "national_number": "2025212022"
        },
        "account_id": "QYR5Z8XDVJNXQ",
        "address": {
          "country_code": "US"
        },
        "payer": {
          "name": {
            "given_name": "Firstname",
            "surname": "Lastname"
          },
          "email_address": "customer@example.com",
          "phone_number": {
            "national_number": "2025212022"
          },
          "payer_id": "QYR5Z8XDVJNXQ",
          "address": {
            "country_code": "US"
          }
        }
      },
      "customer": {
        "id": "208743798"
      }
    },
    "payer": {
      "name": {
        "given_name": "Firstname",
        "surname": "Lastname"
      },
      "email_address": "customer@example.com",
      "phone_number": {
        "national_number": "2025212022"
      },
      "payer_id": "QYR5Z8XDVJNXQ",
      "address": {
        "country_code": "US"
      }
    },
    "purchase_units": [{""
      "reference_id": "d9f80740-38f0-11e8-b467-0ed5f89f718b",
      "payments": {
        "captures": [{""
          "id": "3C679366HH908993F",
          "status": "COMPLETED",
          "amount": {
            "currency_code": "USD",
            "value": "100.00"
          },
          "seller_protection": {
            "status": "ELIGIBLE",
            "dispute_categories": [
              "ITEM_NOT_RECEIVED",
              "UNAUTHORIZED_TRANSACTION"
            ]
          },
          "final_capture": true,
          "seller_receivable_breakdown": {
            "gross_amount": {
              "currency_code": "USD",
              "value": "100.00"
            },
            "paypal_fee": {
              "currency_code": "USD",
              "value": "3.00"
            },
            "net_amount": {
              "currency_code": "USD",
              "value": "97.00"
            }
          },
          "create_time": "2022-01-01T21:20:49Z",
          "update_time": "2022-01-01T21:20:49Z",
          "links": [{""
            "href": "https://api-m.sandbox.paypal.com/v2/payments/captures/3C679366HH908993F",
            "rel": "self",
            "method": "GET"
          },{
            "href": "https://api-m.sandbox.paypal.com/v2/payments/captures/3C679366HH908993F/refund",
            "rel": "refund",
            "method": "POST"
          }]
        }]
      }
    }],
    "links": [{""
      "href": "https://api-m.sandbox.paypal.com/v2/checkout/orders/5O190127TN364715T",
      "rel": "self",
      "method": "GET"
    }]
  }'
```

#### Capture order request

```curl
curl -v -X POST https://api-m.sandbox.paypal.com/v2/checkout/orders/5O190127TN364715T/capture \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ACCESS-TOKEN" \
-d '{"'
    "intent": "CAPTURE",
    "purchase_units": [{""
      "amount": {""
        "currency_code": "USD",
        "value": "100.00"
      }}
    ],
    "payment_source": {
      "paypal": {
        "attributes": {
          "vault": {
            "store_in_vault": "ON_SUCCESS",
            "usage_type": "MERCHANT",
            "customer_type": "CONSUMER"
          }
        },
        "name": {
          "given_name": "Firstname",
          "surname": "Lastname"
        },
        "email_address": "customer@example.com",
        "phone_number": {
          "national_number": "2025212022"
        },
        "account_id": "QYR5Z8XDVJNXQ",
        "address": {
          "country_code": "US"
        },
        "payer": {
          "name": {
            "given_name": "Firstname",
            "surname": "Lastname"
          },
          "email_address": "customer@example.com",
          "phone_number": {
            "national_number": "2025212022"
          },
          "payer_id": "QYR5Z8XDVJNXQ",
          "address": {
            "country_code": "US"
          }
        }
      },
      "customer": {
        "id": "208743798"
      }
    },
    "payer": {
      "name": {
        "given_name": "Firstname",
        "surname": "Lastname"
      },
      "email_address": "customer@example.com",
      "phone_number": {
        "national_number": "2025212022"
      },
      "payer_id": "QYR5Z8XDVJNXQ",
      "address": {
        "country_code": "US"
      }
    },
    "purchase_units": [{""
      "reference_id": "d9f80740-38f0-11e8-b467-0ed5f89f718b",
      "payments": {
        "captures": [{""
          "id": "3C679366HH908993F",
          "status": "COMPLETED",
          "amount": {
            "currency_code": "USD",
            "value": "100.00"
          },
          "seller_protection": {
            "status": "ELIGIBLE",
            "dispute_categories": [
              "ITEM_NOT_RECEIVED",
              "UNAUTHORIZED_TRANSACTION"
            ]
          },
          "final_capture": true,
          "seller_receivable_breakdown": {
            "gross_amount": {
              "currency_code": "USD",
              "value": "100.00"
            },
            "paypal_fee": {
              "currency_code": "USD",
              "value": "3.00"
            },
            "net_amount": {
              "currency_code": "USD",
              "value": "97.00"
            }
          },
          "create_time": "2022-01-01T21:20:49Z",
          "update_time": "2022-01-01T21:20:49Z",
          "links": [{""
            "href": "https://api-m.sandbox.paypal.com/v2/payments/captures/3C679366HH908993F",
            "rel": "self",
            "method": "GET"
          },{
            "href": "https://api-m.sandbox.paypal.com/v2/payments/captures/3C679366HH908993F/refund",
            "rel": "refund",
            "method": "POST"
          }]
        }]
      }
    }],
    "links": [{""
      "href": "https://api-m.sandbox.paypal.com/v2/checkout/orders/5O190127TN364715T",
      "rel": "self",
      "method": "GET"
    }]
  }'
```

### Capture order response

Return the **id** to your client to call the payer approval flow if the **payment_source** needs payer approval.

**Note**: The request to save the PayPal button is made when the order is created through **payment_source.attributes.vault.store_in_vault**. Vault details are available only after an order is authorized or captured.

#### **code**

```javascript
{
    "id": "5O190127TN364715T",
    "status": "COMPLETED",
    "payment_source": {
        "paypal": {}
    },
    "links": [{
        "href": "https://api-m.sandbox.paypal.com/v2/checkout/orders/5O190127TN364715T",
        "rel": "self",
        "method": "GET"
      },{
        "href": "https://www.paypal.com/checkoutnow?token=5O190127TN364715T",
        "rel": "payer-action",
        "method": "GET"
      }
    ]
}
```

## 8. Test your integration

Run the following tests in the PayPal sandbox to ensure you can save PayPal Wallets.

### Save payment method

- On your checkout page, click the PayPal button.
- Log in to the payer account and approve the payment and billing agreement.
- Capture the transaction.
- Store the PayPal-generated customer ID in your system.
- Log in to [sandbox](https://www.sandbox.paypal.com/) with your merchant account and verify the transaction.
- Refresh the page that contains the PayPal button. Ensure the JavaScript SDK is initialized with the PayPal-generated customer ID.
- Ensure the PayPal button displays the payer's preferred payment method.
- Ensure that the payment method you just saved is visible with the other buttons.
- Select the PayPal button again to test the return payer flow.

### Sample integration

See a sample Save Payment Method integration in the [PayPal GitHub repository](https://github.com/paypal-examples/docs-examples/tree/main/save-payment-method).

## Next steps

- [Test and go live](/reference/production/) with this integration.
- Change the credentials and API URLs from api-m.sandbox.paypal.com to api-m.paypal.com when going live with your integration.
- You can [create orders](/docs/api/orders/v2/#orders_create) without the payment_source.paypal.attributes.vault for subsequent or recurring transactions.
- You can [get a payment token](/docs/api/payment-tokens/v3/#payment-tokens_get) , [list all payment tokens](/docs/api/payment-tokens/v3/#payment-tokens_payment-tokens) , [delete a payment token](/docs/api/payment-tokens/v3/#payment-tokens_delete) , and more with the Payment Method Tokens API.

## See also

- [Set up payment buttons](https://www.paypal.com/docs/checkout/standard/)