Quick Start

XentralPay menyediakan REST API untuk generate QRIS, memantau status pembayaran, dan menerima notifikasi otomatis melalui webhook. Tidak memerlukan SDK — cukup HTTP request biasa dengan response JSON.

Belum memiliki API key? Daftarkan akun melalui dashboard, verifikasi email, dan API key akan tersedia di halaman Pengaturan.

Alur Integrasi

1. Panggil POST /v1/qris/generate dengan nominal pembayaran.
2. Tampilkan qr_content sebagai QR code, atau arahkan pelanggan ke checkout_url.
3. Pelanggan memindai dan membayar melalui e-wallet yang mendukung QRIS.
4. XentralPay mengirimkan notifikasi ke webhook URL Anda setelah pembayaran berhasil.

Base URL

https://api.xentralpay.com

Autentikasi

Seluruh request ke endpoint QRIS memerlukan API key yang dikirimkan melalui header Authorization dengan format Bearer token.

cURL
Node.js
PHP
Python
Authorization: Bearer cpay_live_xxxxxxxxxxxxxxxxxxxxxxxx
const headers = {
  'Authorization': 'Bearer cpay_live_xxxxxxxxxxxxxxxxxxxxxxxx',
  'Content-Type': 'application/json'
};
$headers = [
    'Authorization: Bearer cpay_live_xxxxxxxxxxxxxxxxxxxxxxxx',
    'Content-Type: application/json'
];
headers = {
    "Authorization": "Bearer cpay_live_xxxxxxxxxxxxxxxxxxxxxxxx",
    "Content-Type": "application/json"
}
API key berbeda dengan token login dashboard. Jangan pernah menyertakan API key di kode frontend atau client-side — selalu lakukan request dari server Anda.

Generate QRIS

Membuat transaksi QRIS baru. Order ID di-generate secara otomatis oleh XentralPay. Response menyertakan checkout_url yang dapat langsung dikirimkan ke pelanggan.

POST /v1/qris/generate

Parameter

NamaTipeKeterangan
amountwajibintegerJumlah dalam IDR. Minimal 1, maksimal 10.000.000.
callback_urlstringURL untuk menerima webhook khusus transaksi ini. Jika tidak diisi, akan menggunakan webhook URL default yang tersimpan di Pengaturan.
metadataobjectData tambahan (contoh: ID user, ID order internal) yang disimpan bersama transaksi dan dikembalikan dalam webhook.

Contoh Request

cURL
Node.js
PHP
Python
curl -X POST https://api.xentralpay.com/v1/qris/generate \
  -H "Authorization: Bearer cpay_live_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{"amount": 25000, "callback_url": "https://yourapp.com/callback"}'
const res = await fetch('https://api.xentralpay.com/v1/qris/generate', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer cpay_live_xxxxx',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    amount: 25000,
    callback_url: 'https://yourapp.com/callback'
  })
});
const data = await res.json();
$ch = curl_init('https://api.xentralpay.com/v1/qris/generate');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer cpay_live_xxxxx',
    'Content-Type: application/json'
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
    'amount' => 25000,
    'callback_url' => 'https://yourapp.com/callback'
]));
$response = json_decode(curl_exec($ch), true);
import requests

response = requests.post(
    "https://api.xentralpay.com/v1/qris/generate",
    headers={"Authorization": "Bearer cpay_live_xxxxx"},
    json={"amount": 25000, "callback_url": "https://yourapp.com/callback"}
)
data = response.json()
Response 201
{
  "success": true,
  "message": "QRIS created successfully",
  "data": {
    "order_id": "CP-1782021400-9f3a",
    "amount": 25000,
    "status": "pending",
    "qr_content": "00020101021226610014...",
    "qr_image_url": "https://xentralpay.com/qris/CP-1782021400-9f3a/qr.png",
    "checkout_url": "https://xentralpay.com/pay/9f3a...",
    "expires_at": "2026-06-21T15:09:00.000Z",
    "created_at": "2026-06-21T14:54:00.000Z"
  }
}

Field Response

