Set Qris Logo
Set Qris

Overview

Pengantar integrasi SetQris Payment API

SetQris menyediakan REST API untuk membuat transaksi QRIS dinamis, memantau status pembayaran, dan menerima notifikasi real-time ke server Anda melalui webhook. Semua endpoint memerlukan autentikasi menggunakan header x-api-key.

REST API

Format JSON standar. Semua request gunakan header x-api-key.

HTTPS Only

Semua endpoint hanya bisa diakses lewat koneksi HTTPS.

Rate Limit

Batas 120 request per menit per API Key pada endpoint generate.

Production API
https://api.setqris.my.id

Semua request harus menyertakan header x-license-key: YOUR_LICENSE_KEY. API Key tersedia di halaman API Keys.


Generate QRIS

Membuat transaksi QRIS dinamis

Endpoint utama untuk membuat transaksi QRIS dinamis. Sistem otomatis menyisipkan kode unik acak ke nominal untuk membedakan setiap transaksi.

POST/api/generate/v2/qris
ParameterTypeSifatDeskripsi
idstringWAJIBID QRIS Merchant (UUID) yang terdaftar.
amountnumberWAJIBNominal dasar transaksi sebelum kode unik.
useUniqueCodebooleanOPSIONALSematkan kode acak 1-999 di belakang nominal.
packageIdsArray[string]WAJIBPackage ID aplikasi (cth: ["id.dana"]).
expiredInMinutesnumberOPSIONALKedaluwarsa dalam menit (default: 15).
prefixstringOPSIONALPrefix ID Transaksi, maks 8 karakter.
qrTypestringOPSIONAL"dynamic" atau "static".
paymentMethodstringOPSIONAL"qris" (scan) atau "ewallet".
useQrisbooleanOPSIONALSertakan qr_string di respons.
POST /api/generate/v2/qris HTTP/1.1 Host: api.setqris.my.id x-license-key: YOUR_LICENSE_KEY Content-Type: application/json { "id": "986b2a0a-5c95-4177-9800-02d84a35338e", "amount": 15000, "useUniqueCode": true, "packageIds": ["id.dana", "com.shopee.id"], "expiredInMinutes": 15, "qrType": "dynamic", "paymentMethod": "qris", "useQris": true, "prefix": "SQ" }

Cek Status Transaksi

Polling status pembayaran secara real-time

Gunakan endpoint ini untuk memeriksa apakah transaksi sudah lunas, masih pending, dibatalkan, atau expired.

POST/api/generate/check-status
ParameterTypeSifatDeskripsi
transactionIdStringWAJIBID Transaksi unik dari hasil Generate API.
pending
paid
cancel
expired
POST /api/generate/check-status HTTP/1.1 Host: api.setqris.my.id x-license-key: YOUR_LICENSE_KEY Content-Type: application/json { "transactionId": "SQ-c39e25d2-0cb2-4a00-9941-863863486df8" }

Endpoint Lainnya

Cancel, list transaksi, dan hapus riwayat

POST/api/generate/cancel-status

Batalkan transaksi secara paksa sebelum expired.

ParameterTypeSifatDeskripsi
transactionIdStringWAJIBID Transaksi yang ingin dibatalkan.
POST /api/generate/cancel-status HTTP/1.1 Host: api.setqris.my.id x-license-key: YOUR_LICENSE_KEY Content-Type: application/json { "transactionId": "SQ-c39e25d2-0cb2-4a00-9941-863863486df8" }
GET/api/generate/list

Daftar semua transaksi dengan filter status, halaman, dan pencarian.

ParameterTypeSifatDeskripsi
statusStringOPSIONALpending | paid | cancel | expired
pageNumberOPSIONALNomor halaman (default: 1).
limitNumberOPSIONALJumlah per halaman (default: 5).
searchStringOPSIONALCari nominal atau ID transaksi.
sortStringOPSIONAL"newest" atau "oldest".
GET /api/generate/list?status=paid&limit=5 HTTP/1.1 Host: api.setqris.my.id x-license-key: YOUR_LICENSE_KEY
POST/api/generate/list-delete

Hapus log riwayat — satu, massal, atau semua sekaligus.

