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.
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
| Status | Description | Next States |
|---|---|---|
pending | Created, waiting for user | accepted, rejected, expired |
accepted | User accepted, payment created | paid, canceled |
paid | Payment completed | - |
rejected | User declined request | - |
expired | Passed due date | - |
canceled | You 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:
- Reservation Codes - Generate payment codes (coming soon)
- Authorising Transactions - All acceptance methods
- Callbacks - Handle payment notifications
Need Help?
- Questions? → tech_support@paysera.com
- Transaction Guide → Transaction Resource
- Examples → Code Samples
Best for: Invoices, bill splitting, debt collection
User action: Required (accept & pay)
Alternative: Use allowances for automatic payments
Always: Notify users, handle rejections