Recharge APIv1

Get started

Callbacks

Status callbacks

Give us an HTTPS URL and we will call it:

  • when a transaction we answered pending reaches its final state;
  • when a final transaction changes later: a successful transaction our team reverses and refunds is sent again as FAILED (and a cleared complaint re-sends the status, see Complaint callbacks).

A reply that was already final (SUCCESS or FAILED in the response itself) gets no callback, and pending is never called. Delivery is retried with a growing back-off until your endpoint answers 2xx.

Request
GET https://your-server.example/callback
      ?v=2
      &status=SUCCESS
      &orderid=R2607230429028DB15
      &urid=ABC123
      &amount=100.0000
      &account=9876543210
      &ts=1755400000
      &nonce=3f1c0a9b2e5d4867b1a0c7d9e2f34856
      &hash=<hmac>

Verify hash before trusting a callback. It is an HMAC-SHA256, keyed with your API token, over every parameter we send except hash itself — sorted by key and joined as key=value with &. Build the pre-image from what actually arrived rather than from a fixed list of names, so a field we add later is covered automatically:

PHP
$params = $_GET;
unset($params['hash']);
ksort($params, SORT_STRING);

$pairs = [];
foreach ($params as $k => $v) {
    $pairs[] = $k . '=' . $v;
}

$expected = hash_hmac('sha256', implode('&', $pairs), $yourApiToken);
if (!hash_equals($expected, (string) ($_GET['hash'] ?? ''))) {
    http_response_code(403);
    exit;
}

// Freshness: refuse anything outside a five-minute window.
if (abs(time() - (int) ($_GET['ts'] ?? 0)) > 300) {
    http_response_code(403);
    exit;
}

// Replay: refuse a nonce you have already seen inside that window.
if ($youHaveSeen[$_GET['nonce']] ?? false) {
    http_response_code(403);
    exit;
}

// Duplicate delivery: we retry until you answer 2xx, so the SAME orderid and
// status may arrive more than once with different ts/nonce. Book each orderid +
// status once. A FAILED after a SUCCESS is a reversal: the money is back in your
// wallet — apply it, do not drop it as a duplicate.
Do not verify a hand-written list of field names. Every parameter is signed, including amount and account. A verifier that hashes only some of them will happily accept a callback whose amount or destination account was rewritten in transit — the signature over the fields it does check still matches.

Signed JSON callbacks (version 3) are optional; the Full API docs describe them.

Your callback URL must be a publicly resolvable https:// address. Private, loopback and link-local addresses are refused. Callbacks are informational: the status endpoint is always authoritative, so an integration that only polls is still correct.

Complaint callbacks

When a complaint you raised is cleared, two things happen:

  1. The transaction's status is sent to your callback URL again, in the usual status-callback format — SUCCESS if the transaction stands, FAILED if it was failed and refunded. This happens even if you already received that status earlier.
  2. If you have set a complaint callback URL (client portal → API access), the complaint's outcome is sent there.
Request
GET https://your-server.example/complaint-callback
      ?v=2
      &event=complaint
      &complaint_ref=CP261001103000A1B2C3D4E5F6
      &orderid=R2610011030…
      &urid=ABC123
      &status=REFUNDED
      &refund_amount=100.0000
      &resolved_at=2026-10-01 12:05:41
      &ts=1759300541
      &nonce=9c2e…
      &hash=<hmac>
FieldMeaning
eventAlways complaint. A status callback never carries event, so one URL can take both if you prefer.
statusRESOLVED — checked; the transaction stands and no money moved. REFUNDED — the transaction failed and the money is back in your wallet.
refund_amountThe amount returned to your wallet for REFUNDED; 0.0000 for RESOLVED.
resolved_atWhen the complaint was cleared, India time.
complaint_ref, orderid, uridThe complaint, our order id and your own reference.

Verify it exactly like a status callback: the hash covers every parameter except itself (see Status callbacks). Delivery is retried the same way until you answer 2xx, so book each complaint_ref once.

A complaint decided against the transaction whose refund could not be posted automatically is passed to our team: it reads OPEN and no complaint callback is sent for it. GET /api/corp/v1/complaints/{ref} is always authoritative. You can send yourself a test complaint callback from the client portal; it carries test=1 inside the signed message and TEST- references — never refund or credit anyone against it.
ECOBYTE Recharge APIv1