Panduan Setup dari Nol
Panduan ini untuk pemilik platform (admin QRISKu). Tujuannya: aplikasi jalan di server sendiri dengan domain HTTPS, terhubung ke QRIS dan Virtual Account DOKU milik ARENAGO, dan siap dipakai semua brand.
Penting soal nama di aplikasi bank. Semua brand memakai satu QRIS resmi milik ARENAGO di DOKU. Waktu pembeli scan, aplikasi bank/e-wallet menampilkan nama merchant yang terdaftar di DOKU (ARENAGO). Nama brand (AeroConvo, AbsenKuy, dan lain-lain) hanya tampil di halaman checkout QRISKu. Aplikasi ini tidak mengubah isi QR. Kalau mau nama lain muncul di aplikasi bank, nama itu harus didaftarkan resmi sebagai merchant QRIS sendiri lewat DOKU.
#1. Yang perlu disiapkan
- Server/VPS Linux (Ubuntu 22.04/24.04 atau Debian 12), minimal 1 vCPU dan 1 GB RAM, dengan IP publik.
- Domain atau subdomain, misalnya
bayar.arenago.id. - Node.js 22.13 atau lebih baru (disarankan Node 24). Atau Docker kalau mau lewat kontainer.
- Akun DOKU dengan QRIS aktif dan akses API SNAP (sudah jalan untuk ARENAGO).
- Akses ke dashboard DOKU untuk mengambil kredensial.
Tidak perlu database terpisah: data disimpan di satu file SQLite (data/qrisku.db).
#2. Pasang aplikasi
# 1) Install Node 24 (Ubuntu/Debian)
curl -fsSL https://deb.nodesource.com/setup_24.x | sudo -E bash -
sudo apt-get install -y nodejs
# 2) Taruh kode di server (upload qris-platform.tar.gz dulu)
sudo mkdir -p /opt/qrisku && sudo chown $USER /opt/qrisku
tar -xzf qris-platform.tar.gz -C /opt/qrisku --strip-components=1
cd /opt/qrisku
# 3) Install dependensi (tanpa alat dev)
npm ci --omit=dev
# 4) Buat file konfigurasi dan folder data
cp .env.example .env
chmod 600 .env
mkdir -p data backups
Cek cepat: npm start lalu buka http://IP-SERVER:3000/healthz. Harus keluar {"ok":true,...}. Tekan Ctrl+C untuk berhenti. npm start otomatis membaca .env.
#3. Isi file .env
Buka .env dengan editor (nano .env). Semua variabel:
| Variabel | Contoh | Penjelasan |
|---|---|---|
PORT |
3000 |
Port aplikasi di server. Biarkan 3000 kalau pakai Caddy/nginx |
BASE_URL |
https://bayar.arenago.id |
Alamat publik HTTPS. Dipakai untuk link checkout, gambar QR, dan cookie aman. Wajib diganti saat online |
ADMIN_PASSWORD |
(acak, min. 16 karakter) | Password masuk panel admin |
ADMIN_PATH |
/x-3f9a1c7e5b2d |
Alamat rahasia panel admin. Kosong = /admin. Buat dengan echo /x-$(openssl rand -hex 6). Kalau diisi, /admin jadi 404 seperti halaman tak dikenal. Jangan ditaruh di halaman publik |
ADMIN_ALLOW_IPS |
203.0.113.7, 10.0.0.0/8 |
Opsional. Hanya IP/CIDR ini yang bisa membuka panel admin, yang lain dapat 404. Kosong = semua IP (tetap butuh password) |
REGISTER_PER_HOUR / REGISTER_MIN_SECONDS |
5 / 3 |
Batas pendaftaran brand per IP per jam, dan waktu minimal mengisi form (anti-bot) |
SESSION_SECRET |
(acak, 64 karakter) | Kunci tanda tangan cookie login. Buat dengan openssl rand -hex 32. Kalau diganti, semua orang keluar |
PLATFORM_FEE_PERCENT |
0.7 |
Biaya layanan per transaksi sukses (persen). Satu tarif untuk semua brand |
PLATFORM_FEE_FLAT |
0 |
Biaya tetap tambahan per transaksi (rupiah) |
PAYMENT_TTL_MINUTES |
15 |
Masa berlaku QR, 15 sampai 30 menit |
MIN_AMOUNT / MAX_AMOUNT |
1000 / 10000000 |
Batas nominal per transaksi |
MIN_WITHDRAW |
10000 |
Minimal penarikan saldo brand |
TRUST_PROXY |
loopback |
Biarkan loopback kalau Caddy/nginx di server yang sama. Untuk Docker + Caddy di compose pakai uniquelocal |
RATE_LIMIT_API_PER_MIN |
120 |
Batas request API per API key per menit |
RATE_LIMIT_PUBLIC_PER_MIN |
120 |
Batas request halaman publik per IP per menit |
LOGIN_MAX_FAILS / LOGIN_WINDOW_MIN |
5 / 15 |
Login dikunci sementara setelah 5x gagal dalam 15 menit |
ALLOW_PRIVATE_WEBHOOKS |
0 |
Biarkan 0. Isi 1 hanya di laptop untuk tes webhook ke localhost |
DB_PATH |
data/qrisku.db |
Lokasi file database |
DOKU_BASE_URL |
https://api.doku.com |
Production. Untuk sandbox DOKU: https://api-sandbox.doku.com |
DOKU_CLIENT_ID |
BRN-xxxx-xxxxxxxxxx |
Dari DOKU, lihat langkah 4 |
DOKU_CLIENT_SECRET |
SK-xxxxxxxx |
Secret Key dari DOKU, lihat langkah 4. Rahasia |
DOKU_PRIVATE_KEY_PATH |
./keys/private.pem |
Private key RSA kamu (langkah 5). Rahasia |
DOKU_PRIVATE_KEY_PASSPHRASE |
(kosong) | Isi hanya kalau private key diberi passphrase |
DOKU_MERCHANT_ID |
91959 |
Merchant ID QRIS ARENAGO |
DOKU_TERMINAL_ID |
WEB01 |
Kode terminal pilihan kamu, 3 sampai 16 huruf/angka |
DOKU_POSTAL_CODE |
11250 |
Kode pos alamat usaha ARENAGO |
DOKU_NOTIFY_PATH |
/webhooks/doku/qris |
Path URL notifikasi DOKU. Biarkan default |
Jangan pernah kirim isi .env lewat chat, email, atau commit ke Git.
#4. Ambil kredensial di dashboard DOKU
- Client ID dan Secret Key: dashboard DOKU > Settings > Account > API Keys. Salin Client ID ke
DOKU_CLIENT_ID. Klik tampilkan Secret Key (minta OTP), salin keDOKU_CLIENT_SECRET. Yang dipakai adalah Secret Key berawalanSK-. - Merchant ID: Settings > Accept Payments > Checkout Appearance, tab Pengaturan Kredensial QRIS. Angka di kolom Client ID di tab itu (untuk ARENAGO:
91959) masuk keDOKU_MERCHANT_ID. MPAN dan NMID di tab yang sama tidak perlu diisi. - Terminal ID: bebas kamu tentukan, 3 sampai 16 huruf/angka, contoh
WEB01. Pakai yang sama terus. - Kode pos: Settings > Account > Business Info, lihat alamat usaha (untuk ARENAGO:
11250) lalu isiDOKU_POSTAL_CODE.
#5. Buat dan daftarkan kunci RSA
DOKU memakai pasangan kunci RSA untuk minta token. Private key tetap di server, public key diunggah ke DOKU.
cd /opt/qrisku
sh scripts/gen-keys.sh # membuat keys/private.pem dan keys/public.pem
cat keys/public.pem # yang ini aman ditampilkan
Di dashboard DOKU: Settings > Account > API Keys > Edit Merchant Public Key, tempel isi keys/public.pem, simpan.
Kalau server lama sudah punya kunci yang terdaftar di DOKU, jangan buat ulang. Salin saja folder keys/ lama ke server baru. Membuat kunci baru berarti harus unggah ulang public key, dan transaksi gagal sampai itu dilakukan.
DOKU Public Key yang ada di halaman yang sama boleh disimpan sebagai keys/doku_public.pem untuk arsip. Aplikasi saat ini tidak memakainya, karena notifikasi DOKU diverifikasi dengan HMAC dan status selalu dicek ulang ke DOKU sebelum saldo bertambah.
#6. Jalankan terus-menerus
Pilih salah satu.
#Opsi A: pm2
sudo npm install -g pm2
NODE_ENV=production pm2 start ecosystem.config.cjs
pm2 save
pm2 startup # ikuti perintah yang dicetak supaya otomatis jalan setelah reboot
pm2 logs qrisku # lihat log
#Opsi B: systemd
sudo useradd --system --home /opt/qrisku qrisku
sudo chown -R qrisku:qrisku /opt/qrisku
sudo cp deploy/qrisku.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now qrisku
sudo journalctl -u qrisku -f
#Opsi C: Docker
# di folder proyek, setelah .env dan keys/ siap
mkdir -p data backups
sudo chown -R 1000:1000 data backups keys # kontainer jalan sebagai user "node" (uid 1000)
DOMAIN=bayar.arenago.id docker compose up -d --build # app + Caddy (HTTPS otomatis)
docker compose logs -f app
Di Docker, .env dan keys/ dipasang read-only ke kontainer, jadi rahasia tidak ikut masuk ke image.
Atau satu perintah: sh scripts/deploy.sh bayar.arenago.id. Skrip ini membuat .env (dengan password admin dan session secret acak) kalau belum ada, membuat kunci RSA kalau belum ada, lalu menjalankan Docker Compose plus Caddy untuk HTTPS otomatis.
#7. Domain dan HTTPS
- Di pengelola DNS domain kamu, buat A record:
bayarmengarah ke IP publik server. Tunggu sampaiping bayar.arenago.idmenunjukkan IP server. - Buka port 80 dan 443 di firewall (
sudo ufw allow 80,443/tcp). Port 3000 jangan dibuka ke publik.
#Caddy (disarankan, HTTPS otomatis)
sudo apt install -y caddy
sudo cp deploy/Caddyfile /etc/caddy/Caddyfile # ganti bayar.arenago.id dengan domain kamu
sudo systemctl reload caddy
Isi deploy/Caddyfile:
bayar.arenago.id {
encode gzip
reverse_proxy 127.0.0.1:3000
}
#nginx (alternatif)
Pakai deploy/nginx.conf sebagai contoh, lalu pasang sertifikat dengan sudo certbot --nginx -d bayar.arenago.id.
- Ubah
BASE_URL=https://bayar.arenago.iddi.env, lalu restart (pm2 restart qrisku/sudo systemctl restart qrisku/docker compose up -d). - Buka
https://bayar.arenago.id/healthz. Harus{"ok":true,...}.
#8. URL notifikasi DOKU (tidak wajib)
Aplikasi ini tidak butuh URL notifikasi DOKU. Status transaksi real (QRIS maupun Virtual Account) dicek langsung ke API DOKU saat halaman checkout terbuka (tiap 3 detik) dan oleh pengecek latar belakang tiap 15 detik. Begitu DOKU bilang lunas, transaksi ditandai sukses dan saldo brand bertambah.
Kalau akun DOKU kamu sudah dipakai sistem lain (untuk ARENAGO: URL notifikasi sudah mengarah ke aplikasi ARENAGO), jangan diganti. Menggantinya bisa membuat sistem lain itu berhenti menerima kabar pembayaran. QRISKu tetap jalan tanpa notifikasi.
Hanya kalau akun DOKU itu khusus untuk QRISKu dan kamu mau deteksi QRIS sedikit lebih cepat: Dashboard DOKU > Settings > Payment Settings > QR Payment > Edit, isi https://bayar.arenago.id/webhooks/doku/qris (alamat lengkapnya juga tampil di panel admin > Pengaturan), lalu simpan. Notifikasi DOKU tidak pernah dipercaya mentah: saldo baru bertambah setelah status dikonfirmasi lewat API cek status DOKU.
#9. Metode pembayaran: QRIS dan Virtual Account
Halaman checkout punya dua tab:
| Metode | Cara bayar pembeli | Yang tampil di aplikasi bank |
|---|---|---|
| QRIS | Scan QR dari m-banking atau e-wallet apa pun | Nama merchant QRIS ARENAGO |
| Transfer Bank (VA) | Pilih bank, lalu transfer ke nomor Virtual Account sekali pakai | Nama penerima ARENAGO (atau VA_NAME di .env) |
Nomor VA dibuat saat pembeli memilih bank, untuk transaksi yang sama. Nominalnya terkunci (closed amount): pembeli harus transfer persis sesuai total. VA berakhir bersamaan dengan transaksi. Biaya layanan, saldo brand, mode Sandbox/Real, dan Mode brand berlaku sama seperti QRIS. Di mode Sandbox, nomor VA diawali 99 dan tidak bisa dibayar.
Bank yang aktif sekarang: BNI (bni), BTN (btn), Bank Neo Commerce (bnc). Ketiganya sudah dites ke DOKU production (buat VA, cek status "Belum Terbayar", hapus).
- Pembeli dari bank lain tetap bisa bayar ke VA ini lewat transfer antarbank (biaya transfer dari bank pembeli).
- BCA VA dan Mandiri VA belum aktif di akun DOKU ARENAGO, jadi tidak ditawarkan.
- BRI dan Permata sudah disiapkan di aplikasi tapi masih mati, karena DOKU menolak kode BIN-nya ("Identifier for BIN not configured").
- Bank Neo Commerce (BNC): DOKU tidak mengizinkan penghapusan VA. Kalau pembeli ganti bank, nomor BNC lama dibiarkan sampai kedaluwarsa (halaman checkout hanya menampilkan nomor terbaru).
#Mengaktifkan bank lain
- Dashboard DOKU > Settings > Payment Settings > Virtual Account. Cek bank mana yang statusnya aktif. Bank yang belum aktif perlu diminta ke DOKU (tim support atau account manager DOKU).
- Minta ke DOKU BIN (partnerServiceId) dan prefix nomor VA untuk bank itu (di halaman konfigurasi bank tersebut kalau tersedia, atau tanyakan ke support DOKU).
- Isi di
.envserver dengan formatDOKU_VA_<KODE>=BIN:prefix, lalu restart aplikasi:
DOKU_VA_BRI=12345:6 # contoh, ganti dengan BIN dan prefix dari DOKU
DOKU_VA_PERMATA=12345:6
DOKU_VA_DISABLE=bnc # opsional: sembunyikan bank tertentu
- Tes tanpa membayar:
node --env-file=.env scripts/va-live-check.js bri. Skrip ini membuat VA Rp 10.000, memastikan statusnya "Belum Terbayar", lalu menghapusnya. Kalau hasilnyacreate: okdanstatus: pending, bank itu siap tampil di checkout.
Tidak perlu memasang URL notifikasi VA di DOKU. Sama seperti QRIS, status VA dicek lewat API cek status DOKU. Jangan ubah URL notifikasi yang sudah ada.
Kode bank yang dikenal: bni, btn, bnc, bri, permata. Bank lain di luar daftar ini perlu tambahan kode di src/vaBanks.js.
#Tes pertama VA (uang sungguhan)
Status "Belum Terbayar" sudah dites. Teks status "sudah bayar" dari DOKU belum pernah dilihat langsung, jadi lakukan sekali tes bayar sungguhan: buat pembayaran Rp 10.000, pilih Transfer Bank (VA) > BNI, transfer persis sesuai total, dan pastikan halaman berubah jadi "Pembayaran berhasil" dalam beberapa detik. Kalau tidak berubah, buka detail transaksi di panel admin: baris Nomor VA menampilkan teks status terakhir dari DOKU, dan kirim teks itu ke developer.
#10. Pindah dari Sandbox ke DOKU (real)
- Masuk panel admin (
https://bayar.arenago.id+ADMIN_PATH, misalnyahttps://bayar.arenago.id/x-3f9a1c7e5b2d/login) denganADMIN_PASSWORD. - Buka Pengaturan. Bagian Koneksi DOKU harus bertuliskan "Kredensial DOKU lengkap". Kalau ada yang kurang, namanya ditampilkan.
- Klik toggle Real (DOKU) di kanan atas. Mulai saat ini QRIS baru dibuat lewat DOKU. Brand tetap bisa membuat QR tes dengan
test: 1atau centang Mode tes.
#11. Tambah brand
Ada dua cara: brand daftar sendiri atau dibuat admin.
Daftar sendiri. Calon brand membuka /merchant/register (tombol Daftar Brand di beranda) dan mengisi nama brand, username, email, WhatsApp (opsional), password, dan keperluan. Statusnya menunggu: belum bisa masuk portal, belum punya API key, dan tidak bisa membuat pembayaran. Tidak ada email/WA otomatis, jadi cek sendiri:
- Panel admin > Pendaftaran Brand (angka di menu = jumlah yang menunggu).
- Setujui: API key dibuat, brand mulai di mode Sandbox (QR tes). Setelah yakin, buka Brand > Kelola > Mode brand untuk ikut mode platform (Real).
- Tolak: isi alasan (opsional). Brand melihat alasannya saat mencoba masuk.
- Kabari brand lewat kontak yang mereka isi. Semua tindakan tercatat di Riwayat tindakan brand. Brand yang sudah ada sebelum fitur ini otomatis berstatus disetujui.
Dibuat admin:
- Admin > Brand > Tambah brand: nama internal, nama tampil di checkout, URL webhook (opsional), email/username login dan password awal untuk Portal Brand.
- Berikan ke pemilik brand: alamat
https://bayar.arenago.id/merchant/login, login, dan password awal. Minta mereka langsung ganti password di menu Akun. - Developer brand mengikuti Tutorial Pasang.
- Lupa password? Admin > Brand > Kelola > Login Portal Brand > Reset password. Semua sesi brand itu langsung keluar.
#12. Tes pertama Rp 1.000 (uang sungguhan)
- Pastikan mode Real (DOKU) aktif.
- Admin > Buat Pembayaran, pilih brand, nominal
1000, fee ditanggung Brand. - Buka checkout, scan dengan m-banking/e-wallet. Aplikasi bank menampilkan nama merchant ARENAGO.
- Bayar. Dalam beberapa detik halaman checkout berubah jadi "Pembayaran berhasil".
- Cek: transaksi berstatus Sukses, saldo brand bertambah, dan detail transaksi mencatat pembayaran.
- Uang Rp 1.000 masuk ke saldo DOKU ARENAGO, bukan dikembalikan otomatis.
#13. Penarikan saldo brand
Saldo brand adalah catatan di database QRISKu. Uang fisiknya ada di saldo DOKU ARENAGO.
- Brand mengajukan penarikan di Portal Brand. Saldo langsung ditahan.
- Admin > Tarik Dana: transfer manual ke rekening brand dari rekening/saldo ARENAGO, lalu klik Setujui (isi nomor referensi transfer di catatan). Kalau ditolak, klik Tolak dan saldo kembali ke brand.
#14. Backup database
Semua data ada di data/qrisku.db. Backup aman saat aplikasi jalan:
npm run backup # membuat backups/qrisku-YYYYMMDD-HHMMSS.db, simpan 14 terakhir
docker compose exec app node scripts/backup.js # versi Docker
Jadwalkan tiap hari dengan cron (crontab -e):
15 2 * * * cd /opt/qrisku && /usr/bin/npm run backup >> backups/backup.log 2>&1
Salin folder backups/ ke tempat lain secara berkala (Google Drive, S3, atau komputer lain). Untuk restore: hentikan aplikasi, ganti data/qrisku.db dengan file backup, jalankan lagi. Folder keys/ dan file .env juga perlu dibackup, tapi simpan terpisah dan terenkripsi karena berisi rahasia.
#15. Update aplikasi
cd /opt/qrisku
npm run backup
tar -xzf qris-platform-baru.tar.gz --strip-components=1 # .env, keys/, data/ tidak tertimpa
npm ci --omit=dev
pm2 restart qrisku # atau: sudo systemctl restart qrisku / docker compose up -d --build
Perubahan struktur database dijalankan otomatis saat aplikasi start.
#16. Checklist keamanan
-
ADMIN_PASSWORDpanjang dan acak, bukanadmin123. -
SESSION_SECRETacak 64 karakter. -
BASE_URLmemakaihttps://(cookie jadiSecuredan header HSTS aktif). - Port 3000 tidak terbuka ke internet, hanya 80/443 lewat Caddy/nginx.
-
.envdankeys/private.pemhak aksesnya600, tidak ada di Git atau tarball. - URL notifikasi DOKU milik sistem lain tidak diubah (aplikasi ini tidak membutuhkannya).
- Backup harian jalan dan pernah dicoba restore.
- Brand diberi password awal unik, dan diminta menggantinya.
- Server rutin di-update (
sudo apt upgrade). - Pantau
/healthzdengan layanan uptime monitor.
#17. Kalau ada masalah
| Gejala | Penyebab umum |
|---|---|
| Aplikasi langsung berhenti: "masih default" | Mode production menolak jalan kalau ADMIN_PASSWORD atau SESSION_SECRET belum diganti. Isi di .env |
| Halaman Pengaturan admin bilang kredensial belum lengkap | Ada variabel DOKU_* kosong di .env, atau aplikasi belum direstart |
| Buat QRIS real error 502 | Lihat log aplikasi. Biasanya Client ID/Secret Key salah, public key belum diunggah, atau Merchant ID salah |
Nilai .env seperti tidak terbaca |
Variabel yang sama sudah ada di environment shell/sistem. Nilai di environment selalu menang atas .env. Hapus dari environment (unset NAMA) |
| Pembayaran sukses tapi status lambat berubah | Status dicek ke DOKU tiap 3 detik (checkout terbuka) atau 15 detik (latar belakang). Cek log aplikasi kalau lebih lama |
| Bank VA tidak muncul di checkout | BIN bank itu belum diisi (DOKU_VA_<KODE>) atau disembunyikan lewat DOKU_VA_DISABLE |
| Pilih bank VA error "gagal membuat VA" | Bank itu belum aktif di DOKU atau BIN salah. Tes dengan scripts/va-live-check.js |
| Login brand terkunci | Terlalu banyak salah password. Tunggu 15 menit atau restart aplikasi |
| Webhook brand gagal "tidak boleh mengarah ke server lokal" | URL webhook brand menunjuk ke localhost/IP privat. Pakai domain publik |