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.
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.
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"
}
Generate QRIS
Membuat transaksi QRIS baru. Order ID di-generate secara otomatis oleh XentralPay. Response menyertakan checkout_url yang dapat langsung dikirimkan ke pelanggan.
Parameter
| Nama | Tipe | Keterangan |
|---|---|---|
| amountwajib | integer | Jumlah dalam IDR. Minimal 1, maksimal 10.000.000. |
| callback_url | string | URL untuk menerima webhook khusus transaksi ini. Jika tidak diisi, akan menggunakan webhook URL default yang tersimpan di Pengaturan. |
| metadata | object | Data tambahan (contoh: ID user, ID order internal) yang disimpan bersama transaksi dan dikembalikan dalam webhook. |
Contoh Request
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()
{
"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
| Field | Tipe | Keterangan |
|---|---|---|
| order_id | string | ID unik transaksi (format CP-...). Gunakan ID ini untuk cek status atau membatalkan transaksi. |
| qr_content | string | Raw payload QRIS. Render menjadi gambar QR menggunakan library pilihan Anda (qrcode, qrcode.react, dll). |
| qr_image_url | string | URL gambar QR yang sudah di-render, siap ditampilkan langsung. |
| checkout_url | string | Halaman pembayaran yang siap pakai — arahkan pelanggan ke URL ini jika tidak ingin render QR sendiri. |
| expires_at | string | Waktu 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.
Parameter
| Nama | Tipe | Keterangan |
|---|---|---|
| order_id | string | Order ID dari XentralPay (format CP-...). Gunakan salah satu: order_id atau transaction_id. |
| transaction_id | string | Transaction ID dari provider (UUID), jika Anda menyimpannya. |
Contoh Request
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"}
)
{
"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
| Status | Keterangan |
|---|---|
pending | Menunggu pembayaran dari pelanggan. Transaksi belum kedaluwarsa. |
settlement | Pembayaran berhasil diterima. Dana masuk ke saldo tertunda merchant dan akan tersedia setelah periode settlement. |
expire | Waktu pembayaran telah habis tanpa ada transaksi masuk. |
cancel | Transaksi 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.
Parameter
| Nama | Tipe | Keterangan |
|---|---|---|
| order_id | string | Order ID dari XentralPay. Gunakan salah satu: order_id atau transaction_id. |
| transaction_id | string | Transaction ID dari provider (UUID). |
Contoh Request
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"}
)
{
"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
| Header | Keterangan |
|---|---|
Content-Type | application/json |
X-XentralPay-Signature | HMAC-SHA256 dari raw body, ditandatangani menggunakan webhook_secret akun Anda. |
X-XentralPay-Timestamp | Unix timestamp saat webhook dikirim. Gunakan untuk mencegah replay attack. |
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.
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.
| Status | Keterangan |
|---|---|
| 400 | Parameter tidak valid — contoh: amount kosong, bukan integer, atau di luar rentang 1–10.000.000. |
| 401 | API key tidak valid, tidak disertakan, atau format header Authorization salah (harus Bearer cpay_live_...). |
| 403 | Akun merchant sedang dalam status suspended. |
| 404 | order_id atau transaction_id tidak ditemukan, atau bukan milik akun Anda. |
| 409 | Transaksi tidak dapat dibatalkan karena sudah berstatus final (settlement/expire/cancel). |
| 429 | Terlalu banyak request. Kurangi frekuensi dan coba lagi setelah beberapa saat. |
| 500 | Kesalahan internal di sisi XentralPay. Silakan coba lagi, atau hubungi tim support jika masalah berulang. |
| 502 | Provider QRIS tidak merespons atau menolak request. Coba lagi beberapa saat kemudian. |