Skip to main content

Managing Payment Cards

Learn how to create, retrieve, edit, and delete user payment cards.

Create Card

Create a new payment card for a user. The card starts with unrelated status and requires user verification.

Request

POST /rest/v1/card
Content-Type: application/json

Required Scope: cards - Required to manage user's payment cards

Request Body

{
"user_id": 13,
"accounts": [
{
"number": "EVP0123456789012",
"order": 1
}
],
"relation": {
"redirect_back_uri": "https://example.com/card-linked",
"locale": "en"
}
}
View Request Parameters
ParameterTypeRequiredDescription
user_idinteger✅ YesUser ID who owns the card
accountsarray⬜ OptionalCard-account relationships
relationobject⬜ OptionalVerification settings

Accounts Array

FieldTypeRequiredDescription
numberstring✅ YesAccount number
orderinteger✅ YesPriority order (1 = highest)

Relation Object

FieldTypeRequiredDescription
redirect_back_uristring⬜ OptionalReturn URL after verification
localestring⬜ OptionalUI language (ISO 2-letter code)

Response

{
"id": 1,
"status": "unrelated",
"user_id": 13,
"relation": {
"redirect_uri": "https://wallet.paysera.com/card/verify/123abc"
},
"accounts": [
{
"number": "EVP0123456789012",
"order": 1
}
]
}
View Response Fields
FieldTypeDescription
idintegerCard ID
statusstringCard status (unrelated, related, failed)
user_idintegerOwner user ID
relationobjectVerification details (if unrelated)
card_dataobjectCard details (if related)
accountsarrayLinked accounts
commission_ruleobjectCommission info (if related)

Verification Flow

Step-by-Step Process

  1. Create card via API
  2. Redirect user to relation.redirect_uri
  3. User enters card details securely
  4. Wait for verification (poll card status)
  5. Handle result (related or failed)
Implementation Example
async function createAndVerifyCard(userId, accountNumber) {
// 1. Create card
const card = await api.createCard({
user_id: userId,
accounts: [{
number: accountNumber,
order: 1
}],
relation: {
redirect_back_uri: 'https://example.com/card-success',
locale: 'en'
}
});

console.log(`Card ${card.id} created`);
console.log(`Redirect user to: ${card.relation.redirect_uri}`);

// 2. Redirect user (in browser)
window.location.href = card.relation.redirect_uri;

// 3. After redirect back, poll for status
return await pollCardStatus(card.id);
}

async function pollCardStatus(cardId, maxAttempts = 20, interval = 3000) {
for (let i = 0; i < maxAttempts; i++) {
const card = await api.getCard(cardId);

console.log(`Attempt ${i + 1}: Status = ${card.status}`);

if (card.status === 'related') {
console.log('✅ Card verified successfully!');
console.log(`Card: ${card.card_data.type} **** ${card.card_data.number}`);
return card;
}

if (card.status === 'failed') {
throw new Error('Card verification failed');
}

// Wait before next check
await new Promise(resolve => setTimeout(resolve, interval));
}

throw new Error('Card verification timeout');
}

Get Card

Retrieve a specific card by ID.

Request

GET /rest/v1/card/{cardId}

Parameters

ParameterTypeRequiredDescription
cardIdinteger✅ YesCard ID
Response Examples

Response (Unrelated Card)

{
"id": 1,
"status": "unrelated",
"user_id": 13,
"relation": {
"redirect_uri": "https://wallet.paysera.com/card/verify/123abc"
}
}

Response (Related Card)

{
"id": 1,
"status": "related",
"user_id": 13,
"related_at": 1735660800,
"card_data": {
"number": "1234",
"holder": "JOHN SMITH",
"type": "VISA",
"country": "LT",
"expiration": {
"year": 2027,
"month": 12
}
},
"accounts": [
{
"number": "EVP0123456789012",
"order": 1
}
],
"commission_rule": {
"percent": 2,
"fix": 10,
"fix_decimal": "0.10",
"min": 50,
"min_decimal": "0.50",
"max": 500,
"max_decimal": "5.00",
"currency": "EUR"
}
}

Example

async function getCardDetails(cardId) {
const card = await api.getCard(cardId);

console.log(`Card ID: ${card.id}`);
console.log(`Status: ${card.status}`);

if (card.status === 'related') {
console.log(`Type: ${card.card_data.type}`);
console.log(`Number: **** ${card.card_data.number}`);
console.log(`Expires: ${card.card_data.expiration.month}/${card.card_data.expiration.year}`);
console.log(`Holder: ${card.card_data.holder}`);

// Commission info
const comm = card.commission_rule;
console.log(`Commission: ${comm.percent}% + €${comm.fix_decimal}`);
console.log(`Min: €${comm.min_decimal}, Max: €${comm.max_decimal}`);
}

return card;
}

