Skip to main content

Token Payment

Introduction

Token payment enables a seamless experience for repeat purchases. Customers can save their payment method once and reuse it for future transactions, reducing friction and increasing conversion rates.

User Consent

Always ask customers for explicit consent before saving their card. This builds trust and ensures compliance.

1. Save Card for Future Use

During checkout, offer customers the option to save their card for future purchases.

<div className="flex items-center gap-2 mb-4">
<input type="checkbox" id="save-card" name="save-card" className="rounded border-gray-300" />
<label htmlFor="save-card" className="text-sm text-gray-700">Save card for future purchases</label>
</div>

When submitting the payment, include the consent in the SDK call:

const saveCard = document.getElementById('save-card').checked;
const result = await secureForm.submit({ saveToken: saveCard });
// Send result.vault_form_token to your server to create the payment

To save a card during payment, your server calls the Payment API with the save_token flag:

curl -X POST 'https://api.purse-sandbox.com/payment/v2/payments' \
--header 'Content-Type: application/json' \
--header "x-api-key: ${API_KEY}" \
--header "Authorization: Bearer ${ACCESS_TOKEN}" \
--data-raw '{
"entity_id": "{{entity_id}}",
"amount": 100,
"currency": "EUR",
"order": { },
"split": [
{
"vault_form_token": "{{vault_form_token}}",
"save_token": true
}
],
"browser": { }
}'
API Endpoint

2. Pay with a Saved Card

For subsequent purchases, customers can select a saved card. Only CVV is required for extra security.

Your server retrieves the saved tokens for the customer:

curl -X GET 'https://api.purse-sandbox.com/wallet/v3/wallet/merchants/{{merchant_id}}/customers/{{customer_reference}}/tokens' \
--header 'Content-Type: application/json' \
--header "Authorization: Bearer ${ACCESS_TOKEN}"

Display saved cards for selection (client-side, using data returned by your server):

function displaySavedCards(tokens) {
tokens.forEach(token => {
const button = document.createElement('button');
button.textContent = token.description.display_token || token.id;
button.onclick = () => {
startPaymentFlow(token.id);
};
document.getElementById('saved-cards-list').appendChild(button);
});
}

Initialize SecureFields for CVV input (client-side):

import { loadSecureFields } from '@purse-eu/web-sdk';

let secureForm;
async function initCvvOnlyForm() {
const sf = await loadSecureFields('sandbox');
secureForm = await sf.initSecureFields({
tenantId: '[your vault tenant_id]',
apiKey: '[your purse api_key (optional)]',
config: {
fields: {
cvv: {
target: 'cvv-only-placeholder',
placeholder: 'ex: 123',
ariaLabel: 'CVV',
iframeTitle: 'CVV',
}
},
styles: {
color: '#181818'
}
}
});
await secureForm.render();
}

HTML placeholder for CVV field:

<div>
<label id="cvv-only-label" class="block text-sm font-medium text-gray-700 mb-1">CVV</label>
<div id="cvv-only-placeholder" aria-labelledby="cvv-only-label" class="w-full rounded-lg border border-gray-300 bg-white px-2 py-2"></div>
</div>

Submit the CVV (client-side), then send the resulting vault_form_token to your server to create the payment:

async function startPaymentFlow(selectedWalletTokenId) {
await initCvvOnlyForm();
const result = await secureForm.submit({ saveToken: false });
// Send result.vault_form_token and selectedWalletTokenId to your server
await fetch('/your-server/pay-with-token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
vaultFormToken: result.vault_form_token,
walletTokenId: selectedWalletTokenId,
}),
});
}

Your server then calls the Payment API:

curl -X POST 'https://api.purse-sandbox.com/payment/v2/payments' \
--header 'Content-Type: application/json' \
--header "x-api-key: ${API_KEY}" \
--header "Authorization: Bearer ${ACCESS_TOKEN}" \
--data-raw '{
"entity_id": "{{entity_id}}",
"amount": 100,
"currency": "EUR",
"split": [
{
"vault_form_token": "{{vault_form_token}}",
"wallet_token": "{{wallet_token_id}}"
}
],
"browser": { }
}'
API Endpoint
  • Endpoint: /wallet/v3/wallet/merchants/{merchant_id}/customers/{customer_reference}/tokenslist tokens
  • Endpoint: /payment/v2/paymentscreate payment
  • Method: GET (list), POST (payment)

