Integrasikan QRIS, Virtual Account, dan E-Wallet ke website atau aplikasi kamu. Halaman bayar white-label, nominal sinkron dengan aplikasi pembayaran, dan status real-time.
https://www.ruangnotif.com/api/v1
Semua endpoint di bawah memakai prefix ini. Request & response memakai JSON (Content-Type: application/json).
Saat mengintegrasikan payment gateway di website/aplikasi kamu, sediakan pilihan mode (Sandbox / Production). Mode menentukan apakah transaksi bersifat uji coba atau live.
Aturan integrasi (wajib dipatuhi)
"mode":"production" (atau tanpa field mode). Pelanggan diarahkan ke payment_url halaman bayar production (transaksi asli, memengaruhi saldo merchant setelah berhasil)."mode":"sandbox". Pelanggan diarahkan ke payment_url halaman bayar sandbox (transaksi tidak asli: tidak mengkredit saldo akun user/merchant).| Sandbox | Production | |
|---|---|---|
| Field API | "mode": "sandbox" |
"mode": "production" (default) |
| order_id | Otomatis berprefix SANDBOX- jika belum ada |
Sesuai request kamu |
| Halaman bayar | payment_url — alur QR/VA/timer sama, label uji coba |
payment_url — transaksi live |
| Saldo merchant | Tidak berubah | Bertambah setelah status berhasil |
| Merchant menunggu review | Boleh (wajib untuk uji review) | Ditolak (403) sampai merchant aktif |
| Merchant aktif | Boleh (tetap untuk regression test) | Boleh |
| Kredensial provider | Sandbox (dikonfigurasi admin) | Production |
Contoh di backend developer:
jika user memilih Sandbox di panel kamu, kirim body dengan "mode":"sandbox";
jika Production, kirim "mode":"production" atau omit field tersebut.
Jangan memakai mode production untuk pelanggan sungguhan sebelum merchant disetujui dan uji sandbox selesai.
Alternatif uji tanpa API: menu Sandbox di dashboard akun RuangNotif (setelah login) — hasilnya sama-sama tidak memengaruhi saldo.
Setiap request wajib menyertakan 2 header. Secret Key jangan pernah dipakai di sisi browser — selalu panggil API dari backend kamu.
X-Api-Key
API Key merchant (production atau sandbox — lihat tabel di bawah).
X-Signature
HMAC-SHA256 dari body JSON mentah (raw string) yang sama persis dikirim di request, ditandatangani dengan Secret Key pasangan dari API Key tersebut.
| Merchant Production | Merchant Sandbox | |
|---|---|---|
| Cara buat | Menu Merchant → Tambah Merchant (butuh KYC & approval admin) | Menu Sandbox → Buat Merchant Sandbox (tanpa KYC, langsung aktif) |
| API Key | pk_live_... |
pk_test_... |
| Secret Key | sk_live_... |
sk_test_... |
| Header X-Api-Key | Isi pk_live_... |
Isi pk_test_... |
| Signature | HMAC-SHA256 body + sk_live_... |
HMAC-SHA256 body + sk_test_... |
| Mode API | production (default) atau sandbox saat uji |
Selalu dipaksa sandbox (tidak bisa production) |
| Saldo | Transaksi production mengkredit saldo | Tidak pernah mengkredit saldo |
| Kode merchant | MCH-XXXXXX |
SBX-XXXXXX |
pk_test_... harus memakai sk_test_... dari merchant sandbox yang sama.
Secret Key hanya ditampilkan sekali saat pembuatan merchant — simpan di environment backend (.env), jangan di-commit ke git.
Contoh signature (PHP) — production
$apiKey = 'pk_live_xxxxxxxx';
$secretKey = 'sk_live_xxxxxxxx';
$body = json_encode([
'order_id' => 'INV-001',
'amount' => 50000,
'method' => 'qris',
'mode' => 'production',
], JSON_UNESCAPED_SLASHES);
$signature = hash_hmac('sha256', $body, $secretKey);
// Header: X-Api-Key: {apiKey}
// Header: X-Signature: {signature}
Contoh signature (PHP) — merchant sandbox
$apiKey = 'pk_test_xxxxxxxx'; // dari Sandbox → Merchant Sandbox
$secretKey = 'sk_test_xxxxxxxx';
$body = json_encode([
'order_id' => 'TEST-001',
'amount' => 10000,
'method' => 'qris',
'mode' => 'sandbox',
], JSON_UNESCAPED_SLASHES);
$signature = hash_hmac('sha256', $body, $secretKey);
Pastikan string yang di-hash identik dengan body yang dikirim (urutan field & encoding). SDK resmi sudah menangani ini otomatis.
/api/v1/transaction
Membuat transaksi pembayaran baru. Respons berisi payment_url — arahkan pelanggan ke URL tersebut untuk bayar (QRIS / VA / e-wallet, white-label, countdown timer otomatis).
Endpoint ini selalu production (transaksi asli). Untuk uji coba tanpa memengaruhi saldo — terutama saat pengajuan merchant masih di-review — gunakan mode Sandbox (lihat Production vs Sandbox).
Biaya channel selalu ditanggung pelanggan.
amount di request = nominal yang ingin kamu terima (request merchant).amount di respons = total yang harus dibayar pelanggan (sama dengan halaman checkout & aplikasi bank/e-wallet).Body Request
| Field | Tipe | Wajib | Keterangan |
|---|---|---|---|
order_id | string | Ya | ID unik dari sistem kamu, maks. 100 karakter. Dipakai sebagai kunci status & webhook. |
amount | integer | Ya | Nominal request merchant (Rupiah). Minimal sesuai batas channel (lihat bagian 4). Bukan total akhir pelanggan. |
method | string | Ya | Kode channel atau kode custom white-label. Contoh: qris, bri_va. Lihat bagian 4. |
description | string | Tidak | Keterangan transaksi (tampil di riwayat). |
customer_name | string | Tidak | Nama pelanggan. |
customer_email | string | Tidak | Email pelanggan. |
customer_phone | string | Tidak | Nomor HP pelanggan. |
return_url | string | Tidak | URL website kamu untuk diarahkan balik pelanggan setelah pembayaran berhasil (query order_id & status=success otomatis ditambahkan). Kalau tidak diisi, pelanggan tetap lihat halaman sukses bawaan sistem. |
mode | string | Tidak | production (default) atau sandbox. Lihat bagian Production vs Sandbox. |
Contoh cURL
curl -X POST https://www.ruangnotif.com/api/v1/transaction \
-H "Content-Type: application/json" \
-H "X-Api-Key: API_KEY_KAMU" \
-H "X-Signature: HASIL_HMAC_SHA256" \
-d '{"order_id":"INV-001","amount":50000,"method":"qris","description":"Pembelian A","customer_name":"Budi","mode":"production"}'
Uji coba: ganti "mode":"sandbox". Response order_id akan berprefix SANDBOX-.
Contoh Respons (201 Created)
{
"success": true,
"data": {
"order_id": "INV-001",
"amount": 51250,
"method": "qris",
"status": "pending",
"mode": "production",
"payment_url": "https://www.ruangnotif.com/bayar/MCH-XXXX/checkout/INV-001",
"expired_at": "2026-08-22T15:30:00+07:00"
}
}
Field Respons
| Field | Keterangan |
|---|---|
order_id | Sama dengan request. |
amount | Total tagihan pelanggan (request + biaya channel/margin, selaras dengan provider & m-banking). |
method | Kode channel tampilan (kode custom white-label jika kamu set). |
status | Awalnya selalu pending. |
mode | production atau sandbox — sesuai request. |
payment_url | URL halaman bayar. Redirect pelanggan ke sini. Berisi QR/VA, instruksi, dan timer countdown hingga expired_at. Production = transaksi asli; sandbox = uji coba tanpa kredit saldo. |
expired_at | Batas waktu bayar (ISO 8601, timezone aplikasi). Minimal ±60 menit dari saat dibuat. Setelah lewat, status menjadi expired. |
Angka amount 51250 di contoh hanya ilustrasi setelah margin. Nilai aktual mengikuti pengaturan channel di dashboard. Kredit ke saldo merchant tetap = amount request (50000).
/api/v1/transaction/status/{order_id}
Cek status transaksi. Di-scope per merchant — kamu hanya bisa melihat transaksi milik merchant sendiri. Endpoint ini juga dipakai untuk memverifikasi webhook.
Header
Wajib X-Api-Key. Untuk GET tanpa body, signature mengikuti aturan middleware (biasanya body kosong / string kosong — SDK menangani otomatis).
Contoh Respons (200)
{
"success": true,
"data": {
"order_id": "INV-001",
"amount": 51250,
"method": "qris",
"status": "berhasil",
"mode": "production",
"paid_amount": 50000,
"created_at": "2026-08-22T14:30:00+07:00",
"expired_at": "2026-08-22T15:30:00+07:00"
}
}
| Field | Keterangan |
|---|---|
amount | Total tagihan pelanggan (sama seperti create). |
paid_amount | Nominal yang dikreditkan ke saldo merchant. Hanya terisi jika status = berhasil; selain itu null. |
status | pending · berhasil · expired · gagal |
created_at | Waktu transaksi dibuat (ISO 8601). |
expired_at | Batas bayar. Jika lewat dan masih pending → otomatis expired. |
POST /api/v1/transaction.order_id, lalu redirect / tampilkan payment_url ke pelanggan.expired_at (format HH:MM:SS).berhasil.expired.
Timer & kadaluarsa.
Halaman payment_url menampilkan hitung mundur berdasarkan expired_at.
Sistem menjamin sisa waktu minimal sekitar 60 menit saat transaksi dibuat (meski provider mengembalikan nilai lebih pendek).
Nominal di halaman bayar disamakan dengan total yang muncul di aplikasi bank/e-wallet.
method)Real-time sesuai channel yang aktif. Bisa memakai kode custom white-label (di-set di dashboard) tanpa mengekspos nama provider. Detail biaya: Biaya Channel Pembayaran.
rn_qris_all
rn_bca
rn_ovo
rn_dana
rn_shopeepay
rn_bcava
rn_mandiriva
rn_maybankva
rn_bniva
rn_cimbva
rn_permatava
rn_atmbersamava
rn_arthagrahava
rn_bncva
rn_briva
rn_alfamart
rn_pos
rn_indomart
rn_qrisnusapay
Atur URL Webhook di pengaturan Merchant. Sistem akan POST JSON ke URL tersebut saat pembayaran berhasil.
Body yang dikirim
{
"order_id": "INV-001",
"amount": 50000,
"status": "success",
"merchant_code": "MCH-XXXX"
}
amount pada webhook = nominal yang dikreditkan ke merchant (request), bukan total tagihan pelanggan.
Penting: Jangan mengandalkan body webhook saja. Selalu verifikasi ulang lewat GET /api/v1/transaction/status/{order_id} sebelum menandai order lunas. SDK PHP menyediakan isValidWebhookPayload() untuk ini.
| Kode | Arti |
|---|---|
201 | Transaksi berhasil dibuat. |
200 | Request berhasil (cek status, dll). |
401 | API Key / Signature tidak valid atau tidak disertakan. |
404 | Transaksi dengan order_id tidak ditemukan. |
422 | Validasi gagal atau gagal membuat transaksi (lihat field message). |
Tanpa coding berat — pasang modul, isi API Key & Secret Key, langsung jalan.
Invoice pelanggan mendapat tombol bayar QRIS/VA/E-Wallet. Status invoice otomatis Paid setelah terverifikasi.
Muncul sebagai metode bayar di checkout. Pesanan otomatis diproses setelah pembayaran terverifikasi.
Setelah pembayaran berhasil, sistem mengirim HTTP POST JSON ke URL callback merchant kamu.
Gunakan ini agar backend bisa menandai order lunas tanpa menunggu polling status.
URL Webhook, atau edit merchant di dashboard.URL Webhook (opsional tapi disarankan untuk uji callback).POST, dan merespons cepat (timeout server ~5 detik).https://tokokamu.com/api/payment/callback| Item | Nilai |
|---|---|
| Method | POST |
| Content-Type | application/json |
| Kapan dikirim | Saat status transaksi menjadi berhasil (setelah konfirmasi provider) |
| Retry | Tidak ada retry otomatis — jika endpoint gagal, cek status lewat API GET /api/v1/transaction/status/{order_id} |
Body JSON (payload)
{
"order_id": "INV-001",
"amount": 50000,
"status": "success",
"merchant_code": "MCH-A1B2C3"
}
| Field | Tipe | Keterangan |
|---|---|---|
order_id | string | Order ID yang kamu kirim saat create (sandbox berprefix SANDBOX- jika mode sandbox). |
amount | integer | Nominal yang dikreditkan ke merchant (production). Untuk sandbox tetap dikirim untuk uji, tanpa kredit saldo. |
status | string | Selalu success pada callback sukses saat ini. |
merchant_code | string | Kode merchant (MCH-... atau SBX-...). |
// routes/api.php
Route::post('/payment/callback', [PaymentCallbackController::class, 'handle']);
// app/Http/Controllers/PaymentCallbackController.php
public function handle(Request $request)
{
$orderId = $request->input('order_id');
$status = $request->input('status');
$amount = (int) $request->input('amount');
$mch = $request->input('merchant_code');
if ($status !== 'success' || ! $orderId) {
return response()->json(['ok' => false], 400);
}
// 1) Idempotent: jangan proses order yang sama dua kali
// 2) (Opsional) verifikasi ulang ke API status RuangNotif
// 3) Update order lokal → lunas / kirim barang / aktivasi layanan
return response()->json(['ok' => true], 200);
}
app.post('/api/payment/callback', express.json(), async (req, res) => {
const { order_id, amount, status, merchant_code } = req.body;
if (status !== 'success') {
return res.status(400).json({ ok: false });
}
// updateOrderPaid(order_id, amount);
return res.json({ ok: true });
});
GET /api/v1/transaction/status/{order_id} sebelum menandai order lunas (lebih aman).Signing, HTTP, dan verifikasi webhook sudah dibungkus — cukup panggil method create & status.
Unduh SDK PHP (.zip)