If a webhook was missed, or you simply want to double check an order, ask us. Works for both sandbox and live Merchant IDs.
Request#
GET https://maainvest.co.ke/pay/status.php?merchant_id=…&invoice_id=…×tamp=…&signature=…
The signature is hex(HMAC-SHA256(merchant_id + "\n" + invoice_id + "\n" + timestamp, API_SECRET)). The timestamp must be within 5 minutes. Call it from your server only, never from the browser.
<?php
$ts = (string)time();
$sig = hash_hmac('sha256', $MERCHANT_ID . "\n" . 'ORD-1001' . "\n" . $ts, $API_SECRET);
$url = 'https://maainvest.co.ke/pay/status.php?' . http_build_query([
'merchant_id' => $MERCHANT_ID, 'invoice_id' => 'ORD-1001', 'timestamp' => $ts, 'signature' => $sig,
]);
$res = json_decode(file_get_contents($url), true);
// $res['status'] === 'paid' => safe to treat as paid (also check amount_paid)Response#
{
"ok": true,
"sandbox": false,
"checkout_id": "9f2c1e7a0b…",
"invoice_id": "ORD-1001",
"status": "paid",
"amount": "1500.00",
"amount_paid": "1500.00",
"currency": "KES",
"receipt": "SGH4XXXXXX",
"paid_at": "2026-10-23T10:15:30+03:00",
"reason": null
}status is one of created, pending, paid, failed, expired. amount_paid, receipt and paid_at are null until paid; reason carries the failure text when failed. If the same invoice has several checkouts, a paid one is returned first, otherwise the newest. Errors return {"ok": false, "error": "…"} with HTTP 400 (bad or stale request), 403 (signature) or 404 (no checkout for that invoice).
For lightweight polling of the customer's checkout page you can also use GET https://maainvest.co.ke/pay/ajax.php?action=status&t=<checkout token> (the t value in the checkout URL). The webhook and the signed Status API are the sources of truth.
MAA INVEST Pay Docs