62 KiB
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.
- Semua response error mengikuti envelope seragam:
- 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 dengantrace_idjika multi-step.
- Semua request harus membawa
- 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_keyssejak awal Fase 1.
- Menambah kebutuhan service/DB untuk
- 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.
- Spesifikasi detail Step 1 dituangkan di
- 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
401dan tidak boleh melakukan perubahan state apapun.
- Semua callback harus divalidasi signature-nya terlebih dahulu; jika tidak valid, respons harus
- 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_iddan diperlakukan idempotent.
- Callback duplikat harus diidentifikasi deterministik dari
- 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_eventsagar Step 3 dan observability bisa berjalan.
- Setiap perubahan state transaksi harus menghasilkan
- 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.
- Notifikasi pembayaran di Step 3 harus dipicu dari event internal status
- 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_countwajib 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_statusbisanot_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.
- Definisi status device ditetapkan: online (<90s), degraded (signal/battery buruk), stale (<15 menit), offline (>15 menit) berdasarkan
- 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.
- Schema dan model onboarding merchant memakai
- 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_incomesaat transaksi berubah kepaid.
- Fase 1 mencatat audit log untuk aksi admin/webhook penting dan membuat ledger placeholder
- 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_logsmenjadi sumber trace operasional awal untuk entity penting.ledger_entriesFase 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-qruntuk devicecommunication_mode=api. - Dynamic QR API-direct memakai mock QRIS payload lokal sampai integrasi partner QRIS tersedia.
- Fase 2 dimulai dari capability resolver dan endpoint
- 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_directditolak denganDEVICE_CAPABILITY_NOT_SUPPORTED. - Device wajib punya binding aktif ke terminal
qr_mode=dynamic_api; jika tidak, API mengembalikanDEVICE_NOT_BOUND. - Response dynamic QR idempotent memakai
Idempotency-Keyataurequest_id, dan transaksi dibuatawaiting_payment. - Callback paid tetap memakai endpoint webhook yang sama untuk mengubah transaksi menjadi
paiddan memicu notifikasi.
- Device static/MQTT yang tidak memiliki capability
- 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/requestyang mencatat uplink/downlink kemqtt_messages. - Config push memakai
PATCH /admin/devices/{deviceId}/config, disimpan didevice_configs, dipublish ke MQTT outbox, lalu device mengirimPOST /device/config/ack.
- MQTT dynamic QR Fase 2 diimplementasikan sebagai HTTP simulator endpoint
- 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_messagesbisa menjadi outbox/trace awal untuk adapter broker. - Dynamic MQTT request memakai
request_idsebagaicorrelation_iddan idempotency key. - Device config selalu versioned; ACK dicatat terpisah di
device_config_acks.
- Saat broker MQTT dipasang,
- 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-pushtanpa menaikkanconfig_version. - ACK config dari device juga dicatat sebagai uplink trace di
mqtt_messagesdenganmessage_type=config_ack.
- Fase 2 menambahkan status drift config device melalui
- Alasan:
- Operasi perlu membedakan config
applied,pending_ack,failed_ack,stale_ack, dannever_pushedsebelum broker MQTT sungguhan dipasang. - Retry harus mengirim ulang desired config yang sama agar idempotent dan tidak membuat drift versi buatan.
- Operasi perlu membedakan config
- Dampak / implikasi:
- Config yang sudah
appliedtidak boleh di-retry kecuali admin mengirimforce=true. mqtt_messagesmenjadi 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.
- Config yang sudah
- Status: Active
D-023 — Health Summary Device Fase 2
- Tanggal: 2026-05-26
- Keputusan:
- Endpoint admin device menambahkan
health_summaryberisistatus,score,age_seconds, danreasons. derived_statustetap dipertahankan untuk kompatibilitas UI, namun nilainya berasal dari rule health summary yang sama.
- Endpoint admin device menambahkan
- 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.reasonsuntuk 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_summarydan config delivery status. - Retry config push tersedia dari drawer device registry dan halaman detail device.
- UI device registry dan device technical detail menampilkan
- 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/statusdanPOST /admin/devices/{deviceId}/config/retry-push. derived_statustetap dipakai sebagai fallback/compatibility, sementara health score/reasons menjadi konteks tambahan.
- UI memakai endpoint
- Status: Active
D-025 — Dynamic QR Expiry Sweep Fase 2
- Tanggal: 2026-05-26
- Keputusan:
- Dynamic QR
awaiting_paymentyang melewatiexpired_atdapat ditutup oleh sweep internalPOST /admin/transactions/expire-due. - Sweep hanya berlaku untuk transaksi
qr_mode=dynamicdan statusawaiting_payment.
- Dynamic QR
- 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_CHANGEDevent dengan reasondynamic_qr_expired. - Callback paid yang datang setelah transaksi sudah
expiredtetap ditolak oleh state transition guard. - Endpoint admin ini bisa menjadi dasar scheduler/background worker saat fase operasional berikutnya.
- Sweep menulis
- 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_messagessebagai 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_messagessebagai 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, danmerchant_payable. - Platform fee awal memakai konfigurasi
FINANCE_PLATFORM_FEE_BPSagar 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_idkarena ACL Mosquitto memakai patterndevices/%u/....
- Ledger Fase 1 dinaikkan dari placeholder gross-only menjadi finance light:
- 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/rotatemenjadi jalur provisioning credential MQTT awal. - Script
npm run mqtt:provision-devicemenjadi jalur ringan untuk rotate credential dan menghasilkan commandmosquitto_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.
- Callback paid membuat ledger idempotent per
- Status: Active
D-029 — Device Auth Per-Credential dengan Fallback Dev Token
- Tanggal: 2026-05-28
- Keputusan:
- Endpoint
/device/*menerima autentikasi per-device melalui headerX-Device-IddanX-Device-Secret. - Secret device diverifikasi memakai fingerprint credential yang tersimpan di database; plaintext secret tidak disimpan.
- Credential device hanya boleh mengakses resource untuk
device_idyang sama denganX-Device-Id. Authorization: Bearer DEVICE_TOKENtetap diterima sebagai fallback/dev compatibility selama transisi.
- Endpoint
- 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-IddanX-Device-Secret. - API menolak credential valid yang mencoba mengirim payload untuk device lain dengan
403 FORBIDDEN. - Production dapat set
DEVICE_AUTH_ALLOW_LEGACY_TOKEN=falseuntuk mematikan fallbackDEVICE_TOKEN.
- Device production sebaiknya mulai mengirim
- Status: Active
D-030 — Settlement Batch Light dari Merchant Payable
- Tanggal: 2026-05-28
- Keputusan:
- Settlement awal dibuat sebagai batch dari ledger entry
merchant_payableyang 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 menjadipaid. - Settlement light tidak memindahkan dana dan tidak mengelola rekening escrow; batch adalah rekonsiliasi/payable visibility.
- Settlement awal dibuat sebagai batch dari ledger entry
- 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, danPOST /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.
- Ledger entry hanya boleh masuk satu batch lewat unique key
- 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/summarymemuat 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-paidmenerimapaid_referencedanpaid_note, lalu menyimpannya dimetadata_json. - Audit log tetap menyimpan before/after batch agar perubahan status dan bukti payout dapat ditelusuri.
- UI settlement tidak lagi menutup batch dengan satu klik langsung; operator harus mengisi
- 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/summarytidak lagi menghitung total finance dari list batch yang dibatasi pagination.
- Summary settlement/finance untuk admin dashboard dihitung lewat query agregasi langsung ke
- 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_TOKENdan password portal sederhanaMERCHANT_PORTAL_PASSWORD. - UI
merchant-settlement-historymembaca 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, danmarked_paid. - Admin dan merchant detail settlement membaca event history yang sama.
- Settlement batch memiliki event history formal di tabel
- 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.
- Export CSV dari admin maupun merchant menambah event
- Status: Active
D-036 — Settlement Failed dan Cancel Workflow
- Tanggal: 2026-05-28
- Keputusan:
- Batch settlement berstatus
createddapat ditandaifailedataucancelledoleh admin dengan reason wajib. - Endpoint baru:
POST /admin/settlement-batches/{batchId}/mark-faileddanPOST /admin/settlement-batches/{batchId}/cancel. - Reason disimpan ke
failure_reason, detail resolusi disimpan kemetadata_json, dan eventfailed/cancelleddicatat di payout event history.
- Batch settlement berstatus
- 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
failedataucancelleddapat direprocess menjadi batch baru berstatuscreated. - Reprocess memindahkan
settlement_batch_entriesdari 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_iddimetadata_json. - Endpoint baru:
POST /admin/settlement-batches/{batchId}/reprocess.
- Batch settlement berstatus
- 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_idsekaligus 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
reprocessedpada batch lama dancreatedpada 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-managementmembaca 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_referencedanpaid_noteuntuk settlement batch yang sudah berstatuspaid. - Endpoint baru:
PATCH /admin/settlement-batches/{batchId}/reference. - Update reference menyimpan nilai terbaru di
metadata_json, mencatat eventreference_updated, dan membuat audit log before/after. - UI settlement batch management menampilkan form update reference hanya untuk batch
paid.
- Admin dapat memperbarui
- 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_genericlewat query?format=bank_generic. - Format default
standardtetap menjadi payout report detail per transaksi agar kompatibel dengan UI dan tooling yang sudah ada. - Format
bank_genericmenghasilkan 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.
- Endpoint export settlement mendukung format tambahan
- 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_referencedansettlement_account_typemerchant menjadi sumber destination account pada file bank generic.- Event
csv_exportedmenyimpan format dan filename untuk jejak audit. - Format bank spesifik berikutnya dapat ditambahkan sebagai nilai
formatbaru 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.adjustmentsdengan tipecreditataudebit, nominal absolut, signed amount, reason, note, actor, dan timestamp. - Total signed adjustment disimpan di
metadata_json.total_adjustment_amount. - Setiap adjustment mencatat event
adjustment_recordeddan audit log before/after. - UI settlement batch management menyediakan form Record Adjustment dan menampilkan total adjustment.
- Admin dapat mencatat adjustment/dispute ringan pada settlement batch lewat endpoint
- 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.
- Adjustment tidak mengubah
- Status: Active
D-042 — Settlement Adjustment Summary di Finance Dashboard
- Tanggal: 2026-05-28
- Keputusan:
- Summary finance settlement menghitung
adjustment_amountdarimetadata_json.total_adjustment_amountsetiap batch. - Endpoint
/admin/dashboard/summarymenambahkansettlement_adjustment_amountdansettlement_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.
- Summary finance settlement menghitung
- 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_amounttetap nominal paid batch asli;settlement_adjusted_paid_amountadalah paid amount ditambah signed adjustment.
- Status: Active
D-043 — Merchant Settlement Adjustment Visibility
- Tanggal: 2026-05-28
- Keputusan:
- Merchant settlement summary memakai
adjusted_paid_amountuntuk total settled yang ditampilkan di portal merchant. - Portal merchant menampilkan
adjustment_amountsebagai angka terpisah agar koreksi payout tetap transparan. - Drawer settlement merchant menampilkan adjustment/refund dan total disbursed setelah adjustment.
- Merchant settlement summary memakai
- 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_amountdanadjusted_paid_amounttersedia untuk merchant.
- Backend merchant sudah memakai summary yang sama dari
- 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}/adjustmentsmenulis row adjustment formal, lalu mensinkronkan ringkasan kompatibilitas kesettlement_batches.metadata_json. - Detail batch admin dan merchant mengembalikan array
adjustments. - Summary finance menghitung adjustment dari tabel formal, bukan dari metadata.
- Adjustment settlement dipromosikan dari metadata-only menjadi tabel formal
- 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.adjustmentsbukan sumber utama lagi; sumber utama adalahsettlement_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.adjustmentskesettlement_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 NOTHINGsupaya aman dijalankan berulang. - Setelah backfill,
metadata_json.total_adjustment_amountdisinkronkan ulang dari tabel formal.
- Migrasi schema melakukan backfill idempotent dari
- 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.
- Backfill menjaga kompatibilitas metadata, tetapi sumber utama tetap
- Status: Active
D-046 — Settlement Adjustment Report
- Tanggal: 2026-05-28
- Keputusan:
- Admin memiliki endpoint
GET /admin/settlement-adjustmentsuntuk membaca adjustment settlement lintas batch. - Report mendukung filter
merchant_id,adjustment_type,from,to, danlimit. - Payload report menyertakan total count, total credit, total debit, net signed adjustment, serta row dengan konteks batch.
- UI
admin-reconciliation-managementmenampilkan recent adjustment activity dari report formal.
- Admin memiliki endpoint
- 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, danlimit. - CSV menyertakan blok summary, lalu daftar adjustment dengan konteks batch, actor, reason, note, dan nominal signed.
- UI
admin-reconciliation-managementmenyediakan tombol download CSV pada panel Recent Adjustment Activity.
- Admin memiliki endpoint
- 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-managementmenambahkan filter adjustment berdasarkan merchant ID, tipecredit/debit, tanggal awal, dan tanggal akhir. - Filter yang aktif dipakai untuk recent adjustment activity dan download CSV.
- Tombol Clear mengembalikan report ke default terbaru.
- UI
- 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, danreconciliation:read. - Legacy admin token dan login dev tetap default aktif untuk smoke/local, tetapi bisa dimatikan lewat
ADMIN_AUTH_ALLOW_LEGACY_TOKEN=falsedanADMIN_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_SECRETkuat dan menonaktifkan login dev. - Smoke lama tetap kompatibel selama mode dev masih aktif.
- Production wajib mengatur
- Status: Active
D-051 — MQTT Broker Observability dan Uplink Subscriber Opsional
- 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=trueuntuk merekam uplink topicdevices/+/uplink/#kemqtt_messages. - Endpoint
/admin/mqtt/statusmengembalikan 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_adjustmentsmemilikiapproval_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-envuntuk memvalidasi env production kritikal. - Checklist production dituangkan di
DEPLOYMENT_READINESS.md.
- Ditambahkan
- 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-useruntuk create/update user admin production dengan hashscrypt. - Script mendukung role
admin,finance,ops,support,viewer, status inactive, dan password rotation eksplisit. - UI reconciliation menambahkan filter
approval_statusserta tombol approve/reject untuk adjustment pending.
- Ditambahkan script
- 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.
- Production bootstrap admin user bisa dilakukan sebelum
- 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_usersuntuk login production berbasis email/password hashscrypt. - Ditambahkan script
npm run merchant:create-useruntuk create/update merchant portal user. - Login dev lama berbasis
MERCHANT_TOKENdanMERCHANT_PORTAL_PASSWORDtetap default aktif untuk local smoke, tetapi bisa dimatikan viaMERCHANT_AUTH_ALLOW_LEGACY_TOKEN=falsedanMERCHANT_DEV_LOGIN_ENABLED=false.
- Merchant portal mendukung signed session token scoped ke
- 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_migrationsdan advisory lock Postgres untuk mencegah dua proses migrasi bersamaan. - Migration file dibaca dari folder
migrations/dengan format urutNNN_description.sqlatauNNN_description.mts. - Migration awal
001_current_schema_bootstrap.mtsmenjalankan schema bootstrap existing sebagai baseline.
- Ditambahkan command
- 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:migratesebelum start service. - Migration berikutnya sebaiknya dibuat sebagai file terpisah, bukan menambah perubahan besar langsung ke bootstrap.
- Deploy candidate harus menjalankan
- 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=jsontersedia untuk production stdout/stderr log collector; default local tetap readable.- Error middleware menulis structured API/unhandled error log.
- Health endpoint ditambah:
GET /health/deepdanGET /admin/health/deep. - Admin endpoint
GET /admin/observability/summarymerangkum DB, MQTT, notification failure/pending, dan settlement reconciliation mismatch.
- Request logging memakai middleware internal dengan structured fields:
- 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:
morgandihapus karena digantikan request logging internal.- Production sebaiknya set
LOG_FORMAT=jsondan 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.
- Ditambahkan script
- 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 elemendata-admin-permission. - Settlement batch UI dan reconciliation UI menyembunyikan/disable aksi finance sesuai permission seperti
settlement:write,settlement:pay,settlement:adjust, dansettlement:export.
- Admin UI shared helper menyimpan profile admin, membaca
- 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, migration002_export_jobs.sql, dan storeexportJobStore. - Ditambahkan endpoint
POST /admin/exports/settlement-adjustments,GET /admin/exports/:jobId, danGET /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.
- Ditambahkan tabel
- 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
uuiddihapus karena tidak dipakai source dan menjadi sumber audit moderate. npm auditsekarang 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-realdijalankan terhadap brokermqtts://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 statusrunning, lalu menyelesaikan kecompletedataufailed. - Worker memiliki konfigurasi
EXPORT_WORKER_ENABLED,EXPORT_WORKER_INTERVAL_MS,EXPORT_WORKER_BATCH_SIZE,EXPORT_JOB_STALE_RUNNING_MS, danEXPORT_SETTLEMENT_ADJUSTMENT_MAX_ROWS. /admin/observability/summarymenampilkan status worker dan count job per status.
- Export adjustment settlement diproses oleh worker internal
- 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/:jobIdsampai statuscompletedsebelum download. - Hasil export saat ini masih disimpan di DB
result_body; tahap berikutnya adalah storage eksternal/object storage jika ukuran file membesar.
- Client harus polling
- Status: Active
D-065 — Reconciliation UI Async Export Polling
- Tanggal: 2026-05-29
- Keputusan:
- UI
admin-reconciliation-managementmengganti download adjustment CSV sinkron menjadi alur async export job. - Tombol export membuat job via
POST /admin/exports/settlement-adjustments, menampilkan status job, pollingGET /admin/exports/:jobId, lalu download dari endpoint job saatcompleted. - Shared admin API helper menambahkan method create/get/download export job.
- UI
- 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.csvmasih ada untuk kompatibilitas, tetapi UI reconciliation memakai job async. - Tahap berikutnya adalah tampilan riwayat job export jika finance perlu mengambil ulang file lama.
- Export 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, bukanresult_bodydatabase. - Metadata
result_storage_path,result_size_bytes, danexpires_atditambahkan melalui migration003_export_job_storage.sql. - Worker membersihkan hasil export expired berdasarkan
EXPORT_RETENTION_DAYS. - Endpoint
GET /admin/exportsditambahkan untuk history job, dan UI reconciliation menampilkan lima job export adjustment terbaru.
- Hasil export baru disimpan sebagai file di
- 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_DIRharus writable dan masuk strategi backup/retention. - Export lama yang masih punya
result_bodytetap bisa didownload sebagai fallback.
- Directory
- Status: Active
D-067 — MQTT ACL Hardening Helper
- Tanggal: 2026-05-29
- Keputusan:
- Ditambahkan script
npm run mqtt:check-acluntuk print template dan validasi file ACL Mosquitto. - Provisioning device sekarang menampilkan topic scope per device dan command validasi ACL.
- Ditambahkan script
- 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:productionuntuk dump Postgres dan opsional copy Mosquitto passwd/ACL. - Ditambahkan
npm run restore:planyang default hanya mencetak command restore dan hanya execute jika diberi--execute.
- Ditambahkan
- 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:stagingdengan profile lebih besar: callback, heartbeat, dynamic QR, observability read, dan export job.
- Load test mendapat skenario async export adjustment via
- Alasan:
- Baseline lokal belum mencakup worker export dan belum cukup mewakili staging/production-like environment.
- Dampak / implikasi:
- Jalankan profile ini terhadap staging dengan
BASE_URLdan secret/token staging. - Angka hasil staging harus dicatat terpisah dari baseline lokal.
- Jalankan profile ini terhadap staging dengan
- Status: Active
D-070 — MQTT ACL Smoke Test untuk Pilot Device
- Tanggal: 2026-05-29
- Keputusan:
- Ditambahkan
npm run smoke:mqtt-acluntuk validasi credential device terhadap ACL broker. - Smoke memastikan device A bisa akses topic miliknya dan ditolak saat subscribe ke downlink device B.
- Ditambahkan
- 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.
- Ditambahkan
- 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=falsetersedia 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.
- Ditambahkan
- 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.mjsdapat menulis JSON summary keLOAD_REPORT_FILE.npm run load:test:stagingsekarang memakai wrapper yang otomatis menyimpan report kereports/.
- 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.
- Folder
- Status: Active
D-074 — Strict Production Security Preflight
- Tanggal: 2026-05-29
- Keputusan:
npm run deploy:check-envsekarang 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:
.envlokal 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 errorRATE_LIMITED. - Rate limit dipasang untuk admin/merchant login, admin write routes, device routes, dan integration callback routes.
- App menambahkan
JSON_BODY_LIMIT, optionalTRUST_PROXY, referrer policy, dan HSTS saatNODE_ENV=production.
- Ditambahkan middleware rate limit in-memory dengan header
- 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=truedan 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.
- Placeholder
- Alasan:
- Placeholder nav mengganggu manual UI QA dan bisa membingungkan operator pilot.
- Dampak / implikasi:
npm run ui:qasekarang 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.successdanadmin.login.failed. - Merchant login success/failure dicatat ke audit log dengan action
merchant.login.successdanmerchant.login.failed. - Script
admin:create-userdanmerchant:create-usermemperketat password policy: minimal 14 karakter, lowercase, uppercase, angka, simbol, dan tanpa kata default/produk obvious.
- Admin login success/failure dicatat ke audit log dengan action
- 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.mduntuk SOP pre-deploy, deploy, smoke, backup, restore, rollback, dan incident response. - Ditambahkan
PILOT_EXECUTION_CHECKLIST.mduntuk go/no-go pilot.
- Ditambahkan
- 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_DIRabsolute path. - Dokumen menjelaskan single-node pilot, multi-node shared storage, dan batasan sebelum object storage adapter tersedia.
- Ditambahkan
- 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-logsmembaca data real dariGET /admin/audit-logs. - Backend audit log mendukung filter
action_containsuntuk 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.
- Halaman
- Alasan:
- Login audit sudah dicatat, sehingga operator membutuhkan view cepat untuk investigasi login failure/success.
- Dampak / implikasi:
npm run ui:qasekarang memasukkan halaman audit logs ke gate.- Audit UI bisa dipakai untuk review brute force dan credential misuse selama pilot.
- Status: Active