125 lines
5.0 KiB
Markdown
125 lines
5.0 KiB
Markdown
# 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-remote> ../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
|
|
```
|