# omnix-sopiga Collection broadcast worker: mengirim invoice via WhatsApp (Sopiga Collar API) ke nasabah Gadai Mulia. > Repo standalone (bukan bagian monorepo). Dependency internal ada di > [`go-dw-framework`](../go-dw-framework) — lihat `go.mod` untuk detail > `replace` directive yang dipakai selama development lokal. ## 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. Detail permintaan integrasi webhook ke tim Omnix ada di [docs/webhook_integration_request.md](docs/webhook_integration_request.md). ## 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) | ## Development setup Clone this repo and `go-dw-framework` as **siblings** (same parent folder) — `go.mod` uses a local `replace` directive during pre-release development: ``` some-folder/ ├── go-dw-framework/ └── omnix-sopiga/ ← you are here ``` ```bash git clone ../go-dw-framework go build ./... ``` Once `go-dw-framework` is tagged (e.g. `v0.1.0`) and pushed to its remote, switch `go.mod` to a plain `require repository.promas.id/prana/go-dw-framework v0.1.0` (drop the `replace` line) and `go mod tidy`. ## 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 . ``` ## Docker build ```bash # from the parent folder containing both omnix-sopiga/ and go-dw-framework/ docker build -f omnix-sopiga/Dockerfile -t omnix-sopiga .. ``` ## Test ```bash go test ./tests/... -v ```