FieldTipeKeterangan
order_idstringID unik transaksi (format CP-...). Gunakan ID ini untuk cek status atau membatalkan transaksi.
qr_contentstringRaw payload QRIS. Render menjadi gambar QR menggunakan library pilihan Anda (qrcode, qrcode.react, dll).
qr_image_urlstringURL gambar QR yang sudah di-render, siap ditampilkan langsung.
checkout_urlstringHalaman pembayaran yang siap pakai — arahkan pelanggan ke URL ini jika tidak ingin render QR sendiri.
expires_atstringWaktu kedaluwarsa transaksi dalam format ISO 8601.

Cek Status Transaksi

Mengambil status terbaru dari sebuah transaksi. Jika status masih pending, XentralPay akan melakukan sinkronisasi ulang ke provider sebelum memberikan response — status lainnya dianggap final.

POST /v1/qris/status

Parameter

NamaTipeKeterangan
order_idstringOrder ID dari XentralPay (format CP-...). Gunakan salah satu: order_id atau transaction_id.
transaction_idstringTransaction ID dari provider (UUID), jika Anda menyimpannya.

Contoh Request

cURL
Node.js
PHP
Python
curl -X POST https://api.xentralpay.com/v1/qris/status \
  -H "Authorization: Bearer cpay_live_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{"order_id": "CP-1782021400-9f3a"}'
const res = await fetch('https://api.xentralpay.com/v1/qris/status', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer cpay_live_xxxxx',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ order_id: 'CP-1782021400-9f3a' })
});
$ch = curl_init('https://api.xentralpay.com/v1/qris/status');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer cpay_live_xxxxx',
    'Content-Type: application/json'
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(['order_id' => 'CP-1782021400-9f3a']));
$response = json_decode(curl_exec($ch), true);
response = requests.post(
    "https://api.xentralpay.com/v1/qris/status",
    headers={"Authorization": "Bearer cpay_live_xxxxx"},
    json={"order_id": "CP-1782021400-9f3a"}
)
Response 200
{
  "success": true,
  "data": {
    "order_id": "CP-1782021400-9f3a",
    "amount": 25000,
    "net_amount": 24825,
    "fee_amount": 175,
    "status": "settlement",
    "payment_method": "QRIS",
    "created_at": "2026-06-21T14:54:00.000Z",
    "settled_at": "2026-06-21T14:56:12.000Z"
  }
}

Nilai Status

StatusKeterangan
pendingMenunggu pembayaran dari pelanggan. Transaksi belum kedaluwarsa.
settlementPembayaran berhasil diterima. Dana masuk ke saldo tertunda merchant dan akan tersedia setelah periode settlement.
expireWaktu pembayaran telah habis tanpa ada transaksi masuk.
cancelTransaksi dibatalkan melalui endpoint /v1/qris/cancel.

Batalkan Transaksi

Membatalkan transaksi yang masih berstatus pending. Transaksi yang sudah berstatus settlement, expire, atau cancel tidak dapat dibatalkan.

POST /v1/qris/cancel

Parameter

NamaTipeKeterangan
order_idstringOrder ID dari XentralPay. Gunakan salah satu: order_id atau transaction_id.
transaction_idstringTransaction ID dari provider (UUID).

Contoh Request

cURL
Node.js
PHP
Python
curl -X POST https://api.xentralpay.com/v1/qris/cancel \
  -H "Authorization: Bearer cpay_live_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{"order_id": "CP-1782021400-9f3a"}'
await fetch('https://api.xentralpay.com/v1/qris/cancel', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer cpay_live_xxxxx',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ order_id: 'CP-1782021400-9f3a' })
});
$ch = curl_init('https://api.xentralpay.com/v1/qris/cancel');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer cpay_live_xxxxx',
    'Content-Type: application/json'
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(['order_id' => 'CP-1782021400-9f3a']));
$response = json_decode(curl_exec($ch), true);
response = requests.post(
    "https://api.xentralpay.com/v1/qris/cancel",
    headers={"Authorization": "Bearer cpay_live_xxxxx"},
    json={"order_id": "CP-1782021400-9f3a"}
)
Response 200
{
  "success": true,
  "data": {
    "order_id": "CP-1782021400-9f3a",
    "status": "cancel",
    "cancelled_at": "2026-06-21T14:58:30.000Z"
  }
}

