WebQRIS API Documentation

Notifikasi QRIS — REST API v3.6
Base URL: https://webqris.com
Authentication: Bearer Token via header Authorization: Bearer YOUR_API_TOKEN
WebSocket: wss://webqris.com/ws (dashboard) | wss://webqris.com/ws/merchant?token=API_TOKEN (merchant)
Ringkasan Create QRIS Check Status Webhook Outbound GoBiz Detector Webhook APK Callback Notify WebSocket Alur
Ringkasan Integrasi
Aplikasi merchant / backend AndaPanggil POST /api/payments/qris/create dan GET /api/payments/:invoiceId/status dengan Authorization: Bearer YOUR_API_TOKEN.
Server Anda menerima notifikasi dari WebQRISSet Webhook URL di merchant detail. Itu adalah endpoint milik sistem Anda sendiri. WebQRIS akan POST ke sana saat pembayaran sukses.
APK / forwarder mengirim notifikasi ke WebQRISPakai POST /api/webhook/payment atau POST /api/callback/notify dengan Callback Secret. Dua endpoint ini adalah endpoint milik WebQRIS.
GoBiz detector server-sideUntuk merchant GoPay Merchant / GoBiz, WebQRIS dapat membaca transaksi QRIS dari GoBiz source yang terhubung. Callback APK otomatis diabaikan pada merchant yang memakai GoBiz detector aktif agar tidak double paid.
Aturan cepat agar tidak tertukar: API Token untuk backend merchant membuat invoice, Webhook URL adalah URL tujuan di server Anda, dan Callback Secret untuk autentikasi notif masuk ke WebQRIS.
Quick Start
  1. Buat merchant di dashboard, lalu generate minimal satu API Token.
  2. Jika ingin sistem Anda menerima status bayar otomatis, isi Webhook URL di merchant detail.
  3. Dari backend merchant Anda, panggil POST /api/payments/qris/create untuk membuat invoice dan tampilkan QR ke pelanggan.
  4. Jika Anda memakai APK Notification Forwarder, arahkan APK ke POST /api/webhook/payment dan isi Callback Secret.
  5. Jika memakai GoBiz detector, hubungkan GoBiz Source di halaman merchant. Webhook outbound ke sistem Anda tetap memakai format payment.paid yang sama.
  6. Setelah payment sukses, WebQRIS akan mengirim webhook outbound ke Webhook URL Anda. Poll status via API hanya opsional.
Istilah Penting
API TokenKredensial untuk backend merchant Anda saat membuat invoice QRIS dan cek status via API.
Webhook URLURL tujuan di server Anda sendiri. WebQRIS akan mengirim notifikasi pembayaran sukses ke URL ini.
Callback SecretSecret untuk autentikasi notif masuk ke WebQRIS dari APK atau forwarder lain.
GoBiz SourceKoneksi akun GoPay Merchant / GoBiz yang dipakai WebQRIS untuk membaca settlement QRIS secara server-side.
payment_method: gobiz_apiStatus pembayaran berasal dari GoBiz detector. Nama issuer e-wallet seperti DANA, OVO, atau SHOPEEPAY disimpan dari data GoBiz dan dipakai pada notifikasi WhatsApp.
Invoice IDID transaksi dari WebQRIS. Dipakai untuk cek status pembayaran.
merchant_order_idID order dari sistem Anda sendiri. Optional, tapi disarankan agar transaksi mudah dicocokkan.
POST /api/payments/qris/create

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

