https://webqris.comAuthorization: Bearer YOUR_API_TOKENwss://webqris.com/ws (dashboard) | wss://webqris.com/ws/merchant?token=API_TOKEN (merchant)
| Aplikasi merchant / backend Anda | Panggil POST /api/payments/qris/create dan GET /api/payments/:invoiceId/status dengan Authorization: Bearer YOUR_API_TOKEN. |
| Server Anda menerima notifikasi dari WebQRIS | Set 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 WebQRIS | Pakai POST /api/webhook/payment atau POST /api/callback/notify dengan Callback Secret. Dua endpoint ini adalah endpoint milik WebQRIS. |
| GoBiz detector server-side | Untuk 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. |
API Token untuk backend merchant membuat invoice, Webhook URL adalah URL tujuan di server Anda, dan Callback Secret untuk autentikasi notif masuk ke WebQRIS.
POST /api/payments/qris/create untuk membuat invoice dan tampilkan QR ke pelanggan.POST /api/webhook/payment dan isi Callback Secret.payment.paid yang sama.API Token | Kredensial untuk backend merchant Anda saat membuat invoice QRIS dan cek status via API. |
Webhook URL | URL tujuan di server Anda sendiri. WebQRIS akan mengirim notifikasi pembayaran sukses ke URL ini. |
Callback Secret | Secret untuk autentikasi notif masuk ke WebQRIS dari APK atau forwarder lain. |
GoBiz Source | Koneksi akun GoPay Merchant / GoBiz yang dipakai WebQRIS untuk membaca settlement QRIS secara server-side. |
payment_method: gobiz_api | Status pembayaran berasal dari GoBiz detector. Nama issuer e-wallet seperti DANA, OVO, atau SHOPEEPAY disimpan dari data GoBiz dan dipakai pada notifikasi WhatsApp. |
Invoice ID | ID transaksi dari WebQRIS. Dipakai untuk cek status pembayaran. |
merchant_order_id | ID order dari sistem Anda sendiri. Optional, tapi disarankan agar transaksi mudah dicocokkan. |
Buat transaksi QRIS baru. Akan mengembalikan QRIS payload yang bisa di-generate menjadi QR code.
Authorization: Bearer YOUR_API_TOKEN. Ini bukan URL untuk APK Notification Forwarder dan bukan webhook outbound ke sistem Anda.
Authorization | Bearer YOUR_API_TOKEN | Required |
Content-Type | application/json |
{
"amount": 25000,
"merchant_order_id": "ORDER-001",
"customer_name": "John Doe"
}
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"
}'
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...",
"qr_data_url": "data:image/png;base64,iVBORw0KGgo...",
"amount": 25000,
"unique_code": 42,
"total_amount": 25042,
"expired_at": "2026-03-09T10:30:00.000Z"
}
<?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);
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);
});
// 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' }),
});
<?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());
}
}
<?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));
}
}
Cek status pembayaran berdasarkan invoice ID.
Authorization | Bearer YOUR_API_TOKEN | Required |
curl -X GET 'https://webqris.com/api/payments/INV-1710000000-abc123/status' -H 'Authorization: Bearer YOUR_API_TOKEN'
{
"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"
}
}
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.
<?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);
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);
});
// 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);
<?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());
}
}
<?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));
}
}
WebQRIS akan mengirim webhook ke URL yang Anda konfigurasi saat pembayaran berhasil. Ini adalah endpoint milik sistem Anda sendiri, bukan endpoint milik WebQRIS.
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": "gobiz_api",
"funding_source": "DANA",
"paid_at": "2026-03-09T10:15:23.000Z"
}
}
payment_method bernilai gobiz_api.
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.
<?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,
]);
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,
});
});
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.
<?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,
]);
});
// 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]);
}
}
<?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,
]);
}
}
// 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,
]);
}
}
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.
paid oleh GoBiz detector, sehingga payment_method menjadi gobiz_api dan issuer QRIS dikirim sebagai funding_source pada webhook/WebSocket.
payment_method | Menjadi gobiz_api saat invoice paid dari GoBiz detector. |
issuer | Issuer QRIS seperti DANA, OVO, atau AIRPAY SHOPEE disimpan di data internal GoBiz dan dipakai untuk notifikasi WhatsApp, misalnya QRIS DANA. |
matching | Invoice 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 outbound | Tetap memakai event payment.paid dan signature HMAC yang sama seperti jalur APK/callback. |
{
"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"
}
}
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).
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"
}
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"
}'
{
"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": true,
"ignored": true,
"message": "Merchant memakai GoBiz detector; APK notification diabaikan"
}
{
"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"
}
Endpoint milik WebQRIS dengan 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"
}
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"
}'
<?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);
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);
});
// 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.
<?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();
<?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);
{
"success": true,
"invoice_id": "INV-1710000000-abc123"
}
{
"success": true,
"ignored": true,
"message": "Merchant memakai GoBiz detector; APK notification diabaikan"
}
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, GoBiz detector, 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",
"funding_source": "DANA",
"source": "gobiz_api",
"status": "paid"
},
"ts": 1710000000000
}
Kirim pesan ping → server membalas pong. Gunakan untuk keep-alive.
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);
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 POST /api/callback/notifyGET /api/payments/:invoiceId/status (opsional)paid