Skip to main content

Transaction Requests

Transaction Requests allow you to request a payment from a specific user. Instead of creating a payment, you create a request that the user must approve and pay.

Use Cases

Invoices, Bill splitting, Debt collection, Service charges, Peer-to-peer payments

How Requests Work

Key difference from regular payments: User initiates the payment, not you.


Creating a Request

View Request Examples

Basic Request

POST /rest/v1/transaction-request

{
"description": "Invoice #INV-2024-001",
"amount": 10000, // 100 EUR
"currency": "EUR",
"recipient_wallet_id": 14471, // User's wallet to charge
"beneficiary_wallet_id": 11111 // Your wallet to receive
}

Response:

{
"request_id": 12345,
"status": "pending",
"created_at": 1698675600
}

Request with Details

{
"description": "Monthly service fee - October 2024",
"amount": 2500,
"currency": "EUR",
"recipient_wallet_id": 14471,
"beneficiary_wallet_id": 11111,
"due_date": 1701360000, // Deadline timestamp
"items": [
{
"title": "Service subscription",
"price": 2000,
"quantity": 1
},
{
"title": "Extra features",
"price": 500,
"quantity": 1
}
],
"reference": "SUB-2024-10",
"callback_uri": "https://yoursite.com/request-callback"
}

Request Statuses

StatusDescriptionNext States
pendingCreated, waiting for useraccepted, rejected, expired
acceptedUser accepted, payment createdpaid, canceled
paidPayment completed-
rejectedUser declined request-
expiredPassed due date-
canceledYou canceled request-

Notifying Users

View Notification Methods

Via Email

const request = await createTransactionRequest({...});

await sendEmail({
to: user.email,
subject: 'Payment Request',
body: `
You have a payment request:

Description: ${request.description}
Amount: ${request.amount / 100} EUR

View and pay: https://www.paysera.com/wallet/request/${request.request_id}
`
});

Via In-App Notification

await createNotification({
user_id: userId,
type: 'payment_request',
title: 'New Payment Request',
message: request.description,
amount: request.amount,
action_url: getRequestUrl(request.request_id)
});

Request URL Format

https://www.paysera.com/wallet/request/{request_id}

Handling Request Status

View Status Handling

Check Request Status

GET /rest/v1/transaction-request/{request_id}

Response:

{
"request_id": 12345,
"status": "paid",
"description": "Invoice #INV-2024-001",
"amount": 10000,
"currency": "EUR",
"transaction_key": "pDAlAZ3z", // If paid
"created_at": 1698675600,
"paid_at": 1698676800
}

Via Callback

app.post('/request-callback', async (req, res) => {
const { request_id, status, transaction_key } = req.body;

if (status === 'paid') {
await markInvoicePaid(request_id, transaction_key);
await sendReceipt(request_id);
} else if (status === 'rejected') {
await handleRejection(request_id);
}

res.status(200).send('OK');
});

Use Cases

View Real-World Use Cases

Use Case 1: Invoice Payment

async function sendInvoice(invoice) {
// 1. Create request
const request = await createTransactionRequest({
description: `Invoice ${invoice.number}`,
amount: invoice.total,
currency: 'EUR',
recipient_wallet_id: invoice.customer_wallet,
beneficiary_wallet_id: companyWallet,
due_date: invoice.due_date,
items: invoice.items,
reference: invoice.number,
callback_uri: `${siteUrl}/invoice-callback`
});

// 2. Save request reference
await updateInvoice(invoice.id, {
request_id: request.request_id,
status: 'sent'
});

// 3. Notify customer
await sendInvoiceEmail(invoice.customer_email, {
invoice: invoice,
paymentUrl: getRequestUrl(request.request_id)
});

return request;
}

Use Case 2: Bill Splitting