Gunakan endpoint ini dari aplikasi merchant/server untuk membuat invoice QRIS. Pakai bersama header Authorization: Bearer YOUR_API_TOKEN. Ini bukan URL untuk APK Notification Forwarder dan bukan webhook outbound ke sistem Anda.
Headers
AuthorizationBearer YOUR_API_TOKENRequired
Content-Typeapplication/json
Request Body
{
  "amount": 25000,
  "merchant_order_id": "ORDER-001",
  "customer_name": "John Doe"
}
Contoh cURL
curl -X POST 'https://webqris.com/api/payments/qris/create'   -H 'Authorization: Bearer YOUR_API_TOKEN'   -H 'Content-Type: application/json'   -d '{
    "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...",
  "qr_data_url": "data:image/png;base64,iVBORw0KGgo...",
  "amount": 25000,
  "unique_code": 42,
  "total_amount": 25042,
  "expired_at": "2026-03-09T10:30:00.000Z"
}
Contoh Framework Backend: Create Invoice
PHP
<?php

  $payload = json_encode([
    'amount' => 25000,
    'merchant_order_id' => 'ORDER-001',
    'customer_name' => 'John Doe',
  ]);

  $ch = curl_init('https://webqris.com/api/payments/qris/create');
  curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
      'Authorization: Bearer YOUR_API_TOKEN',
      'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => $payload,
  ]);

  $response = curl_exec($ch);
  $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  curl_close($ch);

  $data = json_decode($response, true);
JavaScript / Node.js (Express)
app.post('/payments/create-qris', async (req, res) => {
  const response = await fetch('https://webqris.com/api/payments/qris/create', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer ' + process.env.WEBQRIS_API_TOKEN,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      amount: req.body.amount,
      merchant_order_id: req.body.merchant_order_id,
      customer_name: req.body.customer_name,
    }),
  });

  const data = await response.json();
  return res.status(response.status).json(data);
});
React / Next.js (aman via backend sendiri)
// app/api/webqris/create/route.ts
export async function POST(request: Request) {
  const body = await request.json();

  const response = await fetch('https://webqris.com/api/payments/qris/create', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer ' + process.env.WEBQRIS_API_TOKEN,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(body),
  });

  return Response.json(await response.json(), { status: response.status });
}

// Komponen React memanggil backend Anda sendiri, bukan WebQRIS langsung.
const resp = await fetch('/api/webqris/create', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ amount: 25000, merchant_order_id: 'ORDER-001' }),
});
Laravel
<?php

  namespace AppHttpControllers;

  use IlluminateHttpRequest;
  use IlluminateSupportFacadesHttp;

  class WebqrisPaymentController extends Controller
  {
    public function create(Request $request)
    {
      $response = Http::withToken(config('services.webqris.token'))
        ->post(config('services.webqris.base_url') . '/api/payments/qris/create', [
          'amount' => (int) $request->input('amount'),
          'merchant_order_id' => $request->input('merchant_order_id'),
          'customer_name' => $request->input('customer_name'),
        ]);

      return response()->json($response->json(), $response->status());
    }
  }
CodeIgniter 4
<?php

  namespace AppControllers;

  use CodeIgniterHTTPResponseInterface;
  use ConfigServices;

  class WebqrisPaymentController extends BaseController
  {
    public function create(): ResponseInterface
    {
      $client = Services::curlrequest();
      $response = $client->post(env('webqris.baseUrl') . '/api/payments/qris/create', [
        'headers' => [
          'Authorization' => 'Bearer ' . env('webqris.apiToken'),
          'Content-Type' => 'application/json',
        ],
        'json' => [
          'amount' => (int) $this->request->getJSON(true)['amount'],
          'merchant_order_id' => $this->request->getJSON(true)['merchant_order_id'] ?? null,
          'customer_name' => $this->request->getJSON(true)['customer_name'] ?? null,
        ],
      ]);

      return $this->response
        ->setStatusCode($response->getStatusCode())
        ->setJSON(json_decode($response->getBody(), true));
    }
  }
GET /api/payments/:invoiceId/status

Cek status pembayaran berdasarkan invoice ID.

