Files
Qris-Soundbox/DECISIONS_LOG.md

62 KiB

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

D-026 — Penundaan Broker MQTT Sungguhan

  • Tanggal: 2026-05-28
  • Keputusan:
    • Integrasi broker MQTT sungguhan ditunda sampai infrastruktur broker, credential, network access, dan topic policy siap.
    • Selama broker belum siap, Fase 2 tetap memakai HTTP simulator dan mqtt_messages sebagai outbox/trace untuk dynamic QR MQTT, config push, dan config ACK.
    • Prioritas lanjutan sebelum broker siap adalah hardening UI ops, scheduler dynamic QR expiry, dan observability outbox.
  • Alasan:
    • Broker MQTT membutuhkan persiapan eksternal di luar kode aplikasi agar implementasi adapter tidak menebak-nebak detail koneksi dan policy topic.
    • Simulator/outbox saat ini sudah cukup untuk menguji kontrak payload, idempotency, correlation ID, dan visibilitas admin.
  • Dampak / implikasi:
    • Tidak menambahkan dependency broker/client MQTT sungguhan sampai detail infrastruktur tersedia.
    • Adapter broker nanti harus memakai mqtt_messages sebagai sumber trace/outbox awal, bukan mengganti kontrak Fase 2 yang sudah berjalan.
    • Pekerjaan berikutnya dapat lanjut ke scheduler expiry dan peningkatan UI/filter ops tanpa blocking pada broker.
  • Status: Active

D-027 — Broker MQTT Awal Menggunakan Mosquitto

  • Tanggal: 2026-05-28
  • Keputusan:
    • Broker MQTT sungguhan pertama akan memakai Mosquitto karena ringan, sederhana dioperasikan, dan cukup untuk kebutuhan MVP/early production.
    • Migrasi ke EMQX atau managed MQTT tetap dibuka jika skala, high availability, dashboard broker, rule engine, atau auth/authorization kompleks mulai dibutuhkan.
    • Kontrak topic, credential model, dan adapter backend harus dibuat broker-agnostic agar migrasi tidak mengubah flow device.
  • Alasan:
    • Kebutuhan saat ini masih single broker dengan TLS, username/password, ACL per device, dan publish/subscribe dasar.
    • Mosquitto memberi jalur deploy paling cepat sambil menjaga biaya dan kompleksitas operasional rendah.
    • Target skala besar seperti 100.000 device masih memungkinkan dievaluasi lewat benchmark bertahap sebelum memutuskan migrasi.
  • Dampak / implikasi:
    • Hindari penggunaan fitur broker-spesifik yang sulit dipindahkan.
    • Device topic tetap mengikuti kontrak devices/{deviceId}/....
    • Credential device tetap dikelola sebagai bagian dari domain platform agar tidak terkunci pada format internal broker tertentu.
    • Benchmark kapasitas wajib dilakukan sebelum milestone skala besar, misalnya 20k, 50k, dan 100k connected device.
  • Status: Active

D-028 — Finance Light dan Credential Device Operasional

  • Tanggal: 2026-05-28
  • Keputusan:
    • Ledger Fase 1 dinaikkan dari placeholder gross-only menjadi finance light: gross_income, platform_fee, dan merchant_payable.
    • Platform fee awal memakai konfigurasi FINANCE_PLATFORM_FEE_BPS agar mudah diubah tanpa migrasi schema.
    • Credential MQTT device dikelola di domain device melalui status credential, username MQTT, timestamp issue/rotate/revoke, dan fingerprint secret.
    • Password credential hanya dikembalikan satu kali oleh endpoint rotate dan tidak disimpan sebagai plaintext.
    • Username MQTT device harus sama dengan device_id karena ACL Mosquitto memakai pattern devices/%u/....
  • Alasan:
    • Admin settlement dan reconciliation butuh angka net payable awal sebelum modul settlement penuh.
    • Credential device perlu siap sebelum ACL/per-device broker MQTT dibuat agar provisioning tidak bergantung pada catatan manual.
  • Dampak / implikasi:
    • Callback paid membuat ledger idempotent per transaction_id + entry_type.
    • Model fee saat ini masih global/default; fee profile merchant tetap bisa ditambahkan kemudian tanpa mengubah kontrak ledger entry.
    • Endpoint POST /admin/devices/{deviceId}/credentials/rotate menjadi jalur provisioning credential MQTT awal.
    • Script npm run mqtt:provision-device menjadi jalur ringan untuk rotate credential dan menghasilkan command mosquitto_passwd.
    • UI device technical detail menyediakan rotate credential dan modal one-time secret untuk operator.
    • ACL Mosquitto per device masih perlu disinkronkan dari credential platform saat tahap broker provisioning berikutnya.
  • Status: Active

