WebQRIS API Documentation

Payment Gateway — REST API v1.0
Base URL: https://qrispayment.riyadhul.com
Authentication: Bearer Token via header Authorization: Bearer YOUR_API_TOKEN
WebSocket: wss://qrispayment.riyadhul.com/ws (dashboard) | wss://qrispayment.riyadhul.com/ws/merchant?token=API_TOKEN (merchant)
POST /api/payments/qris/create

Buat transaksi QRIS baru. Akan mengembalikan QRIS payload yang bisa di-generate menjadi QR code.

Headers
AuthorizationBearer YOUR_API_TOKENRequired
Content-Typeapplication/json
Request Body
{
  "amount": 25000,
  "merchant_order_id": "ORDER-001",
  "customer_name": "John Doe"
}
amountintegerNominal pembayaran (Rupiah)Required
merchant_order_idstringID order dari merchantOptional
customer_namestringNama pelangganOptional
Response (201)
{
  "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"
}
GET /api/payments/:invoiceId/status

Cek status pembayaran berdasarkan invoice ID.

Headers
AuthorizationBearer YOUR_API_TOKENRequired
Response (200)
{
  "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"
  }
}
POST YOUR_WEBHOOK_URL

WebQRIS akan mengirim webhook ke URL yang Anda konfigurasi saat pembayaran berhasil.

Headers
X-Webhook-SignatureHMAC-SHA256 signature dari body dengan webhook_secret
X-SignatureSama dengan di atas (legacy header, untuk backward compatibility)
Content-Typeapplication/json
User-AgentWebQRIS-Webhook/1.0
Webhook Body
{
  "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"
  }
}
Retry Policy

Webhook akan di-retry hingga jika gagal (HTTP status non-2xx atau timeout). Delay antar retry menggunakan exponential backoff: 2s, 4s, 8s. Timeout per request: 10 detik.

Validasi Signature (PHP)
$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...
Validasi Signature (Node.js / Express)
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...
POST /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).

Headers
AuthorizationBearer <CALLBACK_SECRET>Required
Content-Typeapplication/json
Request Body (contoh)
{
  "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": "..."
}
Test Koneksi

Untuk test auth & koneksi tanpa memproses pembayaran, kirim event_hash bernilai test.

{
  "message": "test",
  "event_hash": "test"
}
Response (contoh)
{
  "success": true,
  "invoice_id": "INV-1710000000-abc123",
  "parsed_amount": 25042,
  "sender_name": "JOHN DOE"
}
Response — Test Mode (event_hash: "test")
{
  "success": true,
  "message": "Koneksi berhasil! Auth valid ✓ (Merchant: Toko ABC)",
  "test": true
}
Response — Duplicate Notification
{
  "success": false,
  "message": "Duplicate notification",
  "notif_id": "1234567890"
}
Response — Gagal Parse Nominal (422)
{
  "success": false,
  "message": "Tidak dapat mendeteksi nominal dari notifikasi",
  "raw_message": "Pesan yang tidak dikenal"
}
Response — Tidak Ada Payment Cocok (404)
{
  "success": false,
  "message": "No matching pending payment",
  "parsed_amount": 25042,
  "merchant": "Toko ABC"
}
POST /api/callback/notify

Format sederhana untuk menandai payment sebagai paid berdasarkan nominal amount (harus sama dengan total_amount). Cocok untuk integrasi non-APK.

Headers
X-Callback-Key<CALLBACK_SECRET>Required
Content-Typeapplication/json
Request Body
{
  "amount": 25042,
  "sender_name": "JOHN DOE",
  "reference": "optional-reference"
}
Response
{
  "success": true,
  "invoice_id": "INV-1710000000-abc123"
}
WS /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).

Events
new_paymentPayment baru dibuat
payment_paidPayment berhasil dibayar (dari APK, callback, atau manual confirm)
payments_expiredPayment expired (batch, tiap 60 detik)
webhook_deliveredWebhook outbound berhasil terkirim ke merchant
Format Message
{
  "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
}
Ping/Pong

Kirim pesan ping → server membalas pong. Gunakan untuk keep-alive.

Contoh (JavaScript)
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);
Error Responses

Semua endpoint mengembalikan format error yang konsisten:

{
  "success": false,
  "message": "Deskripsi error"
}
400Bad Request — parameter tidak valid atau kurang
401Unauthorized — token tidak valid atau tidak ada
404Not Found — resource tidak ditemukan
422Unprocessable — gagal memproses data (contoh: nominal tidak terdeteksi)
500Internal Server Error
503Service Unavailable — QRIS belum dikonfigurasi atau kode unik habis
Alur Integrasi
  1. Register & login ke WebQRIS Dashboard
  2. Buat Merchant dan generate API Token
  3. Set Webhook URL untuk menerima notifikasi pembayaran
  4. Kirim request POST /api/payments/qris/create dari backend Anda
  5. Tampilkan QR Code dari qris_payload ke pelanggan
  6. Terima notifikasi pembayaran via APK ke POST /api/webhook/payment (atau gunakan POST /api/callback/notify)
  7. Poll status via GET /api/payments/:invoiceId/status (opsional)
  8. Terima webhook outbound dari WebQRIS di sistem Anda saat status menjadi paid
  9. Verifikasi HMAC signature dan proses pembayaran
WebQRIS Payment Gateway v1.0 — Dashboard