- Australia
- Austria
- Belgium
- Bulgaria
- Canada
- China
- Cyprus
- Czech Republic
- Denmark
- Estonia
- Finland
- France
- Germany
- Hong Kong
- Hungary
- Ireland
- Italy
- Japan
- Latvia
- Liechtenstein
- Lithuania
- Luxembourg
- Malta
- Netherlands
- Norway
- Poland
- Portugal
- Romania
- Singapore
- Slovakia
- Slovenia
- Spain
- Sweden
- United Kingdom
- United States
Save cards for purchase later with the JavaScript SDK
CurrentLast updated: June 26th 2024, @ 11:32:06 am
Save payment methods to charge payers 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. No checkout required.
Use the SDK to save a payer's card if you aren't PCI Compliant - SAQ A but want to save credit or debit cards.
Availability
Know before you code
- You are responsible for the front-end user experience. The JavaScript SDK provides back-end support.
- To save payment methods, you must be able to identify payers uniquely. For example, payers create an account and log in to your site.
- Complete the steps in Get started to get the following sandbox account information from the Developer Dashboard:
- Your sandbox account login information
- Your access token
- This client-side integration uses information passed through the
CardFields
component to save a card without a transaction. - The SDK saves the following card types for purchase later:
- American Express
- Discover
- Mastercard
- Visa
- You'll need an existing advanced credit and debit integration. PayPal must approve your business account for advanced credit and debit card payments.
How it works
PayPal encrypts payment method information and stores it in a digital vault for that customer.
- The payer saves their payment method.
- For a first-time payer, PayPal creates a customer ID. Store this within your system for future use.
- When the customer returns to your website and is ready to check out, pass their PayPal-generated customer ID to the JavaScript SDK. The customer ID tells the JavaScript SDK to save or reuse a saved payment method.
- The payer completes a billing agreement.
- The JavaScript SDK populates the checkout page with each saved payment method. Each payment method appears as a one-click button next to other ways to pay.
The checkout process is now shorter because it uses saved payment information.
1. Set up account to save payments
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 > App Feature Options, check Accept payments.
- Expand Advanced options. Confirm that Vault is selected.
2. Add SDK to HTML page
Pass your client ID to the SDK to identify yourself.
Replace CLIENT-ID
with your app's client ID in the following sample:
1<script2 src="https://www.paypal.com/sdk/js?components=card-fields&client-id=CLIENT-ID"></script>
3. Create setup token
You request a setup token from your server. Pass the setup token from your server to the SDK with the createVaultSetupToken
callback.
The createVaultSetupToken
callback:
- Calls the server endpoint you created to generate and retrieve the setup token.
- Makes a request to your server endpoint.
Then, your server uses its access token to create and return the setup token to the client.
Any errors that occur while creating a setup token show up in the onError
callback provided to the card fields component.
Create a setup token for cards that have:
- No verification
- 3D Secure verification
Supported callbacks
- No verification
- 3D Secure
Callback | Returns | Description |
---|---|---|
createVaultSetupToken | Setup token (string) | Your server must receive this callback. To get a setup token, see Create a setup token for card. The SDK then saves the payment method and updates the setup token with payment method details. |
Front-end sample
- No verification
- 3D Secure
1const cardFields = paypal.CardFields({2 createVaultSetupToken: () => {3 // Call your server API to generate a vaultSetupToken and return it here as a string4 const result = await fetch("example.com/create/setup/token")5 return result.token6 },7 onError: (error) => {8 // Capture and log errors from the SDK9 }10 })
Back-end sample
Make this request from your server.
This setup token is generated with an empty card
in the payment_source
object. PayPal hosted fields use this token to securely update the setup token with card details.
1curl -v -k -X POST 'https://api-m.sandbox.paypal.com/v3/vault/setup-tokens' \2 -H "Content-Type: application/json" \3 -H "Authorization: Bearer ACCESS-TOKEN" \4 -H "PayPal-Request-Id: REQUEST-ID" \5 -d '{6 "payment_source": {7 "card": {}8 }9 }'
Modify the code
- Change
ACCESS-TOKEN
to your sandbox app's access token. - Change
REQUEST-ID
to a set of unique alphanumeric characters such as a time stamp. - In the
createVaultSetupToken
, call the endpoint on your server to create a setup token with the Payment Method Tokens API.createVaultSetupToken
returns the setup token as a string.
4. Initialize card fields to save data
After the SDK has a setup token, it renders card fields for the payer to submit card details. The SDK then returns the vaultSetupToken
to the merchant through the onApprove
callback.
When you complete this step, CardFields
are ready to save card details for later use.
Supported callback
- No verification
- 3D Secure
Callback | Returns | Description |
---|---|---|
onApprove | { vaultSetupToken: string } | You get the updated vaultSetupToken when the payment method is saved. Store the vaultSetupToken token in your system. |
Front-end sample
- No verification
- 3D Secure
1const cardFields = paypal.CardFields({2 createVaultSetupToken: () => {3 // Call your server API to generate a vaultSetupToken4 // and return it here as a string5 const result = await fetch("example.com/create/setup/token")6 return result.token7 }8 onApprove: ({9 vaultSetupToken10 }) => {11 // Send the vaultSetupToken to your server12 // for the server to generate a payment token13 return fetch("example.com/create/payment/token", { body: JSON.stringify({ vaultSetupToken }) })14 },15 ...16})
Back-end sample
Make this request from your server.
1curl -v -k -X POST 'https://api-m.sandbox.paypal.com/v3/vault/payment-tokens' \2 -H "Content-Type: application/json" \3 -H "Authorization: Bearer ACCESS-TOKEN" \4 -H "PayPal-Request-Id: REQUEST-ID" \5 -d '{6 "payment_source": {7 "token": {8 "id": "VAULT-SETUP-TOKEN",9 "type": "SETUP_TOKEN"10 }11 }12 }'
Modify the code
- Pass the
vaultSetupToken
returned byonApprove
to your server. - Change
ACCESS-TOKEN
to your sandbox app's access token. - Change
REQUEST-ID
to a set of unique alphanumeric characters such as a time stamp. - Change
VAULT-SETUP-TOKEN
to the value passed from the client. - Save the resulting
payment token
returned from the API to use in future transactions.
Avoid validation errors
CardFields
can't be configured with both the createOrder
callback and the createVaultSetupToken
callback. When saving cards, only pass createVaultSetupToken
.
1// Throws a validation error: can't call both 'createVaultSetupToken' and 'createOrder'2paypal.CardFields({3 createVaultSetupToken: () => {...},4 createOrder: () => {...}5})
5. Show error page
If an error prevents checkout, alert the payer that an error has occurred using the onError
callback.
Note: This script doesn't handle specific errors. It shows a specified error page for all errors.
1paypal.CardFields({2 onError(err) {3 console.error('Something went wrong:', error)4 }5 });
Supported callback
Callback | Returns | Description |
---|---|---|
onError | void | Implement the optional onError() function to handle errors and display generic |
error message or page to the buyers. This error handler is a catch-all. |
6. Handle 3D Secure Cancel
1paypal.CardFields({2 onCancel() {3 console.log("Your order was cancelled due to incomplete verification");4 }5 });
Supported callback
Callback | Returns | Description |
---|---|---|
onCancel | void | Called when the customer closes 3D Secure verification modal. This also means the order is cancelled. |
7. Show saved payment methods to returning payers
When a payer returns to your site, you can show the payer's saved payment methods with the Payment Method Tokens API.
List all saved payment methods
Make the server-side list all payment tokens API call to retrieve payment methods saved to a payer's PayPal-generated customer ID. Based on this list, you can show all saved payment methods to a payer to select during checkout.
Important: Don't expose payment method token IDs on the client side. To protect your payers, create separate IDs for each token and use your server to correlate them.
Sample request: List all saved payment methods
1curl -L -X GET "https://api-m.sandbox.paypal.com/v3/vault/payment-tokens?customer_id=CUSTOMER-ID"\2 -H "Content-Type: application/json" \3 -H "Accept-Language: en_US" \4 -H "Authorization: Bearer ACCESS-TOKEN" \5 -H "PayPal-Request-Id: REQUEST-ID"
Modify the code
- Change
CUSTOMER-ID
to a PayPal-generated customer ID. - Change
ACCESS-TOKEN
to your sandbox app's access token. - Change
REQUEST-ID
to a set of unique alphanumeric characters such as a time stamp.
Show saved card to payer
Display the saved card to the payer and use the Orders API to make another transaction. Use the vault ID the payer selects as an input to the Orders API to capture the payment.
Use supported CSS properties to style the card fields. We recommend showing the card brand and last 4 digits.
8. Integrate back end
The following sample shows a complete back-end integration to save cards for purchase later:
1import "dotenv/config";2 import express from "express";3 const { PORT = 8888 } = process.env;4 const app = express();5 app.set("view engine", "ejs");6 app.use(express.static("public"));7 // Create setup token8 app.post("/api/vault/token", async (req, res) => {9 try {10 // Use your access token to securely generate a setup token11 // with an empty payment_source12 const vaultResponse = await fetch("https://api-m.sandbox.paypal.com/v3/vault/setup-tokens", {13 method: "POST",14 body: JSON.stringify({ payment_source: { card: {}} }),15 headers: {16 Authorization: 'Bearer ${ACCESS-TOKEN}',17 "PayPal-Request-Id": Date.now()18 }19 })20 // Return the reponse to the client21 res.json(vaultResponse);22 } catch (err) {23 res.status(500).send(err.message);24 }25 })26 // Create payment token from a setup token27 app.post("/api/vault/payment-token", async (req, res) => {28 try {29 const paymentTokenResult = await fetch(30 "https://api-m.sandbox.paypal.com/v3/vault/payment-tokens",31 {32 method: "POST",33 body: {34 payment_source: {35 token: {36 id: req.body.vaultSetupToken,37 type: "SETUP_TOKEN"38 }39 }40 },41 headers: {42 Authorization: 'Bearer ${ACCESS-TOKEN}',43 "PayPal-Request-Id": Date.now()44 }45 })46 const paymentMethodToken = paymentTokenResult.id47 const customerId = paymentTokenResult.customer.id48 await save(paymentMethodToken, customerId)49 res.json(captureData);50 } catch (err) {51 res.status(500).send(err.message);52 }53 })54 const save = async function(paymentMethodToken, customerId) {55 // Specify where to save the payment method token56 }57 app.listen(PORT, () => {58 console.log('Server listening at http://localhost:${PORT}/');59 })
9. Integrate front end
The following sample shows how a full script to save cards might appear in HTML:
1<!DOCTYPE html>2 <html>3 <head>4 <!-- Add meta tags for mobile and IE -->5 <meta charset="utf-8" />6 </head>7 <body>8 <!-- Include the PayPal JavaScript SDK -->9 <script10 src="https://www.paypal.com/sdk/js?components=card-fields&client-id=YOUR-CLIENT-ID¤cy=USD"></script>11 <div align="center"> or </div>12 <!-- Advanced credit and debit card payments form -->13 <div class='card_container'>14 <div id='card-number'></div>15 <div id='expiration-date'></div>16 <div id='cvv'></div>17 <div id='card-holder-name'></div>18 <label>19 <input type='checkbox' id='vault' name='vault' /> Vault20 </label>21 <br><br>22 <button value='submit' id='submit' class='btn'>Pay</button>23 </div> 24 <!-- Implementation -->25 <script>26 const cardFields = paypal.CardFields({27 createVaultSetupToken: async () => {28 // Call your server API to generate a vaultSetupToken29 // and return it here as a string30 const result = await fetch("https://example.com/api/vault/token", { method: "POST" })31 const { id } = await result.json();32 return id;33 },34 onApprove: async (data) => {35 // Only for 3D Secure36 if(data.liabilityShift){37 // Handle liability shift38 }39 const result = await fetch(40 "https://example.com/api/vault/payment-token",41 {42 method: "POST",43 body: JSON.stringify(data),44 }45 );46 // id is the payment ID47 const { id } = await result.json();48 },49 onError: (error) => console.error('Something went wrong:', error)50 })51 // Check eligibility and display advanced credit and debit card payments52 if (cardFields.isEligible()) {53 cardFields.NameField().render("#card-holder-name");54 cardFields.NumberField().render("#card-number");55 cardFields.ExpiryField().render("#expiration-date");56 cardFields.CVVField().render("#cvv");57 } else {58 // Handle the workflow when credit and debit cards are not available59 }60 const submitButton = document.getElementById("submit");61 submitButton.addEventListener("click", () => {62 cardFields63 .submit()64 .then(() => {65 console.log("submit was successful");66 })67 .catch((error) => {68 console.error("submit erred:", error);69 });70 });71 </script>72 </body>73 </html>
Note: This setup token is generated with an empty
payment_source
. TheCardFields
script uses this token to securely update the setup token with payment details.
10. Test saving cards
Use the following card numbers to test transactions in the sandbox:
Test number | Card type |
---|---|
371449635398431 | American Express |
376680816376961 | American Express |
36259600000004 | Diners Club |
6304000000000000 | Maestro |
5063516945005047 | Maestro |
2223000048400011 | Mastercard |
4005519200000004 | Visa |
4012000033330026 | Visa |
4012000077777777 | Visa |
4012888888881881 | Visa |
4217651111111119 | Visa |
4500600000000061 | Visa |
4772129056533503 | Visa |
4915805038587737 | Visa |
Test your integration to see if it saves credit and debit cards as expected. Any errors that occur appear in the onError
callback provided to the CardFields
component.
- Render the card fields.
- Create a save button in your UI.
- When the save button is selected:
- Create a setup token.
- Update the setup token with card details.
- On your server, use a server-side call to swap your setup token for a payment token from the Payment Method Tokens API.
- For a first-time payer, save the PayPal-generated
customer.id
. - For a returning payer, use the PayPal-generated
customer.id
to swap thesetup-token
for apayment-token
.
- For a first-time payer, save the PayPal-generated
- Save the
payment-token
for future use. - Show saved payment methods:
- Make a server-side call to the list all payment tokens endpoint. Include the PayPal-generated
customer.id
. - Style the card fields.
- Make a server-side call to the list all payment tokens endpoint. Include the PayPal-generated
Optional: Show saved payment methods
We recommend creating a page on your site where payers can see their saved payment methods as in the following example:
Next step
Go live with your integration.