https://qrispayment.riyadhul.comAuthorization: Bearer YOUR_API_TOKENwss://qrispayment.riyadhul.com/ws (dashboard) | wss://qrispayment.riyadhul.com/ws/merchant?token=API_TOKEN (merchant)
/api/payments/qris/create
Buat transaksi QRIS baru. Akan mengembalikan QRIS payload yang bisa di-generate menjadi QR code.
Authorization | Bearer YOUR_API_TOKEN | Required |
Content-Type | application/json |
{
"amount": 25000,
"merchant_order_id": "ORDER-001",
"customer_name": "John Doe"
}
amount | integer | Nominal pembayaran (Rupiah) | Required |
merchant_order_id | string | ID order dari merchant | Optional |
customer_name | string | Nama pelanggan | Optional |
{
"success": true,
"invoice_id": "INV-1710000000-abc123",
"qris_payload": "0002010211...",
"amount": 25000,
"unique_code": 42,
"total_amount": 25042,
"expired_at": "2026-03-09T10:30:00.000Z"
}
/api/payments/:invoiceId/status
Cek status pembayaran berdasarkan invoice ID.
Authorization | Bearer YOUR_API_TOKEN | Required |
{
"success": true,
"data": {
"invoice_id": "INV-MERCHANT-1710000000-abc123",
"merchant_order_id": "ORDER-001",
"qris_payload": "0002010211...",
"amount": 25000,
"unique_code": 42,
"total_amount": 25042,
"status": "paid",
"payment_method": "com.dana.id",
"paid_at": "2026-03-09T10:15:23.000Z",
"expired_at": "2026-03-09T10:30:00.000Z"
}
}
YOUR_WEBHOOK_URL
WebQRIS akan mengirim webhook ke URL yang Anda konfigurasi saat pembayaran berhasil.
X-Webhook-Signature | HMAC-SHA256 signature dari body dengan webhook_secret |
X-Signature | Sama dengan di atas (legacy header, untuk backward compatibility) |
Content-Type | application/json |
User-Agent | WebQRIS-Webhook/1.0 |
{
"event": "payment.paid",
"data": {
"invoice_id": "INV-MERCHANT-1710000000-abc123",
"merchant_order_id": "ORDER-001",
"amount": 25000,
"unique_code": 42,
"total_amount": 25042,
"customer_name": "John Doe",
"status": "paid",
"payment_method": "com.dana.id",
"paid_at": "2026-03-09T10:15:23.000Z"
}
}
Webhook akan di-retry hingga 3× jika gagal (HTTP status non-2xx atau timeout). Delay antar retry menggunakan exponential backoff: 2s, 4s, 8s. Timeout per request: 10 detik.
$signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
$body = file_get_contents('php://input');
$expected = hash_hmac('sha256', $body, 'YOUR_WEBHOOK_SECRET');
if (!hash_equals($expected, $signature)) {
http_response_code(401);
exit('Invalid signature');
}
$data = json_decode($body, true);
// Process payment...
const crypto = require('crypto');
// IMPORTANT: gunakan raw body string untuk HMAC
// di Express, pakai middleware raw untuk route webhook
// app.post('/webhook', express.raw({ type: 'application/json' }), ...)
const signature = req.headers['x-webhook-signature'];
const body = req.body.toString('utf8');
const expected = crypto.createHmac('sha256', process.env.WEBHOOK_SECRET)
.update(body).digest('hex');
if (signature !== expected) return res.status(401).send('Invalid');
const data = JSON.parse(body);
// Process payment...
/api/webhook/payment
Endpoint untuk menerima notifikasi e-wallet yang di-forward dari APK Notification Forwarder. Server akan mem-parse nominal dari field message, mencari payment pending dengan total_amount yang sama (per-merchant), lalu mengubah status menjadi paid dan mengirim webhook outbound ke sistem merchant (jika dikonfigurasi).
Authorization | Bearer <CALLBACK_SECRET> | Required |
Content-Type | application/json |
{
"app": "com.dana.id",
"title": "DANA",
"message": "Kamu berhasil menerima Rp25.042 dari JOHN DOE",
"timestamp": "2026-03-21T10:15:23.000Z",
"device_id": "device-01",
"notif_id": "1234567890",
"event_hash": "..."
}
Untuk test auth & koneksi tanpa memproses pembayaran, kirim event_hash bernilai test.
{
"message": "test",
"event_hash": "test"
}
{
"success": true,
"invoice_id": "INV-1710000000-abc123",
"parsed_amount": 25042,
"sender_name": "JOHN DOE"
}
event_hash: "test"){
"success": true,
"message": "Koneksi berhasil! Auth valid ✓ (Merchant: Toko ABC)",
"test": true
}
{
"success": false,
"message": "Duplicate notification",
"notif_id": "1234567890"
}
{
"success": false,
"message": "Tidak dapat mendeteksi nominal dari notifikasi",
"raw_message": "Pesan yang tidak dikenal"
}
{
"success": false,
"message": "No matching pending payment",
"parsed_amount": 25042,
"merchant": "Toko ABC"
}
/api/callback/notify
Format sederhana untuk menandai payment sebagai paid berdasarkan nominal amount (harus sama dengan total_amount). Cocok untuk integrasi non-APK.
X-Callback-Key | <CALLBACK_SECRET> | Required |
Content-Type | application/json |
{
"amount": 25042,
"sender_name": "JOHN DOE",
"reference": "optional-reference"
}
{
"success": true,
"invoice_id": "INV-1710000000-abc123"
}
/ws & /ws/merchant?token=API_TOKEN
WebSocket untuk menerima event realtime. /ws untuk dashboard admin, /ws/merchant untuk merchant-specific (auth via query param token).
new_payment | Payment baru dibuat |
payment_paid | Payment berhasil dibayar (dari APK, callback, atau manual confirm) |
payments_expired | Payment expired (batch, tiap 60 detik) |
webhook_delivered | Webhook outbound berhasil terkirim ke merchant |
{
"event": "payment_paid",
"data": {
"id": 123,
"invoice_id": "INV-1710000000-abc123",
"amount": 25000,
"total_amount": 25042,
"unique_code": 42,
"merchant_name": "Toko ABC",
"sender_name": "JOHN DOE",
"status": "paid"
},
"ts": 1710000000000
}
Kirim pesan ping → server membalas pong. Gunakan untuk keep-alive.
const ws = new WebSocket('wss://qrispayment.riyadhul.com/ws/merchant?token=YOUR_API_TOKEN');
ws.onmessage = (e) => {
const { event, data } = JSON.parse(e.data);
if (event === 'payment_paid') {
console.log('Pembayaran masuk:', data.invoice_id, data.total_amount);
}
};
// Keep-alive
setInterval(() => ws.send('ping'), 30000);
Semua endpoint mengembalikan format error yang konsisten:
{
"success": false,
"message": "Deskripsi error"
}
400 | Bad Request — parameter tidak valid atau kurang |
401 | Unauthorized — token tidak valid atau tidak ada |
404 | Not Found — resource tidak ditemukan |
422 | Unprocessable — gagal memproses data (contoh: nominal tidak terdeteksi) |
500 | Internal Server Error |
503 | Service Unavailable — QRIS belum dikonfigurasi atau kode unik habis |
POST /api/payments/qris/create dari backend Andaqris_payload ke pelangganPOST /api/webhook/payment (atau gunakan POST /api/callback/notify)GET /api/payments/:invoiceId/status (opsional)paid