PayQRIS API
API Documentation
Dokumentasi resmi untuk developer yang ingin mengintegrasikan pembayaran QRIS PayQRIS ke website, aplikasi, bot, atau backend. Pelajari cara autentikasi, membuat invoice, mengecek status, webhooks, dan banyak lagi.
Loading...Overview
Alur integrasi yang disarankan adalah: backend merchant membuat invoice → PayQRIS mengembalikan nominal pembayaran dan QRIS → merchant menampilkan checkout atau QR kepada pelanggan →pelanggan membayar → PayQRIS memperbarui status invoice → merchant menerima webhook. Untuk keputusan bisnis seperti mengirim produk atau memberi saldo, selalu gunakan status yang diverifikasi server, bukan hasil redirect browser.
Authentication
Semua endpoint merchant pada API v1 menggunakan header X-API-Key. API Key diterbitkan dari dashboard merchant. Endpoint public payment status tidak membutuhkan API Key.
X-API-Key: YOUR_API_KEY
Content-Type: application/jsonJika credential tidak valid, tidak diberikan, atau merchant tidak diizinkan menggunakan API, server mengembalikan HTTP 401 atau 403 sesuai kondisi.
Quick Start
- Buat atau salin API Key dari dashboard merchant.
- Simpan API Key sebagai environment variable di backend.
- Buat invoice dengan endpoint
POST /invoices. - Arahkan pelanggan ke
checkout_urlatau tampilkanqris_image_url. - Gunakan webhook atau payment-status API untuk memastikan pembayaran.
- Gunakan
Idempotency-Keyyang sama jika request create perlu diulang akibat kendala jaringan.
Create Invoice
/api/v1/invoicesMembuat invoice QRIS baru untuk merchant yang sedang terautentikasi. Invoice berlaku selama 15 menit. Sistem menghasilkan unique_code dan pay_amount sebagai nominal unik yang harus dibayar pelanggan. Jika mengirimkan callback_url, URL tersebut wajib menggunakan HTTPS di production.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| amount | integer | Yes | Nominal order dalam Rupiah. Production: Rp100–Rp1.000.000. |
| reference_id | string | No | ID order unik dari sisi merchant untuk menghubungkan invoice dengan sistem internal. |
| callback_url | string | No | URL webhook spesifik untuk invoice ini. Wajib menggunakan HTTPS di production. |
Example Request
curl -X POST "/invoices" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Idempotency-Key: ORDER-10001" \
-d '{"amount":20000,"reference_id":"ORDER-10001","callback_url":"https://merchant.example/webhook"}'Example Response
{
"success": true,
"data": {
"invoice_id": "2e96c65d36b56c6d",
"reference_id": "QRIS-20260902-1E39FCD51A",
"merchant_reference_id": "ORDER-10001",
"merchant_name": "Your Store",
"amount": 20000,
"unique_code": 157,
"pay_amount": 20157,
"qris_url": "https://payqris.id/qr/REFERENCE_ID",
"qris_image_url": "https://payqris.id/qr/REFERENCE_ID",
"qris_string": "000201...",
"checkout_url": "https://payqris.id/pay/REFERENCE_ID",
"payment_api_url": "https://payqris.id/api/public/payment-links/REFERENCE_ID",
"status": "PENDING",
"expired_at": "2026-09-02 02:19:47"
}
}List Invoices
/api/v1/invoicesMengambil daftar invoice milik merchant yang terautentikasi. Endpoint ini mendukung pagination, filter status, dan pencarian melalui query search berdasarkan invoice ID, reference ID, atau merchant reference ID.
PENDING, PAID, EXPIRED, FAILED, CANCELLEDpage (default 1), limit (1-100, default 10)Example Request
curl "/invoices?status=PAID&page=1&limit=20" \
-H "X-API-Key: YOUR_API_KEY"Example Response
{
"success": true,
"data": {
"invoices": [
{
"invoice_id": "2e96c65d36b56c6d",
"reference_id": "QRIS-20260902-1E39FCD51A",
"merchant_reference_id": "ORDER-10001",
"merchant_name": "Your Store",
"amount": 20000,
"unique_code": 157,
"pay_amount": 20157,
"status": "PAID",
"created_at": "2026-09-02 02:04:47",
"paid_at": "2026-09-02 02:08:12",
"expired_at": "2026-09-02 02:19:47"
}
],
"pagination": {
"total": 1,
"page": 1,
"limit": 20,
"pages": 1
}
}
}Get Invoice
/api/v1/invoices/:invoice_idMengambil detail satu invoice berdasarkan invoice_id. Invoice harus dimiliki oleh merchant yang terautentikasi. Jika invoice tidak ditemukan atau bukan milik merchant tersebut, server mengembalikan HTTP 404.
Example Request
curl "/invoices/INVOICE_ID" \
-H "X-API-Key: YOUR_API_KEY"Example Response
{
"success": true,
"data": {
"invoice_id": "2e96c65d36b56c6d",
"reference_id": "QRIS-20260902-1E39FCD51A",
"merchant_reference_id": "ORDER-10001",
"merchant_name": "Your Store",
"amount": 20000,
"unique_code": 157,
"pay_amount": 20157,
"qris_url": "https://payqris.id/qr/REFERENCE_ID",
"qris_string": "000201...",
"status": "PAID",
"failure_reason": null,
"paid_at": "2026-09-02 02:08:12",
"cancelled_at": null,
"expired_at": "2026-09-02 02:19:47",
"created_at": "2026-09-02 02:04:47",
"updated_at": "2026-09-02 02:08:12"
}
}Cancel Invoice
/api/v1/invoices/:invoice_id/cancelMembatalkan invoice yang masih PENDING dan belum melewati waktu kedaluwarsa. Invoice dengan status lain (seperti PAID atau EXPIRED) tidak dapat dibatalkan.
Example Request
curl -X POST "/invoices/INVOICE_ID/cancel" \
-H "X-API-Key: YOUR_API_KEY"Example Response
{
"success": true,
"message": "Invoice cancelled successfully",
"data": {
"invoice_id": "INVOICE_ID",
"reference_id": "QRIS-20260902-1E39FCD51A",
"merchant_reference_id": "ORDER-10001",
"merchant_name": "Your Store",
"amount": 20000,
"unique_code": 157,
"pay_amount": 20157,
"qris_url": "https://payqris.id/qr/REFERENCE_ID",
"qris_string": "000201...",
"status": "CANCELLED",
"failure_reason": null,
"paid_at": null,
"cancelled_at": "2026-09-02 02:10:00",
"expired_at": "2026-09-02 02:19:47",
"created_at": "2026-09-02 02:04:47",
"updated_at": "2026-09-02 02:10:00"
}
}Payment Status
/api/public/payment-links/:reference_idPublic (No API Key)Endpoint public untuk mengecek status pembayaran menggunakan reference_id. Endpoint ini tidak membutuhkan API Key sehingga dapat digunakan dengan aman oleh halaman checkout frontend atau aplikasi client-side yang tidak menyimpan credential merchant.
PAID.Example Request
curl "/api/public/payment-links/REFERENCE_ID"Example Response
{
"success": true,
"data": {
"reference_id": "QRIS-20260902-1E39FCD51A",
"merchant_reference_id": "ORDER-10001",
"merchant_name": "Your Store",
"amount": 20000,
"unique_code": 157,
"pay_amount": 20157,
"qris_url": "https://payqris.id/qr/REFERENCE_ID",
"qris_image_url": "https://payqris.id/qr/REFERENCE_ID",
"qris_string": "000201...",
"checkout_url": "https://payqris.id/pay/REFERENCE_ID",
"payment_api_url": "https://payqris.id/api/public/payment-links/REFERENCE_ID",
"status": "PAID",
"created_at": "2026-09-02 02:04:47",
"paid_at": "2026-09-02 02:08:12",
"expired_at": "2026-09-02 02:19:47"
}
}QRIS & Checkout
Setelah memanggil endpoint Create Invoice, Anda akan menerima beberapa properti yang berguna untuk menampilkan pembayaran ke pengguna:
checkout_urlPilihan termudah. Halaman siap pakai milik PayQRIS yang berisi instruksi pembayaran, QRIS, dan timer.
qris_image_urlURL file gambar PNG dari QRIS. Cocok jika Anda ingin menampilkan QRIS langsung di website Anda sendiri.
qris_stringPayload asli QRIS. Berguna jika Anda menggunakan library renderer QR code sendiri di aplikasi mobile atau frontend.
Webhooks
Webhook dikirim menggunakan HTTP POST ke callback URL merchant. Event pembayaran yang tersedia adalah payment.pending, payment.paid, payment.expired, payment.failed, dan payment.cancelled. Dashboard juga menyediakan event webhook.test untuk menguji konfigurasi.
Example Payload
{
"event": "payment.paid",
"merchant_name": "Your Store",
"invoice_id": "2e96c65d36b56c6d",
"reference_id": "QRIS-20260902-1E39FCD51A",
"merchant_reference_id": "ORDER-10001",
"amount": 20000,
"unique_code": 157,
"pay_amount": 20157,
"status": "paid",
"created_at": "2026-09-02 02:04:47",
"paid_at": "2026-09-02 02:08:12",
"expired_at": "2026-09-02 02:19:47"
}Webhook Signature Verification
Keamanan webhook dijamin melalui signature yang dikirimkan dalam header X-Signature. Signature ini dihitung menggunakan HMAC-SHA256 dari payload mentah menggunakan Webhook Secret Anda. Anda harus memverifikasi signature ini untuk memastikan bahwa permintaan benar-benar berasal dari PayQRIS dan datanya tidak dimanipulasi.
const crypto = require("crypto");
// Gunakan RAW request body, bukan JSON yang sudah diparse lalu di-stringify ulang.
const signature = crypto
.createHmac("sha256", process.env.PAYQRIS_WEBHOOK_SECRET)
.update(rawBody)
.digest("hex");
const received = req.get("X-Signature") || "";
const valid =
received.length === signature.length &&
crypto.timingSafeEqual(
Buffer.from(signature, "utf8"),
Buffer.from(received, "utf8"),
);Merchant sebaiknya memvalidasi signature, reference ID, nominal, dan status, lalu membuat pemrosesan webhook yang idempotent. Balas HTTP 200 (atau 2xx lainnya) setelah event berhasil diterima/diproses.
Logs
PayQRIS menyediakan API untuk membaca log aktivitas yang terkait dengan merchant Anda. Anda dapat menggunakannya untuk debugging dan rekonsiliasi.
/api/v1/api-logsMelihat request API yang Anda buat menggunakan API Key. Limit maksimum 100 record.
GET /api-logs?limit=50
X-API-Key: YOUR_API_KEY{
"success": true,
"data": {
"logs": [
{
"request_id": "req_abcdef",
"method": "POST",
"path": "/api/v1/invoices",
"status_code": 201,
"duration_ms": 124,
"ip": "192.168.1.1",
"created_at": "2026-09-02T02:04:47.123Z"
}
]
}
}/api/v1/webhook-logsMelihat riwayat pengiriman webhook ke callback URL Anda. Mendukung filter invoice_id dan success=true|false.
GET /webhook-logs?page=1&limit=20
X-API-Key: YOUR_API_KEY{
"success": true,
"data": {
"logs": [
{
"invoice_id": "2e96c65d36b56c6d",
"callback_url": "https://merchant.example/webhook",
"request_headers": {
"Content-Type": "application/json",
"X-Event": "payment.paid",
"X-Signature": "..."
},
"request_body": {
"event": "payment.paid",
"merchant_name": "Your Store",
"invoice_id": "2e96c65d36b56c6d",
"reference_id": "QRIS-20260902-1E39FCD51A",
"merchant_reference_id": "ORDER-10001",
"amount": 20000,
"unique_code": 157,
"pay_amount": 20157,
"status": "paid",
"created_at": "2026-09-02 02:04:47",
"paid_at": "2026-09-02 02:08:12",
"expired_at": "2026-09-02 02:19:47"
},
"response_status": 200,
"response_body": "{"ok":true}",
"success": true,
"duration_ms": 245,
"attempts": 1,
"created_at": "2026-09-02T02:08:13.456Z"
}
],
"pagination": {
"total": 1,
"page": 1,
"limit": 20,
"pages": 1
}
}
}Errors
Error API dikembalikan dengan status kode HTTP yang sesuai, serta payload JSON yang konsisten yang menjelaskan alasan error tersebut.
{
"success": false,
"error": "Nominal transaksi tidak sesuai dengan rentang yang diizinkan."
}Input tidak valid, parameter wajib hilang, nominal tidak valid, atau operasi yang diminta tidak diizinkan pada status saat ini.
API Key tidak valid, sudah dicabut, atau tidak disertakan dalam request header.
Merchant terautentikasi tetapi tidak memiliki akses ke fitur tertentu atau akun sedang ditangguhkan.
Invoice, resource, atau endpoint yang diminta tidak ditemukan, atau tidak dimiliki oleh merchant Anda.
Operasi bertabrakan dengan resource yang ada, seperti reference ID duplikat atau idempotency key tabrakan dengan payload berbeda.
Terlalu banyak permintaan dalam waktu singkat (Rate Limiting). Silakan tunggu sebelum mencoba kembali.
Idempotency & Rate Limits
Idempotency
Saat membuat invoice, disarankan mengirimkan header Idempotency-Key (maksimal 100 karakter, berlaku per merchant). Jika Anda mengalami masalah koneksi jaringan dan harus mencoba kembali request yang sama, server akan mengenali key tersebut dan mengembalikan respons yang sama persis dengan percobaan pertama alih-alih membuat invoice ganda.
Idempotency-Key: ORDER-10001Rate Limits
API menerapkan mekanisme pembatasan untuk menjaga keandalan layanan: /api/v1 dibatasi 300 request/menit per API key, /api/public 120 request/menit, dan /api/auth 60 request per 15 menit. Gunakan request API secara wajar dan hindari polling status secara agresif—gunakan webhook sebagai gantinya. Jika Anda menerima status kode 429 Too Many Requests, hentikan pengiriman request dan gunakan algoritma exponential backoff (contoh: 1s → 2s → 4s → 8s).
Implementation Examples
Contoh berikut mengilustrasikan cara memanggil endpoint Create Invoice dari server-side / backend merchant menggunakan berbagai bahasa pemrograman.
Node.js / TypeScript
const response = await fetch("/invoices", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": process.env.PAYQRIS_API_KEY,
"Idempotency-Key": order.id,
},
body: JSON.stringify({
amount: order.total,
reference_id: order.id,
callback_url: "https://merchant.example/webhook",
}),
});
const result = await response.json();
if (!response.ok) throw new Error(result.error || "Request failed");PHP (cURL)
$payload = json_encode([
'amount' => 20000,
'reference_id' => 'ORDER-10001',
'callback_url' => 'https://merchant.example/webhook',
]);
$ch = curl_init('/invoices');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'X-API-Key: ' . getenv('PAYQRIS_API_KEY'),
'Idempotency-Key: ORDER-10001',
],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($ch);
curl_close($ch);Python (requests)
import requests
response = requests.post(
"/invoices",
headers={
"X-API-Key": "YOUR_API_KEY",
"Idempotency-Key": "ORDER-10001",
},
json={
"amount": 20000,
"reference_id": "ORDER-10001",
"callback_url": "https://merchant.example/webhook",
},
timeout=15,
)
response.raise_for_status()
payment = response.json()["data"]