Get Cards

Retrieve all cards for a specific user.

Request

GET /rest/v1/cards?user_id={userId}&limit={limit}&offset={offset}
Query Parameters & Response

Query Parameters

ParameterTypeRequiredDescription
user_idinteger✅ YesUser ID
limitinteger⬜ OptionalMax results (default: 20, max: 200)
offsetinteger⬜ OptionalSkip results (default: 0)

Response

{
"cards": [
{
"id": 1,
"status": "related",
"user_id": 13,
"card_data": {
"number": "1234",
"type": "VISA"
}
},
{
"id": 2,
"status": "unrelated",
"user_id": 13
}
],
"_metadata": {
"total": 2,
"offset": 0,
"limit": 20
}
}

Example

async function listUserCards(userId) {
const response = await api.getCards({
user_id: userId,
limit: 50
});

console.log(`Total cards: ${response._metadata.total}`);

response.cards.forEach(card => {
const status = card.status === 'related' ? '✅' :
card.status === 'unrelated' ? '⏳' : '❌';
const cardInfo = card.card_data ?
`${card.card_data.type} **** ${card.card_data.number}` :
'Pending verification';

console.log(`${status} Card ${card.id}: ${cardInfo}`);
});

return response.cards;
}

Edit Card

Update card-account relationships. Only available for unrelated and related cards.

Request

PUT /rest/v1/card/{cardId}
Content-Type: application/json
{
"accounts": [
{
"number": "EVP0123456789012",
"order": 1
},
{
"number": "EVP9876543210123",
"order": 2
}
]
}
View Examples

Request Body

ParameterTypeRequiredDescription
accountsarray⬜ OptionalUpdated card-account links

Response: Returns updated card object.

Example: Change Default Account

async function setCardDefaultAccount(cardId, newAccountNumber) {
const updated = await api.editCard(cardId, {
accounts: [{
number: newAccountNumber,
order: 1
}]
});

console.log('✅ Default account updated');
return updated;
}

Example: Add Secondary Account

async function addSecondaryAccount(cardId, primaryAccount, secondaryAccount) {
const updated = await api.editCard(cardId, {
accounts: [
{
number: primaryAccount,
order: 1
},
{
number: secondaryAccount,
order: 2
}
]
});

console.log('✅ Secondary account added');
return updated;
}

Delete Card

Remove a card from the system. Available for all statuses.

Request

DELETE /rest/v1/card/{cardId}

Response: HTTP/1.1 204 No Content

Example

async function deleteCard(cardId) {
try {
await api.deleteCard(cardId);
console.log(`✅ Card ${cardId} deleted successfully`);
} catch (error) {
if (error.code === 'not_found') {
console.error('Card not found');
} else {
console.error('Delete failed:', error.message);
}
throw error;
}
}

Advanced Topics

Complete Card Manager Class

Full Implementation

