Skip to main content

Batch Transfers

Learn how to create and manage multiple transfers efficiently using batch operations.

Overview​

Batch transfers allow you to create multiple transfers efficiently, perfect for:

  • Salary payments to employees
  • Bulk vendor payments
  • Commission distributions
  • Dividend payments
API Limitation

The Transfer API does not support creating multiple transfers in a single request. Each transfer must be created individually using POST /transfer/rest/v1/transfers. This guide shows you how to efficiently process multiple transfers using sequential or parallel requests.

Creating Batch Transfers​

Create transfers one by one (safer, easier error handling):

const axios = require('axios');
const crypto = require('crypto');

const API_BASE = 'https://wallet.paysera.com/transfer/rest/v1';
const CLIENT_ID = 'wkVd93h2uS';
const MAC_KEY = 'your_mac_key';

function generateMacAuth(method, uri, host) {
const timestamp = Math.floor(Date.now() / 1000);
const nonce = crypto.randomBytes(16).toString('hex');
const normalizedString = [timestamp, nonce, method, uri, host, '443', ''].join('\n') + '\n';
const mac = crypto.createHmac('sha256', MAC_KEY).update(normalizedString).digest('base64');
return `MAC id="${CLIENT_ID}", ts="${timestamp}", nonce="${nonce}", mac="${mac}"`;
}

const salaryPayments = [
{
amount: { amount: '1500.00', currency: 'EUR' },
beneficiary: {
type: 'paysera',
name: 'Alice Johnson',
paysera_account: { account_number: 'EVP1111111111' }
},
payer: { account_number: 'EVP9876543210' },
purpose: { details: 'Salary - October 2024' }
},
{
amount: { amount: '1800.00', currency: 'EUR' },
beneficiary: {
type: 'paysera',
name: 'Bob Smith',
paysera_account: { account_number: 'EVP2222222222' }
},
payer: { account_number: 'EVP9876543210' },
purpose: { details: 'Salary - October 2024' }
},
{
amount: { amount: '2000.00', currency: 'EUR' },
beneficiary: {
type: 'bank',
name: 'Carol Williams',
bank_account: { iban: 'LT123456789012345678' }
},
payer: { account_number: 'EVP9876543210' },
purpose: { details: 'Salary - October 2024' }
}
];

async function processBatchSequential() {
const results = [];

for (const transfer of salaryPayments) {
try {
const response = await axios.post(
`${API_BASE}/transfers`,
transfer,
{
headers: {
'Authorization': generateMacAuth('POST', '/transfer/rest/v1/transfers', 'wallet.paysera.com'),
'Content-Type': 'application/json'
}
}
);

results.push({ success: true, id: response.data.id, data: response.data });
console.log(`Transfer created: ${response.data.id}`);

} catch (error) {
results.push({ success: false, error: error.response?.data || error.message });
console.error(`Transfer failed:`, error.response?.data);
}
}

return results;
}

processBatchSequential().then(results => {
const successful = results.filter(r => r.success).length;
const failed = results.filter(r => !r.success).length;
console.log(`Completed: ${successful} successful, ${failed} failed`);
});

Error Handling​

When processing multiple transfers, implement robust error handling:

async function createTransferWithErrorHandling(transferData) {
try {
const response = await axios.post(
`${API_BASE}/transfers`,
transferData,
{
headers: {
'Authorization': generateMacAuth('POST', '/transfer/rest/v1/transfers', 'wallet.paysera.com'),
'Content-Type': 'application/json'
}
}
);

return { success: true, id: response.data.id, data: response.data };

} catch (error) {
const errorData = error.response?.data || {};

// Handle specific error types
if (errorData.error === 'insufficient_funds') {
return {
success: false,
error: 'insufficient_funds',
message: 'Not enough balance to complete transfer',
available: errorData.available,
required: errorData.required
};
}

if (errorData.error === 'invalid_beneficiary') {
return {
success: false,
error: 'invalid_beneficiary',
message: 'Invalid beneficiary account',
field: errorData.field
};
}

return {
success: false,
error: errorData.error || 'unknown_error',
message: errorData.error_description || error.message
};
}
}

