Polling vs Webhooks
Two strategies for knowing when a bill has been paid.
Polling
Poll the GET /bills/{id} endpoint at regular intervals until the status changes.
Pros: Simple to implement, no server configuration needed, works everywhere.
Cons: Adds API load, slight delay between payment and detection.
Basic polling
async function pollPayment(api, billId) {
const interval = 5000; // 5 seconds
const timeout = 300000; // 5 minutes
const start = Date.now();
while (Date.now() - start < timeout) {
const bill = await api.get(billId);
if (bill.status.name === 'FULLY_PAID') {
return bill;
}
await new Promise(r => setTimeout(r, interval));
}
throw new Error('Payment timeout');
}
Exponential backoff
For production, increase the interval over time to reduce API load:
async function pollWithBackoff(api, billId) {
let interval = 3000; // Start at 3s
const maxInterval = 30000; // Cap at 30s
const timeout = 600000; // 10 minutes
const start = Date.now();
while (Date.now() - start < timeout) {
const bill = await api.get(billId);
if (bill.status.name === 'FULLY_PAID') return bill;
if (bill.status.name === 'PARTIALLY_PAID') {
interval = 3000; // Reset to fast polling on partial payment
}
await new Promise(r => setTimeout(r, interval));
interval = Math.min(interval * 1.5, maxInterval);
}
throw new Error('Payment timeout');
}
Webhooks
Cashless sends a POST request to your server when a bill is paid. See Webhooks for setup.
Pros: Real-time, no polling overhead, scales well.
Cons: Requires a publicly accessible server, needs retry handling.
Example
app.post('/webhooks/cashless', async (req, res) => {
const { bill } = req.body;
// Always verify via API
const verified = await api.get(bill.id);
if (verified.status.name === 'FULLY_PAID') {
await fulfillOrder(bill.reference);
}
res.sendStatus(200);
});
Recommended: Both
For maximum reliability, use webhooks as the primary notification and polling as a fallback:
// On bill creation, start a background poll
const bill = await api.issue(params);
startBackgroundPoll(bill.id, bill.reference);
// Webhook handler (fast path)
app.post('/webhooks/cashless', async (req, res) => {
const { bill } = req.body;
const verified = await api.get(bill.id);
if (verified.status.name === 'FULLY_PAID') {
stopBackgroundPoll(bill.id);
await fulfillOrder(bill.reference);
}
res.sendStatus(200);
});
This ensures payment is detected even if the webhook delivery fails.