WhatsApp Service
WhatsApp Service adalah notification service yang mengirim WhatsApp message (OTP, free-form, dan Meta-approved template) via Meta Cloud API, menerima delivery callback dari Meta, dan menyimpan audit trail per message. Service ini live di production.
Dokumen pendamping:
architecture.md(desain endpoint + persistence + observability, canonical).
1. Service Identity
| Atribut | Nilai |
|---|---|
| Port | 127.0.0.1:8091 (loopback) |
| Main DB | db_kesles_merchant_notification (schema whatsapp) |
| VM dir | /home/enalfarid/kesles_merchant/merchant_whatsapp/ |
| Binary | whatsapp-service (Linux ELF amd64) |
| Systemd unit | whatsapp-service.service |
| Provider | Meta Cloud API (graph.facebook.com/v23.0) |
| Go module | go 1.25, driver pgx/v5 via stdlib adapter |
2. Responsibilities
- Kirim OTP WhatsApp via template (default
merchant_otp_code) untuk auth mobile (registrasi/login). - Kirim free-form text dalam 24h customer service window.
- Kirim Meta-approved template generik (transactional / marketing) dengan body params + button URL param, dibatasi whitelist
WHATSAPP_ALLOWED_TEMPLATES. - Terima dan verifikasi Meta delivery status webhook (HMAC SHA-256), simpan delivery event.
- Simpan audit trail per message: header, send attempts (1:N), delivery events (1:N append-only).
- Sediakan endpoint read-only (message logs, overview KPI, send-volume timeseries, provider health) + template catalog CRUD untuk panel dashboard.
3. Endpoint Inventory
| Method | Path | Auth | Tujuan |
|---|---|---|---|
GET | /health | none | Liveness probe |
GET | /ready | none | Postgres ping + provider config check |
POST | /internal/whatsapp/otp | API key | OTP via template (default merchant_otp_code) |
POST | /internal/whatsapp/messages | API key | Free-form text (24h customer service window) |
POST | /internal/whatsapp/template | API key | Meta-approved template generik (whitelist-guarded) |
GET | /internal/whatsapp/messages/{id} | API key | Lookup message + attempts + delivery events |
GET/POST | /internal/whatsapp/templates | API key | Template catalog list / create |
GET/PATCH/DELETE | /internal/whatsapp/templates/{id} | API key | Template catalog detail / update / soft-delete |
GET | /internal/whatsapp/message-logs | API key | Message logs (read-only, dashboard) |
GET | /internal/whatsapp/overview | API key | Overview KPI (read-only) |
GET | /internal/whatsapp/send-volume | API key | Send-volume timeseries (chart) |
GET | /internal/whatsapp/provider-health | API key | Provider health card |
GET | /webhooks/whatsapp/status | verify token | Meta hub.challenge handshake |
POST | /webhooks/whatsapp/status | HMAC | Meta delivery status callback |
Endpoint /internal/whatsapp/* middleware chain: withInternalKey → withTenantID → (idempotency, untuk endpoint kirim) → handler.
4. Template Wiring (current)
Template Meta-approved di-dispatch async best-effort dari caller service. Pola: caller fire ke /internal/whatsapp/template (atau lewat fireMerchantWhatsAppAsync di core_api) dengan template name + body params; service eksekusi Meta Cloud send + persist audit.
| Template | Use case | Caller |
|---|---|---|
merchant_otp_code | OTP auth (registrasi, login) | whatsapp_service OTP endpoint (default template) |
merchant_transaction_alert | Notif QRIS payment received | merchant_core_api internal_transaction_status_handlers.go (fireMerchantWhatsAppAsync) |
merchant_payment_receipt | Konfirmasi payment | dashboard_api dashboard_sales_orders.go (admin mark paid) |
merchant_order_shipped | Notif shipping + tracking | dashboard_api dashboard_shipping_orders.go (dispatch shipping, dual-dispatch FCM + WA) |
merchant_payment_due | Reminder sebelum payment deadline | order_service worker_reminder.go (background reminder worker) |
5. VM Deployment
| Atribut | Nilai |
|---|---|
| Host dir | /home/enalfarid/kesles_merchant/merchant_whatsapp/ |
| Binary | whatsapp-service |
| Systemd unit | whatsapp-service.service |
| Bind | 127.0.0.1:8091 (loopback; diakses via nginx upstream) |
| Container build | Dockerfile multi-stage, Stage 2 gcr.io/distroless/static-debian12:nonroot |
| Logs | journald (slog JSON ke stdout) |
Meta webhook public URL: https://kesles.com/merchant/api/webhook/whatsapp (dikonfigurasi di Meta dashboard) → nginx proxy langsung ke 127.0.0.1:8091/webhooks/whatsapp/status. core_api tidak menerima webhook traffic; service yang verifikasi HMAC X-Hub-Signature-256 dan menulis delivery event.
6. Config / Env Vars
| Env | Fungsi |
|---|---|
APP_ENV | production di VM (wajib eksplisit; default development) |
APP_PORT | Port bind (default 8091) |
POSTGRES_DSN | DSN db_kesles_merchant_notification |
INTERNAL_NOTIFICATION_API_KEY | Auth internal endpoint (wajib di production) |
WHATSAPP_PROVIDER | Kode provider (meta-cloud-api; mock untuk dev) |
WHATSAPP_BASE_URL | https://graph.facebook.com/v23.0 |
WHATSAPP_API_KEY | Meta Cloud API access token |
WHATSAPP_API_SECRET | Webhook HMAC verification secret (wajib di production) |
WHATSAPP_PHONE_NUMBER_ID | Meta phone number ID |
WHATSAPP_VERIFY_TOKEN | Meta webhook handshake token (wajib di production) |
WHATSAPP_TEMPLATE_NAME | Template OTP default (merchant_otp_code) |
WHATSAPP_TEMPLATE_LANGUAGE | Bahasa template default |
WHATSAPP_ALLOWED_TEMPLATES | Whitelist template comma-separated (wajib non-empty di production) |
REDIS_ADDR / REDIS_USERNAME / REDIS_PASSWORD / REDIS_DB | Idempotency store (kosong = middleware no-op) |
WEBHOOK_SIGNATURE_BYPASS | Dev-only flag; ditolak validateConfig di production |
validateConfig menolak start di production jika INTERNAL_NOTIFICATION_API_KEY, WHATSAPP_API_SECRET, WHATSAPP_VERIFY_TOKEN, atau WHATSAPP_ALLOWED_TEMPLATES kosong, atau jika WEBHOOK_SIGNATURE_BYPASS=true.
7. Security Hardening (current configuration)
- Internal auth: header API key dibandingkan dengan
subtle.ConstantTimeCompare(timing-safe); fail-closed kalau key di config kosong. - Webhook: GET handshake verify token via constant-time compare; POST callback wajib HMAC SHA-256 valid di production. Body dibatasi
http.MaxBytesReader(1 MB cap) sebelumio.ReadAll. - HTTP server timeout guards:
ReadHeaderTimeout 5s,ReadTimeout 30s,WriteTimeout 30s,IdleTimeout 120s(Slowloris / slow-client mitigation). - Graceful shutdown:
signal.NotifyContext(SIGINT/SIGTERM) +server.Shutdownwindow untuk in-flight request selesai, mencegah half-write audit row saat deploy. - Panic recovery:
withRecoverymiddleware catch panic → log → 500. - Postgres pool:
MaxOpenConns(25),MaxIdleConns(5),ConnMaxLifetime(5m). Driverpgx/v5via stdlib. Store soft-fail saat DB unreachable (persistence endpoint return 503, service tetap up).
8. Database
Schema whatsapp di db_kesles_merchant_notification:
| Tabel | Isi |
|---|---|
whatsapp.whatsapp_messages | Header per message (provider, status, message_id, wamid, tenant_id) |
whatsapp.whatsapp_send_attempts | Send attempt 1:N (retry trail) |
whatsapp.whatsapp_delivery_events | Delivery event 1:N append-only dari Meta callback |
whatsapp.template_catalog | Katalog template untuk manajemen dashboard (CRUD, soft-delete) |
9. Observability
Event whatsapp_send taxonomy (classifyWAError)
| Class | Trigger |
|---|---|
ok | err == nil |
meta_auth | HTTP 401/403 — token expired/invalid (rotate WHATSAPP_API_KEY) |
meta_rate_limit | HTTP 429 — quota habis |
meta_template_invalid | HTTP 400 — template name/params mismatch |
meta_4xx | HTTP 4xx lain |
meta_5xx | HTTP 5xx — Meta server transient |
network | err contains "dial"/"connection"/"tls"/"no such host" |
network_timeout | err contains "timeout"/"deadline"/"i/o" |
unknown | fallback |
Event whatsapp_webhook
Status enum: accepted (signature valid + body saved) · rejected (HMAC mismatch) · invalid_body (JSON decode fail).
Nomor telepon di-mask (+628***68) via maskPhone di semua log stream. Detail schema di architecture.md §6 Observability.
10. Operasional Artifact
| File | Purpose |
|---|---|
architecture.md | Desain endpoint + persistence + observability |
deploy-runbook.md | VM systemd deploy + env rotation + monitoring + rollback |
verification-checklist.md | Production readiness checklist |
webhook-meta-setup.md | Registrasi webhook di Meta dashboard |
services/whatsapp_service/README.md | Service-level runbook + smoke test |
Dockerfile | Multi-stage build distroless |