// Process batch with error tracking
async function processBatchWithErrors() {
const results = [];

for (const transfer of salaryPayments) {
const result = await createTransferWithErrorHandling(transfer);
results.push(result);

if (!result.success) {
console.error(`Transfer failed: ${result.message}`);
// Send notification, log to database, etc.
}
}

return results;
}

Processing Batch Transfers​

After creating batch transfers, you can process them individually or all at once.

Reserve and register all transfers in the batch:

const axios = require('axios');
const crypto = require('crypto');

const CLIENT_ID = 'wkVd93h2uS';
const MAC_KEY = 'your_mac_key';

function generateMacAuth(method, uri, host) {
const timestamp = Math.floor(Date.now() / 1000);
const nonce = crypto.randomBytes(16).toString('hex');
const normalizedString = [timestamp, nonce, method, uri, host, '443', ''].join('\n') + '\n';
const mac = crypto.createHmac('sha256', MAC_KEY).update(normalizedString).digest('base64');
return `MAC id="${CLIENT_ID}", ts="${timestamp}", nonce="${nonce}", mac="${mac}"`;
}

async function processBatchTransfers(transferIds, password) {
const API_BASE = 'https://wallet.paysera.com/transfer/rest/v1';

for (const transferId of transferIds) {
try {
// Reserve
await axios.put(
`${API_BASE}/transfers/${transferId}/reserve`,
{},
{ headers: { 'Authorization': generateMacAuth('PUT', `/transfer/rest/v1/transfers/${transferId}/reserve`, 'wallet.paysera.com') } }
);

// Provide password
await axios.put(
`${API_BASE}/transfers/${transferId}/provide-password`,
{ password },
{ headers: { 'Authorization': generateMacAuth('PUT', `/transfer/rest/v1/transfers/${transferId}/provide-password`, 'wallet.paysera.com') } }
);

// Register
await axios.put(
`${API_BASE}/transfers/${transferId}/register`,
{},
{ headers: { 'Authorization': generateMacAuth('PUT', `/transfer/rest/v1/transfers/${transferId}/register`, 'wallet.paysera.com') } }
);

console.log(`Transfer ${transferId} completed`);
} catch (error) {
console.error(`Transfer ${transferId} failed:`, error.response?.data);
}
}
}

// Usage
const transferIds = ['100001', '100002', '100003'];
processBatchTransfers(transferIds, 'your_password');

Retrieving Batch Transfer Status​

Request:

GET /transfer/rest/v1/transfers?created_from=1729425600&created_to=1729429200&limit=100 HTTP/1.1
Host: wallet.paysera.com
Authorization: MAC id="wkVd93h2uS", ts="1343811600", nonce="nQnNaSNyubfPErjRO55yaaEYo9YZfKHN", mac="Bp22nWw9qFsz7ux5xOYkCIYJjXAz8mhxTSfJsoOKV3A="

Response:

{
"_links": {
"self": {"href": "/transfer/rest/v1/transfers?created_from=1729425600&created_to=1729429200&limit=100"}
},
"_embedded": {
"transfers": [
{
"id": "100001",
"status": "done",
"amount": {
"amount": "1500.00",
"currency": "EUR"
},
"beneficiary": {
"type": "paysera",
"name": "Alice Johnson"
},
"performed_at": 1729426000
},
{
"id": "100002",
"status": "done",
"amount": {
"amount": "1800.00",
"currency": "EUR"
},
"beneficiary": {
"type": "paysera",
"name": "Bob Smith"
},
"performed_at": 1729426010
},
{
"id": "100003",
"status": "failed",
"amount": {
"amount": "2000.00",
"currency": "EUR"
},
"beneficiary": {
"type": "bank",
"name": "Carol Williams"
},
"failure_reason": "invalid_account_number"
}
]
},
"total": 3
}

Complete Batch Transfer Workflow​

Here's a complete example for salary payments:

const axios = require('axios');
const crypto = require('crypto');