Menerima Webhook

Ketika status transaksi berubah (terutama menjadi settlement), XentralPay mengirimkan POST request ke callback_url yang ditetapkan saat generate QRIS, atau ke webhook URL default di halaman Pengaturan.

Payload

{
  "event": "payment.completed",
  "timestamp": "2026-06-21T14:56:12.000Z",
  "data": {
    "order_id": "CP-1782021400-9f3a",
    "amount": 25000,
    "net_amount": 24825,
    "fee_amount": 175,
    "status": "settlement",
    "payment_method": "QRIS",
    "metadata": {}
  }
}

Header

HeaderKeterangan
Content-Typeapplication/json
X-XentralPay-SignatureHMAC-SHA256 dari raw body, ditandatangani menggunakan webhook_secret akun Anda.
X-XentralPay-TimestampUnix timestamp saat webhook dikirim. Gunakan untuk mencegah replay attack.
Server Anda harus mengembalikan HTTP 200 dalam 10 detik. Pastikan endpoint bersifat idempotent — webhook dengan order_id yang sama mungkin dikirim lebih dari satu kali.

Verifikasi Signature

Selalu verifikasi header X-XentralPay-Signature sebelum memproses webhook untuk memastikan bahwa notifikasi benar-benar berasal dari XentralPay. Signature dihitung dari raw body menggunakan webhook_secret akun Anda sebagai key.

Node.js
PHP
Python
const crypto = require('crypto');

app.post('/webhook', (req, res) => {
  const signature = req.headers['x-centralpay-signature'];
  const expected = crypto
    .createHmac('sha256', WEBHOOK_SECRET)
    .update(JSON.stringify(req.body))
    .digest('hex');

  if (signature !== expected) return res.status(401).end();

  const { data } = req.body;
  if (data.status === 'settlement') {
    // Pembayaran berhasil — proses order
  }
  res.json({ received: true });
});
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_CENTRALPAY_SIGNATURE'] ?? '';

$expected = hash_hmac('sha256', $payload, WEBHOOK_SECRET);
if (!hash_equals($expected, $signature)) {
    http_response_code(401);
    exit('Invalid signature');
}

$event = json_decode($payload, true);
if ($event['data']['status'] === 'settlement') {
    // Pembayaran berhasil — proses order
}
echo json_encode(['received' => true]);
import hmac, hashlib

@app.route("/webhook", methods=["POST"])
def webhook():
    signature = request.headers.get("X-XentralPay-Signature", "")
    expected = hmac.new(
        WEBHOOK_SECRET.encode(), request.data, hashlib.sha256
    ).hexdigest()

    if not hmac.compare_digest(expected, signature):
        return "", 401

    event = request.get_json()
    if event["data"]["status"] == "settlement":
        pass  # Pembayaran berhasil — proses order

    return {"received": True}

Kode Error

Seluruh response error menggunakan format yang konsisten: {"success": false, "message": "..."}. HTTP status code menunjukkan kategori error.

StatusKeterangan
400Parameter tidak valid — contoh: amount kosong, bukan integer, atau di luar rentang 1–10.000.000.
401API key tidak valid, tidak disertakan, atau format header Authorization salah (harus Bearer cpay_live_...).
403Akun merchant sedang dalam status suspended.
404order_id atau transaction_id tidak ditemukan, atau bukan milik akun Anda.
409Transaksi tidak dapat dibatalkan karena sudah berstatus final (settlement/expire/cancel).
429Terlalu banyak request. Kurangi frekuensi dan coba lagi setelah beberapa saat.
500Kesalahan internal di sisi XentralPay. Silakan coba lagi, atau hubungi tim support jika masalah berulang.
502Provider QRIS tidak merespons atau menolak request. Coba lagi beberapa saat kemudian.
Butuh bantuan? Hubungi tim kami di Telegram @cogil.