⚡ API Docs
📡 ABA Pay — QR Checkout API

Ahnajak Pay
Developer Docs

Generate secure, mobile-optimized ABA Pay QR checkouts for your customers using the Ahnajak gateway — no official merchant keys required.

🏆
ABA Access Plan Required — You need an active merchant profile with an ABA Pay Link configured in your Dashboard to use this API.

Authentication & Hash Signing

All payment requests are signed with a SHA-256 HMAC hash to prevent tampering. Never expose your secret_key in client-side code.

âš ī¸ Generate the request hash server-side only. Exposing your secret key in JavaScript or HTML will compromise your account.
Hash Formula (Request)
sha256(profile_id + transaction_id + amount + secret_key)

You can find your Profile ID and Secret Key in the API Credentials tab of your Dashboard.


Managed Checkout — Redirect Flow

Redirect your customers to our hosted ABA Pay checkout page. They'll see a live KHQR code verified by Bakong, with an Open in ABA Mobile deeplink button for one-tap payment.

GET v2 /api/payment/requestv2/{profile_id}
Creates a new payment session and immediately redirects the customer to the ABA Pay checkout page.
Full Endpoint URL
https://abapay.kesor.cam/api/payment/requestv2/{YOUR_PROFILE_ID}
Key Type Required Notes
transaction_id string REQUIRED Your internal unique Order ID. Must be unique per transaction.
amount decimal REQUIRED USD amount (Min: 0.01). Use format: 12.50
success_url url OPTIONAL URL to redirect customer to after payment. Must be URL-encoded.
remark string OPTIONAL Order note or customer username shown on receipt.
hash sha256 REQUIRED
sha256(profile_id + transaction_id + amount + secret_key)
â„šī¸ The profile_id is part of the URL path (not a query param). Get it from your API Credentials tab.

Checkout Page URL (Direct Link)

After a payment session is created, customers are redirected to the hosted checkout page at this URL pattern:

Checkout Page URL Pattern
https://abapay.kesor.cam/payment/khqrcc/{tx_id}

The checkout page automatically shows the QR code, merchant info, countdown timer, and ABA Mobile deeplink button. No extra setup needed.


PHP — Redirect Flow

The simplest integration. Generate the signed URL server-side and redirect the customer directly to the ABA Pay checkout.

aba_checkout.php
// 1. CONFIGURATION
$gateway_url = "https://abapay.kesor.cam/api/payment/requestv2";
$profile_id  = "YOUR_PROFILE_ID";
$secret_key  = "YOUR_SECRET_KEY";

// 2. TRANSACTION DETAILS
$payment_data = [
    "transaction_id" => "ORDER_" . time(),
    "amount"         => 12.50,
    "success_url"    => "https://yoursite.com/thank-you",
    "remark"         => "Order #88"
];

// 3. SECURITY HASH: sha256(profile_id + transaction_id + amount + secret_key)
$raw = $profile_id
      . $payment_data['transaction_id']
      . $payment_data['amount']
      . $secret_key;
$payment_data['hash'] = hash('sha256', $raw);

// 4. REDIRECT CUSTOMER TO ABA CHECKOUT
$final_url = $gateway_url . "/" . $profile_id . "?" . http_build_query($payment_data);
header("Location: " . $final_url);
exit;

HTML / JS — Modal Plugin

Instead of redirecting customers away, embed the ABA Pay checkout inside a responsive modal or bottom-sheet using our client-side JavaScript plugin. Payment status is verified automatically and your callback fires on success.

✅ Recommended — Modal mode keeps users on your site and provides a premium checkout experience without a page redirect.
index.php
<?php
// 1. SERVER-SIDE: Generate signed checkout URL (keep secret_key private!)
$gateway_url = "https://abapay.kesor.cam/api/payment/requestv2";
$profile_id  = "YOUR_PROFILE_ID";
$secret_key  = "YOUR_SECRET_KEY";