class BatchTransferManager {
constructor(clientId, macKey) {
this.API_BASE = 'https://wallet.paysera.com/transfer/rest/v1';
this.clientId = clientId;
this.macKey = macKey;
}

generateMacAuth(method, uri, host) {
const timestamp = Math.floor(Date.now() / 1000);
const nonce = crypto.randomBytes(16).toString('hex');
const normalizedString = [timestamp, nonce, method, uri, host, '443', ''].join('\n') + '\n';
const mac = crypto.createHmac('sha256', this.macKey).update(normalizedString).digest('base64');
return `MAC id="${this.clientId}", ts="${timestamp}", nonce="${nonce}", mac="${mac}"`;
}

async createBatchTransfer(employees, payerAccount) {
const createdTransfers = [];

for (const emp of employees) {
const transferData = {
amount: {
amount: emp.salary.toFixed(2),
currency: 'EUR'
},
beneficiary: {
type: 'paysera',
name: emp.name,
paysera_account: {
account_number: emp.accountNumber
}
},
payer: {
account_number: payerAccount
},
purpose: {
details: `Salary - ${new Date().toISOString().slice(0, 7)}`
}
};

try {
const response = await axios.post(
`${this.API_BASE}/transfers`,
transferData,
{
headers: {
'Authorization': this.generateMacAuth('POST', '/transfer/rest/v1/transfers', 'wallet.paysera.com'),
'Content-Type': 'application/json'
}
}
);

createdTransfers.push(response.data);
} catch (error) {
console.error(`Failed to create transfer for ${emp.name}:`, error.response?.data);
}
}

return createdTransfers;
}

async processBatch(transfers, password) {
const results = {
successful: [],
failed: []
};

for (const transfer of transfers) {
try {
// Reserve
await this.reserveTransfer(transfer.id);

// Provide password
await this.providePassword(transfer.id, password);

// Register
await this.registerTransfer(transfer.id);

results.successful.push({
id: transfer.id,
beneficiary: transfer.beneficiary.name,
amount: transfer.amount
});

} catch (error) {
results.failed.push({
id: transfer.id,
beneficiary: transfer.beneficiary.name,
error: error.response?.data?.error || error.message
});
}
}

return results;
}

async reserveTransfer(transferId) {
return axios.put(
`${this.API_BASE}/transfers/${transferId}/reserve`,
{},
{ headers: { 'Authorization': this.generateMacAuth('PUT', `/transfer/rest/v1/transfers/${transferId}/reserve`, 'wallet.paysera.com') } }
);
}

async providePassword(transferId, password) {
return axios.put(
`${this.API_BASE}/transfers/${transferId}/provide-password`,
{ password },
{
headers: {
'Authorization': this.generateMacAuth('PUT', `/transfer/rest/v1/transfers/${transferId}/provide-password`, 'wallet.paysera.com'),
'Content-Type': 'application/json'
}
}
);
}

async registerTransfer(transferId) {
return axios.put(
`${this.API_BASE}/transfers/${transferId}/register`,
{},
{ headers: { 'Authorization': this.generateMacAuth('PUT', `/transfer/rest/v1/transfers/${transferId}/register`, 'wallet.paysera.com') } }
);
}
}

// Usage
const manager = new BatchTransferManager('wkVd93h2uS', 'your_mac_key');

const employees = [
{ name: 'Alice Johnson', accountNumber: 'EVP1111111111', salary: 1500, employeeId: 'EMP001' },
{ name: 'Bob Smith', accountNumber: 'EVP2222222222', salary: 1800, employeeId: 'EMP002' },
{ name: 'Carol Williams', accountNumber: 'LT123456789012345678', salary: 2000, employeeId: 'EMP003' }
];

async function processPayroll() {
// Create batch
const transfers = await manager.createBatchTransfer(employees, 'EVP9876543210');
console.log(`Created ${transfers.length} transfers`);

// Process batch
const results = await manager.processBatch(transfers, 'your_password');

console.log(`Successful: ${results.successful.length}`);
console.log(`Failed: ${results.failed.length}`);

if (results.failed.length > 0) {
console.log('Failed transfers:', results.failed);
}
}

processPayroll();

Batch Size Limits​

OperationMaximum Batch Size
Create Transfers100 transfers per request
Process Transfers50 transfers per minute
Query Transfers100 results per page

Common Errors​

{
"error": "batch_size_exceeded",
"error_description": "Maximum 100 transfers allowed per batch",
"max_allowed": 100,
"provided": 150
}

Next Steps​