async function splitBill(bill, participants) {
const perPerson = Math.floor(bill.total / participants.length);

const requests = await Promise.all(
participants.map(person =>
createTransactionRequest({
description: `Your share: ${bill.description}`,
amount: perPerson,
currency: 'EUR',
recipient_wallet_id: person.wallet_id,
beneficiary_wallet_id: organizer.wallet_id,
reference: `SPLIT-${bill.id}-${person.id}`
})
)
);

// Notify everyone
for (const [idx, person] of participants.entries()) {
await sendNotification(person.user_id, {
message: `Your share: ${perPerson/100} EUR`,
paymentUrl: getRequestUrl(requests[idx].request_id)
});
}

return requests;
}

Use Case 3: Subscription Renewal

async function requestSubscriptionPayment(subscription) {
const request = await createTransactionRequest({
description: `${subscription.plan} - Monthly`,
amount: subscription.price,
currency: 'EUR',
recipient_wallet_id: subscription.user_wallet,
beneficiary_wallet_id: companyWallet,
due_date: subscription.next_billing_date,
reference: `SUB-${subscription.id}`
});

await sendEmail({
to: subscription.user_email,
template: 'subscription_renewal',
data: {
plan: subscription.plan,
amount: subscription.price / 100,
payUrl: getRequestUrl(request.request_id)
}
});
}

Advanced Topics

Canceling Requests

Cancel pending request:

DELETE /rest/v1/transaction-request/{request_id}

When to cancel: Invoice voided, Service canceled, Request sent by mistake, User asked to cancel

Example

async function cancelInvoice(invoiceId) {
const invoice = await getInvoice(invoiceId);

if (invoice.request_id) {
await cancelTransactionRequest(invoice.request_id);
}

await updateInvoice(invoiceId, { status: 'canceled' });
await sendCancellationEmail(invoice.customer_email);
}
Monitoring Requests

Track Request Metrics

class RequestMonitor {
async getStats(period) {
return {
created: await this.countByStatus('pending', period),
paid: await this.countByStatus('paid', period),
rejected: await this.countByStatus('rejected', period),
expired: await this.countByStatus('expired', period),
paymentRate: await this.calculatePaymentRate(period),
averageTime: await this.averagePaymentTime(period)
};
}

async calculatePaymentRate(period) {
const total = await this.countTotal(period);
const paid = await this.countByStatus('paid', period);
return (paid / total * 100).toFixed(2);
}
}

Send Reminders

async function sendPaymentReminders() {
// Get pending requests near due date
const pending = await db.query(`
SELECT * FROM transaction_requests
WHERE status = 'pending'
AND due_date BETWEEN NOW() AND DATE_ADD(NOW(), INTERVAL 3 DAY)
AND reminder_sent = 0
`);

for (const request of pending) {
await sendReminder(request.user_email, {
description: request.description,
amount: request.amount / 100,
dueDate: request.due_date,
payUrl: getRequestUrl(request.request_id)
});

await markReminderSent(request.request_id);
}
}

// Run daily
cron.schedule('0 9 * * *', sendPaymentReminders);

Limitations & Alternatives

Limitations

  • ⚠️ Requires wallet ID - Must know recipient's wallet
  • ⚠️ User can reject - Not guaranteed payment
  • ⚠️ Manual user action - User must accept and pay
  • ⚠️ No auto-charge - Use allowances for automatic payments

When to Use Allowances Instead

Use allowances for:

  • ✅ Recurring subscriptions
  • ✅ Automatic billing
  • ✅ Pre-authorized charges

Use requests for:

  • ✅ One-time invoices
  • ✅ Ad-hoc charges
  • ✅ User-initiated payments
  • ✅ When approval needed per transaction

What's Next?

Complete your integration knowledge:

  1. Reservation Codes - Generate payment codes (coming soon)
  2. Authorising Transactions - All acceptance methods
  3. Callbacks - Handle payment notifications

Need Help?

Quick Reference

Best for: Invoices, bill splitting, debt collection
User action: Required (accept & pay)
Alternative: Use allowances for automatic payments
Always: Notify users, handle rejections