
1. Instalasi
  ✓ Sebelum instalasi semua halaman diarahkan ke /install
  ✓ Installer tampil dan membuat kode bootstrap di folder privat
  ✓ Kode instalasi salah ditolak
  ✓ Instalasi berhasil dengan kode benar
  ✓ Kode bootstrap dihapus setelah instalasi
  ✓ Installer terkunci setelah selesai (404)
  ✓ Tidak ada sandi admin bawaan: admin = data yang diisi

2. Publik & header keamanan
  ✓ Beranda 200
  ✓ CSP script-src tanpa unsafe-inline/unsafe-eval dan memakai nonce
  ✓ Header nosniff, frame-ancestors, referrer
  ✓ Tidak ada statistik/testimoni karangan di beranda
  ✓ Katalog menampilkan 4 desain
  ✓ Demo glass-wonder 200 & berlabel demo
  ✓ Demo burgundy-vintage-hitam 200 & berlabel demo
  ✓ Demo isometric-castle 200 & berlabel demo
  ✓ Demo istana-kertas 200 & berlabel demo
  ✓ Traversal pada URL media ditolak
  ✓ robots.txt menutup area privat

3. Akun & peran
  ✓ Pendaftaran berhasil
  ✓ Parameter role=admin diabaikan: pendaftar selalu member
  ✓ Sandi di-hash (Argon2id/bcrypt), bukan teks asli
  ✓ Email ganda (beda huruf besar) ditolak
  ✓ POST tanpa token CSRF ditolak (419)
  ✓ Member tidak bisa membuka panel admin (403)
  ✓ Tamu diarahkan ke halaman masuk
  ✓ Percobaan masuk berulang dibatasi
  ✓ Admin masuk

4. Pembelian & pembayaran manual
  ✓ Belum bisa membeli sebelum admin konfirmasi lisensi & metode bayar
  ✓ Admin mengonfirmasi lisensi & mengubah kuota WA produk
  ✓ Pesanan dibuat (pending_payment) dengan snapshot harga
  ✓ Kirim ganda dengan kunci idempoten tidak membuat pesanan ganda
  ✓ Ubah harga produk tidak mengubah pesanan lama
  ✓ Bukti transfer diunggah → menunggu verifikasi
  ✓ Bukti transfer saja tidak membuat hak aktif
  ✓ Bukti transfer tidak bisa dibuka member lain
  ✓ Bukti transfer tidak bisa dibuka tamu
  ✓ Member tidak dapat menyetujui pembayaran sendiri
  ✓ Admin menyetujui → lunas, 1 hak, 1 undangan
  ✓ Persetujuan ganda idempoten (tetap 1 hak)
  ✓ Masa aktif 365 hari sejak konfirmasi
  ✓ Pemilik pesanan lain tidak bisa melihat invoice orang lain
  ✓ Admin aktivitas tercatat di audit

5. Editor & undangan
  ✓ Draft dapat dibaca pemilik
  ✓ Member lain tidak bisa membaca draft (404)
  ✓ Simpan draft berhasil
  ✓ Revisi usang ditolak sebagai konflik (409)
  ✓ Mengganti template lewat data tidak mengubah template undangan
  ✓ Member lain tidak bisa menyimpan draft (404)
  ✓ Terbit ditolak bila acara belum lengkap (422)
  ✓ URL peta javascript: tidak pernah tersimpan (ditolak/dibuang)
  ✓ Terbit berhasil
  ✓ Undangan publik 200
  ✓ Nama berisi <script> tidak tereksekusi (di-escape)
  ✓ Tidak ada javascript: di halaman publik
  ✓ Foto model contoh template tidak dimuat di undangan member
  ✓ Draft yang belum diterbitkan tidak tampil ke tamu
  ✓ RSVP publik tersimpan
  ✓ Ucapan tersimpan sebagai teks (HTML tidak dieksekusi saat tampil)
  ✓ Honeypot ucapan menolak bot
  ✓ Masa aktif habis → editor terkunci (403)
  ✓ Masa aktif habis → tautan publik halaman tidak aktif tanpa data pribadi
  ✓ Perpanjangan memperbarui hak & undangan yang sama
  ✓ Perpanjangan setelah habis dihitung dari waktu aktivasi

6. Gateway Midtrans
  ✓ Pesanan gateway dibuat
  ✓ Notifikasi bertanda tangan salah ditolak (403) & tidak melunasi
  ✓ Tanda tangan valid tetapi status tidak terverifikasi ke API → tidak lunas
  ✓ Halaman kembali dari browser tidak melunasi pesanan
  ✓ Kunci server tersimpan terenkripsi (tidak ada teks asli di DB)
  ✓ Kunci server tidak tampil di halaman admin

7. Kontak, impor, pesan
  ✓ Berkas CSV diunggah ke penyimpanan privat
  ✓ Validasi: 2 valid, 2 tidak valid, 1 ganda
  ✓ CSV baris ditolak tersedia
  ✓ Impor menyimpan kontak valid & menghapus berkas asli
  ✓ Simpan impor ulang tidak menggandakan kontak
  ✓ Kontak member lain tidak terlihat
  ✓ Ekspor CSV menetralkan formula (=HYPERLINK diawali apostrof)
  ✓ Templat: variabel tak dikenal ditolak
  ✓ Templat: render hanya variabel daftar-putih
  ✓ Normalisasi nomor (lokal, +62, notasi ilmiah ditolak, huruf ditolak, internasional)
  ✓ Pengaturan pesan disimpan per member

8. WhatsApp: kampanye, worker, webhook
  ✓ Kampanye draf dibuat dengan snapshot pesan per penerima
  ✓ Pesan berisi nama tamu & tautan pribadi
  ✓ Kampanye member lain tidak dapat dibuka (404)
  ✓ Konfirmasi tanpa persetujuan ditolak
  ✓ Kampanye masuk antrean setelah persetujuan
  ✓ Worker tanpa tanda tangan ditolak (401)
  ✓ Worker dengan tanda tangan salah ditolak (401)
  ✓ Stempel waktu kedaluwarsa ditolak (401)
  ✓ Klaim sah mendapat 1 pekerjaan (1 pesan dalam proses per nomor)
  ✓ Nonce yang sama ditolak (anti-replay, 409)
  ✓ Jeda per pengirim: klaim berikutnya kosong selama jeda
  ✓ Pekerjaan memuat Idempotency-Key = correlation id
  ✓ Worker lain ditolak saat lease dipegang (409)
  ✓ Laporan dengan lease token salah diabaikan
  ✓ Laporan sah → status diterima server
  ✓ Webhook bertanda tangan salah ditolak (401)
  ✓ Webhook ack "read" diterapkan
  ✓ Ack terlambat "delivered" tidak menurunkan status "read"
  ✓ Webhook duplikat diabaikan (idempoten)
  ✓ Kuota: pesan terkirim tercatat 1 di buku kuota
  ✓ Gagal pasti → status gagal & kuota dikembalikan
  ✓ Kampanye selesai dengan kegagalan tercatat
  ✓ Kirim ulang yang gagal atas permintaan member
  ✓ Nomor dikecualikan → penerima tertunda dilewati
  ✓ API key OpenWA tidak pernah dikirim ke browser member

9. Admin
  ✓ Admin memberi hak manual (+1 undangan, tercatat)
  ✓ Member diblokir langsung keluar
  ✓ Cadangan dibuat (VACUUM INTO + kunci)
  ✓ Arsip pemulihan dengan jalur ../ ditolak
  ✓ Unduh cadangan dengan traversal ditolak
  ✓ Pemeriksaan sistem tampil

Hasil: 111 lulus, 0 gagal