ParameterTypeSifatDeskripsi
transactionIdStringOPSIONALID tunggal yang ingin dihapus.
transactionIdsArray[String]OPSIONALArray ID untuk hapus massal.
clearAllBooleanOPSIONALtrue = hapus seluruh log merchant.
POST /api/generate/list-delete HTTP/1.1 Host: api.setqris.my.id x-license-key: YOUR_LICENSE_KEY Content-Type: application/json { "transactionIds": ["SQ-c39e25d2-0cb2-4a00-9941-863863486df8"] }
GET/api/profile

Mendapatkan data profil lisensi aktif (aman, mengecualikan kunci lisensi atau secret key).

GET /api/profile HTTP/1.1 Host: api.setqris.my.id x-license-key: YOUR_LICENSE_KEY

Webhook Callback

Notifikasi otomatis saat transaksi lunas

SetQris mengirim HTTP POST ke URL webhook Anda secara otomatis saat transaksi berubah menjadi paid. Konfigurasi URL webhook di halaman Webhook Developer.

Notifikasi Instan

Dikirim dalam hitungan detik setelah transaksi terverifikasi lunas.

HMAC-SHA256

Setiap payload ditandatangani untuk menjamin keaslian data.

Auto Retry 3x

Retry otomatis hingga 3 kali jika server tidak merespons 10 detik.

Struktur Payload

Format JSON yang dikirim ke server Anda

POSTYOUR_WEBHOOK_URL
application/json
ParameterTypeSifatDeskripsi
transactionIdStringWAJIBID unik transaksi.
amountNumberWAJIBNominal total transaksi.
packageNameStringWAJIBPackage ID aplikasi pengirim.
appNameStringWAJIBNama tampilan aplikasi pengirim.
statusStringWAJIB"paid" — status transaksi terverifikasi lunas.
paidAtStringWAJIBTimestamp ISO 8601 saat pembayaran selesai.
webhook-payload.json
{ "transactionId": "SQ-c39e25d2-0cb2-4a00-9941-863863486df8", "amount": 55000, "packageName": "com.company.paymentapp", "appName": "Payment App Name", "status": "paid", "paidAt": "2026-06-04T03:38:35Z" }

Signature Verification

Verifikasi keaslian webhook dengan HMAC-SHA256

Setiap webhook request ditandatangani menggunakan Webhook Secret Anda. Tanda tangan dikirimkan melalui header x-setqris-signature.

1

Ambil header signature

Baca nilai header X-SetQris-Signature dari request masuk.

2

Gunakan raw body

Ambil body sebelum di-parse. Parsing JSON mengubah urutan key sehingga signature tidak cocok.

3

Hitung HMAC-SHA256

Kalkulasikan HMAC-SHA256 menggunakan raw body sebagai message dan Webhook Secret sebagai key.

4

Bandingkan constant-time

Gunakan timingSafeEqual / hash_equals. Jangan gunakan == biasa (rentan timing attack).

⚠️

Penting: Gunakan raw body (sebelum di-parse) sebagai input HMAC. Parsing JSON mengubah urutan key sehingga signature tidak cocok.

const crypto = require('crypto'); app.post('/webhook/setqris', express.raw({ type: 'application/json' }), (req, res) => { const signature = req.headers['x-setqris-signature']; const secret = process.env.WEBHOOK_SECRET; // TODO: ganti dengan nilai Anda const expected = crypto .createHmac('sha256', secret) .update(req.body) // raw Buffer — jangan parse dulu .digest('hex'); const isValid = crypto.timingSafeEqual( Buffer.from(signature, 'hex'), Buffer.from(expected, 'hex') ); if (!isValid) return res.status(401).json({ error: 'Invalid signature' }); const payload = JSON.parse(req.body); if (payload.status === 'paid') { console.log('Paid:', payload.transactionId); } res.sendStatus(200); // balas 200 dalam 10 detik } );

Gunakan crypto.timingSafeEqual (Node.js) atau hash_equals (PHP) untuk perbandingan. String comparison biasa rentan terhadap timing attack.

Retry Policy

Jaminan pengiriman dengan retry otomatis

Sistem retry pengiriman webhook jika server Anda tidak merespons dengan HTTP 2xx dalam waktu 10 detik.

Percobaan 1 — Segera setelah transaksi lunas terdeteksi

Retry ke-1 — 30 detik setelah gagal pertama

Retry ke-2 — 2 menit setelah retry pertama gagal

Berhenti — Tidak ada retry selanjutnya

⚠️

Setelah 3 kali gagal, webhook tidak dikirim ulang. Balas dengan 200 segera, lalu proses payload secara asynchronous di background.