Flow Lengkap Kesles Merchant
Dokumentasi tahapan end-to-end: dari registrasi user sampai perangkat online dan transaksi QRIS Plus, plus flow procurement (pembelian ke vendor), pergerakan stok, dan lifecycle serial number.
Disusun: 2026-06-13. Semua referensi route/handler/status diverifikasi langsung dari kode.
Bagian 1 — Flow Utama (User → Merchant → QRIS Plus Aktif)
Tahap 1 — Registrasi User (mobile_user)
- Onboarding → input nomor HP → OTP SMS via Firebase.
- Verifikasi OTP → buat akun + setup PIN.
- Verifikasi email (Gmail) opsional/lanjutan.
- Login segar (
is_fresh_login=true) memicu notifikasi status langsung setelah OTP verify.
Tahap 2 — Pendaftaran Merchant (KYC, 7 step)
Mulai dari halaman intro aktivasi (merchant_activation_intro_page), lalu:
| Step | Halaman | Isi |
|---|---|---|
| 1 | merchant_business_entity_page | Bentuk Usaha (perorangan/badan usaha) |
| 2 | merchant_registration_page | Data Usaha (profil usaha, kategori) |
| 3 | merchant_owner_identity_page | Verifikasi Identitas — foto KTP + foto wajah (OCR confidence + hash SHA256 anti-fraud, via POST /merchant/registration/photo) |
| 4 | merchant_business_photo_page | Foto Tempat Usaha |
| 5 | merchant_identity_details_page | Identitas Pemilik (detail NIK, dsb) |
| 6 | merchant_location_page | Lokasi (provinsi/kota/alamat) |
| 7 | merchant_bank_account_page | Rekening Bank |
Lalu page Ringkasan (merchant_activation_summary_page) → submit final ke POST /merchant/registration (profil usaha, data bank, referral) → merchant_activation_complete_page.
- Setiap step tersimpan sebagai draft (
POST /merchant/registration/draft), bisa dilanjut kapan saja. - Data masuk
merchant.merchant_registration_requests, status:draft → pending → pending_review. - User dapat FCM
merchant_status_pending_review(via firebase_service:8093).
Tahap 3 — Review Admin & Verifikasi Bank
A. Review internal (dashboard, aksi start-review → save-review)
- Ceklist 6 kategori: Identity 15%, Contact 10%, Business 50%, Bank 10%, Legal 15%, Referral 0%.
- Panel KYC Audit: OCR confidence, parsing NIK (tanggal lahir/gender), sinyal fraud (foto duplikat, edit user).
- Hasil review internal:
- Lengkap → lanjut kirim ke bank (B).
- Data kurang lengkap (sebelum ke bank) → aksi
request-revision→ statusrevision_required(revision_phase='pre_bank'), FCMmerchant_status_revision_required. User perbaiki data lalu submit ulang (eventrevision_submitted) → status kembali kepending_review. Cap maksimal 3 round revisi — lewat itu admin wajib pilih Approve atau Tolak Final. - Tolak final → status
rejected, FCMmerchant_status_rejected; row rejected bisa di-reuse user untuk daftar ulang dari draft (ada hitungan max attempts).
B. Send to Bank (aksi submit-acquirer)
- Admin kirim dokumen merchant ke bank acquirer melalui email (bank tujuan dari master
ref_payment_service_provider), lalu catat di dashboard:acquirer_bank_code+submission_ref+ notes → statuspending_acquirer, FCMmerchant_status_pending_acquirer. (Shortcut kirim email langsung dari modal review: planned, belum dibangun.) - Bank melakukan verifikasi administrasi dan faktual.
- Hasil dari bank dicatat via aksi
acquirer-response:- Disetujui — bank kirim email berisi NMID, MID, dan TID → admin input NMID/MID/TID di dashboard (wajib untuk
response_status='approved', format divalidasi core-api) → statusacquirer_approved. - Dikembalikan untuk perbaikan (setelah dikirim ke bank) — aksi
request-revision→revision_required(revision_phase='post_submit'). User perbaiki & submit ulang → status kembali kepending_acquirer(NMID/MID/TID yang ada tidak diubah), admin kirim ulang ke bank. Cap 3 round berlaku sama. - Ditolak bank (belum final) —
response_status='rejected'wajib disertairejection_reason; jikaIsFinal=falsestatus tetappending_acquireragar bisa diperbaiki & dikirim ulang; jika final →rejected(alasan disimpan direjection_reason_post_acquireruntuk audit regulator).
- Disetujui — bank kirim email berisi NMID, MID, dan TID → admin input NMID/MID/TID di dashboard (wajib untuk
C. Approve final
FinalApprove() → buat row merchant.merchants (status active), relasi owner di merchant_users, FCM merchant_status_approved.
State machine registrasi: draft → pending → pending_review → pending_acquirer → acquirer_approved → approved (+ rejected, cancelled, revision_required dengan phase pre_bank/post_submit/post_bank_approval).
Tahap 4 — Pembelian Perangkat QRIS Plus (oleh merchant)
- Buat pesanan —
POST /merchant/sales-order(RBAC: owner only, financial action) + accept-terms (persetujuan device fee). - Alamat & ongkir —
POST /merchant/shipping-address,GET /merchant/shipping-rate,GET /merchant/shipping-preview(rate via integrasi Komerce). - Bayar & upload bukti —
POST /merchant/sales-order/payment-proof→ orderpending → payment_review; tim internal dapat notifikasi Slack. - Verifikasi pembayaran admin →
payment_received.
Tahap 5 — Pengemasan & Pengiriman (sisi Kesles Merchant)
Via panel Shipping di dashboard (aksi: View | Pack | Dispatch | Mark Delivered | Mark Returned | Print Label), backend /api/dashboard/sales/shipping-orders:
- Admin klik "Ready to Ship" (
createShippingOrderHandler) — gate: sales_order haruspayment_received. Buatshipping_orders(statusnot_processed), alokasi unit terminal (allocated), sales_order →processing, FCMorder_ready_to_shipke merchant. - Packing barang (
{id}/mark-packed) — admin input serial number tiap unit → shippingpacked. Stok belum dipotong. - Print Resi / Cetak Label Pengiriman (
_printLabel→ShippingLabelPdf) — PDF label A6, optimal untuk label printer 100mm. Isi: nomor shipping + tracking block, logo kurir (assets/logo_courier/<CODE>.svg), alamat pengirim & penerima, daftar item ([SKU] qty× model). Tombol aktif setelahpackedatau tracking number terisi. - Dispatch / Pickup Courier (
{id}/dispatch) — gate: haruspacked. Input kurir + nomor resi → shippingin_transit, stok dipotong + unit di-assign via inventory_service, auditshipping_dispatched, notifikasi dual FCM + WhatsAppmerchant_order_shipped. - Retur (jika ada) —
{id}/mark-returned: dariin_transit/delivered→returned, sales_order revert kepayment_received(detail kondisi unit: lihat Bagian 3).
State machine shipping: not_processed → packed → in_transit → delivered (+ returned).
State machine sales order: pending → accepted → payment_review → payment_received → processing → ready_to_ship → in_transit → delivered → completed (+ cancelled, returned).
Tahap 6 — Penerimaan Barang dan Aktivasi oleh Merchant
- Terima barang + foto barang — merchant konfirmasi via
POST /merchant/payment-terminal/confirm-receiptdengan upload foto barang (merchant_received_photo_url+merchant_received_atdi shipping_orders; gate: shipping harusin_transit) → shippingdelivered, sales_order →completed, flag onboarding ter-set. - Scan QR perangkat untuk pairing —
POST /merchant/terminals/pair; QR berisiterminal_code/serial_number; terminaldelivered/assigned → active, hasil tampil di dialog pairing di app. - Perangkat online — push token FCM terdaftar (
POST /auth/push-tokens→ firebase_service:8093), device trust (pending_verification → trustedvia OTP challenge), heartbeat updatelast_seen_at(state: online/offline/maintenance/suspended). - QRIS Plus aktif — NMID + TID dari Tahap 3 tersimpan di
merchant.qris_config(+qr_contentpayload EMVCo dari payment gateway, di-input admin); TID per terminal diterminal_unit_extensions.tid. Saatis_active=true, opsi QRIS muncul diGET /merchant/payment-destinations. - Transaksi — QR ditampilkan → customer bayar → callback/webhook acquirer → backend update status transaksi → FCM ke semua push token staff merchant via
POST /internal/fcm/send→ notifikasi pembayaran masuk di perangkat.
Ringkasan jalur utama
OTP → KYC 7 step + Ringkasan → review internal (revisi pre_bank max 3x / reject)
→ email ke bank → verifikasi bank (revisi post_submit / reject)
→ bank kirim NMID+MID+TID via email → admin input → approve final
→ beli perangkat → bayar → packing + print resi → dispatch
→ merchant foto barang & konfirmasi → scan QR pairing → online
→ transaksi QRIS + notifikasi FCM
Bagian 2 — Flow Procurement (Kesles Merchant → Vendor)
Tahap A — Purchase Order (PO)
- Admin purchasing buat PO ke vendor (produk, qty, harga, currency + exchange rate).
- State machine PO:
draft → approved → sent → partial_received / received → closed(+cancelled, bisa reopen kedraft). - GR hanya bisa dibuat dari PO
sent/partial_received— POreceived/closedditolak (gr_po_fully_received).
Tahap B — Goods Receipt (GR) — scan serial number oleh gudang
- Buat GR dari PO (
CreateGoodsReceipt) — nomor dari sequencepurchasing.goods_receipt_seq, statusdraft, terikat warehouse. Satu PO hanya boleh punya satu GR draft aktif. - Input serial number progresif (Ship-9.a) — gudang scan barcode tiap unit via
POST .../goods-receipts/{id}/items/{itemId}/serials:- Source tercatat per input:
scan|type|paste|api(audit bisa bedakan scan vs ketik manual). - RBAC: hanya role
purchasingReceiveRoles. - Duplikat ditolak (
serial_conflict, dicek juga terhadap GRpostedsebelumnya). - Salah scan bisa dihapus (DELETE per serial) selama belum di-lock.
- Progress per line item:
serial_numbers[],last_serial_appended_at; dual-write ke inventory_service.
- Source tercatat per input:
- Post GR (
PostGoodsReceipt) — hanya daridraft:- Serial di-lock (
serials_locked_at). - Buat
product_inventory_itemsper serial number → unit masuk stok (statusstock) dengan unit cost dari PO. - Status PO:
partial_received(sebagian) ataureceived(lengkap).
- Serial di-lock (
- GR
draftbisa di-cancel;postedtidak.
Tahap C — Purchase Invoice
- Buat invoice dari PO/GR — nomor, tanggal, due date, currency + exchange rate; upload dokumen invoice vendor.
- State machine:
draft → awaiting_approval → approved(+void). - Allocate Landed Cost — invoice biaya (ongkir/impor) dialokasikan ke unit inventory → mempengaruhi WAC (weighted average cost).
- Aging PO/invoice terpantau via report PO Aging.
Tahap D — Pay (Pembayaran ke Vendor)
- Buat Vendor Payment (
CreateVendorPayment) — nomor payment, vendor, tanggal, payment method, bank reference, currency + exchange rate, amount, bukti transfer (proof_file_url), notes → statuspending. - Payment application — satu payment bisa di-apply ke beberapa invoice sekaligus (
vendor_payment_applications,amount_appliedper invoice) — mendukung pembayaran parsial maupun gabungan. - Post Payment (
PostVendorPayment) —pending → posted,amount_paidtiap invoice ter-update:amount_paid >= total_amount→ invoicepaidamount_paid > 0belum lunas → invoicepartially_paid
- Void hanya dari
pending. - Bukti transfer/bank reference bisa di-attach ulang kapan saja, termasuk setelah
posted.
State machine payment: pending → posted (+ void).
Status invoice lengkap: draft → awaiting_approval → approved → partially_paid → paid (+ void).
Tahap E — Lanjut ke Sales Order
Unit hasil GR (status stock, ber-serial) dipakai di flow penjualan ke merchant (Bagian 1, Tahap 4–6): sales order → bayar → Ready to Ship (alokasi stock → allocated) → packing (serial dicocokkan) → dispatch (stok keluar, assigned ke merchant) → delivered → pairing → active.
Ringkasan procurement
PO (draft→approved→sent) → barang datang → GR draft → gudang scan serial per unit
→ POST GR (stok masuk per serial, PO received)
→ Purchase Invoice (draft→awaiting_approval→approved, landed cost→WAC)
→ Pay (vendor payment, apply ke invoice, post → paid)
→ stok siap dijual via Sales Order
Bagian 3 — Pergerakan Stok per Tahapan
Sisi procurement (barang masuk)
| Tahapan | Efek stok |
|---|---|
| Purchase Order | Tidak berubah — dokumen komitmen beli ("on order" di PO Aging) |
| GR draft + scan serial | Belum berubah — serial hanya progress di line item, bisa dihapus |
| Post GR | BERTAMBAH — satu-satunya titik barang masuk; product_inventory_items per serial, status stock |
| Purchase Invoice | Tidak berubah jumlah; Allocate Landed Cost mengubah nilai stok (WAC), bukan qty |
| Pay (Vendor Payment) | Tidak berubah — murni finansial |
Sisi penjualan (barang keluar)
| Tahapan | Efek stok |
|---|---|
| Sales Order + pembayaran merchant | Tidak berubah |
| Ready to Ship | Dicadangkan (stock → allocated), belum berkurang |
| Mark Packed (input serial) | Belum berkurang — validasi: serial harus stock & belum ter-link ke merchant |
| Dispatch (Pickup Courier) | BERKURANG — satu-satunya titik barang keluar; unit di-assign ke merchant via inventory_service |
| Delivered + pairing | Tidak berubah — unit sudah bukan stok gudang, hanya ganti status operasional |
Retur (mark-returned)
Picker kondisi per serial:
- Unit kondisi baik →
returnUnits(..., "stock")→ kembali ke pool stok, bisa dijual ulang. - Unit rusak → status
damaged/returned→ keluar dari pool permanen. - Sales order direvert ke
payment_receivedagar bisa diproses ulang.
Ringkasan satu baris
PO (0) → GR draft/scan (0) → POST GR (+1 per serial) → Invoice (0, nilai WAC saja)
→ Pay (0) → SO + bayar (0) → Ready to Ship (0, reserve) → Packed (0)
→ DISPATCH (−1 per unit) → Delivered/Pairing (0) → Retur baik (+1) / rusak (0)
Prinsip desain: stok hanya bergerak saat ada peristiwa fisik terverifikasi (barang diterima ber-serial saat Post GR; barang diserahkan ke kurir saat Dispatch). Dokumen, pembayaran, dan alokasi tidak pernah mengubah jumlah.
Bagian 4 — Lifecycle Serial Number
- Lahir di vendor — serial dicetak di label/barcode tiap unit; belum ada di sistem.
- Masuk sistem: scan saat GR — gudang scan barcode per unit; tersimpan progresif di
serial_numbers[]line item GR; sourcescan/type/paste/api; duplikat ditolak lintas GR; bisa dihapus selama draft. - Jadi identitas stok: Post GR — serial di-lock; satu row
product_inventory_itemsper serial (statusstock, warehouse, unit cost). Sejak ini serial = identitas unik unit seumur hidup. - Dipilih untuk pesanan: alokasi & packing — Ready to Ship (
allocated) → Mark Packed: admin scan/input serial lagi untuk mencocokkan unit fisik dengan shipping order (validasistock& belum ter-link); serial ikut tercetak di label pengiriman. - Terikat ke merchant: Dispatch — unit di-assign ke merchant via inventory_service; stok berkurang;
in_transit→deliveredsetelah konfirmasi + foto barang. - Aktivasi: scan QR pairing — QR perangkat berisi
terminal_code/serial_number; backend lookup unit by serial milik merchant (listUnitsBySerial), validasidelivered/assigned→ terminalactive; response mengembalikan serial yang ter-pair. - Operasional — serial jadi kunci identitas terminal: dashboard Devices/Terminal Stock, device monitor di app, TID QRIS per unit (
terminal_unit_extensions.tid), heartbeat online/offline. - Akhir hidup / retur — kondisi baik → kembali ke pool
stock(riwayat tetap ada); rusak →damaged/returned, keluar permanen.
Barcode vendor → scan GR (draft) → Post GR: product_inventory_items (stock)
→ allocated → scan ulang saat packing → tercetak di label resi
→ dispatch: assigned ke merchant → delivered
→ scan QR pairing: terminal active (+TID QRIS)
→ monitoring online ←→ retur: balik ke stock / damaged
Serial discan manusia di 3 titik — gudang saat GR (masuk), admin saat packing (keluar), merchant saat pairing (aktivasi) — masing-masing memverifikasi perpindahan fisik barang antar pihak.
Bagian 5 — Role & SOP per Tahapan
Daftar role
Dashboard internal (sumber: services/dashboard_api/internal/app/rbac.go + gate per handler): superadmin, admin, finance, operations, support, partner_admin, viewer.
Sisi merchant (mobile_user): owner (pemilik, satu-satunya yang boleh aksi finansial & onboarding), staff/kasir (operasional harian, dikelola owner).
Matriks role per tahapan
| Tahapan | Pelaku | Role yang berwenang (dari kode) |
|---|---|---|
| 1. Registrasi user | Calon merchant | Publik (belum ada role) |
| 2. Pendaftaran KYC | Calon merchant | User terverifikasi OTP |
| 3A. Review internal | Reviewer dashboard | merchantWriteRoles: superadmin, admin, operations |
| 3B. Send to Bank + input NMID/MID/TID | Admin acquirer relation | superadmin, admin, operations |
| 3C. Approve final | Reviewer senior | superadmin, admin, operations |
| 4. Sales order (buat pesanan, accept terms, bayar) | Merchant | owner only (Phase B.3 RBAC — financial action) |
| 4. Verifikasi pembayaran | Tim sales/ops Kesles | salesWriteRoles: superadmin, admin, operations |
| 5. Ready to Ship, Pack, Print Label, Dispatch, Returned | Tim gudang/ops Kesles | salesWriteRoles: superadmin, admin, operations |
| 6. Confirm-receipt (foto barang) | Merchant | owner only (menentukan flag onboarding) |
| 6. Pairing & operasional perangkat | Merchant | owner/staff dengan akses app |
| 6. QRIS config (NMID/TID/qr_content) | Admin Kesles | write: superadmin + admin (paymentSettingsWriteRoles) |
| A. Purchase Order | Tim purchasing/finance | purchasingWriteRoles: superadmin, admin, finance |
| B. Goods Receipt + scan serial | Bagian gudang | purchasingReceiveRoles: superadmin, admin, finance, operations |
| C. Purchase Invoice | Finance | purchasingWriteRoles: superadmin, admin, finance |
| D. Pay (vendor payment) | Finance | purchasingWriteRoles: superadmin, admin, finance |
| Master vendor | Finance/admin | vendorWriteRoles: superadmin, admin, finance |
| Report & export | Manajemen | read: superadmin, admin, finance; export CSV: superadmin, admin; planning/WAC drill-down: superadmin only |
SOP per tahapan
SOP Tahap 3 — Review Merchant (role: admin/operations)
- Buka antrean
pending_review, klikstart-reviewagar status terkunci ke reviewer. - Periksa ceklist 6 kategori sambil bandingkan dengan foto KTP (panel sticky). Perhatikan panel KYC Audit: OCR confidence rendah, NIK tidak konsisten dengan tanggal lahir/gender, sinyal foto duplikat, atau jejak edit user = wajib investigasi.
- Jika data kurang: pilih
request-revision, tulis instruksi perbaikan yang spesifik per field. Ingat cap 3 round — jangan habiskan round untuk catatan yang tidak jelas. - Jika lolos:
submit-acquirer— kirim email dokumen ke bank sesuai masterref_payment_service_provider, catatsubmission_refdi dashboard hari yang sama. - Saat balasan bank tiba: input hasil via
acquirer-response. Jika approved, salin NMID/MID/TID langsung dari email bank (jangan ketik ulang dari ingatan); sistem memvalidasi format. approvefinal hanya setelahacquirer_approved. Pastikan FCM approved terkirim (cek log notifikasi).
SOP Tahap 4–5 — Verifikasi Pembayaran & Pengiriman (role: admin/operations)
- Cek notifikasi Slack order baru; verifikasi bukti transfer terhadap mutasi rekening sebelum set
payment_received. - Klik Ready to Ship hanya setelah pembayaran terverifikasi — aksi ini mengalokasikan unit dari stok.
- Saat packing: scan/input serial unit yang benar-benar masuk kardus. Sistem menolak serial yang bukan status
stockatau sudah ter-link ke merchant lain — jangan di-bypass dengan memilih unit lain tanpa memperbarui fisik. - Print Label setelah
packed; tempel di paket; pastikan logo kurir dan alamat sesuai. - Dispatch saat kurir pickup: isi kurir + nomor resi dengan benar — ini titik stok berkurang dan notifikasi FCM+WA ke merchant.
- Jika paket kembali: mark-returned dan isi kondisi per serial dengan jujur (baik → stok; rusak → damaged). Salah isi = stok hantu.
SOP Tahap 6 — Penerimaan & Aktivasi (pelaku: merchant owner)
- Saat paket tiba, buka app → konfirmasi terima → foto barang dalam kondisi diterima (bukti untuk dispute).
- Scan QR di perangkat untuk pairing. Gagal pairing = cek perangkat memang dialamatkan ke akun ini (serial milik merchant lain akan ditolak).
- Pastikan notifikasi app aktif (push token terdaftar) agar notifikasi transaksi QRIS masuk.
SOP Tahap A–B — PO & Goods Receipt (role: finance/operations gudang)
- PO dibuat finance/admin, approve internal, lalu
sentke vendor. GR tidak bisa dibuat sebelum POsent. - Saat barang datang, gudang (operations) buat GR draft dari PO terkait — satu PO satu GR draft aktif.
- Scan barcode serial tiap unit fisik satu per satu — gunakan scanner (source
scan), hindari ketik manual kecuali barcode rusak (sourcetypetercatat di audit). Jangan paste massal dari file vendor tanpa mencocokkan fisik. - Serial duplikat ditolak sistem — jika terjadi, cek apakah unit pernah diterima di GR lain (indikasi kiriman dobel/salah label).
- Sebelum Post GR: hitung fisik = jumlah serial discan = qty PO line. Selisih → GR partial, sisanya tunggu kiriman berikut.
- Post GR = stok resmi masuk dan serial terkunci. Salah scan setelah post tidak bisa dihapus — pastikan sebelum post.
SOP Tahap C–D — Invoice & Pay (role: finance)
- Cocokkan invoice vendor dengan PO dan GR (qty, harga, currency) sebelum buat Purchase Invoice; upload dokumen asli.
- Submit → approve oleh approver berbeda dari pembuat (empat mata).
- Invoice biaya kirim/impor: jalankan Allocate Landed Cost agar WAC akurat sebelum penjualan unit batch tsb.
- Pembayaran: buat Vendor Payment, apply ke invoice yang dibayar (boleh parsial/gabungan), lampirkan bukti transfer + bank reference.
- Post payment hanya setelah dana benar keluar —
postedtidak bisa di-void.
Referensi Kode Utama
| Area | Lokasi |
|---|---|
| Registrasi & KYC mobile | apps/mobile_user/lib/features/merchant/presentation/pages/ |
| Registrasi backend | merchant_core_api/internal/httpapi/merchant_registration_*.go, internal/merchant/merchant.go |
| Review & acquirer | services/dashboard_api/internal/app/dashboard_merchant_registration.go |
| Revisi (mig 066) | merchant_database/db_kesles_merchant/migrations/v1/066_merchant_registration_revision.sql |
| Sales order & payment proof | merchant_core_api/internal/httpapi/merchant_sales_order_*.go, merchant_payment_proof_handler.go |
| Shipping & label | services/dashboard_api/internal/app/dashboard_shipping_orders.go, apps/merchant_dashboard/.../shipping_panel.dart, shipping_label_pdf.dart |
| Confirm receipt + foto | merchant_core_api/internal/httpapi/merchant_terminal_receipt_handler.go |
| Pairing | merchant_core_api/internal/httpapi/merchant_terminal_pair_handler.go |
| QRIS config | merchant_core_api/internal/merchant/qris_config.go |
| PO / GR / Invoice / Payment | services/inventory_service/internal/inventory/store_purchase_orders.go, store_goods_receipts.go, store_purchase_invoices.go, store_vendor_payments.go |
| GR serial entry dashboard | services/dashboard_api/internal/app/dashboard_goods_receipts.go |