Headers
AuthorizationBearer YOUR_API_TOKENRequired
Contoh cURL
curl -X GET 'https://webqris.com/api/payments/INV-1710000000-abc123/status'   -H 'Authorization: Bearer YOUR_API_TOKEN'
Response (200)
{
  "success": true,
  "data": {
    "invoice_id": "INV-MERCHANT-1710000000-abc123",
    "merchant_order_id": "ORDER-001",
    "qris_payload": "0002010211...",
    "qr_data_url": "data:image/png;base64,iVBORw0KGgo...",
    "amount": 25000,
    "unique_code": 42,
    "total_amount": 25042,
    "status": "paid",
    "payment_method": "gobiz_api",
    "paid_at": "2026-03-09T10:15:23.000Z",
    "expired_at": "2026-03-09T10:30:00.000Z"
  }
}
Catatan payment_method: nilai gobiz_api berarti pembayaran diproses oleh GoBiz detector. Untuk jalur APK lama, nilainya bisa berupa package aplikasi sumber notifikasi seperti com.dana.id atau com.gojek.gopaymerchant. Field waktu API memakai ISO timestamp. Tampilan WIB dipakai di dashboard dan notifikasi WhatsApp.
Contoh Framework Backend: Check Status
PHP
<?php

  $invoiceId = 'INV-1710000000-abc123';

  $ch = curl_init('https://webqris.com/api/payments/' . urlencode($invoiceId) . '/status');
  curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
      'Authorization: Bearer YOUR_API_TOKEN',
    ],
  ]);

  $response = curl_exec($ch);
  $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  curl_close($ch);

  $data = json_decode($response, true);
JavaScript / Node.js (Express)
app.get('/payments/:invoiceId/status', async (req, res) => {
  const response = await fetch(
    'https://webqris.com/api/payments/' + encodeURIComponent(req.params.invoiceId) + '/status',
    {
      headers: {
        'Authorization': 'Bearer ' + process.env.WEBQRIS_API_TOKEN,
      },
    }
  );

  const data = await response.json();
  return res.status(response.status).json(data);
});
React / Next.js (aman via backend sendiri)
// app/api/webqris/status/[invoiceId]/route.ts
export async function GET(
  _request: Request,
  { params }: { params: { invoiceId: string } }
) {
  const response = await fetch(
    'https://webqris.com/api/payments/' + encodeURIComponent(params.invoiceId) + '/status',
    {
      headers: {
        'Authorization': 'Bearer ' + process.env.WEBQRIS_API_TOKEN,
      },
      cache: 'no-store',
    }
  );

  return Response.json(await response.json(), { status: response.status });
}

// Komponen React cukup memanggil endpoint backend Anda sendiri.
const resp = await fetch('/api/webqris/status/' + invoiceId);
Laravel
<?php

  namespace AppHttpControllers;

  use IlluminateSupportFacadesHttp;

  class WebqrisStatusController extends Controller
  {
    public function show(string $invoiceId)
    {
      $response = Http::withToken(config('services.webqris.token'))
        ->get(config('services.webqris.base_url') . '/api/payments/' . $invoiceId . '/status');

      return response()->json($response->json(), $response->status());
    }
  }
CodeIgniter 4
<?php

  namespace AppControllers;

  use CodeIgniterHTTPResponseInterface;
  use ConfigServices;

  class WebqrisStatusController extends BaseController
  {
    public function show(string $invoiceId): ResponseInterface
    {
      $client = Services::curlrequest();
      $response = $client->get(env('webqris.baseUrl') . '/api/payments/' . $invoiceId . '/status', [
        'headers' => [
          'Authorization' => 'Bearer ' . env('webqris.apiToken'),
        ],
      ]);

      return $this->response
        ->setStatusCode($response->getStatusCode())
        ->setJSON(json_decode($response->getBody(), true));
    }
  }
POST YOUR_WEBHOOK_URL