3. Delete a Saved Card

Customers may want to remove a saved card for privacy or security reasons. Your server calls the Wallet API to delete the token:

curl -X DELETE 'https://api.purse-sandbox.com/wallet/v3/wallet/merchants/{{merchant_id}}/customers/{{customer_reference}}/tokens/{{token_id}}' \
--header 'Content-Type: application/json' \
--header "Authorization: Bearer ${ACCESS_TOKEN}"
API Endpoint
  • Endpoint: /wallet/v3/wallet/merchants/{merchant_id}/customers/{customer_reference}/tokens/{id}
  • Method: DELETE
  • API Reference

Further Information

For more details about tokenization and payment tokens, see the Token Payment Glossary.

Next Steps

Full Working Example

The flow below illustrates a complete token payment integration: the client-side handles the UI and SecureFields, while all API calls are made server-side.

Client-side — Display saved cards and collect CVV:

import { loadSecureFields } from '@purse-eu/web-sdk';

let secureForm;

async function initCvvOnlyForm() {
const sf = await loadSecureFields('sandbox');
secureForm = await sf.initSecureFields({
tenantId: '[your vault tenant_id]',
apiKey: '[your purse api_key (optional)]',
config: {
fields: {
cvv: {
target: 'cvv-only-placeholder',
placeholder: 'ex: 123',
ariaLabel: 'CVV',
iframeTitle: 'CVV',
}
},
styles: { color: '#181818' }
}
});
await secureForm.render();
}

async function displaySavedCards() {
// Fetch saved tokens from your server (your server calls the Wallet API)
const response = await fetch('/your-server/tokens');
const tokens = await response.json();

tokens.forEach(token => {
const button = document.createElement('button');
button.textContent = token.description.display_token || token.id;
button.onclick = async () => {
await initCvvOnlyForm();
const result = await secureForm.submit({ saveToken: false });
// Send vault_form_token and token id to your server
await fetch('/your-server/pay-with-token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
vaultFormToken: result.vault_form_token,
walletTokenId: token.id,
}),
});
};
document.getElementById('saved-cards-list').appendChild(button);
});
}

async function deleteToken(tokenId) {
await fetch(`/your-server/tokens/${tokenId}`, { method: 'DELETE' });
}

Server-side — Wallet and Payment API calls:

# List saved tokens
curl -X GET 'https://api.purse-sandbox.com/wallet/v3/wallet/merchants/{{merchant_id}}/customers/{{customer_reference}}/tokens' \
--header 'Content-Type: application/json' \
--header "Authorization: Bearer ${ACCESS_TOKEN}"

# Create payment with saved token
curl -X POST 'https://api.purse-sandbox.com/payment/v2/payments' \
--header 'Content-Type: application/json' \
--header "x-api-key: ${API_KEY}" \
--header "Authorization: Bearer ${ACCESS_TOKEN}" \
--data-raw '{
"entity_id": "{{entity_id}}",
"amount": 100,
"currency": "EUR",
"split": [
{
"vault_form_token": "{{vault_form_token}}",
"wallet_token": "{{wallet_token_id}}"
}
],
"browser": { }
}'

# Delete a token
curl -X DELETE 'https://api.purse-sandbox.com/wallet/v3/wallet/merchants/{{merchant_id}}/customers/{{customer_reference}}/tokens/{{token_id}}' \
--header 'Content-Type: application/json' \
--header "Authorization: Bearer ${ACCESS_TOKEN}"

HTML placeholders:

<div id="saved-cards-list"></div>
<div>
<label id="cvv-only-label" class="block text-sm font-medium text-gray-700 mb-1">CVV</label>
<div id="cvv-only-placeholder" aria-labelledby="cvv-only-label" class="w-full rounded-lg border border-gray-300 bg-white px-2 py-2"></div>
</div>