On this page
No Headings
Last updated: July 7, 2026
Save customer credit or debit cards to charge them at a later 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.
Use the iOS SDK to save a payer's credit or debit cards.
PayPal encrypts payment method information and stores it in a digital vault for that customer.
The checkout process is now shorter because it uses saved payment information.
Set up your sandbox and live business accounts to save payment methods:
Add the CardPayments package dependency for your app using Swift Package Manager or CocoaPods:
https://github.com/paypal/paypal-ios/ as the repository URL.CardPayments framework.Add a button to your app's UI to save a card.
Button("Save Card") {
// Create a setup token on server-side (see next step)
}On your server, you need to create a setup token.
From your server, create a setup token for cards that have:
Generate a setup token with an empty card as its payment_source. Later, you will attach card details to the setup token in the SDK.
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": {
"card": {}
}
}'For a returning payer with previously stored payment sources, include the PayPal-generated customerId in the request to save a different payment method for the same customer.
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": {
"card": {}
},
"customer": {
"id": "llPdZofmwR"
}
}'ACCESS-TOKEN to your sandbox app's access token.REQUEST-ID to a set of unique alphanumeric characters such as a timestamp.The card payment source in the response is empty. You'll attach additional information to the setup token in the steps that follow.
{
"id": "86S246162E316080B",
"customer": {
"id": "ZUzLMNMrJD"
},
"status": "CREATED",
"payment_source": {
"card": {}
},
"links": [
{
"href": "https://api-m.sandbox.paypal.com/v3/vault/setup-tokens/86S246162E316080B",
"rel": "self",
"method": "GET",
"encType": "application/json"
}
]
}id is the setup token ID.customer.id.In the iOS SDK, you need to create a CardVaultRequest to pass into the vault function.
The vault method:
Build a Card object with the buyer's card details:
let card = Card(
number: "4005519200000004",
expirationMonth: "01",
expirationYear: "2025",
securityCode: "123",
cardholderName: "Jane Smith",
billingAddress: Address(
addressLine1: "123 Main St.",
addressLine2: "Apt. 1A",
locality: "City",
region: "IL",
postalCode: "12345",
countryCode: "US"
)
)Collecting a billing address can reduce the probability of an authentication challenge.
Build a CardVaultRequest with the card object and your SETUP-TOKEN:
let cardVaultRequest = CardVaultRequest(
setupTokenID: "SETUPTOKEN",
card: card
)After your CardVaultRequest has the card details and setupTokenID, call cardClient.vault() to process the payment.
let config = CoreConfig(clientID: "CLIENT_ID", environment: .live)
let cardClient = CardClient(config: config)
cardClient.vaultDelegate = self
cardClient.vault(cardVaultRequest)extension MyViewController: CardVaultDelegate {
// MARK: - CardVaultDelegate
func card(_ cardClient: CardClient, didFinishWithVaultResult vaultResult: CardVaultResult) {
// Vaulting has been approved and is ready to be used to create a paymentToken or vaultID
// The CardVaultResult contains a setupTokenID string used to create the paymentToken from your server
// Make sure you pass the cardVaultResult.setupTokenID to your server
}
func card(_ cardClient: CardClient, didFinishWithVaultError error: CoreSDKError) {
// Handle the error by accessing `error.localizedDescription`
}
func cardVaultDidCancel(_ cardClient: CardClient) {
// 3D Secure auth was canceled by the user
}
func cardThreeDSecureWillLaunch(_ cardClient: CardClient) {
// 3D Secure auth will launch
}
func cardThreeDSecureDidFinish(_ cardClient: CardClient) {
// 3D Secure auth finished
}
}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.When a payer returns to your app, you can show the payer's saved payment methods with the Payment Method Tokens API.
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.
curl -L -X GET "https://api-m.sandbox.paypal.com/v3/vault/payment-tokens?customer_id=CUSTOMER-ID" \
-H "Content-Type: application/json" \
-H "Accept-Language: en_US" \
-H "Authorization: Bearer ACCESS-TOKEN" \
-H "PayPal-Request-Id: REQUEST-ID"CUSTOMER-ID to a PayPal-generated customer ID.ACCESS-TOKEN to your sandbox app's access token.REQUEST-ID to a set of unique alphanumeric characters such as a time stamp.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.
We recommend showing the card brand and the last 4 digits.
Run the following tests in the PayPal sandbox to ensure that you can save cards.
api-m.sandbox.paypal.com to api-m.paypal.com when going live with your integration.payment_source.paypal.attributes.vault for subsequent or recurring transactions.