D-029 — Device Auth Per-Credential dengan Fallback Dev Token

  • Tanggal: 2026-05-28
  • Keputusan:
    • Endpoint /device/* menerima autentikasi per-device melalui header X-Device-Id dan X-Device-Secret.
    • Secret device diverifikasi memakai fingerprint credential yang tersimpan di database; plaintext secret tidak disimpan.
    • Credential device hanya boleh mengakses resource untuk device_id yang sama dengan X-Device-Id.
    • Authorization: Bearer DEVICE_TOKEN tetap diterima sebagai fallback/dev compatibility selama transisi.
  • Alasan:
    • Credential MQTT yang sudah diprovision perlu menjadi identitas device yang sama untuk API platform.
    • Token global device terlalu luas untuk production karena satu token bocor bisa mengakses semua device.
    • Fallback dev token menjaga smoke test, simulator, dan tooling lama tetap berjalan saat migrasi bertahap.
  • Dampak / implikasi:
    • Device production sebaiknya mulai mengirim X-Device-Id dan X-Device-Secret.
    • API menolak credential valid yang mencoba mengirim payload untuk device lain dengan 403 FORBIDDEN.
    • Production dapat set DEVICE_AUTH_ALLOW_LEGACY_TOKEN=false untuk mematikan fallback DEVICE_TOKEN.
  • Status: Active

D-030 — Settlement Batch Light dari Merchant Payable

  • Tanggal: 2026-05-28
  • Keputusan:
    • Settlement awal dibuat sebagai batch dari ledger entry merchant_payable yang belum pernah masuk batch.
    • Batch dikelompokkan per merchant dan menyimpan gross_amount, platform_fee_amount, net_payable_amount, entry_count, cutoff_at, dan status.
    • Status awal batch adalah created; admin dapat menandai batch menjadi paid.
    • Settlement light tidak memindahkan dana dan tidak mengelola rekening escrow; batch adalah rekonsiliasi/payable visibility.
  • Alasan:
    • Finance light sudah menghasilkan merchant payable, sehingga admin membutuhkan unit batch untuk proses settlement manual/merchant-direct.
    • Model ini tetap cocok dengan keputusan payout merchant-direct, karena batch adalah daftar kewajiban bayar, bukan instruksi transfer otomatis.
  • Dampak / implikasi:
    • Ledger entry hanya boleh masuk satu batch lewat unique key settlement_batch_entries.ledger_entry_id.
    • Batch berikutnya hanya mengambil payable yang belum dibatch.
    • Endpoint baru: POST /admin/settlement-batches, GET /admin/settlement-batches, GET /admin/settlement-batches/{batchId}, GET /admin/settlement-batches/{batchId}/export.csv, dan POST /admin/settlement-batches/{batchId}/mark-paid.
    • UI settlement batch management membaca batch real, menampilkan KPI, membuka detail drawer, generate batch, mark paid, dan download CSV payout report.
    • Tahap berikutnya bisa menambahkan format bank-specific payout file jika dibutuhkan.
  • Status: Active

D-031 — Dashboard Settlement / Finance Summary

  • Tanggal: 2026-05-28
  • Keputusan:
    • Admin dashboard overview menampilkan ringkasan settlement dan finance dari data batch real.
    • Summary backend /admin/dashboard/summary memuat pending payout, paid payout, total platform fee, jumlah batch open/paid/total.
    • Dashboard menampilkan panel "Settlement Finance" dengan batch terbaru dan tombol download CSV payout report per batch.
  • Alasan:
    • Setelah settlement batch light aktif, operator perlu melihat exposure payable dan fee tanpa harus masuk dulu ke halaman settlement detail.
    • Summary di dashboard membantu sanity check finance harian sebelum payout manual/merchant-direct dijalankan.
  • Dampak / implikasi:
    • Angka dashboard saat ini bersumber dari maksimal 500 batch terbaru, cukup untuk fase awal dan smoke/manual ops ringan.
    • Untuk volume besar, summary finance perlu dipindah ke query agregasi khusus atau materialized summary agar tidak bergantung pada limit list batch.
  • Status: Active

D-032 — Settlement Mark Paid Membutuhkan Bukti Payout

  • Tanggal: 2026-05-28
  • Keputusan:
    • UI settlement tidak lagi menutup batch dengan satu klik langsung; operator harus mengisi paid_at, reference payout, dan note rekonsiliasi.
    • Endpoint POST /admin/settlement-batches/{batchId}/mark-paid menerima paid_reference dan paid_note, lalu menyimpannya di metadata_json.
    • Audit log tetap menyimpan before/after batch agar perubahan status dan bukti payout dapat ditelusuri.
  • Alasan:
    • Mark paid adalah aksi finansial operasional, sehingga perlu bukti transfer/reference sebelum status batch ditutup.
    • Menyimpan bukti di metadata menjaga schema tetap ringan pada fase awal sambil mempertahankan traceability.
  • Dampak / implikasi:
    • UI mewajibkan reference payout, tetapi API masih kompatibel dengan payload lama untuk tooling internal.
    • Jika workflow payout makin formal, field bukti payout dapat dipromosikan menjadi kolom dedicated atau tabel payout events.
  • Status: Active

D-033 — Dashboard Finance Summary Menggunakan Agregasi DB

  • Tanggal: 2026-05-28
  • Keputusan:
    • Summary settlement/finance untuk admin dashboard dihitung lewat query agregasi langsung ke settlement_batches.
    • Endpoint /admin/dashboard/summary tidak lagi menghitung total finance dari list batch yang dibatasi pagination.
  • Alasan:
    • Angka payable, paid payout, fee, dan jumlah batch harus mewakili seluruh data, bukan hanya batch terbaru.
    • Jalur agregasi khusus lebih siap untuk volume data yang bertambah tanpa mengubah kontrak UI dashboard.
  • Dampak / implikasi:
    • UI dashboard tetap memakai kontrak field yang sama.
    • Jika volume settlement makin besar, agregasi ini bisa ditingkatkan menjadi materialized summary tanpa mengubah respons API.
  • Status: Active

D-034 — Merchant Portal Settlement View

  • Tanggal: 2026-05-28
  • Keputusan:
    • Merchant portal memiliki endpoint settlement merchant-scoped untuk profile, summary payout, daftar batch, detail batch, dan export CSV.
    • Merchant login fase awal memakai token dev MERCHANT_TOKEN dan password portal sederhana MERCHANT_PORTAL_PASSWORD.
    • UI merchant-settlement-history membaca data settlement real sesuai merchant yang login.
  • Alasan:
    • Settlement tidak hanya perlu visible untuk admin; merchant juga perlu transparansi pending payout, paid payout, detail batch, dan report.
    • Merchant-scoped endpoint mencegah UI merchant memakai API admin atau melihat batch merchant lain.
  • Dampak / implikasi:
    • Auth merchant saat ini masih ringan untuk fase development; production perlu diganti ke user/session auth merchant yang sebenarnya.
    • Kontrak endpoint sudah dipisah dari admin sehingga hardening auth bisa dilakukan tanpa mengubah UI settlement merchant secara besar.
  • Status: Active

D-035 — Payout Event History

  • Tanggal: 2026-05-28
  • Keputusan:
    • Settlement batch memiliki event history formal di tabel settlement_batch_events.
    • Event awal yang dicatat: created, csv_exported, dan marked_paid.
    • Admin dan merchant detail settlement membaca event history yang sama.
  • Alasan:
    • Metadata dan audit log cukup untuk jejak internal, tetapi operator dan merchant membutuhkan timeline payout yang mudah dibaca.
    • Event history menjadi fondasi untuk workflow berikutnya seperti failed/cancelled payout, reference update, dan dispute.
  • Dampak / implikasi:
    • Export CSV dari admin maupun merchant menambah event csv_exported.
    • Event history tidak menggantikan audit log; audit log tetap dipakai untuk compliance internal admin.
  • Status: Active

D-036 — Settlement Failed dan Cancel Workflow

  • Tanggal: 2026-05-28
  • Keputusan:
    • Batch settlement berstatus created dapat ditandai failed atau cancelled oleh admin dengan reason wajib.
    • Endpoint baru: POST /admin/settlement-batches/{batchId}/mark-failed dan POST /admin/settlement-batches/{batchId}/cancel.
    • Reason disimpan ke failure_reason, detail resolusi disimpan ke metadata_json, dan event failed/cancelled dicatat di payout event history.
  • Alasan:
    • Workflow payout operasional perlu menangani transfer gagal dan batch yang dibatalkan sebelum dibayar.
    • Reason wajib menjaga keputusan finansial tetap dapat ditelusuri oleh operator dan merchant.
  • Dampak / implikasi:
    • Batch failed/cancelled menjadi final state, tetapi dapat direprocess lewat aturan D-037.
  • Status: Active

D-037 — Reprocess Settlement Failed/Cancelled

  • Tanggal: 2026-05-28
  • Keputusan:
    • Batch settlement berstatus failed atau cancelled dapat direprocess menjadi batch baru berstatus created.
    • Reprocess memindahkan settlement_batch_entries dari batch lama ke batch baru, karena ledger entry hanya boleh aktif pada satu batch.
    • Batch lama tetap menjadi arsip final dan menyimpan reprocessed_to_batch_id di metadata_json.
    • Endpoint baru: POST /admin/settlement-batches/{batchId}/reprocess.
  • Alasan:
    • Payable dari payout gagal/cancel perlu dapat diproses ulang tanpa membuat ledger entry duplikat.
    • Memindahkan entry menjaga constraint unik settlement_batch_entries.ledger_entry_id sekaligus membuat batch baru siap mengikuti workflow normal.
  • Dampak / implikasi:
    • Detail report batch lama setelah reprocess tidak lagi menampilkan entry karena entry dipindah ke batch baru; angka aggregate batch lama tetap tersimpan sebagai arsip.
    • Reprocess hanya boleh satu kali per batch lama untuk menghindari batch duplikat.
    • Event history mencatat reprocessed pada batch lama dan created pada batch baru.
  • Status: Active

D-038 — Settlement Reconciliation Mismatch Report

  • Tanggal: 2026-05-28
  • Keputusan:
    • Admin memiliki report reconciliation untuk membandingkan aggregate settlement batch dengan ledger dan transaksi aktual.
    • Endpoint baru: GET /admin/reconciliation/settlement-batches.
    • UI admin-reconciliation-management membaca data real dari endpoint tersebut untuk KPI matched, discrepancy, issue count, raw payload, dan tabel batch.
    • Batch arsip yang sudah direprocess tidak dihitung sebagai mismatch palsu karena entry settlement-nya memang dipindah ke batch baru.
  • Alasan:
    • Operator finance butuh cara cepat melihat batch yang angka gross, fee, net payable, entry count, atau status transaksinya tidak sinkron.
    • Report ini menjadi sanity layer sebelum workflow koreksi payout/reference yang lebih detail.
  • Dampak / implikasi:
    • Report masih internal-system reconciliation, belum mencocokkan bank statement eksternal.
    • Smoke test memverifikasi endpoint report mengembalikan aggregate dan rows.
  • Status: Active

D-039 — Settlement Payout Reference Update

  • Tanggal: 2026-05-28
  • Keputusan:
    • Admin dapat memperbarui paid_reference dan paid_note untuk settlement batch yang sudah berstatus paid.
    • Endpoint baru: PATCH /admin/settlement-batches/{batchId}/reference.
    • Update reference menyimpan nilai terbaru di metadata_json, mencatat event reference_updated, dan membuat audit log before/after.
    • UI settlement batch management menampilkan form update reference hanya untuk batch paid.
  • Alasan:
    • Bukti payout manual bisa salah input atau perlu dilengkapi setelah rekonsiliasi.
    • Perubahan bukti payout harus traceable tanpa membuka ulang status finansial batch.
  • Dampak / implikasi:
    • Reference update tidak mengubah status, nominal, entry, atau paid_at batch.
    • Batch yang belum paid, failed, atau cancelled tidak dapat mengubah payout reference lewat endpoint ini.
  • Status: Active

D-040 — Bank Generic Payout Export

  • Tanggal: 2026-05-28
  • Keputusan:
    • Endpoint export settlement mendukung format tambahan bank_generic lewat query ?format=bank_generic.
    • Format default standard tetap menjadi payout report detail per transaksi agar kompatibel dengan UI dan tooling yang sudah ada.
    • Format bank_generic menghasilkan satu baris payout per batch berisi beneficiary account, account type, beneficiary name, amount, currency, remark, batch code, dan transaction count.
    • Admin dan merchant export memakai pilihan format yang sama; UI admin settlement menyediakan pilihan General CSV atau Bank Upload.
  • Alasan:
    • Operasional finance membutuhkan file yang lebih dekat ke pola upload bank/payment rail, bukan hanya report detail transaksi.
    • Format generik memberi baseline sebelum membuat template bank spesifik seperti BCA, Mandiri, BRI, atau payment gateway tertentu.
  • Dampak / implikasi:
    • settlement_account_reference dan settlement_account_type merchant menjadi sumber destination account pada file bank generic.
    • Event csv_exported menyimpan format dan filename untuk jejak audit.
    • Format bank spesifik berikutnya dapat ditambahkan sebagai nilai format baru tanpa mengubah endpoint.
  • Status: Active

D-041 — Settlement Dispute/Adjustment Record

  • Tanggal: 2026-05-28
  • Keputusan:
    • Admin dapat mencatat adjustment/dispute ringan pada settlement batch lewat endpoint POST /admin/settlement-batches/{batchId}/adjustments.
    • Adjustment disimpan di metadata_json.adjustments dengan tipe credit atau debit, nominal absolut, signed amount, reason, note, actor, dan timestamp.
    • Total signed adjustment disimpan di metadata_json.total_adjustment_amount.
    • Setiap adjustment mencatat event adjustment_recorded dan audit log before/after.
    • UI settlement batch management menyediakan form Record Adjustment dan menampilkan total adjustment.
  • Alasan:
    • Setelah reconciliation, operator perlu mencatat koreksi/selisih payout tanpa langsung mengubah ledger historis.
    • Event dan metadata cukup untuk fase awal dispute tracking sebelum dibuat ledger adjustment formal.
  • Dampak / implikasi:
    • Adjustment tidak mengubah gross_amount, platform_fee_amount, net_payable_amount, status batch, atau ledger entries.
    • Batch arsip yang sudah direprocess tidak dapat menerima adjustment baru agar arsip tetap final.
    • Tahap berikutnya dapat mempromosikan adjustment menjadi tabel/ledger entry khusus jika proses finance membutuhkannya.
  • Status: Active

D-042 — Settlement Adjustment Summary di Finance Dashboard

  • Tanggal: 2026-05-28
  • Keputusan:
    • Summary finance settlement menghitung adjustment_amount dari metadata_json.total_adjustment_amount setiap batch.
    • Endpoint /admin/dashboard/summary menambahkan settlement_adjustment_amount dan settlement_adjusted_paid_amount.
    • UI admin dashboard menampilkan paid payout sebagai adjusted paid amount dan menampilkan adjustment sebagai angka terpisah.
    • UI settlement batch management menampilkan total adjustment pada KPI finance.
  • Alasan:
    • Setelah adjustment/dispute dicatat, operator perlu melihat dampaknya di ringkasan finance tanpa membuka batch satu per satu.
    • Paid payout asli tetap dibutuhkan sebagai angka batch historis, sementara adjusted paid memberi pandangan operasional setelah koreksi.
  • Dampak / implikasi:
    • Adjustment summary masih berbasis metadata, belum menjadi ledger formal.
    • settlement_paid_amount tetap nominal paid batch asli; settlement_adjusted_paid_amount adalah paid amount ditambah signed adjustment.
  • Status: Active

D-043 — Merchant Settlement Adjustment Visibility

  • Tanggal: 2026-05-28
  • Keputusan:
    • Merchant settlement summary memakai adjusted_paid_amount untuk total settled yang ditampilkan di portal merchant.
    • Portal merchant menampilkan adjustment_amount sebagai angka terpisah agar koreksi payout tetap transparan.
    • Drawer settlement merchant menampilkan adjustment/refund dan total disbursed setelah adjustment.
  • Alasan:
    • Merchant perlu melihat dampak koreksi payout yang sama dengan admin finance, bukan hanya nominal batch asli.
    • Menampilkan adjustment terpisah menjaga transparansi tanpa mencampurkan koreksi dengan gross/net historis.
  • Dampak / implikasi:
    • Backend merchant sudah memakai summary yang sama dari getSettlementFinanceSummary.
    • Smoke test memverifikasi adjustment_amount dan adjusted_paid_amount tersedia untuk merchant.
  • Status: Active

D-044 — Settlement Adjustment Formal Table

  • Tanggal: 2026-05-28
  • Keputusan:
    • Adjustment settlement dipromosikan dari metadata-only menjadi tabel formal settlement_batch_adjustments.
    • Endpoint POST /admin/settlement-batches/{batchId}/adjustments menulis row adjustment formal, lalu mensinkronkan ringkasan kompatibilitas ke settlement_batches.metadata_json.
    • Detail batch admin dan merchant mengembalikan array adjustments.
    • Summary finance menghitung adjustment dari tabel formal, bukan dari metadata.
  • Alasan:
    • Adjustment/dispute adalah data finance auditable dan perlu dapat di-query tanpa parsing metadata batch.
    • Metadata tetap dipertahankan sebagai ringkasan agar UI dan payload lama tidak langsung patah.
  • Dampak / implikasi:
    • metadata_json.adjustments bukan sumber utama lagi; sumber utama adalah settlement_batch_adjustments.
    • UI admin dan merchant membaca adjustment formal dari detail response saat tersedia, dengan fallback metadata.
    • Jika ada data metadata-only lama di environment yang sudah berjalan, perlu migrasi backfill sebelum summary formal dianggap lengkap.
  • Status: Active

D-045 — Backfill Adjustment Metadata ke Tabel Formal

  • Tanggal: 2026-05-28
  • Keputusan:
    • Migrasi schema melakukan backfill idempotent dari settlement_batches.metadata_json.adjustments ke settlement_batch_adjustments.
    • Backfill memakai ID adjustment lama jika tersedia; jika tidak tersedia, ID dibuat deterministik dari batch dan urutan array.
    • Insert backfill memakai ON CONFLICT (id) DO NOTHING supaya aman dijalankan berulang.
    • Setelah backfill, metadata_json.total_adjustment_amount disinkronkan ulang dari tabel formal.
  • Alasan:
    • Ada kemungkinan environment yang sudah berjalan memiliki adjustment metadata-only dari D-041 sebelum tabel formal D-044.
    • Summary finance sekarang menghitung dari tabel formal, sehingga data lama perlu ikut masuk agar laporan tidak turun nilainya.
  • Dampak / implikasi:
    • Backfill menjaga kompatibilitas metadata, tetapi sumber utama tetap settlement_batch_adjustments.
    • Metadata lama yang tidak memiliki reason akan diberi reason default Backfilled settlement adjustment.
  • Status: Active

D-046 — Settlement Adjustment Report

  • Tanggal: 2026-05-28
  • Keputusan:
    • Admin memiliki endpoint GET /admin/settlement-adjustments untuk membaca adjustment settlement lintas batch.
    • Report mendukung filter merchant_id, adjustment_type, from, to, dan limit.
    • Payload report menyertakan total count, total credit, total debit, net signed adjustment, serta row dengan konteks batch.
    • UI admin-reconciliation-management menampilkan recent adjustment activity dari report formal.
  • Alasan:
    • Setelah adjustment menjadi tabel formal, finance perlu audit trail lintas batch tanpa membuka detail batch satu per satu.
    • Reconciliation dashboard adalah tempat natural untuk melihat koreksi payout terbaru.
  • Dampak / implikasi:
    • Summary report dihitung dari semua row yang sesuai filter, sedangkan daftar row tetap dibatasi limit untuk UI.
    • Report ini belum berupa export CSV khusus; export bisa ditambahkan jika finance membutuhkan lampiran audit.
  • Status: Active

D-047 — Settlement Adjustment CSV Export

  • Tanggal: 2026-05-28
  • Keputusan:
    • Admin memiliki endpoint GET /admin/settlement-adjustments/export.csv.
    • Export CSV memakai filter yang sama dengan report JSON: merchant_id, adjustment_type, from, to, dan limit.
    • CSV menyertakan blok summary, lalu daftar adjustment dengan konteks batch, actor, reason, note, dan nominal signed.
    • UI admin-reconciliation-management menyediakan tombol download CSV pada panel Recent Adjustment Activity.
  • Alasan:
    • Finance perlu lampiran audit adjustment lintas batch yang bisa disimpan atau dikirim di luar dashboard.
    • CSV lebih cepat dipakai untuk proses operasional awal dibanding PDF/reporting engine.
  • Dampak / implikasi:
    • Export dibatasi maksimal 500 row mengikuti batas report saat ini.
    • Jika volume adjustment besar, perlu pagination export atau async report job.
  • Status: Active

D-048 — Settlement Adjustment Report Filter UI

  • Tanggal: 2026-05-28
  • Keputusan:
    • UI admin-reconciliation-management menambahkan filter adjustment berdasarkan merchant ID, tipe credit/debit, tanggal awal, dan tanggal akhir.
    • Filter yang aktif dipakai untuk recent adjustment activity dan download CSV.
    • Tombol Clear mengembalikan report ke default terbaru.
  • Alasan:
    • Finance perlu menelusuri adjustment lintas batch berdasarkan merchant atau periode audit tertentu.
    • Export CSV harus konsisten dengan data yang sedang dilihat operator di dashboard.
  • Dampak / implikasi:
    • Filter merchant masih berupa input ID agar tidak menambah dependency daftar merchant pada panel reconciliation.
    • Jika kebutuhan operasional meningkat, filter merchant dapat dinaikkan menjadi searchable select.
  • Status: Active

D-049 — Searchable Merchant Filter untuk Adjustment Report

  • Tanggal: 2026-05-28
  • Keputusan:
    • Filter merchant pada adjustment report di UI reconciliation memakai daftar merchant dari endpoint admin.
    • Control memakai native datalist agar operator bisa mencari merchant tanpa dependency UI baru.
    • Input dapat menerima ID merchant langsung atau nilai yang cocok dengan kode, brand, atau legal name merchant.
  • Alasan:
    • Operator finance tidak seharusnya perlu copy-paste merchant ID manual untuk audit adjustment.
    • Native datalist cukup ringan untuk fase sekarang dan tetap kompatibel dengan filter API yang menerima merchant_id.
  • Dampak / implikasi:
    • Untuk ribuan merchant, datalist perlu diganti menjadi remote search/pagination.
    • Query backend tetap memakai merchant ID sehingga kontrak API tidak berubah.
  • Status: Active

D-050 — Production Admin Session dan RBAC Baseline

  • Tanggal: 2026-05-29
  • Keputusan:
    • Admin auth mendukung signed session token berbasis user/role selain legacy dev token.
    • Role awal disiapkan: admin, finance, ops, support, viewer.
    • Endpoint settlement sensitif dipagari permission granular seperti settlement:pay, settlement:adjust, settlement:export, dan reconciliation:read.
    • Legacy admin token dan login dev tetap default aktif untuk smoke/local, tetapi bisa dimatikan lewat ADMIN_AUTH_ALLOW_LEGACY_TOKEN=false dan ADMIN_DEV_LOGIN_ENABLED=false.
  • Alasan:
    • Production tidak boleh bergantung pada satu token admin global.
    • Finance/ops/support/viewer membutuhkan batas akses yang berbeda saat pilot makin dekat.
  • Dampak / implikasi:
    • Production wajib mengatur ADMIN_SESSION_SECRET kuat dan menonaktifkan login dev.
    • Smoke lama tetap kompatibel selama mode dev masih aktif.
  • Status: Active
  • Tanggal: 2026-05-29
  • Keputusan:
    • MQTT publisher menyimpan status runtime: koneksi terakhir, disconnect terakhir, error terakhir, dan counter publish success/failure.
    • Service subscriber broker opsional dapat diaktifkan dengan MQTT_SUBSCRIBE_ENABLED=true untuk merekam uplink topic devices/+/uplink/# ke mqtt_messages.
    • Endpoint /admin/mqtt/status mengembalikan status publisher, subscriber, dan trace pesan terakhir.
  • Alasan:
    • Operator perlu melihat health broker dan jejak message tanpa masuk ke broker langsung.
    • Subscriber dibuat opsional agar local smoke tetap stabil tanpa dependency broker.
  • Dampak / implikasi:
    • Uplink dari broker saat ini direkam untuk observability; pemrosesan business flow device tetap memakai endpoint device yang sudah ada.
    • Untuk production broker, ACL Mosquitto tetap harus disiapkan sesuai panduan.
  • Status: Active

D-052 — Approval Workflow untuk Settlement Adjustment

  • Tanggal: 2026-05-29
  • Keputusan:
    • settlement_batch_adjustments memiliki approval_status (pending, approved, rejected) dan audit field approval/rejection.
    • Summary finance, metadata total adjustment, dan UI detail hanya menghitung adjustment approved.
    • Production dapat mewajibkan approval dengan SETTLEMENT_ADJUSTMENT_REQUIRE_APPROVAL=true.
    • Endpoint admin ditambahkan untuk approve/reject adjustment pending.
  • Alasan:
    • Adjustment payout adalah kontrol finance dan tidak boleh selalu final saat dicatat.
    • Local smoke perlu tetap kompatibel, sehingga default dev masih auto-approved.
  • Dampak / implikasi:
    • Merchant hanya melihat nominal adjustment final/approved dalam summary dan detail.
    • Finance dapat memfilter report adjustment berdasarkan approval_status.
  • Status: Active

D-053 — Deployment Readiness Preflight

  • Tanggal: 2026-05-29
  • Keputusan:
    • Ditambahkan npm run deploy:check-env untuk memvalidasi env production kritikal.
    • Checklist production dituangkan di DEPLOYMENT_READINESS.md.
  • Alasan:
    • Banyak flag dev yang sengaja aktif untuk smoke/local dan harus eksplisit dimatikan sebelum production.
  • Dampak / implikasi:
    • Deploy candidate harus menjalankan typecheck, smoke e2e, dan env preflight.
    • Preflight sengaja gagal jika secret/default dev masih dipakai.
  • Status: Active

D-054 — Admin User Bootstrap Script dan Finance Approval UI

  • Tanggal: 2026-05-29
  • Keputusan:
    • Ditambahkan script npm run admin:create-user untuk create/update user admin production dengan hash scrypt.
    • Script mendukung role admin, finance, ops, support, viewer, status inactive, dan password rotation eksplisit.
    • UI reconciliation menambahkan filter approval_status serta tombol approve/reject untuk adjustment pending.
  • Alasan:
    • RBAC production perlu cara operasional membuat user nyata tanpa edit database manual.
    • Approval workflow adjustment harus bisa dipakai finance dari UI, bukan hanya lewat API.
  • Dampak / implikasi:
    • Production bootstrap admin user bisa dilakukan sebelum ADMIN_DEV_LOGIN_ENABLED=false.
    • Finance dapat memproses pending adjustment dari panel Recent Adjustment Activity.
  • Status: Active

D-055 — Merchant Session Auth dan Merchant User Bootstrap

  • Tanggal: 2026-05-29
  • Keputusan:
    • Merchant portal mendukung signed session token scoped ke merchant_id.
    • Ditambahkan tabel merchant_users untuk login production berbasis email/password hash scrypt.
    • Ditambahkan script npm run merchant:create-user untuk create/update merchant portal user.
    • Login dev lama berbasis MERCHANT_TOKEN dan MERCHANT_PORTAL_PASSWORD tetap default aktif untuk local smoke, tetapi bisa dimatikan via MERCHANT_AUTH_ALLOW_LEGACY_TOKEN=false dan MERCHANT_DEV_LOGIN_ENABLED=false.
  • Alasan:
    • Merchant portal tidak boleh bergantung pada satu password/token global saat production.
    • Setiap merchant membutuhkan user yang jelas dan token yang tidak bisa dipakai lintas merchant.
  • Dampak / implikasi:
    • Production wajib membuat merchant users sebelum mematikan dev login.
    • UI merchant tetap kompatibel dengan response profile lama dan baru.
  • Status: Active

D-056 — Versioned Migration Runner

  • Tanggal: 2026-05-29
  • Keputusan:
    • Ditambahkan command npm run db:migrate.
    • Migration runner memakai tabel schema_migrations dan advisory lock Postgres untuk mencegah dua proses migrasi bersamaan.
    • Migration file dibaca dari folder migrations/ dengan format urut NNN_description.sql atau NNN_description.mts.
    • Migration awal 001_current_schema_bootstrap.mts menjalankan schema bootstrap existing sebagai baseline.
  • Alasan:
    • Schema sudah besar dan production deploy membutuhkan riwayat migration yang eksplisit.
    • Baseline menjaga kompatibilitas dengan schema bootstrap yang sudah ada tanpa menggandakan SQL besar.
  • Dampak / implikasi:
    • Deploy candidate harus menjalankan npm run db:migrate sebelum start service.
    • Migration berikutnya sebaiknya dibuat sebagai file terpisah, bukan menambah perubahan besar langsung ke bootstrap.
  • Status: Active

D-057 — Production Structured Logging dan Observability Summary

  • Tanggal: 2026-05-29
  • Keputusan:
    • Request logging memakai middleware internal dengan structured fields: request_id, trace_id, method, path, status, latency, user-agent, dan IP.
    • LOG_FORMAT=json tersedia untuk production stdout/stderr log collector; default local tetap readable.
    • Error middleware menulis structured API/unhandled error log.
    • Health endpoint ditambah: GET /health/deep dan GET /admin/health/deep.
    • Admin endpoint GET /admin/observability/summary merangkum DB, MQTT, notification failure/pending, dan settlement reconciliation mismatch.
  • Alasan:
    • Pilot production membutuhkan log yang bisa dicari berdasarkan request/trace dan indikator operasional cepat.
    • Mengurangi dependency pada log format dev dan console manual.
  • Dampak / implikasi:
    • morgan dihapus karena digantikan request logging internal.
    • Production sebaiknya set LOG_FORMAT=json dan mengirim stdout/stderr ke log collector.
  • Status: Active

D-058 — Initial Load Test Harness

  • Tanggal: 2026-05-29
  • Keputusan:
    • Ditambahkan script npm run load:test.
    • Harness menguji transaction create, QRIS paid callback, device heartbeat, dynamic QR API, dan observability summary dengan concurrency configurable.
    • Output berupa JSON summary berisi total success/error, durasi total, throughput perkiraan, serta p50/p95/max per label.
    • Data merchant/transaksi load test dibersihkan setelah run.
  • Alasan:
    • Setelah structured logging dan observability tersedia, perlu baseline performa awal sebelum pilot.
    • Script ringan cukup untuk menemukan bottleneck awal tanpa memasang load testing framework eksternal.
  • Dampak / implikasi:
    • Angka baseline lokal pertama: 190 request, 0 error, durasi 1202.49 ms, throughput perkiraan 158.01 req/s.
    • p95 lokal: transaction create 119.54 ms, callback paid 119.30 ms, heartbeat 45.93 ms, dynamic QR 41.75 ms, observability summary 69.63 ms.
    • Angka ini bukan kapasitas production final; perlu run ulang di environment target dan dengan data lebih besar.
  • Status: Active

D-059 — Admin UI RBAC Awareness

  • Tanggal: 2026-05-29
  • Keputusan:
    • Admin UI shared helper menyimpan profile admin, membaca GET /admin/me, dan menyediakan helper permission untuk elemen data-admin-permission.
    • Settlement batch UI dan reconciliation UI menyembunyikan/disable aksi finance sesuai permission seperti settlement:write, settlement:pay, settlement:adjust, dan settlement:export.
  • Alasan:
    • RBAC backend sudah aktif, tetapi UI perlu memberi sinyal operasional yang jelas sebelum user menekan aksi yang akan ditolak API.
  • Dampak / implikasi:
    • UI menjadi role-aware tanpa mengganti enforcement backend.
    • API tetap menjadi sumber otorisasi final.
  • Status: Active

D-060 — Async Settlement Adjustment Export Jobs

  • Tanggal: 2026-05-29
  • Keputusan:
    • Ditambahkan tabel export_jobs, migration 002_export_jobs.sql, dan store exportJobStore.
    • Ditambahkan endpoint POST /admin/exports/settlement-adjustments, GET /admin/exports/:jobId, dan GET /admin/exports/:jobId/download.
    • Job export saat ini dieksekusi segera di request lifecycle sebagai skeleton async, tetapi contract status/result/download sudah siap dipisah ke worker.
  • Alasan:
    • Export report adjustment bisa membesar dan perlu contract job sebelum dipindah ke background worker production.
  • Dampak / implikasi:
    • UI/API consumer bisa mulai memakai pola job polling.
    • Tahap berikutnya adalah worker queue dan storage object jika volume export besar.
  • Status: Active

D-061 — Load Test Level 2 dan Audit Cleanup

  • Tanggal: 2026-05-29
  • Keputusan:
    • Load test lokal level 2 dijalankan dengan 300 callback, 600 heartbeat, 300 dynamic QR, 100 observability read, concurrency 25.
    • Dependency uuid dihapus karena tidak dipakai source dan menjadi sumber audit moderate.
    • npm audit sekarang menghasilkan 0 vulnerability.
  • Alasan:
    • Pilot membutuhkan baseline lebih besar dari smoke awal dan dependency audit bersih.
  • Dampak / implikasi:
    • Hasil lokal level 2: 1610 request, 0 error, durasi 4955.45 ms, throughput perkiraan 324.9 req/s.
    • p95 lokal: transaction create 183.56 ms, callback paid 129.03 ms, heartbeat 90.4 ms, dynamic QR 71.57 ms, observability summary 230.96 ms.
    • Angka ini tetap baseline lokal, bukan kapasitas production final.
  • Status: Active

D-062 — Real MQTT Broker Smoke Validation

  • Tanggal: 2026-05-29
  • Keputusan:
    • npm run smoke:mqtt-real dijalankan terhadap broker mqtts://mqtt.iptek.co:8883.
    • Test memverifikasi broker connect, subscribe devices/+/downlink/#, payment success downlink, config push, dan dynamic QR response.
  • Alasan:
    • MQTT broker production-like harus divalidasi terpisah dari simulator sebelum pilot device real.
  • Dampak / implikasi:
    • Smoke broker real terakhir lulus dengan 3 message diterima dan cleanup data smoke berhasil.
    • Smoke e2e utama tetap memakai simulator agar CI/local tidak bergantung broker eksternal.
  • Status: Active

D-063 — Merchant Portal Session Polish

  • Tanggal: 2026-05-29
  • Keputusan:
    • Merchant API helper menyimpan user session dan auth mode.
    • Merchant settlement UI menampilkan nama/session role user serta menyediakan tombol logout yang membersihkan session lokal.
  • Alasan:
    • Setelah merchant session auth production tersedia, portal perlu feedback login yang lebih jelas dan alur keluar yang eksplisit.
  • Dampak / implikasi:
    • Merchant operator bisa melihat identitas session aktif dan logout tanpa menghapus storage manual.
  • Status: Active

D-064 — Export Job Worker Productionization

  • Tanggal: 2026-05-29
  • Keputusan:
    • Export adjustment settlement diproses oleh worker internal exportJobWorker, bukan lagi langsung di request lifecycle.
    • Endpoint create job mengembalikan job pending; worker melakukan atomic claim ke status running, lalu menyelesaikan ke completed atau failed.
    • Worker memiliki konfigurasi EXPORT_WORKER_ENABLED, EXPORT_WORKER_INTERVAL_MS, EXPORT_WORKER_BATCH_SIZE, EXPORT_JOB_STALE_RUNNING_MS, dan EXPORT_SETTLEMENT_ADJUSTMENT_MAX_ROWS.
    • /admin/observability/summary menampilkan status worker dan count job per status.
  • Alasan:
    • Export besar tidak boleh menahan request admin dan perlu pola polling yang production-friendly.
    • Stale running job perlu bisa di-reset setelah restart/crash worker.
  • Dampak / implikasi:
    • Client harus polling GET /admin/exports/:jobId sampai status completed sebelum download.
    • Hasil export saat ini masih disimpan di DB result_body; tahap berikutnya adalah storage eksternal/object storage jika ukuran file membesar.
  • Status: Active

D-065 — Reconciliation UI Async Export Polling

  • Tanggal: 2026-05-29
  • Keputusan:
    • UI admin-reconciliation-management mengganti download adjustment CSV sinkron menjadi alur async export job.
    • Tombol export membuat job via POST /admin/exports/settlement-adjustments, menampilkan status job, polling GET /admin/exports/:jobId, lalu download dari endpoint job saat completed.
    • Shared admin API helper menambahkan method create/get/download export job.
  • Alasan:
    • Finance user perlu UX yang selaras dengan worker export agar report besar tidak bergantung request CSV sinkron.
  • Dampak / implikasi:
    • Export lama GET /admin/settlement-adjustments/export.csv masih ada untuk kompatibilitas, tetapi UI reconciliation memakai job async.
    • Tahap berikutnya adalah tampilan riwayat job export jika finance perlu mengambil ulang file lama.
  • Status: Active

D-066 — Export File Storage, Retention, dan Job History

  • Tanggal: 2026-05-29
  • Keputusan:
    • Hasil export baru disimpan sebagai file di EXPORT_STORAGE_DIR, bukan result_body database.
    • Metadata result_storage_path, result_size_bytes, dan expires_at ditambahkan melalui migration 003_export_job_storage.sql.
    • Worker membersihkan hasil export expired berdasarkan EXPORT_RETENTION_DAYS.
    • Endpoint GET /admin/exports ditambahkan untuk history job, dan UI reconciliation menampilkan lima job export adjustment terbaru.
  • Alasan:
    • File CSV besar tidak ideal disimpan sebagai body di database.
    • Finance membutuhkan visibility status/download ulang job export terbaru.
  • Dampak / implikasi:
    • Directory EXPORT_STORAGE_DIR harus writable dan masuk strategi backup/retention.
    • Export lama yang masih punya result_body tetap bisa didownload sebagai fallback.
  • Status: Active

D-067 — MQTT ACL Hardening Helper

  • Tanggal: 2026-05-29
  • Keputusan:
    • Ditambahkan script npm run mqtt:check-acl untuk print template dan validasi file ACL Mosquitto.
    • Provisioning device sekarang menampilkan topic scope per device dan command validasi ACL.
  • Alasan:
    • Pilot hardware real butuh guard agar device hanya publish/subscribe topic miliknya sendiri.
  • Dampak / implikasi:
    • Production preflight harus menjalankan validasi ACL setelah perubahan file broker.
    • Username MQTT device tetap harus sama dengan device_id.
  • Status: Active

D-068 — Backup/Restore Readiness Scripts

  • Tanggal: 2026-05-29
  • Keputusan:
    • Ditambahkan npm run backup:production untuk dump Postgres dan opsional copy Mosquitto passwd/ACL.
    • Ditambahkan npm run restore:plan yang default hanya mencetak command restore dan hanya execute jika diberi --execute.
  • Alasan:
    • Pilot production membutuhkan backup yang bisa dijalankan dan restore drill yang tidak mudah terpanggil destruktif tanpa sadar.
  • Dampak / implikasi:
    • Restore harus diuji pada database disposable sebelum pilot live.
    • Backup directory dan file Mosquitto perlu masuk retention/secure storage operational.
  • Status: Active

D-069 — Staging Load Test Profile

  • Tanggal: 2026-05-29
  • Keputusan:
    • Load test mendapat skenario async export adjustment via LOAD_EXPORTS.
    • Ditambahkan script npm run load:test:staging dengan profile lebih besar: callback, heartbeat, dynamic QR, observability read, dan export job.
  • Alasan:
    • Baseline lokal belum mencakup worker export dan belum cukup mewakili staging/production-like environment.
  • Dampak / implikasi:
    • Jalankan profile ini terhadap staging dengan BASE_URL dan secret/token staging.
    • Angka hasil staging harus dicatat terpisah dari baseline lokal.
  • Status: Active

D-070 — MQTT ACL Smoke Test untuk Pilot Device

  • Tanggal: 2026-05-29
  • Keputusan:
    • Ditambahkan npm run smoke:mqtt-acl untuk validasi credential device terhadap ACL broker.
    • Smoke memastikan device A bisa akses topic miliknya dan ditolak saat subscribe ke downlink device B.
  • Alasan:
    • File ACL yang benar secara template belum cukup; perlu validasi runtime sebelum device fisik pilot.
  • Dampak / implikasi:
    • Smoke membutuhkan dua username device test yang sudah ada di broker.
    • Jalankan setelah provisioning credential device dan reload Mosquitto.
  • Status: Active

D-071 — Restore Drill Validation

  • Tanggal: 2026-05-29
  • Keputusan:
    • Ditambahkan npm run restore:validate.
    • Validator menjalankan migration idempotent, cek /health, /admin/health/deep, dan memastikan tabel kunci tersedia.
  • Alasan:
    • Restore drill harus berakhir dengan bukti service bisa start dan schema masih konsisten.
  • Dampak / implikasi:
    • Jalankan terhadap service/database restore disposable, bukan production live.
    • RESTORE_DRILL_RUN_MIGRATE=false tersedia jika migration ingin dijalankan manual.
  • Status: Active

D-072 — UI QA Lightweight Gate

  • Tanggal: 2026-05-29
  • Keputusan:
    • Ditambahkan npm run ui:qa.
    • Checker memvalidasi inline script halaman operasional, async export reconciliation, dan permission-aware settlement actions.
    • Placeholder navigation di halaman operasional utama dibersihkan dan kini menjadi failure jika muncul lagi.
  • Alasan:
    • Perubahan UI HTML inline rawan regresi sintaks yang tidak tertangkap TypeScript.
  • Dampak / implikasi:
    • UI QA ringan menjadi gate untuk mencegah placeholder nav kembali di halaman operasional utama.
  • Status: Active

D-073 — Staging Load Report Artifact

  • Tanggal: 2026-05-29
  • Keputusan:
    • scripts/load-test.mjs dapat menulis JSON summary ke LOAD_REPORT_FILE.
    • npm run load:test:staging sekarang memakai wrapper yang otomatis menyimpan report ke reports/.
  • Alasan:
    • Staging baseline perlu artifact yang bisa disimpan di handoff/release note, bukan hanya terminal output.
  • Dampak / implikasi:
    • Folder reports/ berisi hasil run staging dan dapat diarsipkan oleh operator.
  • Status: Active

D-074 — Strict Production Security Preflight

  • Tanggal: 2026-05-29
  • Keputusan:
    • npm run deploy:check-env sekarang gagal jika legacy admin/merchant/device auth masih aktif.
    • Production preflight mewajibkan finance approval, export worker, export storage dir, retention positif, dan secret minimal 24 karakter.
  • Alasan:
    • Pilot live tidak boleh berjalan dengan fallback dev auth atau secret pendek.
  • Dampak / implikasi:
    • .env lokal dev memang akan gagal preflight production; validasi ini ditujukan untuk environment candidate.
  • Status: Active

D-075 — Rate Limiting dan Request Security Polish

  • Tanggal: 2026-05-29
  • Keputusan:
    • Ditambahkan middleware rate limit in-memory dengan header RateLimit-* dan error RATE_LIMITED.
    • Rate limit dipasang untuk admin/merchant login, admin write routes, device routes, dan integration callback routes.
    • App menambahkan JSON_BODY_LIMIT, optional TRUST_PROXY, referrer policy, dan HSTS saat NODE_ENV=production.
  • Alasan:
    • Endpoint login, device, admin write, dan webhook/callback perlu guard dasar sebelum pilot publik.
  • Dampak / implikasi:
    • Default local dibuat cukup longgar agar smoke/load lokal tetap berjalan.
    • Production wajib RATE_LIMIT_ENABLED=true dan tuning limit sesuai trafik pilot.
  • Status: Active

D-076 — Placeholder Navigation Cleanup

  • Tanggal: 2026-05-29
  • Keputusan:
    • Placeholder href="#" dibersihkan dari halaman operasional utama: reconciliation, settlement batch, merchant settlement history, dan device technical detail.
    • Link diarahkan ke halaman UI nyata seperti dashboard, settlement, reconciliation, device detail, transaction history, hub, atau login.
  • Alasan:
    • Placeholder nav mengganggu manual UI QA dan bisa membingungkan operator pilot.
  • Dampak / implikasi:
    • npm run ui:qa sekarang hijau tanpa warning placeholder.
    • Beberapa target masih berupa halaman representatif sampai navigasi produk final dirapikan.
  • Status: Active

D-077 — Login Audit dan Bootstrap Password Policy

  • Tanggal: 2026-05-29
  • Keputusan:
    • Admin login success/failure dicatat ke audit log dengan action admin.login.success dan admin.login.failed.
    • Merchant login success/failure dicatat ke audit log dengan action merchant.login.success dan merchant.login.failed.
    • Script admin:create-user dan merchant:create-user memperketat password policy: minimal 14 karakter, lowercase, uppercase, angka, simbol, dan tanpa kata default/produk obvious.
  • Alasan:
    • Pilot production membutuhkan visibility login dan guard sederhana sebelum user bootstrap.
  • Dampak / implikasi:
    • Audit log dapat dipakai untuk review brute force/credential misuse.
    • Password bootstrap lama yang terlalu sederhana akan ditolak.
  • Status: Active

D-078 — Operational Runbook dan Pilot Checklist

  • Tanggal: 2026-05-29
  • Keputusan:
    • Ditambahkan OPERATIONAL_RUNBOOK.md untuk SOP pre-deploy, deploy, smoke, backup, restore, rollback, dan incident response.
    • Ditambahkan PILOT_EXECUTION_CHECKLIST.md untuk go/no-go pilot.
  • Alasan:
    • Artefak operasional perlu eksplisit agar handover tidak bergantung pada chat atau ingatan developer.
  • Dampak / implikasi:
    • Operator punya satu dokumen SOP dan satu checklist eksekusi pilot yang bisa dicentang.
  • Status: Active

D-079 — Export Storage Deployment Readiness

  • Tanggal: 2026-05-29
  • Keputusan:
    • Ditambahkan EXPORT_STORAGE_READINESS.md.
    • Production preflight mewajibkan EXPORT_STORAGE_DIR absolute path.
    • Dokumen menjelaskan single-node pilot, multi-node shared storage, dan batasan sebelum object storage adapter tersedia.
  • Alasan:
    • Export sudah file-based, sehingga deployment multi-node perlu aturan storage yang jelas.
  • Dampak / implikasi:
    • Untuk multi-node, export directory harus shared atau download bisa gagal di node berbeda.
    • S3/object storage tetap menjadi peningkatan future jika skala membutuhkan.
  • Status: Active

D-080 — Admin Audit Log UI Wiring

  • Tanggal: 2026-05-29
  • Keputusan:
    • Halaman admin-system-audit-logs membaca data real dari GET /admin/audit-logs.
    • Backend audit log mendukung filter action_contains untuk preset login events lintas admin/merchant.
    • UI audit menambahkan filter action/entity/date, search client-side, KPI total/login failed/login success, dan drawer JSON payload.
  • Alasan:
    • Login audit sudah dicatat, sehingga operator membutuhkan view cepat untuk investigasi login failure/success.
  • Dampak / implikasi:
    • npm run ui:qa sekarang memasukkan halaman audit logs ke gate.
    • Audit UI bisa dipakai untuk review brute force dan credential misuse selama pilot.
  • Status: Active