$payment_data = [
    "transaction_id" => "ORDER_" . time(),
    "amount"         => 12.50,
    "success_url"    => "https://yoursite.com/thank-you",
    "remark"         => "Order #88"
];
$raw = $profile_id . $payment_data['transaction_id'] . $payment_data['amount'] . $secret_key;
$payment_data['hash'] = hash('sha256', $raw);
$checkout_url = $gateway_url . "/" . $profile_id . "?" . http_build_query($payment_data);
?>

<!-- 1. Include the Checkout Plugin Script -->
<script src="https://abapay.kesor.cam/khqrcc-plugin.js"></script>

<!-- 2. Trigger the Iframe Modal via JavaScript -->
<script>
function payWithAhnajak() {
    const checkoutUrl = "<?php echo $checkout_url; ?>";

    KhqrPayway.openCheckout({
        checkout_url: checkoutUrl,
        onSuccess: function(response) {
            console.log("Payment successful!", response);
            window.location.href = "/success";
        },
        onError: function(error) {
            console.error("Modal closed or payment failed", error);
        }
    });
}
</script>

<!-- 3. Your custom pay button -->
<button onclick="payWithAhnajak()">
    Pay with ABA Pay
</button>
â„šī¸ The success_url is optional with the plugin. The onSuccess callback fires instead, giving you full control of post-payment flow.

Callback / Server Notification

After the customer pays via ABA Mobile, the gateway sends a signed HTTP POST to your Webhook URL (configured in Dashboard → Gateway Setup).

👤 Customer receives

  • Redirected to your success_url
  • Query param: success_hash
  • Query param: success_time
  • Query param: success_amount

đŸ–Ĩī¸ Your server receives

  • Signed POST application/json
  • Body field: transaction_id
  • Body field: amount
  • Body field: status = "SUCCESS"
  • Body field: hash (verify this!)
🚨 Always verify the callback hash before fulfilling any order. Never trust a success notification without signature validation.

Webhook Hash Verification

Every webhook POST includes a hash field and a X-Ahnajak-Signature header for double-verification.

Payload Hash Formula
sha256(transaction_id + amount + secret_key)
Header Signature Formula (HMAC)
hmac-sha256(secret_key, JSON.stringify(payload))
webhook_receiver.php
// 1. READ INCOMING POST BODY
$raw_body  = file_get_contents('php://input');
$data      = json_decode($raw_body, true);
$secret    = 'YOUR_SECRET_KEY';

// 2. VERIFY HEADER SIGNATURE (recommended)
$sig_header    = $_SERVER['HTTP_X_AHNAJAK_SIGNATURE'] ?? '';
$expected_sig  = hash_hmac('sha256', $raw_body, $secret);
if (!hash_equals($expected_sig, $sig_header)) {
    http_response_code(403);
    exit('Invalid signature');
}

// 3. VERIFY PAYLOAD HASH (double-check)
$expected_hash = hash('sha256',
    $data['transaction_id'] . $data['amount'] . $secret
);
if ($data['hash'] !== $expected_hash) {
    http_response_code(403);
    exit('Hash mismatch');
}

// 4. FULFILL THE ORDER ✅
if ($data['status'] === 'SUCCESS') {
    $order_id = $data['transaction_id'];
    $amount   = $data['amount'];
    // ... mark order as paid in your database
    http_response_code(200);
    echo json_encode(['received' => true]);
}

Custom Webhook Payload

By default the webhook sends transaction_id, amount, status, and hash. You can configure a Custom Payload Template in Dashboard → Gateway Setup to match the exact format your backend expects.

Template VariableValue
{{transaction_id}}Your original transaction ID
{{amount}}USD amount as string e.g. "12.50"
{{status}}Always "SUCCESS" on success webhook
{{hash}}sha256(transaction_id + amount + secret_key)
Custom Payload Template (JSON)
{
  "order_id": "{{transaction_id}}",
  "paid_amount": "{{amount}}",
  "payment_status": "{{status}}",
  "signature": "{{hash}}"
}