DevelopersAPI referenceSupport

Hosted QR checkout

Payment status

Two read-only calls tell you where a payment stands. Use them as a fallback to the postback, not instead of it.

The three statuses

statusMeaningWhat you do
pendingNot final yet. The customer has not paid, or has not even submitted their details.Wait. Do not ship, do not cancel.
completedPaid.Fulfil the order (once).
failedNot paid: expired or failed.Offer the customer a new payment, with a new reference.

success: true in an answer only means the call worked. It is true for a failed payment too. Always read status.

By your reference

GET https://api.antpay.network/api/v1/transactions/{reference}. URL-encode the reference. This is the call to use after a create that timed out, because you always know your own reference. Once you have a checkoutId, prefer the call by checkout id below: it names exactly one checkout.

The lookup is across your whole merchant account, not per API key. If you use more than one key, keep your references unique across all of them.

cURL

# Ask for the payment by YOUR reference, or by checkoutId. A 404 whose "error" is "Transaction not found" means there
# is no such payment. Any other 404 means the address itself was refused: check the reference, do not create again.
antpay_get_by_reference() { antpay_call GET "/api/v1/transactions/$(antpay_urlencode "$1")"; }
antpay_get_checkout()     { antpay_call GET "/api/v1/checkouts/$(antpay_urlencode "$1")"; }

Part of antpay.sh, the complete tested file

Node.js

// Ask for the payment by YOUR reference. Use it after an unclear create, and as a fallback to the postback.
// Returns null ONLY when the API itself says there is no such payment. Any other 404 means the address was refused
// (a wrong path, or a reference with characters that cannot travel in a URL): that is an error, not "no payment".
async function getByReference(reference) {
  const { http: status, json } = await call('GET', '/api/v1/transactions/' + encodeURIComponent(reference));
  if (status === 200 && json && json.success) return json.transaction;     // .status is pending | completed | failed
  if (status === 404 && json && json.error === 'Transaction not found') return null;
  throw new Error(`status call failed (HTTP ${status}${json && json.error ? ', ' + JSON.stringify(json.error) : ''})`);
}

async function getCheckout(checkoutId) {
  const { http: status, json } = await call('GET', '/api/v1/checkouts/' + encodeURIComponent(checkoutId));
  if (status === 200 && json && json.success) return json.checkout;
  if (status === 404 && json && json.error === 'Checkout session not found') return null;
  throw new Error(`status call failed (HTTP ${status}${json && json.error ? ', ' + JSON.stringify(json.error) : ''})`);
}

Part of antpay.js, the complete tested file

PHP

// Ask for the payment by YOUR reference. Use it after an unclear create, and as a fallback to the postback.
// Returns null ONLY when the API itself says there is no such payment. Any other 404 means the address was refused
// (a wrong path, or a reference with characters that cannot travel in a URL): that is an error, not "no payment".
function antpay_get_by_reference(string $reference): ?array {
    [$status, $data] = antpay_call('GET', '/api/v1/transactions/' . rawurlencode($reference));
    if ($status === 200 && is_array($data) && !empty($data['success'])) return $data['transaction'];   // ['status'] is pending | completed | failed
    if ($status === 404 && is_array($data) && ($data['error'] ?? null) === 'Transaction not found') return null;
    throw new RuntimeException("status call failed (HTTP $status, " . json_encode($data['error'] ?? null) . ')');
}

function antpay_get_checkout(string $checkoutId): ?array {
    [$status, $data] = antpay_call('GET', '/api/v1/checkouts/' . rawurlencode($checkoutId));
    if ($status === 200 && is_array($data) && !empty($data['success'])) return $data['checkout'];
    if ($status === 404 && is_array($data) && ($data['error'] ?? null) === 'Checkout session not found') return null;
    throw new RuntimeException("status call failed (HTTP $status, " . json_encode($data['error'] ?? null) . ')');
}

Part of antpay.php, the complete tested file

Python

# Ask for the payment by YOUR reference. Use it after an unclear create, and as a fallback to the postback.
# Returns None ONLY when the API itself says there is no such payment. Any other 404 means the address was refused
# (a wrong path, or a reference with characters that cannot travel in a URL): that is an error, not "no payment".
def get_by_reference(reference):
    status, data = call("GET", "/api/v1/transactions/" + urllib.parse.quote(reference, safe=""))
    if status == 200 and data and data.get("success"):
        return data["transaction"]          # ["status"] is pending | completed | failed
    if status == 404 and data and data.get("error") == "Transaction not found":
        return None
    raise RuntimeError(f"status call failed (HTTP {status}, {data.get('error') if data else None})")


def get_checkout(checkout_id):
    status, data = call("GET", "/api/v1/checkouts/" + urllib.parse.quote(checkout_id, safe=""))
    if status == 200 and data and data.get("success"):
        return data["checkout"]
    if status == 404 and data and data.get("error") == "Checkout session not found":
        return None
    raise RuntimeError(f"status call failed (HTTP {status}, {data.get('error') if data else None})")

Part of antpay.py, the complete tested file

{
  "success": true,
  "transaction": {
    "reference": "ORD-10021",
    "txnId": "FDX-1A2B3C4D",
    "checkoutId": "3f6c1d2e-8a4b-4c1e-9d7a-2b5e6f7a8c90",
    "amount": 1500,
    "paymentMethod": "qr",
    "status": "completed",
    "statusCode": "SUCCESS",
    "createdAt": "2026-11-01T10:00:00.000Z",
    "updatedAt": "2026-11-01T10:04:12.000Z",
    "walletName": "easypaisa",
    "accountNumber": "0312•••4567"
  }
}

statusCode is null while the payment is pending.

Two different 404s. 404 with "error": "Transaction not found" comes from the API: there is no payment with that reference. 404 with "error": "NOT_FOUND" means the address itself was refused before it reached the API (a wrong path, or a reference containing characters other than letters, digits, ., _ and -): it says nothing about whether the payment exists. The samples return "none" only for the first and raise an error for the second.

By checkout id

GET https://api.antpay.network/api/v1/checkouts/{checkoutId}. Use the checkoutId you stored for the order. This is the reliable way to ask about the checkout your customer was sent to.

{
  "success": true,
  "checkout": {
    "checkoutId": "3f6c1d2e-8a4b-4c1e-9d7a-2b5e6f7a8c90",
    "txnId": "FDX-1A2B3C4D",
    "reference": "ORD-10021",
    "amount": 1500,
    "paymentMethod": "qr",
    "status": "pending",
    "createdAt": "2026-11-01T10:00:00.000Z",
    "walletName": "easypaisa",
    "accountNumber": "0312•••4567"
  }
}

When to ask

SituationWhat to do
Normal flowDo not poll. Wait for the postback.
The customer is back on your returnUrl page and your order is still unpaidOne status call, then show "we are confirming your payment" if it is still pending. The postback will follow.
No postback an hour after you created the checkoutAsk by reference. If it is final, record it exactly as you would a postback. (An unpaid payment is not final until at least 30 minutes after the customer got the QR, and they may take up to expiresIn to ask for it.)
The create call timed out or returned 5xxAsk by reference before you create again.
Your server was down for a whileAsk by reference for every order still unpaid on your side.

If you do poll one payment, leave at least 10 seconds between calls. Status calls count against your rate limit.

You can show checkoutUrl on your order page or in your order email as "Check payment status".