# Panduan OpenWA untuk KabarNikah

Fitur **Sebar Undangan** mengirim pesan WhatsApp dari nomor milik masing-masing member melalui gateway **OpenWA** (rmyndharis/OpenWA). KabarNikah tidak menyertakan atau menjalankan WhatsApp sendiri; Anda menjalankan OpenWA di server Anda.

- Versi yang dipakai saat pengembangan dan uji kompatibilitas: **OpenWA v0.30.0**, commit `6bb8b5749ed6aefb120eb11842472359516ae7c5` (sama dengan ZIP `OpenWA-main.zip` yang diberikan, kecuali berkas uji SDK PHP).
- OpenWA membutuhkan Node.js ≥ 22.19 atau Docker, dan biasanya **VPS** (bukan shared hosting).

## 1. Arsitektur

```
Browser member ──HTTPS──> KabarNikah (PHP)
                             │  (API key OpenWA hanya di server)
                             ├──> OpenWA API: buat sesi, QR, webhook
                             ▲
OpenWA ──webhook bertanda tangan (HMAC per koneksi)──┘  status sesi & ack pesan
Worker (Node, tanpa dependensi) ──HMAC──> KabarNikah /internal/worker/{claim,report}
Worker ──X-API-Key + Idempotency-Key──> OpenWA /messages/send-text
```

- API key OpenWA **tidak pernah** dikirim ke browser. Browser member hanya menerima gambar QR saat menautkan nomornya sendiri.
- Setiap koneksi member memiliki rahasia webhook sendiri; webhook ditolak bila tanda tangan `X-OpenWA-Signature: sha256=…` tidak cocok, dan diproses sekali saja (idempoten).
- Worker mengambil pekerjaan dengan *lease*; hanya satu worker aktif pada satu waktu. Jeda antarpesan, batas harian, kuota, dan pengecualian diatur di server KabarNikah.

## 2. Menjalankan OpenWA

Gunakan repositori resmi pada versi yang di-pin:

```bash
git clone https://github.com/rmyndharis/OpenWA.git
cd OpenWA
git checkout 6bb8b5749ed6aefb120eb11842472359516ae7c5
cp .env.example .env
```

Setel minimal di `.env` (lihat komentar lengkap di `.env.example` OpenWA):

```
NODE_ENV=production
PORT=2785
API_MASTER_KEY=<string acak minimal 32 karakter>   # kunci ADMIN pertama (boot pertama)
ENGINE_TYPE=baileys                               # tanpa Chromium; ringan
# Bila KabarNikah dipanggil lewat alamat privat/jaringan internal (mis. http://kabarnikah:80),
# daftarkan host tersebut, karena OpenWA memblokir webhook ke alamat privat (SSRF guard):
# SSRF_ALLOWED_HOSTS=kabarnikah
```

Jalankan dengan Docker Compose bawaan OpenWA (`docker compose up -d`) atau `npm ci && npm run build && node dist/main.js`. Lindungi port 2785 (firewall/reverse proxy dengan HTTPS); jangan membukanya ke internet tanpa TLS.

### Membuat API key untuk KabarNikah

KabarNikah perlu membuat sesi baru untuk setiap member, sehingga butuh kunci berperan **OPERATOR** (atau ADMIN) yang **tidak dibatasi** ke sesi/chat tertentu (OpenWA menolak pembuatan sesi oleh kunci yang dibatasi). Buat kunci OPERATOR lewat dashboard OpenWA atau `POST /api/auth/api-keys` menggunakan kunci ADMIN, lalu simpan kunci baru itu di KabarNikah. Jangan memakai kunci ADMIN untuk operasional bila tidak perlu.

## 3. Mengisi pengaturan di KabarNikah

**Admin → Pengaturan → OpenWA (WhatsApp)**

| Kolom | Isi |
|---|---|
| URL OpenWA | Alamat yang dapat dijangkau server KabarNikah, tanpa `/api`, mis. `https://wa.domainanda.com` |
| API key | Kunci OPERATOR tanpa batasan sesi |
| Awalan nama sesi | Default `kn` (nama sesi: `kn-u<ID>-<acak>`) |
| Basis URL webhook | Kosongkan bila OpenWA dapat menjangkau URL publik KabarNikah. Isi bila harus lewat alamat lain |

Tekan **Uji koneksi OpenWA** (membaca `/api/health` dan daftar sesi). Atur **Batas & jeda pengiriman WA**: default jeda 20–45 detik, 300 pesan per nomor per 24 jam, maksimal 500 penerima per kampanye, 3 percobaan, jeda otomatis setelah 5 kegagalan beruntun.

## 4. Menjalankan worker pengirim

Berkas ada di `KabarNikah_OpenWA_Worker.zip`: `kabarnikah-openwa-worker.mjs` (tanpa dependensi, Node.js ≥ 18.17), `package.json`/`package-lock.json` (tanpa paket pihak ketiga), `.env.example`, `docker-compose.yml` (OpenWA di-pin ke commit yang diuji + worker + health check), dan contoh unit systemd. Paket ini untuk VPS/Docker, **tidak** berjalan hanya dengan diekstrak di shared hosting PHP.

1. Ambil rahasia worker dari server KabarNikah (tidak ditampilkan di browser):
   ```bash
   php /home/NAMAAKUN/kabarnikah/bin/maintenance.php worker-secret
   ```
2. Isi variabel lingkungan (lihat `.env.example` di ZIP worker):
   ```
   KN_BASE_URL=https://undangan.domainanda.com
   KN_WORKER_SECRET=<hasil langkah 1>
   OPENWA_URL=http://openwa:2785
   OPENWA_API_KEY=<kunci OPERATOR>
   ```