class CardManager {
constructor(api, userId) {
this.api = api;
this.userId = userId;
this.cards = [];
}

async initialize() {
await this.loadCards();
}

async loadCards() {
const response = await this.api.getCards({
user_id: this.userId,
limit: 100
});
this.cards = response.cards;
return this.cards;
}

async addCard(accountNumber, redirectUri) {
const card = await this.api.createCard({
user_id: this.userId,
accounts: [{
number: accountNumber,
order: 1
}],
relation: {
redirect_back_uri: redirectUri,
locale: 'en'
}
});

this.cards.push(card);
return card;
}

async waitForVerification(cardId, timeout = 60000) {
const startTime = Date.now();
const interval = 3000;

while (Date.now() - startTime < timeout) {
const card = await this.api.getCard(cardId);

if (card.status === 'related') {
const index = this.cards.findIndex(c => c.id === cardId);
if (index >= 0) {
this.cards[index] = card;
}
return card;
}

if (card.status === 'failed') {
throw new Error('Card verification failed');
}

await new Promise(resolve => setTimeout(resolve, interval));
}

throw new Error('Card verification timeout');
}

async updateCardPriority(cardId, newOrder) {
const card = this.cards.find(c => c.id === cardId);
if (!card) {
throw new Error('Card not found');
}

const updated = await this.api.editCard(cardId, {
accounts: card.accounts.map(acc => ({
...acc,
order: newOrder
}))
});

const index = this.cards.findIndex(c => c.id === cardId);
this.cards[index] = updated;

return updated;
}

async removeCard(cardId) {
await this.api.deleteCard(cardId);
this.cards = this.cards.filter(c => c.id !== cardId);
}

getRelatedCards() {
return this.cards.filter(c => c.status === 'related');
}

getDefaultCard() {
const related = this.getRelatedCards();
if (related.length === 0) return null;

return related.reduce((prev, curr) => {
const prevOrder = prev.accounts[0]?.order || 999;
const currOrder = curr.accounts[0]?.order || 999;
return currOrder < prevOrder ? curr : prev;
});
}

async setDefaultCard(cardId) {
const related = this.getRelatedCards();

for (const card of related) {
const newOrder = card.id === cardId ? 1 :
(card.accounts[0]?.order || 10) + 1;

if (card.accounts[0]?.order !== newOrder) {
await this.updateCardPriority(card.id, newOrder);
}
}

await this.loadCards();
}

displayCards() {
console.log(`\n=== Cards for User ${this.userId} ===`);

if (this.cards.length === 0) {
console.log('No cards found');
return;
}

this.cards.forEach((card, index) => {
const status = {
'related': '✅',
'unrelated': '⏳',
'failed': '❌'
}[card.status] || '❓';

let cardInfo = `Card #${index + 1} (ID: ${card.id})`;

if (card.card_data) {
cardInfo += ` - ${card.card_data.type} **** ${card.card_data.number}`;
if (card.accounts[0]?.order === 1) {
cardInfo += ' [DEFAULT]';
}
} else {
cardInfo += ' - Pending verification';
}

console.log(`${status} ${cardInfo}`);
});
}
}

// Usage Example
const manager = new CardManager(api, 13);
await manager.initialize();

// Add new card
const newCard = await manager.addCard('EVP0123456789012', 'https://example.com/success');
console.log(`Redirect to: ${newCard.relation.redirect_uri}`);

// Wait for verification
try {
const verified = await manager.waitForVerification(newCard.id);
console.log('✅ Card verified!');
} catch (error) {
console.error('Verification failed:', error.message);
}

// Display all cards
manager.displayCards();

// Set as default
await manager.setDefaultCard(newCard.id);
Best Practices

1. Handle Verification Timeout

async function verifyCardWithTimeout(cardId, maxWaitTime = 300000) {
const timeout = new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), maxWaitTime)
);

const verification = pollCardStatus(cardId);

try {
return await Promise.race([verification, timeout]);
} catch (error) {
if (error.message === 'Timeout') {
console.log('Verification taking longer than expected');
}
throw error;
}
}

2. Validate Before Operations

async function ensureCardReady(cardId) {
const card = await api.getCard(cardId);

if (card.status !== 'related') {
throw new Error(
`Card is ${card.status}. Only related cards can be used for deposits.`
);
}

return card;
}

3. Cache Card Data

class CardCache {
constructor(api, ttl = 600000) { // 10 minutes
this.api = api;
this.ttl = ttl;
this.cache = new Map();
}

async getCard(cardId) {
const cached = this.cache.get(cardId);

if (cached && Date.now() - cached.timestamp < this.ttl) {
return cached.data;
}

const card = await this.api.getCard(cardId);
this.cache.set(cardId, {
data: card,
timestamp: Date.now()
});

return card;
}

invalidate(cardId) {
this.cache.delete(cardId);
}
}
Common Issues

Issue: Card Stuck in "unrelated"

Cause: User didn't complete verification

Solution:

const card = await api.getCard(cardId);
if (card.status === 'unrelated') {
console.log('Verification incomplete');
console.log(`Please visit: ${card.relation.redirect_uri}`);
}

Issue: Cannot Edit Card

Cause: Card status is failed

Solution:

try {
await api.editCard(cardId, {...});
} catch (error) {
if (error.code === 'invalid_state') {
console.error('Cannot edit failed cards. Please delete and create new.');
}
}

Issue: Multiple Default Cards

Cause: Incorrect order management

Solution:

async function fixDefaultCards(userId) {
const cards = await api.getCards({ user_id: userId });
const related = cards.cards.filter(c => c.status === 'related');

for (let i = 0; i < related.length; i++) {
await api.editCard(related[i].id, {
accounts: [{
...related[i].accounts[0],
order: i + 1
}]
});
}
}

Next Steps