Webhooks vs Polling for Recharge Status: Which and Why

By Supayo 2 min read
Webhooks vs Polling for Recharge Status

A recharge does not complete instantly. You submit, it queues, the operator processes it, and somewhere between two seconds and two minutes later it succeeds or fails. The question is how your system learns the outcome.

The arithmetic

Polling every five seconds for two minutes is twenty-four requests per transaction. Twenty-three of them return nothing new.

PollingWebhook
Requests per transactionup to 241
At 1,000 recharges a day24,0001,000
Customer waitsup to 5s after settlingimmediate
If your cron stallsorders sit unresolvedunaffected
If the receiver is downcatches up on next pollretried automatically

How a webhook works

  1. You register one HTTPS URL and receive a signing secret
  2. A transaction settles
  3. The platform POSTs signed JSON to your URL
  4. You verify, update the order, and reply 200

Verify the signature — always

Your callback URL is reachable by anyone who guesses it. Without a signature check, a stranger could POST a fake success and your system would mark an unpaid order delivered.

$body = file_get_contents('php://input');   // raw bytes
$sent = $_SERVER['HTTP_X_SIGNATURE'] ?? '';
$mine = 'sha256=' . hash_hmac('sha256', $body, $secret);

if (!hash_equals($mine, $sent)) {
    http_response_code(401);
    exit;
}

Two details people get wrong. Hash the raw body — re-encoding parsed JSON changes the bytes and the signature never matches. And use hash_equals, not ==, because a plain comparison leaks the secret one byte at a time to anyone measuring response times.

Make the handler idempotent

Webhooks are delivered at least once. A retry can arrive because your 200 was lost on the way back. If your handler credits a customer every time it runs, a retry credits them twice.

UPDATE orders SET status = ?, settled_at = ?
 WHERE reference = ? AND status = 'pending'

The AND status = 'pending' is what makes it safe. A second delivery matches no rows and changes nothing.

Reply fast, work afterwards

Most senders time out at ten to fifteen seconds. If your handler sends an SMS or calls another API before replying, you will hit that limit and be retried for something that actually succeeded. Reply 200 first, queue the slow work.

The one thing that catches everyone

Framework CSRF protection. A webhook sender is a server, not a browser, and carries no CSRF token. If every delivery returns 403, this is why. Exempt the route — withoutMiddleware in Laravel, csrf_exempt in Django, the exclusion list in CodeIgniter.

Use both

Webhooks for the normal path, and a status sweep every ten minutes for anything still pending on your side. That combination survives an outage on either end. Webhooks alone leave a gap if your server is down through the whole retry window.

Share WhatsApp
WhatsApp