3. Jalankan sebagai layanan (systemd/pm2/Docker; contoh disertakan). Log worker menyamarkan nomor dan tidak mencatat isi pesan.

Alternatif tanpa Node: cron PHP setiap menit `php kabarnikah/bin/wa-dispatch.php` (bekerja ±55 detik per menit). Jangan menjalankan keduanya; bila salah satu memegang lease, yang lain menunggu.

Protokol worker: header `X-KN-Worker`, `X-KN-Timestamp` (toleransi ±300 detik — pastikan jam server sinkron), `X-KN-Nonce` (sekali pakai), `X-KN-Signature = HMAC-SHA256(rahasia, ts\nnonce\nMETHOD\npath\nsha256(body))`.

## 5. Restart, pemulihan, dan health check

- **OpenWA restart**: data sesi tersimpan di volume `/app/data` (lihat `docker-compose.yml`), sehingga nomor yang sudah tertaut tidak perlu memindai QR lagi, kecuali WhatsApp mencabut perangkat tertaut. Status terbaru masuk lewat webhook `session.status`; kampanye yang tertahan karena terputus dapat **Dilanjutkan** setelah tersambung.
- **Worker restart/mati**: pekerjaan yang sedang dipegang worker memiliki lease 120 detik. Bila worker tidak melapor, penerima ditandai **Tidak pasti** (mungkin terkirim) dan tidak dikirim ulang otomatis. Pekerjaan lain tetap di antrean dan diambil saat worker hidup lagi.
- **Server KabarNikah tidak dapat dihubungi**: worker mencoba ulang dengan jeda bertahap (sampai 2 menit) dan mengulang laporan hasil kirim hingga 6 kali.
- **Health check**: `node kabarnikah-openwa-worker.mjs --health` (exit 0 bila detak < 3 menit) dipakai `docker-compose.yml`; OpenWA memakai `GET /api/health/ready`. Status worker juga tampil di Admin → WhatsApp / OpenWA dan Pemeriksaan Sistem.
- **Scope kunci**: KabarNikah membutuhkan kunci OPERATOR tanpa batasan sesi karena membuat sesi baru per member. Batasi risikonya dengan menjalankan OpenWA di jaringan privat/localhost, HTTPS, dan memutar kunci bila diduga bocor (buat kunci baru → simpan di KabarNikah & worker → cabut kunci lama).

## 6. Status pesan

| Status | Arti |
|---|---|
| Menunggu | Antre, menunggu jeda/jadwal |
| Sedang dikirim | Dipegang worker (lease 120 detik) |
| Diterima server | OpenWA menerima permintaan dan memberi ID pesan |
| Sampai / Dibaca | Dari ack WhatsApp (bisa tidak muncul bila penerima mematikan tanda baca) |
| Gagal | Pasti tidak terkirim (mis. nomor tidak terdaftar). Kuota dikembalikan |
| Tidak pasti | Mungkin terkirim (timeout setelah permintaan dikirim / worker tidak melapor). Tidak diulang otomatis |
| Dilewati | Nomor dikecualikan atau tautan tamu dicabut |

Pengiriman ulang memakai `Idempotency-Key` yang sama sehingga OpenWA dapat mencegah pesan ganda selama kunci masih tersimpan di sisi OpenWA.

## 7. Hasil uji

- Diuji terhadap **build OpenWA v0.30.0 asli** di lingkungan lokal: health, buat sesi (termasuk penanganan nama ganda/409), daftar webhook dengan 5 event, start → `qr_ready`, QR diterima sebagai data URL, kirim pesan saat sesi belum tertaut diklasifikasikan "coba lagi" (409), hapus sesi; webhook bertanda tangan dari OpenWA asli diterima dan diverifikasi KabarNikah.
- Alur penuh kampanye (QR → tersambung → kirim → sampai/dibaca/gagal/coba ulang) diuji dengan **OpenWA tiruan** yang meniru API dan tanda tangan webhook (tidak ada pesan nyata).
- **Belum diuji**: pengiriman ke nomor WhatsApp sungguhan dengan akun tertaut (tidak ada akun/ponsel uji yang digunakan).

## 8. Keamanan & etika

- Hanya kirim ke kontak yang mengenal pengantin. Member wajib mencentang pernyataan persetujuan sebelum kampanye berjalan.
- Tidak ada fitur scraping nomor, rotasi nomor, atau janji "anti-banned".
- Admin dapat menjeda/membatalkan kampanye dari panel admin.
- Simpan `.env` worker & OpenWA dengan izin terbatas; putar ulang rahasia worker dari Admin → Pengaturan bila bocor.

## 9. Pemecahan masalah

| Gejala | Solusi |
|---|---|
| Uji koneksi: "API key ditolak" | Kunci salah/kadaluwarsa atau perannya VIEWER; buat kunci OPERATOR tanpa batasan |
| QR tidak muncul | Periksa log OpenWA; pastikan server OpenWA dapat menjangkau WhatsApp |
| Status tidak berubah dari "Diterima server" | Webhook tidak sampai: cek URL webhook, HTTPS, dan `SSRF_ALLOWED_HOSTS` untuk host privat |
| Worker "klaim ditolak HTTP 401" | Rahasia worker salah atau jam server berbeda >5 menit |
| Worker "worker lain sedang aktif" | Ada worker/cron lain yang berjalan; matikan salah satu |
| Kampanye "Tertahan" | Baca alasannya (nomor terputus, masa aktif habis, kuota habis, API key ditolak); perbaiki lalu **Lanjutkan** |