WebQRIS akan mengirim webhook ke URL yang Anda konfigurasi saat pembayaran berhasil. Ini adalah endpoint milik sistem Anda sendiri, bukan endpoint milik WebQRIS.

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": "gobiz_api",
    "funding_source": "DANA",
    "paid_at": "2026-03-09T10:15:23.000Z"
  }
}
Webhook outbound memakai format yang sama untuk semua sumber paid: APK Notification Forwarder, callback sederhana, manual confirm, maupun GoBiz detector. Pada GoBiz detector, payment_method bernilai gobiz_api.
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.

Contoh Receiver Webhook (PHP)
<?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);
    echo json_encode(['success' => false, 'message' => 'Invalid signature']);
    exit;
}

$payload = json_decode($body, true);
if (!is_array($payload) || ($payload['event'] ?? '') !== 'payment.paid') {
    http_response_code(400);
    echo json_encode(['success' => false, 'message' => 'Invalid event']);
    exit;
}

$payment = $payload['data'] ?? [];
$invoiceId = $payment['invoice_id'] ?? null;
$merchantOrderId = $payment['merchant_order_id'] ?? null;
$status = $payment['status'] ?? null;

// TODO: cocokan invoice ke database Anda, tandai paid, lalu proses order.

http_response_code(200);
header('Content-Type: application/json');
echo json_encode([
    'success' => true,
    'invoice_id' => $invoiceId,
    'merchant_order_id' => $merchantOrderId,
    'status' => $status,
]);
Contoh Receiver Webhook (Node.js / Express)
const express = require('express');
const crypto = require('crypto');

const app = express();

app.post('/webhook/qris', express.raw({ type: 'application/json' }), (req, res) => {
  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).json({ success: false, message: 'Invalid signature' });
  }

  const payload = JSON.parse(body);
  if (payload.event !== 'payment.paid') {
    return res.status(400).json({ success: false, message: 'Invalid event' });
  }

  const payment = payload.data || {};
  const invoiceId = payment.invoice_id;
  const merchantOrderId = payment.merchant_order_id;
  const status = payment.status;

  // TODO: cocokan invoice ke database Anda, tandai paid, lalu proses order.

  return res.status(200).json({
    success: true,
    invoice_id: invoiceId,
    merchant_order_id: merchantOrderId,
    status,
  });
});
Contoh Receiver Webhook: React / Next.js
import crypto from 'node:crypto';

// app/api/webhook/qris/route.ts
export async function POST(request: Request) {
  const signature = request.headers.get('x-webhook-signature') ?? '';
  const body = await request.text();
  const expected = crypto
    .createHmac('sha256', process.env.WEBHOOK_SECRET || '')
    .update(body)
    .digest('hex');

  if (signature !== expected) {
    return Response.json({ success: false, message: 'Invalid signature' }, { status: 401 });
  }

  const payload = JSON.parse(body);
  if (payload.event !== 'payment.paid') {
    return Response.json({ success: false, message: 'Invalid event' }, { status: 400 });
  }

  const payment = payload.data || {};

  // TODO: update order di database Anda.

  return Response.json({
    success: true,
    invoice_id: payment.invoice_id,
    merchant_order_id: payment.merchant_order_id,
    status: payment.status,
  });
}

// React frontend tidak menerima webhook langsung.
// Frontend cukup membaca status dari backend Anda sendiri.
Contoh Receiver Webhook: Laravel
<?php

  use IlluminateHttpRequest;
  use IlluminateSupportFacadesLog;
  use IlluminateSupportFacadesRoute;

  Route::post('/webhook/qris', function (Request $request) {
    $signature = $request->header('X-Webhook-Signature', '');
    $body = $request->getContent();
    $expected = hash_hmac('sha256', $body, env('WEBHOOK_SECRET'));

    if (!hash_equals($expected, $signature)) {
      return response()->json([
        'success' => false,
        'message' => 'Invalid signature',
      ], 401);
    }

    $payload = json_decode($body, true);
    if (($payload['event'] ?? '') !== 'payment.paid') {
      return response()->json([
        'success' => false,
        'message' => 'Invalid event',
      ], 400);
    }

    $payment = $payload['data'] ?? [];

    // TODO: cocokkan invoice/order di database Anda.
    Log::info('WebQRIS payment paid', $payment);

    return response()->json([
      'success' => true,
      'invoice_id' => $payment['invoice_id'] ?? null,
      'merchant_order_id' => $payment['merchant_order_id'] ?? null,
      'status' => $payment['status'] ?? null,
    ]);
  });
