Lewati ke konten utama

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

AtributNilai
Port127.0.0.1:8091 (loopback)
Main DBdb_kesles_merchant_notification (schema whatsapp)
VM dir/home/enalfarid/kesles_merchant/merchant_whatsapp/
Binarywhatsapp-service (Linux ELF amd64)
Systemd unitwhatsapp-service.service
ProviderMeta Cloud API (graph.facebook.com/v23.0)
Go modulego 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

MethodPathAuthTujuan
GET/healthnoneLiveness probe
GET/readynonePostgres ping + provider config check
POST/internal/whatsapp/otpAPI keyOTP via template (default merchant_otp_code)
POST/internal/whatsapp/messagesAPI keyFree-form text (24h customer service window)
POST/internal/whatsapp/templateAPI keyMeta-approved template generik (whitelist-guarded)
GET/internal/whatsapp/messages/{id}API keyLookup message + attempts + delivery events
GET/POST/internal/whatsapp/templatesAPI keyTemplate catalog list / create
GET/PATCH/DELETE/internal/whatsapp/templates/{id}API keyTemplate catalog detail / update / soft-delete
GET/internal/whatsapp/message-logsAPI keyMessage logs (read-only, dashboard)
GET/internal/whatsapp/overviewAPI keyOverview KPI (read-only)
GET/internal/whatsapp/send-volumeAPI keySend-volume timeseries (chart)
GET/internal/whatsapp/provider-healthAPI keyProvider health card
GET/webhooks/whatsapp/statusverify tokenMeta hub.challenge handshake
POST/webhooks/whatsapp/statusHMACMeta delivery status callback

Endpoint /internal/whatsapp/* middleware chain: withInternalKeywithTenantID → (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.

TemplateUse caseCaller
merchant_otp_codeOTP auth (registrasi, login)whatsapp_service OTP endpoint (default template)
merchant_transaction_alertNotif QRIS payment receivedmerchant_core_api internal_transaction_status_handlers.go (fireMerchantWhatsAppAsync)
merchant_payment_receiptKonfirmasi paymentdashboard_api dashboard_sales_orders.go (admin mark paid)
merchant_order_shippedNotif shipping + trackingdashboard_api dashboard_shipping_orders.go (dispatch shipping, dual-dispatch FCM + WA)
merchant_payment_dueReminder sebelum payment deadlineorder_service worker_reminder.go (background reminder worker)

5. VM Deployment

AtributNilai
Host dir/home/enalfarid/kesles_merchant/merchant_whatsapp/
Binarywhatsapp-service
Systemd unitwhatsapp-service.service
Bind127.0.0.1:8091 (loopback; diakses via nginx upstream)
Container buildDockerfile multi-stage, Stage 2 gcr.io/distroless/static-debian12:nonroot
Logsjournald (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

EnvFungsi
APP_ENVproduction di VM (wajib eksplisit; default development)
APP_PORTPort bind (default 8091)
POSTGRES_DSNDSN db_kesles_merchant_notification
INTERNAL_NOTIFICATION_API_KEYAuth internal endpoint (wajib di production)
WHATSAPP_PROVIDERKode provider (meta-cloud-api; mock untuk dev)
WHATSAPP_BASE_URLhttps://graph.facebook.com/v23.0
WHATSAPP_API_KEYMeta Cloud API access token
WHATSAPP_API_SECRETWebhook HMAC verification secret (wajib di production)
WHATSAPP_PHONE_NUMBER_IDMeta phone number ID
WHATSAPP_VERIFY_TOKENMeta webhook handshake token (wajib di production)
WHATSAPP_TEMPLATE_NAMETemplate OTP default (merchant_otp_code)
WHATSAPP_TEMPLATE_LANGUAGEBahasa template default
WHATSAPP_ALLOWED_TEMPLATESWhitelist template comma-separated (wajib non-empty di production)
REDIS_ADDR / REDIS_USERNAME / REDIS_PASSWORD / REDIS_DBIdempotency store (kosong = middleware no-op)
WEBHOOK_SIGNATURE_BYPASSDev-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) sebelum io.ReadAll.
  • HTTP server timeout guards: ReadHeaderTimeout 5s, ReadTimeout 30s, WriteTimeout 30s, IdleTimeout 120s (Slowloris / slow-client mitigation).
  • Graceful shutdown: signal.NotifyContext (SIGINT/SIGTERM) + server.Shutdown window untuk in-flight request selesai, mencegah half-write audit row saat deploy.
  • Panic recovery: withRecovery middleware catch panic → log → 500.
  • Postgres pool: MaxOpenConns(25), MaxIdleConns(5), ConnMaxLifetime(5m). Driver pgx/v5 via stdlib. Store soft-fail saat DB unreachable (persistence endpoint return 503, service tetap up).

8. Database

Schema whatsapp di db_kesles_merchant_notification:

TabelIsi
whatsapp.whatsapp_messagesHeader per message (provider, status, message_id, wamid, tenant_id)
whatsapp.whatsapp_send_attemptsSend attempt 1:N (retry trail)
whatsapp.whatsapp_delivery_eventsDelivery event 1:N append-only dari Meta callback
whatsapp.template_catalogKatalog template untuk manajemen dashboard (CRUD, soft-delete)

9. Observability

Event whatsapp_send taxonomy (classifyWAError)

ClassTrigger
okerr == nil
meta_authHTTP 401/403 — token expired/invalid (rotate WHATSAPP_API_KEY)
meta_rate_limitHTTP 429 — quota habis
meta_template_invalidHTTP 400 — template name/params mismatch
meta_4xxHTTP 4xx lain
meta_5xxHTTP 5xx — Meta server transient
networkerr contains "dial"/"connection"/"tls"/"no such host"
network_timeouterr contains "timeout"/"deadline"/"i/o"
unknownfallback

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

FilePurpose
architecture.mdDesain endpoint + persistence + observability
deploy-runbook.mdVM systemd deploy + env rotation + monitoring + rollback
verification-checklist.mdProduction readiness checklist
webhook-meta-setup.mdRegistrasi webhook di Meta dashboard
services/whatsapp_service/README.mdService-level runbook + smoke test
DockerfileMulti-stage build distroless