291 lines
14 KiB
Markdown
291 lines
14 KiB
Markdown
# Decisions Log — QRIS Soundbox Platform
|
|
|
|
Log keputusan arsitektur dan implementasi yang harus dijadikan acuan eksekusi.
|
|
|
|
## Format Entri
|
|
- ID: [D-XXX]
|
|
- Tanggal:
|
|
- Keputusan:
|
|
- Alasan:
|
|
- Dampak / implikasi:
|
|
- Status:
|
|
|
|
## D-001 — Basis Implementasi Fase 0 dan 1
|
|
- Tanggal: 2026-05-23
|
|
- Keputusan:
|
|
- Menjalankan eksekusi langsung berdasarkan fase roadmap, tanpa pembuatan jadwal rinci.
|
|
- Alasan:
|
|
- Tim sudah punya pembagian fase yang jelas dan siap mulai implementasi langsung.
|
|
- Dampak / implikasi:
|
|
- Fokus pada deliverable per fase dan DoD, bukan timeline.
|
|
- Status: Active
|
|
|
|
## D-002 — Scope Fase 1 Step 1
|
|
- Tanggal: 2026-05-23
|
|
- Keputusan:
|
|
- Step 1 Fase 1 dibatasi pada core foundation: auth baseline, schema MVP, merchant/outlet/terminal/device/binding, observability dasar.
|
|
- Alasan:
|
|
- Memastikan jalur static payment bisa dipersiapkan stabil sebelum dynamic flow.
|
|
- Dampak / implikasi:
|
|
- Fitur lain (settlement, dynamic QR, merchant portal) ditunda sampai Step 1 stabil.
|
|
- Status: Active
|
|
|
|
## D-003 — API Contract Error Standard
|
|
- Tanggal: 2026-05-23
|
|
- Keputusan:
|
|
- Semua response error mengikuti envelope seragam:
|
|
- `code`, `message`, `details`, `request_id`, `timestamp`.
|
|
- Alasan:
|
|
- Memudahkan debug dan tracing lintas service/device.
|
|
- Dampak / implikasi:
|
|
- Semua handler API harus mematuhi middleware response formatter yang sama.
|
|
- Status: Active
|
|
|
|
## D-004 — Traceability Requirement
|
|
- Tanggal: 2026-05-23
|
|
- Keputusan:
|
|
- Semua request harus membawa `request_id`; webhook/device callback harus dihubungkan dengan `trace_id` jika multi-step.
|
|
- Alasan:
|
|
- Root-cause analysis perlu konteks end-to-end dari callback sampai notifikasi.
|
|
- Dampak / implikasi:
|
|
- Framework logging dan parser event wajib men-generate field ini secara konsisten.
|
|
- Status: Active
|
|
|
|
## D-005 — Idempotency Rule
|
|
- Tanggal: 2026-05-23
|
|
- Keputusan:
|
|
- Setiap path sensitif terhadap duplicate (create merchant/create device/binding/webhook request/dynamic QR nanti) wajib men-support idempotency key/table.
|
|
- Alasan:
|
|
- Menghindari double create, double binding, dan state drift akibat retry.
|
|
- Dampak / implikasi:
|
|
- Menambah kebutuhan service/DB untuk `idempotency_keys` sejak awal Fase 1.
|
|
- Status: Active
|
|
|
|
## D-006 — Device Binding
|
|
- Tanggal: 2026-05-23
|
|
- Keputusan:
|
|
- Notifikasi pembayaran hanya boleh dipush ke device dari binding aktif yang valid (active_flag=true).
|
|
- Alasan:
|
|
- Mencegah notifikasi salah kirim jika device pernah dipindahkan antar outlet/terminal.
|
|
- Dampak / implikasi:
|
|
- Query notification selalu harus resolve binding aktif secara explicit.
|
|
- Status: Active
|
|
|
|
## D-007 — Fallback Strategy untuk Fase 1
|
|
- Tanggal: 2026-05-23
|
|
- Keputusan:
|
|
- Retry MQTT pada Fase 1 memakai retry sederhana (fixed/backoff pendek), tanpa DLQ kompleks.
|
|
- Alasan:
|
|
- Menghemat effort awal sambil menjaga fitur core berjalan.
|
|
- Dampak / implikasi:
|
|
- Fase 4 mengelola policy DLQ dan retry matang.
|
|
- Status: Active
|
|
|
|
## D-008 — Minimal RBAC untuk Mulai Eksekusi
|
|
- Tanggal: 2026-05-23
|
|
- Keputusan:
|
|
- Implement RBAC baseline minimum di Fase 1 (admin-only) sambil menjaga token device terpisah.
|
|
- Alasan:
|
|
- Mengurangi risiko akses silang awal tanpa memperlambat pengembangan.
|
|
- Dampak / implikasi:
|
|
- Permission matrix akan disempurnakan di Fase 4.
|
|
- Status: Active
|
|
|
|
## D-009 — Dokumentasi Eksekusi Step 1
|
|
- Tanggal: 2026-05-23
|
|
- Keputusan:
|
|
- Spesifikasi detail Step 1 dituangkan di `12-fase1-step1-core-foundation-spec.md`.
|
|
- Alasan:
|
|
- Menghindari ketidakpastian saat tim mulai coding.
|
|
- Dampak / implikasi:
|
|
- Semua implementer wajib merujuk dokumen ini saat menyiapkan branch dan PR.
|
|
- Status: Active
|
|
|
|
## D-010 — Webhook Signature dan Parsing
|
|
- Tanggal: 2026-05-23
|
|
- Keputusan:
|
|
- Semua callback harus divalidasi signature-nya terlebih dahulu; jika tidak valid, respons harus `401` dan tidak boleh melakukan perubahan state apapun.
|
|
- Alasan:
|
|
- Payment callback adalah sumber trust utama dan harus dijaga dari spoofing/replay.
|
|
- Dampak / implikasi:
|
|
- Service callback wajib mengimplementasikan validator HMAC (atau skema cryptographic sesuai partner) sebelum mapping transaksi.
|
|
- Status: Active
|
|
|
|
## D-011 — Callback Idempotency di Level Transaksi
|
|
- Tanggal: 2026-05-23
|
|
- Keputusan:
|
|
- Callback duplikat harus diidentifikasi deterministik dari `partner_reference + payment_status + partner_event_id` dan diperlakukan idempotent.
|
|
- Alasan:
|
|
- Callback retry sangat umum dari partner dan dapat memicu double state update.
|
|
- Dampak / implikasi:
|
|
- state transition dilakukan hanya jika perubahan state valid sesuai mesin state.
|
|
- Status: Active
|
|
|
|
## D-012 — Evented Transaction State Change
|
|
- Tanggal: 2026-05-23
|
|
- Keputusan:
|
|
- Setiap perubahan state transaksi harus menghasilkan `transaction_events` agar Step 3 dan observability bisa berjalan.
|
|
- Alasan:
|
|
- Auditability dan troubleshooting menuntut timeline per transaksi.
|
|
- Dampak / implikasi:
|
|
- update transaksi tanpa menulis event dianggap bug di Step 1.
|
|
- Status: Active
|
|
|
|
## D-013 — Terminal Event `paid` sebagai Trigger Notification
|
|
- Tanggal: 2026-05-23
|
|
- Keputusan:
|
|
- Notifikasi pembayaran di Step 3 harus dipicu dari event internal status `paid`, bukan dari polling callback.
|
|
- Alasan:
|
|
- Menghindari race dan duplikasi notifikasi.
|
|
- Dampak / implikasi:
|
|
- Implementasi Step 3 harus subscribe dan consume event transaction paid.
|
|
- Status: Active
|
|
|
|
## D-014 — Retry Notifikasi Fase 1
|
|
- Tanggal: 2026-05-23
|
|
- Keputusan:
|
|
- Step 3 menggunakan retry dasar maksimum 3 kali dengan jadwal 15/30/60 detik.
|
|
- Alasan:
|
|
- Menyeimbangkan reliability dan kecepatan operasional tanpa kompleksitas DLQ penuh pada fase awal.
|
|
- Dampak / implikasi:
|
|
- `notifications.retry_count` wajib ditulis dan dibatasi maksimal 3.
|
|
- Status: Active
|
|
|
|
## D-015 — Notifikasi Tanpa Ack Fase 1
|
|
- Tanggal: 2026-05-23
|
|
- Keputusan:
|
|
- Pada fase 1, absence of MQTT ack tidak dianggap error akhir; status sukses ditentukan dari publish outcome, bukan ack dari device.
|
|
- Alasan:
|
|
- Device ecosystem belum konsisten untuk ack schema, dan goal fase 1 adalah stabilitas flow utama.
|
|
- Dampak / implikasi:
|
|
- `ack_status` bisa `not_supported`, dan operasi tetap lanjut dengan monitoring via retry/publish status.
|
|
- Status: Active
|
|
|
|
## D-016 — Heartbeat Status Threshold Fase 1
|
|
- Tanggal: 2026-05-23
|
|
- Keputusan:
|
|
- Definisi status device ditetapkan: online (<90s), degraded (signal/battery buruk), stale (<15 menit), offline (>15 menit) berdasarkan `last_seen_at`.
|
|
- Alasan:
|
|
- Konsistensi status dibutuhkan untuk ops triage cepat.
|
|
- Dampak / implikasi:
|
|
- Setiap list/detail device menampilkan status yang dihitung dari rule yang sama.
|
|
- Status: Active
|
|
|
|
## D-017 — Dashboard KPI Fase 1
|
|
- Tanggal: 2026-05-23
|
|
- Keputusan:
|
|
- Dashboard ops minimum menampilkan: transaksi hari ini, success rate hari ini, active devices, pending notifications, stale/offline counts.
|
|
- Alasan:
|
|
- Tim operasi perlu indikator cepat tanpa menunggu custom analytics.
|
|
- Dampak / implikasi:
|
|
- Endpoint summary dashboard wajib dihitung dari data transaksi/device/notification inti.
|
|
- Status: Active
|
|
|
|
## D-018 — Pencairan Dana Non-Tersentral per Merchant
|
|
- Tanggal: 2026-05-24
|
|
- Keputusan:
|
|
- Pencairan dana tidak dilakukan di rekening perusahaan; setiap merchant memakai rekening tujuan sendiri (atau reference payout miliknya) untuk settlement.
|
|
- Alasan:
|
|
- Menghindari ketergantungan izin/operasional PJP di tahap awal dan mempercepat launch MVP.
|
|
- Dampak / implikasi:
|
|
- Schema dan model onboarding merchant memakai `payout_account_reference`/rekening merchant, bukan rekening vault pusat.
|
|
- Modul settlement platform difokuskan ke rekonsiliasi, status payout, dan visibility, bukan pengelolaan rekening pusat.
|
|
- Callback payout dan payout execution dianggap partner-oriented (tergantung integrasi penyedia), bukan core di fase awal.
|
|
- Status: Active
|
|
|
|
## D-019 — Fase 1 Audit Log dan Ledger Placeholder
|
|
- Tanggal: 2026-05-26
|
|
- Keputusan:
|
|
- Fase 1 mencatat audit log untuk aksi admin/webhook penting dan membuat ledger placeholder `gross_income` saat transaksi berubah ke `paid`.
|
|
- Alasan:
|
|
- Acceptance Fase 1 membutuhkan audit aksi CRUD penting, callback state changes, dan placeholder ledger untuk transaksi sukses tanpa menunggu modul finance penuh.
|
|
- Dampak / implikasi:
|
|
- `audit_logs` menjadi sumber trace operasional awal untuk entity penting.
|
|
- `ledger_entries` Fase 1 hanya placeholder gross income; fee/platform payable detail tetap masuk Fase 3.
|
|
- Duplicate paid callback tetap idempotent dan tidak membuat ledger duplikat karena unique key per `transaction_id + entry_type`.
|
|
- Status: Active
|
|
|
|
## D-020 — Awal Fase 2 Dynamic QR API-Direct
|
|
- Tanggal: 2026-05-26
|
|
- Keputusan:
|
|
- Fase 2 dimulai dari capability resolver dan endpoint `POST /device/transactions/dynamic-qr` untuk device `communication_mode=api`.
|
|
- Dynamic QR API-direct memakai mock QRIS payload lokal sampai integrasi partner QRIS tersedia.
|
|
- Alasan:
|
|
- Capability/routing harus stabil sebelum MQTT dynamic dan config push dibangun.
|
|
- Mock partner memungkinkan transaksi dynamic tersimpan dan callback Fase 1 tetap diuji sebagai source of truth.
|
|
- Dampak / implikasi:
|
|
- Device static/MQTT yang tidak memiliki capability `dynamic_qr.api_direct` ditolak dengan `DEVICE_CAPABILITY_NOT_SUPPORTED`.
|
|
- Device wajib punya binding aktif ke terminal `qr_mode=dynamic_api`; jika tidak, API mengembalikan `DEVICE_NOT_BOUND`.
|
|
- Response dynamic QR idempotent memakai `Idempotency-Key` atau `request_id`, dan transaksi dibuat `awaiting_payment`.
|
|
- Callback paid tetap memakai endpoint webhook yang sama untuk mengubah transaksi menjadi `paid` dan memicu notifikasi.
|
|
- Status: Active
|
|
|
|
## D-021 — MQTT Dynamic QR dan Device Config Fase 2
|
|
- Tanggal: 2026-05-26
|
|
- Keputusan:
|
|
- MQTT dynamic QR Fase 2 diimplementasikan sebagai HTTP simulator endpoint `POST /device/mqtt/uplink/dynamic-qr/request` yang mencatat uplink/downlink ke `mqtt_messages`.
|
|
- Config push memakai `PATCH /admin/devices/{deviceId}/config`, disimpan di `device_configs`, dipublish ke MQTT outbox, lalu device mengirim `POST /device/config/ack`.
|
|
- Alasan:
|
|
- Belum ada broker MQTT sungguhan di stack lokal, tapi kontrak topic/payload dan idempotency perlu bisa diuji end-to-end.
|
|
- Outbox membuat downlink response/config push observable lewat admin sebelum integrasi broker asli.
|
|
- Dampak / implikasi:
|
|
- Saat broker MQTT dipasang, `mqtt_messages` bisa menjadi outbox/trace awal untuk adapter broker.
|
|
- Dynamic MQTT request memakai `request_id` sebagai `correlation_id` dan idempotency key.
|
|
- Device config selalu versioned; ACK dicatat terpisah di `device_config_acks`.
|
|
- Status: Active
|
|
|
|
## D-022 — Config Drift dan Retry Push Fase 2
|
|
- Tanggal: 2026-05-26
|
|
- Keputusan:
|
|
- Fase 2 menambahkan status drift config device melalui `GET /admin/devices/{deviceId}/config/status`.
|
|
- Retry config dilakukan via `POST /admin/devices/{deviceId}/config/retry-push` tanpa menaikkan `config_version`.
|
|
- ACK config dari device juga dicatat sebagai uplink trace di `mqtt_messages` dengan `message_type=config_ack`.
|
|
- Alasan:
|
|
- Operasi perlu membedakan config `applied`, `pending_ack`, `failed_ack`, `stale_ack`, dan `never_pushed` sebelum broker MQTT sungguhan dipasang.
|
|
- Retry harus mengirim ulang desired config yang sama agar idempotent dan tidak membuat drift versi buatan.
|
|
- Dampak / implikasi:
|
|
- Config yang sudah `applied` tidak boleh di-retry kecuali admin mengirim `force=true`.
|
|
- `mqtt_messages` menjadi timeline awal untuk config push dan ACK device.
|
|
- Saat broker MQTT asli masuk, endpoint retry tetap menjadi trigger adapter/outbox, bukan tempat menyimpan logic konfigurasi baru.
|
|
- Status: Active
|
|
|
|
## D-023 — Health Summary Device Fase 2
|
|
- Tanggal: 2026-05-26
|
|
- Keputusan:
|
|
- Endpoint admin device menambahkan `health_summary` berisi `status`, `score`, `age_seconds`, dan `reasons`.
|
|
- `derived_status` tetap dipertahankan untuk kompatibilitas UI, namun nilainya berasal dari rule health summary yang sama.
|
|
- Alasan:
|
|
- Operasi membutuhkan konteks kenapa device online/degraded/stale/offline, bukan hanya label status.
|
|
- Skor 0-100 memudahkan sorting/filter dashboard tanpa mengubah threshold status Fase 1.
|
|
- Dampak / implikasi:
|
|
- Status masih mengikuti threshold Fase 1: online <90 detik, stale >90 detik, offline >15 menit, degraded untuk sinyal/baterai buruk.
|
|
- UI dapat memakai `health_summary.reasons` untuk badge/tooltip ops.
|
|
- Status: Active
|
|
|
|
## D-024 — UI Ops Device Fase 2
|
|
- Tanggal: 2026-05-26
|
|
- Keputusan:
|
|
- UI device registry dan device technical detail menampilkan `health_summary` dan config delivery status.
|
|
- Retry config push tersedia dari drawer device registry dan halaman detail device.
|
|
- Alasan:
|
|
- Operator perlu melihat status Fase 2 tanpa membuka raw payload/API response.
|
|
- Retry config adalah tindakan operasional langsung, sehingga harus dekat dengan status drift config.
|
|
- Dampak / implikasi:
|
|
- UI memakai endpoint `GET /admin/devices/{deviceId}/config/status` dan `POST /admin/devices/{deviceId}/config/retry-push`.
|
|
- `derived_status` tetap dipakai sebagai fallback/compatibility, sementara health score/reasons menjadi konteks tambahan.
|
|
- Status: Active
|
|
|
|
## D-025 — Dynamic QR Expiry Sweep Fase 2
|
|
- Tanggal: 2026-05-26
|
|
- Keputusan:
|
|
- Dynamic QR `awaiting_payment` yang melewati `expired_at` dapat ditutup oleh sweep internal `POST /admin/transactions/expire-due`.
|
|
- Sweep hanya berlaku untuk transaksi `qr_mode=dynamic` dan status `awaiting_payment`.
|
|
- Alasan:
|
|
- Fase 2 tidak boleh bergantung penuh pada callback partner untuk menutup QR yang kadaluarsa.
|
|
- Expiry internal menjaga daftar transaksi pending tetap akurat untuk ops dan device flow.
|
|
- Dampak / implikasi:
|
|
- Sweep menulis `STATE_CHANGED` event dengan reason `dynamic_qr_expired`.
|
|
- Callback paid yang datang setelah transaksi sudah `expired` tetap ditolak oleh state transition guard.
|
|
- Endpoint admin ini bisa menjadi dasar scheduler/background worker saat fase operasional berikutnya.
|
|
- Status: Active
|