Route + Controller Laravel (Versi Terpisah)
// routes/api.php
  use AppHttpControllersWebqrisWebhookController;

  Route::post('/webhook/qris', [WebqrisWebhookController::class, 'paid']);

  // app/Http/Controllers/WebqrisWebhookController.php
  namespace AppHttpControllers;

  use IlluminateHttpRequest;
  use IlluminateSupportFacadesLog;

  class WebqrisWebhookController extends Controller
  {
    public function paid(Request $request)
    {
      $signature = $request->header('X-Webhook-Signature', '');
      $body = $request->getContent();
      $expected = hash_hmac('sha256', $body, env('WEBHOOK_SECRET'));

      if (!hash_equals($expected, $signature)) {
        return response()->json(['success' => false, 'message' => 'Invalid signature'], 401);
      }

      $payload = json_decode($body, true);
      $payment = $payload['data'] ?? [];
      Log::info('WebQRIS payment paid', $payment);

      return response()->json(['success' => true]);
    }
  }
Contoh Receiver Webhook: CodeIgniter 4
<?php

  namespace AppControllers;

  use CodeIgniterHTTPResponseInterface;

  class WebqrisWebhook extends BaseController
  {
    public function paid(): ResponseInterface
    {
      $signature = $this->request->getHeaderLine('X-Webhook-Signature');
      $body = $this->request->getBody();
      $expected = hash_hmac('sha256', $body, env('webqris.webhookSecret'));

      if (!hash_equals($expected, $signature)) {
        return $this->response
          ->setStatusCode(401)
          ->setJSON([
            'success' => false,
            'message' => 'Invalid signature',
          ]);
      }

      $payload = json_decode($body, true);
      if (($payload['event'] ?? '') !== 'payment.paid') {
        return $this->response
          ->setStatusCode(400)
          ->setJSON([
            'success' => false,
            'message' => 'Invalid event',
          ]);
      }

      $payment = $payload['data'] ?? [];

      // TODO: cocokkan invoice/order di database Anda.

      return $this->response->setJSON([
        'success' => true,
        'invoice_id' => $payment['invoice_id'] ?? null,
        'merchant_order_id' => $payment['merchant_order_id'] ?? null,
        'status' => $payment['status'] ?? null,
      ]);
    }
  }
Route + Controller CodeIgniter 4 (Versi Terpisah)
// app/Config/Routes.php
    $routes->post('webhook/qris', 'WebqrisWebhook::paid');

    // app/Controllers/WebqrisWebhook.php
    namespace AppControllers;

    class WebqrisWebhook extends BaseController
    {
      public function paid()
      {
        $signature = $this->request->getHeaderLine('X-Webhook-Signature');
        $body = $this->request->getBody();
        $expected = hash_hmac('sha256', $body, env('webqris.webhookSecret'));

        if (!hash_equals($expected, $signature)) {
          return $this->response->setStatusCode(401)->setJSON([
            'success' => false,
            'message' => 'Invalid signature',
          ]);
        }

        $payload = json_decode($body, true);
        $payment = $payload['data'] ?? [];

        return $this->response->setJSON([
          'success' => true,
          'invoice_id' => $payment['invoice_id'] ?? null,
        ]);
      }
    }
INFO GoBiz Detector Server-side

