omnix-sopiga/services/omnix-broadcast/README.md

92 lines
4.0 KiB
Markdown
Raw Normal View History

2026-08-07 12:21:42 +07:00
# omnix-broadcast
Collection broadcast worker: mengirim invoice via WhatsApp (Sopiga Collar API) ke nasabah Gadai Mulia.
## Architecture
```
Gadai Collection Service
→ INSERT broadcast_staging (message_payload JSONB, status=pending)
→ BroadcastWorker (poll every 30s)
→ Query template_variable_mapping
→ Build message dynamically
→ POST Sopiga Collar API
→ status=dispatched (sopiga_recipient_detail_id disimpan)
→ DeliverySyncWorker (poll every 5 min)
→ GET recipient detail dari Sopiga
→ status=delivered (delivered_at diisi) atau status=failed
```
Tiga komponen jalan bersamaan (lihat `main.go`):
- **BroadcastWorker** (`worker/broadcast_worker.go`) — poll `pending`, dispatch ke Sopiga.
- **Webhook receiver** (`handler/webhook.go`, `POST /webhooks/sopiga/delivery-status`) —
jalur utama update status delivery. Begitu Omnix push callback, status langsung
diupdate tanpa perlu polling balik ke Sopiga sama sekali.
- **DeliverySyncWorker** (`worker/delivery_sync_worker.go`) — polling **fallback/backstop**
untuk record `dispatched` yang tidak kunjung dapat webhook (mis. delivery gagal
terkirim/network hiccup di sisi Omnix). Karena webhook jadi jalur utama, interval ini
bisa diperlonggar jauh lebih besar dari 5 menit tanpa menambah beban signifikan ke Omnix.
> ⚠️ **Catatan:** payload webhook di `dto/webhook.go` dan skema signature (`X-Sopiga-Signature`,
> HMAC-SHA256) masih **asumsi kita sendiri** — belum dikonfirmasi tim Omnix, karena endpoint
> registrasi callback tidak ditemukan di dokumentasi resmi (`/docs?api-docs.yaml`) saat
> implementasi ini dibuat. Sebelum pakai di production: (1) konfirmasi ke tim Omnix apakah
> mereka support outgoing webhook untuk status collar recipient, (2) minta format payload &
> skema signature asli mereka, (3) sesuaikan `dto/webhook.go` dan `handler/webhook.go`,
> (4) daftarkan URL `/webhooks/sopiga/delivery-status` ke mereka.
Template baru cukup didaftarkan lewat database (`sopiga_template_config` +
`template_variable_mapping` + `sopiga_collar_config`) — tanpa redeploy kode.
Lihat [docs/implementasi_guide.md](../../docs/implementasi_guide.md) dan
[docs/quick_start.md](../../docs/quick_start.md) untuk panduan lengkap.
## Layers
| Layer | Responsibility |
|---|---|
| `worker/` | Poller loop untuk dispatch + delivery sync fallback, fan-out ke concurrent processing |
| `handler/` | `WebhookHandler` — HTTP receiver untuk callback status delivery dari Omnix |
| `transformer/` | Entity ↔ Domain mapping, `BuildDynamicMessage` interpolasi template |
| `service/` | Business logic: fetch pending/dispatched, validate, dispatch, sync delivery (polling & webhook), retry, mark failed |
| `repository/` | Query `broadcast_staging` & `template_variable_mapping`, update status, lookup by `recipient_detail_id` |
| `client/` | HTTP client ke Sopiga Collar API (add-recipient + get recipient detail) |
| `domain/` | `Broadcast`, `TemplateVariable` |
| `entity/` | DB row mapping |
| `dto/` | Sopiga API request/response |
| `validator/` | Validasi payload sebelum dispatch |
| `migrations/` | Schema `collection_broadcasts` (tables, views, functions) |
## Configuration (env vars)
| Var | Default |
|---|---|
| `DATABASE_DSN` | — |
| `DB_MAX_CONNS` | 25 |
| `DB_MIN_CONNS` | 5 |
| `SOPIGA_BASE_URL` | `https://omnix.promas.site` |
| `SOPIGA_TOKEN` | — |
| `WORKER_CHECK_INTERVAL_SEC` | 30 |
| `WORKER_BATCH_SIZE` | 100 |
| `WORKER_MAX_RETRIES` | 3 |
| `WORKER_SYNC_INTERVAL_SEC` | 300 (fallback saja — perlonggar kalau webhook sudah aktif) |
| `WORKER_SYNC_BATCH_SIZE` | 100 |
| `WEBHOOK_PORT` | 8081 |
| `WEBHOOK_SECRET` | — (HMAC-SHA256 shared secret; kosongkan untuk skip verifikasi saat dev) |
## Run
```bash
psql -d gadai_mulia -f migrations/001_create_collection_broadcasts.up.sql
export DATABASE_DSN="postgres://user:pass@localhost:5432/gadai_mulia"
export SOPIGA_TOKEN="your_api_token"
go run ./...
```
## Test
```bash
go test ./tests/... -v
```