Dokumentasi Builder Aplikasi PPOB
Panduan lengkap untuk pemilik toko dan programmer: alur Builder, produk & supplier, pembayaran, cara kerja HTML di aplikasi, placeholder, link, API JavaScript, dan solusi saat kode tidak berfungsi.
Mulai cepat
Urutan yang disarankan untuk toko baru. Setiap menu di Builder juga punya bagian Cara penggunaan di paling bawah halaman.
- Daftar & isi Info TokoNama aplikasi, warna, logo (PNG persegi min. 512×512), nomor WhatsApp CS, teks sambutan. Menu: Toko → Info Toko.
- Hubungkan supplierIsi akun Digiflazz / MDPPay, daftarkan IP relay di panel supplier, pasang URL callback, lalu Uji koneksi. Menu: Toko → Supplier Produk.
- Siapkan produk & hargaTekan Update harga sekarang: produk supplier utama otomatis masuk. Atur markup, kategori, provider, dan supplier cadangan. Menu: Produk.
- Siapkan isi saldoTambah rekening untuk transfer manual dan/atau aktifkan pembayaran otomatis QRIS/VA. Menu: Rekening Pembayaran, Pembayaran Otomatis.
- Desain tampilanPilih tema di Store, atur Tampilan Home, menu navbar, font, lalu sesuaikan HTML bila perlu. Menu: Tampilan Aplikasi, Edit Lanjutan.
- Build APKTekan Build, tunggu selesai, unduh APK dan bagikan ke pelanggan. Menu: Aplikasi → Build APK.
- Jalankan & pantauTerima deposit manual, pantau transaksi dan laporan laba, perpanjang langganan sebelum habis.
Konsep penting
Builder, aplikasi pengguna, dan APK
- Builder = panel pemilik toko (halaman ini bagian darinya).
- Aplikasi pengguna = aplikasi yang dipakai pembeli. Tersedia sebagai APK Android (dan versi web bila diaktifkan platform).
- Server menyimpan produk, harga, saldo, transaksi, dan tampilan HTML. APK mengambil semuanya saat dibuka.
Kapan perlu build ulang APK?
| Perubahan | Berlaku |
|---|---|
| Produk, harga, markup, supplier, kategori, provider | Langsung |
| HTML halaman, Halaman Kustom, CSS global, tema, banner, navbar, font, perilaku | Langsung (pengguna buka ulang aplikasi / tarik layar) |
| Rekening, payment gateway, link isi saldo | Langsung |
| Nama aplikasi, logo/ikon, package | Perlu build ulang |
| Builder menampilkan "versi mesin baru" (fitur aplikasi baru) | Perlu build ulang |
Info toko & dokumen
- Info Toko: nama, warna utama (dipakai sebagai
--primarydi CSS), logo, nomor CS ({{wa_cs}}), teks sambutan ({{sambutan}}). - Kebijakan & Syarat: kosongkan untuk memakai teks bawaan. Link-nya dibutuhkan saat mendaftar ke Google Play.
- ID Member: awalan + nomor (mis.
HJM-00001), tampil lewat{{id_member}}.
Supplier
Builder mendukung beberapa supplier sekaligus. Setiap supplier punya pricelist sendiri yang disimpan terpisah.
- Isi kredensial supplier di Supplier Produk. Data disimpan terenkripsi dan tidak pernah dikirim ke aplikasi pengguna.
- Daftarkan IP relay (ditampilkan di halaman itu) di whitelist panel supplier.
- Pasang URL callback di panel supplier. Tanpa callback, transaksi bisa tertahan di status "Diproses".
- Tekan Uji koneksi → saldo supplier tampil bila berhasil.
MDPPay: yang otomatis tersedia setelah sinkron
- Prabayar: pulsa, paket data, telp/SMS, masa aktif, token PLN, game, voucher, e-wallet, saldo ojol.
- Kuota promo: paket Telkomsel Omni, Indosat Only4U, dan paket berlabel promo/spesial dari operator lain masuk tab Kuota Promo di provider masing-masing. Paket ini hanya berhasil untuk nomor yang memang mendapat penawaran tersebut.
- E-wallet bebas nominal (kategori
wlbb): pembeli mengetik nominal sendiri. Total bayar = nominal + harga jual produk (sebagai biaya admin). Batas nominal diatur di Produk → ubah produk → Bebas nominal (awal Rp10.000 – Rp1.000.000). - Tagihan pascabayar: PLN pascabayar, BPJS, PDAM, internet, TV kabel, multifinance. Wajib isi Email akun MDPPay di halaman Supplier (langkah 3). Hasil cek tagihan berlaku 5 menit; lewat dari itu pembeli diminta cek ulang.
Produk: Kategori → Provider → Produk
- Kategori: menu di beranda (Pulsa & Data, Token PLN, E-Money, Games, Tagihan, …).
- Provider: Telkomsel, Indosat, DANA, Mobile Legends, … dibuat sekali, bisa berisi produk dari beberapa kategori.
- Produk: nama + harga jual yang dilihat pembeli. Setiap produk punya satu atau lebih jalur supplier.
- Jenis (opsional): tab di dalam provider, mis. Pulsa / Paket Data / Masa Aktif.
Tipe produk
| Tipe | Alur di aplikasi | Isian tujuan |
|---|---|---|
| Prabayar | Isi tujuan → pilih produk → PIN → selesai | Angka (nomor HP, meter, ID game; game boleh ID+server) |
| Pascabayar | Isi ID pelanggan → Cek tagihan → rincian → PIN → bayar | Huruf/angka 4–30 |
| Inject voucher | Scan / ketik nomor seri voucher fisik → pilih produk | Huruf/angka 6–40 |
Prabayar & pascabayar di beranda
Tipe diatur per produk (form produk), atau sekaligus per kategori (Kategori → tombol Tipe). Kategori yang semua produknya pascabayar dianggap kategori pascabayar. Di Tampilan Home → Prabayar & Pascabayar centang Pisahkan menu agar {{grid_kategori}} tampil dua bagian dengan judul sendiri (mis. "Isi Ulang" dan "Bayar Tagihan"). Untuk menata sendiri di HTML, pakai {{grid_prabayar}} dan {{grid_pascabayar}}.
<section class="card gaya-bulat"><h2>Isi Ulang</h2>{{grid_prabayar}}</section>
<section class="card gaya-bulat"><h2>Bayar Tagihan</h2>{{grid_pascabayar}}</section>Gambar produk
- Per provider (paling cepat): Produk → Provider → Gambar produk → pilih gambar → berlaku untuk semua produk provider itu atau satu kategorinya saja. Hilangkan centang Timpa agar gambar yang sudah dipasang tidak diganti. Tombol Pakai logo provider memakai logo yang sudah diunggah.
- Pilihan produk: centang beberapa produk di daftar Produk → aksi Pasang gambar… / Hapus gambar.
- Satu produk: buka produk → bagian Gambar produk.
- Gambar dipotong persegi 256px. PNG berlatar transparan terlihat paling rapi.
- Sembunyikan semua gambar di aplikasi tanpa menghapusnya: Produk → Setelan → Gambar produk di aplikasi → Sembunyikan.
Produk gangguan
Bila semua jalur supplier sebuah produk tutup/gangguan, produk tampil berlabel Gangguan dan tidak bisa dibeli (bisa diubah menjadi disembunyikan di Setelan).
Kategori & provider
- Urutan tampil diatur dengan tombol ▲▼.
- Logo provider: unggah PNG/JPG (diperkecil 256px otomatis).
- Awalan nomor provider (mis.
0811,0812,0813) dipakai untuk mendeteksi operator saat pengguna mengetik nomor HP. - Ganti nama kategori: ikon menu kategori ikut pindah. Ganti alamat (slug): link lama berhenti berfungsi.
Link siap pakai ada di Link & navigasi.
Harga jual, markup & update otomatis
Harga jual = modal supplier + markup, lalu dibulatkan ke atas (bawaan Rp50). Markup yang dipakai adalah yang paling spesifik:
- Markup khusus produk
- Markup provider
- Markup kategori
- Markup jenis (cara lama)
- Markup umum
Modal Rp5.320 + markup Rp1.000 = Rp6.320 → dibulatkan Rp50 → Rp6.350 Modal Rp5.320 + markup 10% = Rp5.852 → dibulatkan Rp50 → Rp5.900
- Harga tetap (manual): harga tidak ikut supplier. Bila modal naik melewati harga jual, produk masuk filter Di bawah modal dan pemilik mendapat WhatsApp (maks sekali sehari).
- Update otomatis: setiap 15 menit – 6 jam (Setelan). Minimal 15 menit agar akun supplier tidak dibatasi.
- Riwayat harga tiap produk tersimpan 60 hari.
- Pascabayar: harga modal = admin supplier; markup menjadi biaya admin toko yang ditambahkan ke setiap tagihan.
Supplier cadangan (pindah otomatis tanpa dobel)
Setiap produk bisa punya beberapa jalur: utama lalu cadangan. Aturan pemindahan:
| Jawaban supplier | Tindakan |
|---|---|
| Sukses | Selesai |
| Gagal karena supplier (gangguan, stok/saldo habis, produk tutup, maintenance) | Pindah ke cadangan (maks 3 percobaan) |
| Gagal karena tujuan (nomor salah, ID tidak ditemukan, transaksi dobel) | Tidak pindah: gagal & saldo dikembalikan |
| Pending / tidak ada jawaban / koneksi terputus | Tidak pindah: tunggu callback / cek status |
- Pembeli dipotong saldo sekali. Setiap percobaan memakai nomor referensi sendiri (
REF,REF-2,REF-3), jadi callback yang terlambat dari supplier pertama diabaikan. - Supplier yang 3× gagal berturut-turut diistirahatkan 15 menit.
- Detail transaksi di Builder menampilkan semua jalur yang dicoba. Laporan punya bagian Per supplier.
- Cara cepat memasang cadangan: Produk → Cadangan → pilih supplier → centang saran → Pasang.
Impor massal & Excel (CSV)
- Impor: pilih supplier → centang kelompok (provider · jenis) → Impor. Kategori & provider dibuat otomatis.
- Ekspor CSV (Setelan & Excel) → buka di Excel / Google Sheets → ubah → Impor CSV. Pemisah koma atau titik koma dikenali otomatis.
| Kolom | Isi |
|---|---|
kode | Kode produk toko (wajib, huruf/angka/titik/strip). Kode yang sudah ada = diubah; kode baru = dibuat. |
nama, kategori, provider, jenis | Nama kategori/provider baru dibuat otomatis |
tipe | prabayar / pascabayar / inject |
aktif | 1 / 0 |
mode_harga, harga_jual | markup atau manual (+ harga_jual) |
markup_tipe, markup_nilai | rupiah / persen; kosong = ikut provider/kategori/umum |
supplier_1, kode_1 … supplier_3, kode_3 | digiflazz / mdppay + kode produk supplier (harus ada di pricelist) |
Isi saldo (deposit)
- Transfer manual: pengguna transfer nominal + 3 digit kode unik, unggah bukti, lalu Anda menerima di menu Deposit Member.
- Otomatis (QRIS / Virtual Account): Pakasir atau MDPPay. Saldo masuk otomatis saat callback lunas diterima. Pasang URL callback di panel gateway.
- Halaman Isi saldo, Bayar, dan Isi saldo berhasil bisa didesain dengan HTML. Logika tagihan & saldo tetap di server.
Link isi saldo untuk tombol HTML
<a class="btn" href="{{url_path}}/isi-saldo">Isi saldo</a>
<a class="btn" href="{{url_path}}/isi-saldo?metode=qris&nominal=50000">QRIS 50rb</a>
<!-- langsung buat tagihan & buka halaman bayar -->
<a class="btn" href="{{url_path}}/isi-saldo?metode=mdppay:002&nominal=100000&langsung=1">VA BRI 100rb</a>Daftar metode yang aktif di toko Anda ada di Builder → Pembayaran Otomatis → Link pembayaran untuk HTML.
Member
- Pendaftaran: nama, nomor WhatsApp, email, password, PIN 6 angka. Masuk dengan nomor WA/email + password; PIN untuk konfirmasi transaksi.
- Member lupa password: Builder → Member → detail → Password sementara, lalu kirim lewat WhatsApp.
- Log aktivitas mencatat masuk, transaksi, isi saldo, ganti PIN (plus lokasi bila diaktifkan di Perilaku Aplikasi).
Tiga tempat menulis kode
Ini bagian paling penting untuk programmer. Kode yang sama bisa berfungsi di satu tempat dan dibuang di tempat lain.
| Halaman bawaan (Edit Lanjutan / Tampilan) | Halaman Kustom | CSS Global | |
|---|---|---|---|
| Contoh | HTML Beranda, Halaman Produk, Akun, Riwayat, Detail transaksi, Notifikasi, Isi saldo, Bayar, Isi saldo berhasil, Gagal memuat | Menu Halaman Kustom (maks 50 halaman) | Edit Lanjutan → CSS Global |
HTML & <style> | Ya | Ya (dokumen lengkap boleh) | CSS saja |
<script>, onclick | Dibuang | Ya | — |
<form>, <input>, <select> | Dibuang (pakai placeholder) | Ya | — |
<iframe>, <video> | Dibuang | Ya (https) | — |
Placeholder {{…}} | Ya | Ya | Tidak |
| Data toko lewat JS | — | PPOB.* | — |
| Berlaku di | Halaman itu saja | Halaman itu / tempat ia disematkan | Semua halaman |
<div data-halaman="ID-halaman"></div>.Tampilan siap pakai, per kategori & menu Profil
Tampilan siap pakai
Builder → Halaman Aplikasi → Siap Pakai: pilih gaya Elegan (kepala bergradasi, kartu melayang) atau Modern (judul besar, tombol pil), tekan Lihat untuk pratinjau, lalu Pasang ke 14 halaman sekaligus. Satu halaman saja? Buka halamannya, pilih di bagian Tampilan siap pakai, lalu Simpan. Warna mengikuti warna utama toko dan berlaku tanpa build ulang APK. Hasilnya tetap HTML biasa, jadi bisa Anda ubah lagi.
Tampilan per kategori
Builder → Halaman Aplikasi → Per Kategori: setiap kategori (Pulsa, Paket Data, Token PLN, E-Money, Games, tagihan, dll) bisa punya tampilan sendiri. Kategori yang belum diatur memakai Halaman Produk umum. Placeholder sama dengan Halaman Produk, ditambah {{ikon_kategori}}, {{slug_kategori}}, dan {{jumlah_produk}}. Tampilan per kategori butuh APK versi mesin 49 ke atas; APK lama tetap memakai Halaman Produk umum.
Menu Profil
Builder → Halaman Aplikasi → Menu Profil: centang menu yang tampil di halaman Akun, termasuk Halaman Kustom pilihan Anda (maks 10). Menu baru dari update BosAplikasi otomatis masuk daftar ini. Bila HTML Profil Anda menulis menu sendiri, menu tercentang yang belum ada di HTML itu ditambahkan otomatis di bawahnya (bisa dimatikan).
Aturan HTML halaman bawaan
HTML halaman bawaan dibersihkan (sanitasi) setiap kali ditampilkan demi keamanan pembeli. Yang tidak ada di daftar berikut dibuang tanpa pesan di aplikasi, tetapi Pemeriksa kode di editor Builder memberi tahu Anda.
Tag yang diizinkan
Atribut yang diizinkan
class id style href src alt title width height colspan rowspan target rel role align open data-* aria-*, serta atribut SVG (viewBox, fill, stroke, d, cx, points, …). Semua atribut on… (onclick, onload, …) dibuang.
Alamat (href / src) yang diizinkan
#/…halaman aplikasi,{{url_path}}/…halaman kustom & aksihttps://…,http://…(di APK sebaiknya selalu https),tel:,mailto:- Gambar: juga
data:image/png|jpeg|gif|webp;base64,…dan{{logo_url}} - Alamat relatif seperti
beli.html,/produk,../gambar.pngdibuang.
CSS
- Boleh
<style>…</style>di dalam HTML dan atributstyle="…". url()hanyahttps://;@import,expression(),javascript:dibuang.- Font: pilih di menu Font (Google Fonts dimuat otomatis), lalu pakai
font-family: var(--font-utama).
Contoh: benar vs salah
<button onclick="location.href='pulsa.html'">Pulsa</button> <!-- onclick & alamat relatif dibuang -->
<script>document.querySelector('#saldo').innerText = '…'</script> <!-- script dibuang -->
<form action="/beli"><input name="nomor"></form> <!-- form & input dibuang --><a class="btn" href="{{url_path}}/kategori/pulsa-data">Pulsa</a>
<div class="big">{{saldo}}</div>
{{input_tujuan}} <!-- kolom isian disediakan placeholder -->
<div data-halaman="promo-interaktif"></div> <!-- butuh JS? sematkan Halaman Kustom -->Placeholder
Placeholder ditulis {{nama}} dan diganti saat aplikasi dibuka. Ada dua jenis:
- Teks (mis.
{{nama}},{{saldo}}): aman dipakai di dalam teks atau atribut, otomatis di-escape. - Komponen (mis.
{{grid_kategori}},{{input_tujuan}}): diganti HTML lengkap buatan aplikasi, letakkan sebagai elemen sendiri (jangan di dalam atribut).
Batalkan deposit: member boleh punya sampai 5 tagihan isi saldo yang belum dibayar. Di halaman bayar pakai {{tombol_batal_deposit}}; di halaman Isi saldo pakai {{tombol_batal_semua_deposit}}. Tombol buatan sendiri tanpa JavaScript: <button data-batal-deposit="{{id_deposit}}">Batalkan</button> atau data-batal-deposit="semua" (berlaku di semua halaman, termasuk Halaman Kustom). Di Halaman Kustom dengan JavaScript: PPOB.batalDeposit(id).
Level member: {{level}} (nama level, mis. Gold), {{level_sampai}} (tanggal berakhir atau "Selamanya"), {{level_untung}} (mis. "Harga 5% lebih murah"), dan komponen {{tombol_level}} (lencana yang membuka halaman Level). Kosong bila toko belum memakai level. Tautan ke halaman Level: {{url_path}}/level.
Placeholder yang salah ketik: di halaman bawaan tampil kosong, di Halaman Kustom tampil apa adanya. Beberapa komponen wajib (ditandai "wajib") ditambahkan otomatis bila Anda lupa memasangnya.
Pilih halaman:
Placeholder berparameter
{{grid_provider}} <!-- grid logo semua provider -->
{{grid_provider:e-money}} <!-- provider di kategori e-money saja (slug ada di menu Produk → Kategori) -->Link & navigasi
Aplikasi berjalan sebagai satu halaman (single page). Pindah halaman memakai #/… atau {{url_path}}/…, bukan nama file.
Halaman aplikasi
| Link | Membuka |
|---|---|
#/ | Beranda |
#/beli?k=Pulsa %26 Data | Menu kategori (nama persis; & ditulis %26) |
#/beli?k=Pulsa %26 Data&b=Telkomsel&j=Paket Data | Langsung ke provider & jenis |
#/deposit · #/transfer · #/riwayat · #/akun · #/notif | Isi saldo · Transfer saldo · Riwayat · Akun · Notifikasi |
#/trx?id=123 | Detail transaksi |
Link {{url_path}}
Tulis tanpa https://. Bentuk yang tersedia:
| Aksi | Link |
|---|---|
| Keluar / pengaturan / edit profil | {{url_path}}/logout · /setting · /edit-profil |
| Ganti PIN / password / PIN transaksi / hapus akun | /ganti-pin · /ganti-password · /pin-transaksi · /hapus-akun |
| Kebijakan / syarat / FAQ / tentang kami / kontak kami / chat CS | /kebijakan · /syarat · /faq · /tentang · /kontak · /cs |
| Beranda / akun / riwayat / notifikasi | /beranda · /akun · /riwayat · /notifikasi |
Karakter khusus di link
| Karakter | Tulis | Contoh |
|---|---|---|
& di dalam nama | %26 | #/beli?k=Pulsa %26 Data |
| spasi | spasi biasa / %20 | #/beli?k=Token PLN |
+ | %2B | b=Smartfren %2B |
/ · # · ' | %2F · %23 · %27 |
Link luar
https://…, https://wa.me/62…, tel:, mailto: otomatis dibuka di browser / aplikasi HP, bukan di dalam aplikasi toko.
Halaman Kustom (HTML + CSS + JavaScript)
Halaman Kustom menjalankan kode Anda di iframe terisolasi (sandbox tanpa akses ke data login). Anda bebas menulis HTML lengkap, CSS, dan JavaScript, termasuk memuat library dari CDN https://.
Cara memakai
- Builder → Halaman Kustom → buat halaman, isi ID halaman (mis.
promo-ramadan). - Buka sebagai halaman penuh:
<a href="{{url_path}}/promo-ramadan">…</a>(boleh dari Beranda, navbar, banner). - Atau sematkan di halaman bawaan:
<div data-halaman="promo-ramadan"></div>(tinggi menyesuaikan isi otomatis).
Yang perlu diketahui
- CSS aplikasi (
ui.css), warna toko (--primary), font, dan CSS Global otomatis tersedia: kelascard,btn, dll langsung bisa dipakai. location.href = '#/deposit',location.hash,window.open(url)dan link<a href>otomatis dialihkan ke aplikasi / browser.- Tidak tersedia:
localStorage,sessionStorage,indexedDB,document.cookie(error SecurityError), aksestop/parent. Simpan data di variabel. fetch()ke server lain hanya berhasil bila server itu mengizinkan CORS untuk originnull. Data toko ambil lewat APIPPOB.- File relatif (
style.css,app.js,gambar.png) tidak ada. Pakai alamat lengkaphttps://atau tulis langsung di halaman. - Halaman tidak bisa melakukan transaksi tanpa pengguna: arahkan ke halaman produk (
PPOB.buka('#/beli?k=…')/ link provider), pembelian tetap meminta PIN. - Maksimal 200.000 karakter per halaman.
Contoh lengkap
<style>
.promo { display:grid; gap:10px; padding:16px; }
.promo .item { display:flex; justify-content:space-between; background:#fff; padding:12px 14px; border-radius:12px; }
</style>
<div class="promo">
<h2>Halo, {{nama}} 👋</h2>
<div class="muted">Saldo: <b id="saldo">{{saldo}}</b></div>
<div id="daftar">Memuat…</div>
<button class="btn block" id="isi">Isi saldo</button>
</div>
<script>
PPOB.produk('Pulsa & Data').then(function (list) {
var tsel = list.filter(function (p) { return p.brand === 'Telkomsel'; }).slice(0, 5);
document.getElementById('daftar').innerHTML = tsel.map(function (p) {
return '<a class="item" href="#/provider?p=telkomsel"><span>' + p.nama + '</span><b>' + PPOB.rupiah(p.harga) + '</b></a>';
}).join('');
}).catch(function (e) { PPOB.toast(e.message); });
document.getElementById('isi').addEventListener('click', function () { PPOB.buka('#/deposit'); });
</script>Komponen produk & form beli (semua halaman)
{{daftar_produk}}, {{input_tujuan}}, {{filter_brand}}, {{tab_jenis}} hanya berfungsi di template Halaman produk. Di Beranda, Akun, Riwayat, Halaman Kustom, dan halaman lain, pakai komponen di bawah. Slug kategori/provider ada di Builder → Produk → Kategori / Provider (tombol {{produk}} / {{form_beli}} menyalin kodenya).
| Placeholder | Hasil |
|---|---|
{{produk:pulsa-data}} | Daftar produk satu kategori, dikelompokkan per provider. Diketuk → halaman beli produk itu. |
{{produk_provider:dana}} | Daftar produk satu provider, dikelompokkan per jenis. |
{{form_beli:mobile-legends}} | Kolom tujuan sesuai format provider (mis. User ID + Zone ID) + daftar produk. Diketuk → konfirmasi + PIN → transaksi. |
Slug salah atau produk kosong tidak lagi tampil kosong tanpa kabar: muncul kotak merah berisi penjelasan. Pemeriksa kode di editor juga memberi tahu bila placeholder dipakai di halaman yang salah.
Struktur HTML yang dihasilkan (untuk ditata dengan CSS)
<h3 class="produk-grup">Telkomsel</h3> <!-- hanya bila lebih dari 1 kelompok -->
<div class="list daftar-produk-komp">
<a class="item produk-item" href="#/beli?…" data-kode="TSEL10">
<img class="pg-gambar"> <!-- bila produk punya gambar -->
<span class="grow"><span class="t">Telkomsel 10.000</span><span class="s">keterangan</span></span>
<span class="harga">Rp11.200</span> <!-- atau <span class="lbl-gangguan"> -->
</a>
</div><div class="form-beli" data-provider="mobile-legends">
<label class="fb-label">User ID<span class="tujuan-baris"><input class="fb-tujuan"></span></label>
<label class="fb-label fb-label2">Zone ID<input class="fb-tujuan2"></label> <!-- hanya format ID + Server -->
<div class="list">
<button type="button" class="item produk-item" data-ppob-beli="ML86">…</button>
</div>
</div>.form-beli .fb-label { color: var(--primary); }
.daftar-produk-komp { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; background: none; padding: 0; }
.daftar-produk-komp .produk-item { flex-direction: column; align-items: flex-start; background: #fff; border-radius: 12px; padding: 12px; border: 0; }Format kolom tujuan
Atur di Produk → Provider → ✏️ (berlaku untuk semua produk provider), atau di form produk (menimpa provider).
| Format | Contoh | Kolom di aplikasi |
|---|---|---|
| Otomatis | — | Aturan lama: prabayar angka, pascabayar ID pelanggan, inject nomor seri |
| Nomor HP | Pulsa, DANA, OVO | 1 kolom + pilih kontak; +62/62 → 0 |
| ID pelanggan | PLN, PDAM, BPJS | 1 kolom huruf/angka |
| ID akun | Free Fire, PUBG | 1 kolom |
| ID + Server / Zone | Mobile Legends | 2 kolom, digabung saat dikirim ke supplier: langsung (123456781234), plus, kurung, garis tegak, titik, atau spasi. Ikuti format yang diminta supplier Anda. |
| Akun berbasis email | 1 kolom email | |
| Nomor seri | Inject voucher | 1 kolom + scan |
Cek nama sebelum bayar
Di Produk → Provider → ✏️ → Cek nama sebelum bayar. Pembeli melihat nama di layar konfirmasi sebelum mengetik PIN.
| Pilihan | Sumber | Catatan |
|---|---|---|
| Nama pelanggan PLN | Inquiry PLN Digiflazz (resmi) | Butuh akun Digiflazz terhubung. Menampilkan nama + daya. |
| Nickname game (Mobile Legends, Free Fire, Genshin, CODM, AOV, Wild Rift, dll) | Data Codashop (tidak resmi) | Bisa sewaktu-waktu tidak tersedia. Bila gagal dicek, pembeli tetap bisa membayar. |
| E-wallet (DANA, OVO, …) | — | Belum ada sumber gratis yang sah. |
Hasil: hijau = nama ditemukan, merah = ID tidak ditemukan (pembeli diminta memeriksa ulang), kuning = tidak bisa dicek. Maksimal 30 cek per 10 menit per member. Di Halaman Kustom: await PPOB.cekNama({ kode, tujuan, tujuan2 }) → { ok, nama, pesan }.
Link beli langsung
<a href="{{url_path}}/beli?kode=TSEL10">Beli pulsa 10rb</a> <!-- pengguna isi nomor -->
<a href="{{url_path}}/beli?kode=ML86&tujuan=12345678&tujuan2=1234">Top up</a> <!-- langsung konfirmasi -->PIN selalu diketik di layar konfirmasi aplikasi; link tidak bisa (dan tidak boleh) membawa PIN. Bila produk & tujuan yang sama baru dibeli dalam 2 menit, aplikasi bertanya dulu agar saldo tidak terpotong dua kali.
API JavaScript PPOB
Tersedia otomatis di setiap Halaman Kustom (tanpa perlu dimuat). Fungsi yang mengembalikan data berbentuk Promise (pakai .then() atau await), batas waktu 20 detik.
| API | Hasil / fungsi |
|---|---|
PPOB.user | { nama, hp, saldo } saat halaman dibuka (null bila belum masuk) |
PPOB.toko | { nama_brand, warna, logo_url, wa_cs } |
PPOB.url_path | Awalan link halaman kustom (#/p) |
await PPOB.kategori() | Daftar nama kategori, mis. ['Pulsa & Data', 'Token PLN'] |
await PPOB.produk('Token PLN')await PPOB.produk({ kategori: 'e-money' })await PPOB.produk({ provider: 'mobile-legends' })await PPOB.produk({ kode: 'ML86' }) | Daftar produk (JSON): [{ kode, nama, harga, provider, provider_nama, kategori, kategori_nama, jenis, tipe, gangguan, deskripsi, gambar_url, form, link }]. form = format kolom tujuan: { format, label, label2 }, format salah satu auto | hp | idpel | id | id_server | email | seri. |
await PPOB.beli({ kode, tujuan, tujuan2, nominal }) | Membuka konfirmasi + PIN milik aplikasi (PIN tidak pernah lewat HTML Anda). tujuan2 hanya untuk format id_server (mis. Zone ID). nominal hanya untuk produk bebas nominal (bisa diubah pembeli di layar konfirmasi). Tanpa tujuan → membuka halaman beli produk itu. Hasil: data transaksi, atau { status: 'batal' }. Salah isi → error berisi pesan yang bisa ditampilkan. |
await PPOB.saya() | Data terbaru { nama, hp, saldo } |
await PPOB.riwayat() | 20 transaksi terakhir [{ id, produk, tujuan, harga, status, sn, waktu }] |
await PPOB.riwayat({ jenis, dari, sampai, cari, status, sebelum, limit }) | Riwayat dengan filter. jenis: 'transaksi' / 'deposit' / 'mutasi'; dari/sampai: '2026-10-01' (tanggal WIB); cari: nomor tujuan / produk / no. ref; status: 'sukses' / 'pending' / 'gagal'; sebelum: ID data terakhir untuk memuat yang lebih lama; limit maks 100 |
await PPOB.notifikasi({ jenis }) | Notifikasi (maks 30 terbaru). jenis: 'semua' / 'transaksi' / 'info' → [{ id, judul, isi, waktu, jenis, link }] |
await PPOB.deposit() | Tagihan isi saldo yang belum dibayar [{ id, total, nominal, metode, waktu, link }] (maksimal 5) |
await PPOB.batalDeposit(id) | Membatalkan satu tagihan isi saldo (setelah konfirmasi di aplikasi). PPOB.batalDeposit('semua') membatalkan semuanya. Hasil: jumlah yang dibatalkan |
await PPOB.kontak() | Pilih kontak HP → { nama, nomor } (APK) |
await PPOB.scan({ judul }) | Scan barcode/QR dengan kamera → teks, null bila batal |
PPOB.buka('#/deposit') | Pindah ke halaman aplikasi (#/… atau {{url_path}}/…) |
PPOB.bukaLuar('https://…') | Buka di browser / WhatsApp |
PPOB.toast('Tersimpan') | Pesan singkat di bawah layar |
PPOB.muatUlang() | Muat ulang data aplikasi |
PPOB.rupiah(10000) | 'Rp10.000' |
<div id="daftar">Memuat…</div>
<input id="uid" placeholder="User ID"> <input id="zone" placeholder="Zone ID">
<script>
PPOB.produk({ provider: 'mobile-legends' }).then(function (list) {
document.getElementById('daftar').innerHTML = list.map(function (p) {
return '<button class="item" data-kode="' + p.kode + '">' + p.nama + ' · ' + PPOB.rupiah(p.harga) + '</button>';
}).join('');
});
document.addEventListener('click', function (e) {
var b = e.target.closest('[data-kode]');
if (!b) return;
PPOB.beli({ kode: b.dataset.kode, tujuan: uid.value, tujuan2: zone.value })
.catch(function (err) { PPOB.toast(err.message); }); // mis. "Zone ID belum benar"
});
</script>Keamanan: jangan pernah membuat kolom PIN sendiri atau menaruh PIN di link. Server tetap memeriksa token login member, PIN, harga dari database, format tujuan, transaksi kembar (2 menit), dan batas 15 transaksi/menit, jadi HTML/JS toko tidak bisa mengubah harga atau melewati PIN.
(async function () {
try {
const saya = await PPOB.saya();
const kode = await PPOB.scan({ judul: 'Scan voucher' });
if (kode) PPOB.toast('Terbaca: ' + kode + ' · saldo ' + PPOB.rupiah(saya.saldo));
} catch (e) { PPOB.toast(e.message); }
})();CSS: variabel & kelas siap pakai
Variabel
| Variabel | Isi |
|---|---|
--primary / --primary-soft | Warna utama toko / versi lembut |
--bg · --card · --ink · --muted · --line | Latar, kartu, teks, teks sekunder, garis |
--ok · --warn · --bad (+ -bg) | Warna status |
--font-utama | Font pilihan di menu Font |
Kelas siap pakai
| Kelas | Kegunaan |
|---|---|
card | Kotak putih bersudut |
btn, btn sec, btn line, btn danger, btn block, btn sm | Tombol (utama, lembut, garis, merah, lebar penuh, kecil) |
row, between, grow, center | Baris flex, rata kiri-kanan, isi sisa ruang, rata tengah |
grid2, grid3 | Grid 2 / 3 kolom |
list + item | Daftar baris |
tile, gaya-bulat, gaya-kotak | Ikon menu (bungkus {{grid_kategori}}) |
note info / note warn / note bad | Kotak pesan |
badge, chips, tabs | Label kecil, pilihan berbentuk pil, tab |
big, muted, mono, hero, topbar | Angka besar, teks abu, huruf monospace, header berwarna, bilah judul |
grid-provider | Grid logo provider (dipakai {{grid_provider}}) |
:root { --primary: #6A1B9A; --primary-soft: #F1E6F7; }
.card { border-radius: 20px; box-shadow: 0 6px 20px rgba(0,0,0,.06); }
.hero { background: linear-gradient(135deg, var(--primary), #AB47BC); }Struk, printer Bluetooth & kirim JPG
Di detail transaksi pengguna menekan Cetak struk atau Bagikan struk. Struk digambar dari HTML Tampilan → Struk Transaksi, jadi hasil cetak dan gambar JPG sama persis dengan desain toko.
- Pasangkan printer sekali: nyalakan printer thermal → HP: Pengaturan → Bluetooth → cari perangkat (mis. RPP02N, MTP-II, BlueTooth Printer) → pasangkan (PIN biasanya
0000atau1234). - Di aplikasi: Cetak struk → Cetak ke printer Bluetooth → pilih printer (diingat untuk berikutnya, bisa diganti).
- Pilih kertas 58 mm atau 80 mm (diingat per HP). Kolom Harga di struk bisa diubah kasir (harga jual ke pelanggan).
- Kirim ke WhatsApp membuka WhatsApp dengan gambar JPG struk; Bagikan / simpan membuka pilihan aplikasi (di browser: diunduh).
- Printer yang didukung: printer thermal Bluetooth ESC/POS (hampir semua printer kasir mini). Printer BLE-saja (tanpa Bluetooth klasik) belum didukung.
- Android 12+ akan meminta izin Perangkat di sekitar saat pertama mencetak. Bila ditolak: Pengaturan → Aplikasi → (aplikasi toko) → Izin.
- Printer hanya bisa tersambung ke satu HP. Gagal cetak? Matikan-nyalakan printer dan pastikan tidak tersambung ke HP lain.
- Desain: lebar 384px (58 mm) / 576px (80 mm), hitam-putih. Gunakan class
struk,s-kepala,s-nama,s-garis,s-baris(duaspan: label kiri, nilai kanan),s-sn,s-total,s-kaki. Sisipkan gambar (logo/QR) dengan tombol Sisipkan gambar di editor.
Alamat web toko
Setiap toko punya alamat sendiri, misalnya https://hjmtronik.bosaplikasi.com. Lihat dan ganti di Info Toko → Alamat web toko.
https://namatoko.bosaplikasi.commembuka aplikasi versi web toko itu (pelanggan bisa daftar, isi saldo, dan membeli dari browser).https://namatoko.bosaplikasi.com/unduhmenampilkan tombol unduh APK terbaru toko. Cocok dibagikan di WhatsApp, Instagram, atau dicetak di brosur.- Alamat bisa diganti kapan saja (3–32 huruf kecil, angka, tanda minus). Alamat lama langsung berhenti berfungsi.
- APK tetap memakai alamat server utama; alamat toko hanya untuk versi web dan halaman unduh, jadi tidak perlu build ulang.
Suara notifikasi & push notification
Suara per kejadian (Builder → Suara Notifikasi)
| Bunyi | Keterangan |
|---|---|
| Diam | Notifikasi tampil tanpa suara khusus |
| Nada | Cling (kasir), koin, lonceng, atau notifikasi lembut |
| Suara bicara | HP membacakan kalimat Bahasa Indonesia, mis. Saldo {nominal} sudah masuk → "Saldo lima puluh ribu rupiah sudah masuk". Placeholder: {produk} {tujuan} {harga} {sn} {nominal} {nama} {judul} |
| Suara sendiri | Unggah MP3/OGG/WAV/M4A maksimal 700 KB (mis. rekaman "Terima kasih, pembayaran diterima") |
Kejadian member: transaksi berhasil, transaksi gagal, isi saldo masuk, isi saldo ditolak, pengumuman toko. Kejadian pemilik: isi saldo menunggu konfirmasi, isi saldo otomatis masuk, penjualan berhasil, transaksi gagal. Untuk pemilik, centang Bunyikan di perangkat ini di HP/PC kasir yang membuka Builder.
Member bisa mematikan suara untuk HP-nya sendiri: Akun → Pengaturan akun → Bunyikan suara notifikasi di HP ini. Suara bicara memakai mesin suara Indonesia bawaan HP (Google Speech Services).
Push notification (untuk pemilik platform)
Tanpa push, notifikasi & suara hanya muncul saat aplikasi sedang terbuka. Dengan push (Firebase Cloud Messaging, gratis), notifikasi tetap datang walau aplikasi ditutup. Cukup diatur sekali untuk semua toko:
- Buka console.firebase.google.com → Create a project (mis. "BosAplikasi"). Google Analytics boleh dimatikan.
- Project settings (ikon gerigi) → tab Service accounts → Generate new private key → file JSON terunduh. Rahasiakan file ini.
- Buka console.cloud.google.com/iam-admin/iam (pilih proyek yang sama) → cari akun
firebase-adminsdk-…→ ✏️ → Add another role → Firebase Admin → Save. Peran ini dipakai Worker untuk mendaftarkan aplikasi tiap toko secara otomatis. - Pasang di Worker (Termux):bash
cat firebase-key.json | wrangler secret put FIREBASE_SA
- Build ulang APK tiap toko (dan APK Builder). Saat build, Worker mendaftarkan package aplikasi ke Firebase dan memasang
google-services.jsonotomatis.
Bila log build menampilkan PERINGATAN push notification: …, biasanya peran Firebase Admin belum diberikan atau Firebase Management API belum aktif (aktifkan di console.cloud.google.com → APIs & Services). APK tetap jadi, hanya tanpa push.
Build APK
- Build pertama membuat package (mis.
com.namatoko) yang terkunci setelah sukses. - Setiap build menaikkan nomor versi otomatis, jadi APK baru bisa dipasang menimpa yang lama.
- Bagikan APK lewat link unduhan dari menu Build, atau unggah ke Google Play (perlu akun developer sendiri).
Langganan & verifikasi
- Toko baru mendapat masa trial. Setelah habis, aplikasi pengguna tidak bisa bertransaksi sampai diperpanjang.
- Bila verifikasi KTP diwajibkan platform, selesaikan di menu Verifikasi Identitas sebelum membayar paket.
- Bayar otomatis (QRIS/VA) langsung aktif; transfer manual menunggu pemeriksaan admin.
Laporan
Laba kotor = harga jual − harga modal (transaksi sukses). Tersedia ringkasan, harian, per kategori, per produk, per supplier, serta unduhan Excel & PDF.
Keamanan
- Kredensial supplier & payment gateway disimpan terenkripsi dan tidak pernah dikirim ke aplikasi pengguna.
- URL callback berisi kode rahasia, jangan dibagikan selain ke panel supplier/gateway.
- HTML pemilik toko dibersihkan dan Halaman Kustom berjalan terisolasi, sehingga kode tidak bisa mencuri data login pembeli.
Kode saya tidak berfungsi
Tempel kode Anda di pemeriksa berikut. Pemeriksa memakai aturan yang sama persis dengan aplikasi.
Penyebab paling sering
| Gejala | Penyebab | Solusi |
|---|---|---|
| Tombol tidak bereaksi | onclick / <script> di halaman bawaan dibuang | Pakai <a href="#/…"> atau {{url_path}}/…; logika JS pindah ke Halaman Kustom |
| Kolom isian hilang | <form>/<input> dibuang di halaman bawaan | Pakai placeholder ({{input_tujuan}}, {{form_isi_saldo}}, {{kotak_cari}}) atau Halaman Kustom |
| Link tidak pindah halaman | Alamat relatif (beli.html, /produk) | Pakai #/… atau {{url_path}}/… |
| Link {{url_path}} tidak jalan | Ditulis http://{{url_path}} atau ada spasi | Tulis persis href="{{url_path}}/id-halaman" |
{{daftar_produk}} / {{input_tujuan}} kosong | Placeholder ini hanya untuk template Halaman produk | Pakai {{form_beli:slug}} atau {{produk:slug}} |
| Kotak merah "Tidak ada produk untuk {{…}}" | Slug kategori/provider salah atau produknya nonaktif | Salin kode dari Produk → Kategori / Provider (tombol {{produk}} / {{form_beli}}) |
| Game butuh ID + server tapi hanya 1 kolom | Format tujuan provider masih Otomatis | Produk → Provider → ✏️ → Kolom tujuan: ID + Server / Zone |
| Menu kategori kosong | Nama di #/beli?k= tidak persis / & tidak ditulis %26 | Salin link dari menu Produk → Kategori |
Teks {{…}} hilang / tampil apa adanya | Placeholder salah ketik atau tidak berlaku di halaman itu | Lihat daftar placeholder halaman (bawah editor) dan tabel placeholder |
| Gambar / CSS / JS luar tidak muncul | Alamat http:// atau file relatif | Pakai alamat lengkap https:// |
| Halaman Kustom error "SecurityError" | localStorage / cookie dipakai | Simpan di variabel; data pengguna lewat PPOB.saya() |
fetch gagal (CORS) | Server tujuan menolak origin null | Aktifkan CORS di server tujuan, atau gunakan API PPOB |
| Form di Halaman Kustom memuat ulang halaman | Submit bawaan browser | form.addEventListener('submit', e => { e.preventDefault(); … }) |
| Perubahan tidak terlihat | Aplikasi belum dimuat ulang | Tutup-buka aplikasi atau tarik layar ke bawah. Pastikan sudah tekan Simpan. |
| Tampilan berbeda dari pratinjau | CSS Global / tema menimpa gaya Anda | Gunakan selektor lebih spesifik atau kelas sendiri |
Pertanyaan umum
Transaksi lama "Diproses"
Callback supplier belum sampai. Cek Supplier Produk → Callback terakhir. Bila supplier sudah sukses/gagal, tandai manual di detail transaksi.
Isi saldo QRIS/VA sudah dibayar tapi saldo belum masuk
Pastikan URL callback terpasang di panel gateway. Lihat log callback di Pembayaran Otomatis; callback yang tadinya ditolak bisa diproses ulang.
Bagaimana mengirim pengumuman?
Marketing → Push Notifikasi. Pengumuman tampil di menu Notifikasi aplikasi dan notifikasi HP.
Bisakah halaman memakai framework (Vue/React/Tailwind)?
Bisa di Halaman Kustom dengan memuat dari CDN https:// (mis. <script src="https://cdn.tailwindcss.com"></script>). Di halaman bawaan tidak, karena script dibuang.