GoBiz detector adalah opsi deteksi pembayaran QRIS untuk merchant GoPay Merchant / GoBiz. Merchant tetap membuat invoice lewat POST /api/payments/qris/create, lalu WebQRIS membaca transaksi QRIS dari GoBiz Source yang terhubung dan menandai invoice sebagai paid jika nominal total serta waktu transaksi valid.

Transparansi: fitur ini adalah integrasi detector server-side terhadap dashboard/portal merchant GoBiz. WebQRIS tetap bukan payment gateway dan tidak menampung dana. Untuk jalur resmi penuh berbasis order ID, merchant dapat memakai GoBiz Open API/Midtrans bila sudah memiliki akses resmi.
Syntax API tidak berubah: aplikasi merchant tetap memakai endpoint create/status dan webhook outbound WebQRIS yang sama. Yang berubah hanya sumber deteksi pembayaran: invoice ditandai paid oleh GoBiz detector, sehingga payment_method menjadi gobiz_api dan issuer QRIS dikirim sebagai funding_source pada webhook/WebSocket.
Yang berubah saat merchant memakai GoBiz detector
payment_methodMenjadi gobiz_api saat invoice paid dari GoBiz detector.
issuerIssuer QRIS seperti DANA, OVO, atau AIRPAY SHOPEE disimpan di data internal GoBiz dan dipakai untuk notifikasi WhatsApp, misalnya QRIS DANA.
matchingInvoice dicocokkan memakai total_amount unik dalam scope GoBiz Source, lalu divalidasi terhadap waktu transaksi agar transaksi lama tidak menempel ke QRIS baru.
APK callback/api/webhook/payment dan /api/callback/notify akan mengembalikan ignored: true untuk merchant yang sudah memakai GoBiz detector aktif.
webhook outboundTetap memakai event payment.paid dan signature HMAC yang sama seperti jalur APK/callback.
Contoh data paid GoBiz
{
  "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": "gobiz_api",
    "funding_source": "DANA",
    "paid_at": "2026-03-09T10:15:23.000Z"
  }
}
Konfigurasi di dashboard
  1. Buka halaman merchant yang akan memakai GoBiz detector.
  2. Isi atau pilih GoBiz Source Group sesuai akun GoBiz merchant tersebut.
  3. Masukkan GoBiz Merchant ID / NMID, email GoBiz, dan password/token sesuai instruksi form.
  4. Jalankan Test Login dan Test Transaksi sampai transaksi terbaca.
  5. Setelah aktif, invoice baru tetap dibuat lewat API WebQRIS yang sama; detector akan mem-paid-kan invoice saat settlement GoBiz cocok.
POST /api/webhook/payment

Endpoint milik WebQRIS 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).

Jika merchant sudah terhubung ke GoBiz detector aktif, endpoint ini sengaja mengabaikan notifikasi APK agar satu pembayaran tidak diproses dua jalur.
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"
}
Contoh cURL Test
curl -X POST 'https://webqris.com/api/webhook/payment'   -H 'Authorization: Bearer YOUR_CALLBACK_SECRET'   -H 'Content-Type: application/json'   -d '{
    "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 — Diabaikan karena GoBiz aktif
{
  "success": true,
  "ignored": true,
  "message": "Merchant memakai GoBiz detector; APK notification diabaikan"
}
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

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

Jika merchant sudah memakai GoBiz detector aktif, callback sederhana ini juga diabaikan. Sumber paid yang dipakai adalah settlement GoBiz agar tidak ada double processing.
Headers
X-Callback-Key<CALLBACK_SECRET>Required
Content-Typeapplication/json
Request Body
{
  "amount": 25042,
  "sender_name": "JOHN DOE",
  "reference": "optional-reference"
}
Contoh cURL
curl -X POST 'https://webqris.com/api/callback/notify'   -H 'X-Callback-Key: YOUR_CALLBACK_SECRET'   -H 'Content-Type: application/json'   -d '{
    "amount": 25042,
    "sender_name": "JOHN DOE",
    "reference": "optional-reference"
  }'
