API Documentation
Accept ABA KHQR payments on your own website through a simple REST API — real QR codes, a hosted checkout page, and signed webhooks. Payments settle directly into your own ABA account.
1. You need your own ABA Payment Link. This comes from your own ABA Bank Business account, via the ABA Mobile Business app — not from TopUpKH, and we can't create one for you. Money paid through it goes directly into your own ABA account. If you don't have one yet, get it from ABA Bank first, then paste it into your Profile page.
2. You don't need to build a payment system. TopUpKH handles the checkout page, QR code, and payment status for you. The simplest option needs zero code — send customers to the link we give you and you're done. Want your own website to detect "paid" automatically? That needs one small piece of code from a developer (see Webhooks below) — not a full payment system.
1. Getting Started
- Go to topupkh.store/signup
- Enter your business name, your own ABA PayWay Payment Link, and (optionally) a phone number
- You'll immediately receive an API Key and API Secret
| Credential | Used for |
|---|---|
API Key | Authenticates every API call |
API Secret | Verifies webhook signatures, and logs you into your dashboard |
Keep both server-side only — never expose them in browser JavaScript, mobile app code, or a public repository.
2. Authentication
Every API request (except signup) requires your API Key in the Authorization header:
Authorization: Bearer kg_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
x-api-key: kg_live_... also works as an alternative header.
3. Generate a Payment
Call this the moment a customer is ready to pay.
/generate-paymentQuery parameters
| Parameter | Required | Description |
|---|---|---|
amount | Yes | Decimal USD amount, up to 2 decimals (e.g. 5.00) |
reference | Yes | A unique ID for this payment — your order ID works well |
payment_link | No | Overrides your default ABA PayWay link for this one payment |
success_url | No | Where the customer is redirected after payment confirms |
webhook_url | No | Your endpoint to receive a signed payment notification |
Example request
curl -G https://topupkh.store/generate-payment \ -H "Authorization: Bearer kg_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ --data-urlencode "amount=5.00" \ --data-urlencode "reference=ORDER-1234" \ --data-urlencode "success_url=https://yourshop.com/thank-you" \ --data-urlencode "webhook_url=https://yourshop.com/api/khqr-webhook"
Example response
{
"status": "success",
"reference": "ORDER-1234",
"checkout_url": "https://topupkh.store/aba_khqr/checkout/ORDER-1234",
"qr_string": "00020101021230510016abaakhppxxx...",
"deeplink": "abamobilebank://ababank.com?type=payway&qrcode=...",
"amount": "5.00",
"currency": "USD",
"expire_in_sec": 180
}
Error responses
| HTTP | Cause |
|---|---|
| 400 | Missing amount/reference, or amount isn't a valid decimal |
| 401 | Missing or invalid API key |
| 409 | reference already exists — use a new one |
| 502 | Could not generate the KHQR code (ABA-side issue) |
4. Send the Customer to Pay
Option A — Hosted checkout (recommended)
Redirect the customer to checkout_url from the response above. It shows a branded page with the QR code, a live countdown, and an "Open in ABA Mobile" button, and automatically updates to Success or Expired. Zero frontend work required.
Option B — Embed it yourself
Render your own QR image from qr_string, and link a button to deeplink for one-tap payment on mobile. Use this if you want the payment step to stay visually inside your own site.
5. Webhooks
If you passed webhook_url, we POST this the moment the payment resolves:
{
"status": "success",
"reference": "ORDER-1234",
"aba_tran_id": "178440319630275",
"receipt_url": "https://pwapp.ababank.com/.../download-receipt?...",
"bank_reference": null,
"amount": "5.00",
"currency": "USD",
"payment_method": "ABA Pay & KHQR",
"created_at": "2026-07-19T04:20:00.000Z",
"paid_at": "2026-07-19T04:22:14.000Z"
}
status is "success" or "expire".
Headers sent with every webhook
X-KHQR-Timestamp: 1784403196 X-KHQR-Signature: sha256=<hmac-hex>
reference.Verify in Node.js
const crypto = require('crypto');
function isValidWebhook(rawBody, headers, apiSecret) {
const ts = headers['x-khqr-timestamp'];
const expected = 'sha256=' + crypto
.createHmac('sha256', apiSecret)
.update(`${ts}.${rawBody}`)
.digest('hex');
return headers['x-khqr-signature'] === expected;
}
Verify in PHP
<?php
function isValidWebhook($rawBody, $timestamp, $signatureHeader, $apiSecret) {
$expected = 'sha256=' . hash_hmac('sha256', "$timestamp.$rawBody", $apiSecret);
return hash_equals($expected, $signatureHeader);
}
$rawBody = file_get_contents('php://input');
$timestamp = $_SERVER['HTTP_X_KHQR_TIMESTAMP'] ?? '';
$signature = $_SERVER['HTTP_X_KHQR_SIGNATURE'] ?? '';
if (!isValidWebhook($rawBody, $timestamp, $signature, $yourApiSecret)) {
http_response_code(400);
exit('invalid signature');
}
$payment = json_decode($rawBody, true);
if ($payment['status'] === 'success') {
// mark $payment['reference'] as paid in your own database
}
If your endpoint doesn't return a 2xx response, delivery is retried up to 5 times with exponential backoff.
6. Polling (alternative to webhooks)
/api/checkout/status/:referencecurl https://topupkh.store/api/checkout/status/ORDER-1234 \ -H "Authorization: Bearer kg_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
{
"reference": "ORDER-1234",
"status": "pending",
"amount": "5.00",
"currency": "USD",
"aba_tran_id": null,
"receipt_url": null,
"bank_reference": null
}
status is one of: pending, success, expire.
7. Quick Integration Examples
Node.js / Express
const fetch = require('node-fetch');
app.post('/checkout', async (req, res) => {
const params = new URLSearchParams({
amount: req.body.total.toFixed(2),
reference: `ORDER-${req.body.orderId}`,
success_url: 'https://yourshop.com/thank-you',
webhook_url: 'https://yourshop.com/api/khqr-webhook',
});
const r = await fetch(`https://topupkh.store/generate-payment?${params}`, {
headers: { Authorization: `Bearer ${process.env.TOPUPKH_API_KEY}` },
});
const payment = await r.json();
res.redirect(payment.checkout_url);
});
PHP
<?php
$params = http_build_query([
'amount' => number_format($total, 2, '.', ''),
'reference' => 'ORDER-' . $orderId,
'success_url' => 'https://yourshop.com/thank-you',
'webhook_url' => 'https://yourshop.com/api/khqr-webhook',
]);
$ch = curl_init("https://topupkh.store/generate-payment?$params");
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Bearer ' . TOPUPKH_API_KEY]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$payment = json_decode(curl_exec($ch), true);
curl_close($ch);
header('Location: ' . $payment['checkout_url']);
exit;
8. Testing Before Going Live
There's no separate sandbox — every request generates a real KHQR code against your real ABA PayWay link. To test safely:
- Use a small real amount, e.g.
amount=0.01 - Scan it yourself and confirm the webhook/status flow works end to end before wiring up real customer traffic
9. All Endpoints
| Endpoint | Method | Auth | Purpose |
|---|---|---|---|
/signup | POST | None | Create a merchant account |
/generate-payment | GET | API Key | Create a new KHQR payment |
/api/checkout/status/:reference | GET | API Key | Check a payment's status |
/aba_khqr/checkout/:reference | GET | None | Hosted checkout page — send customers here |