Contoh Backend Kirim Callback Notify
PHP
<?php

  $payload = json_encode([
    'amount' => 25042,
    'sender_name' => 'JOHN DOE',
    'reference' => 'optional-reference',
  ]);

  $ch = curl_init('https://webqris.com/api/callback/notify');
  curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
      'X-Callback-Key: YOUR_CALLBACK_SECRET',
      'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => $payload,
  ]);

  $response = curl_exec($ch);
  curl_close($ch);
JavaScript / Node.js (Express)
app.post('/forward-qris-notify', async (req, res) => {
  const response = await fetch('https://webqris.com/api/callback/notify', {
    method: 'POST',
    headers: {
      'X-Callback-Key': process.env.WEBQRIS_CALLBACK_SECRET,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      amount: req.body.amount,
      sender_name: req.body.sender_name,
      reference: req.body.reference,
    }),
  });

  const data = await response.json();
  return res.status(response.status).json(data);
});
React / Next.js (aman via backend sendiri)
// app/api/webqris/callback-notify/route.ts
export async function POST(request: Request) {
  const body = await request.json();

  const response = await fetch('https://webqris.com/api/callback/notify', {
    method: 'POST',
    headers: {
      'X-Callback-Key': process.env.WEBQRIS_CALLBACK_SECRET || '',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(body),
  });

  return Response.json(await response.json(), { status: response.status });
}

// React frontend memanggil backend Anda sendiri, jangan expose callback secret ke browser.
Laravel
<?php

  use IlluminateSupportFacadesHttp;

  $response = Http::withHeaders([
    'X-Callback-Key' => config('services.webqris.callback_secret'),
  ])->post(config('services.webqris.base_url') . '/api/callback/notify', [
    'amount' => 25042,
    'sender_name' => 'JOHN DOE',
    'reference' => 'optional-reference',
  ]);

  $data = $response->json();
CodeIgniter 4
<?php

  $client = ConfigServices::curlrequest();
  $response = $client->post(env('webqris.baseUrl') . '/api/callback/notify', [
    'headers' => [
      'X-Callback-Key' => env('webqris.callbackSecret'),
      'Content-Type' => 'application/json',
    ],
    'json' => [
      'amount' => 25042,
      'sender_name' => 'JOHN DOE',
      'reference' => 'optional-reference',
    ],
  ]);

  $data = json_decode($response->getBody(), true);
Response
{
  "success": true,
  "invoice_id": "INV-1710000000-abc123"
}
Response — Diabaikan karena GoBiz aktif
{
  "success": true,
  "ignored": true,
  "message": "Merchant memakai GoBiz detector; APK notification diabaikan"
}
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, GoBiz detector, 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",
    "funding_source": "DANA",
    "source": "gobiz_api",
    "status": "paid"
  },
  "ts": 1710000000000
}
Ping/Pong

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

Contoh (JavaScript)
const ws = new WebSocket('wss://webqris.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. Pilih sumber deteksi pembayaran: APK Notification Forwarder, callback sederhana, atau GoBiz detector server-side.
  5. Kirim request POST /api/payments/qris/create dari backend Anda
  6. Tampilkan QR Code dari qris_payload ke pelanggan
  7. Jika memakai APK / notif forwarder, kirim notifikasi masuk ke endpoint WebQRIS: POST /api/webhook/payment atau POST /api/callback/notify
  8. Jika memakai GoBiz detector, hubungkan GoBiz Source di dashboard merchant. Untuk merchant tersebut, callback APK akan diabaikan dan settlement GoBiz menjadi sumber paid.
  9. Poll status via GET /api/payments/:invoiceId/status (opsional)
  10. Terima webhook outbound dari WebQRIS di Webhook URL milik sistem Anda saat status menjadi paid
  11. Verifikasi HMAC signature dan proses pembayaran
WebQRIS v3.6 — Dashboard