Lewati ke konten utama

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)​

  1. Onboarding → input nomor HP → OTP SMS via Firebase.
  2. Verifikasi OTP → buat akun + setup PIN.
  3. Verifikasi email (Gmail) opsional/lanjutan.
  4. 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:

StepHalamanIsi
1merchant_business_entity_pageBentuk Usaha (perorangan/badan usaha)
2merchant_registration_pageData Usaha (profil usaha, kategori)
3merchant_owner_identity_pageVerifikasi Identitas — foto KTP + foto wajah (OCR confidence + hash SHA256 anti-fraud, via POST /merchant/registration/photo)
4merchant_business_photo_pageFoto Tempat Usaha
5merchant_identity_details_pageIdentitas Pemilik (detail NIK, dsb)
6merchant_location_pageLokasi (provinsi/kota/alamat)
7merchant_bank_account_pageRekening 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)​

  1. Ceklist 6 kategori: Identity 15%, Contact 10%, Business 50%, Bank 10%, Legal 15%, Referral 0%.
  2. Panel KYC Audit: OCR confidence, parsing NIK (tanggal lahir/gender), sinyal fraud (foto duplikat, edit user).
  3. Hasil review internal:
    • Lengkap → lanjut kirim ke bank (B).
    • Data kurang lengkap (sebelum ke bank) → aksi request-revision → status revision_required (revision_phase='pre_bank'), FCM merchant_status_revision_required. User perbaiki data lalu submit ulang (event revision_submitted) → status kembali ke pending_review. Cap maksimal 3 round revisi — lewat itu admin wajib pilih Approve atau Tolak Final.
    • Tolak final → status rejected, FCM merchant_status_rejected; row rejected bisa di-reuse user untuk daftar ulang dari draft (ada hitungan max attempts).

B. Send to Bank (aksi submit-acquirer)​

  1. 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 → status pending_acquirer, FCM merchant_status_pending_acquirer. (Shortcut kirim email langsung dari modal review: planned, belum dibangun.)
  2. Bank melakukan verifikasi administrasi dan faktual.
  3. 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) → status acquirer_approved.
    • Dikembalikan untuk perbaikan (setelah dikirim ke bank) — aksi request-revision → revision_required (revision_phase='post_submit'). User perbaiki & submit ulang → status kembali ke pending_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 disertai rejection_reason; jika IsFinal=false status tetap pending_acquirer agar bisa diperbaiki & dikirim ulang; jika final → rejected (alasan disimpan di rejection_reason_post_acquirer untuk audit regulator).

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)​

  1. Buat pesanan — POST /merchant/sales-order (RBAC: owner only, financial action) + accept-terms (persetujuan device fee).
  2. Alamat & ongkir — POST /merchant/shipping-address, GET /merchant/shipping-rate, GET /merchant/shipping-preview (rate via integrasi Komerce).
  3. Bayar & upload bukti — POST /merchant/sales-order/payment-proof → order pending → payment_review; tim internal dapat notifikasi Slack.
  4. 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:

  1. Admin klik "Ready to Ship" (createShippingOrderHandler) — gate: sales_order harus payment_received. Buat shipping_orders (status not_processed), alokasi unit terminal (allocated), sales_order → processing, FCM order_ready_to_ship ke merchant.
  2. Packing barang ({id}/mark-packed) — admin input serial number tiap unit → shipping packed. Stok belum dipotong.
  3. 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 setelah packed atau tracking number terisi.
  4. Dispatch / Pickup Courier ({id}/dispatch) — gate: harus packed. Input kurir + nomor resi → shipping in_transit, stok dipotong + unit di-assign via inventory_service, audit shipping_dispatched, notifikasi dual FCM + WhatsApp merchant_order_shipped.
  5. Retur (jika ada) — {id}/mark-returned: dari in_transit/delivered → returned, sales_order revert ke payment_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​

  1. Terima barang + foto barang — merchant konfirmasi via POST /merchant/payment-terminal/confirm-receipt dengan upload foto barang (merchant_received_photo_url + merchant_received_at di shipping_orders; gate: shipping harus in_transit) → shipping delivered, sales_order → completed, flag onboarding ter-set.
  2. Scan QR perangkat untuk pairing — POST /merchant/terminals/pair; QR berisi terminal_code/serial_number; terminal delivered/assigned → active, hasil tampil di dialog pairing di app.
  3. Perangkat online — push token FCM terdaftar (POST /auth/push-tokens → firebase_service:8093), device trust (pending_verification → trusted via OTP challenge), heartbeat update last_seen_at (state: online/offline/maintenance/suspended).
  4. QRIS Plus aktif — NMID + TID dari Tahap 3 tersimpan di merchant.qris_config (+ qr_content payload EMVCo dari payment gateway, di-input admin); TID per terminal di terminal_unit_extensions.tid. Saat is_active=true, opsi QRIS muncul di GET /merchant/payment-destinations.
  5. 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)​

  1. Admin purchasing buat PO ke vendor (produk, qty, harga, currency + exchange rate).
  2. State machine PO: draft → approved → sent → partial_received / received → closed (+ cancelled, bisa reopen ke draft).
  3. GR hanya bisa dibuat dari PO sent/partial_received — PO received/closed ditolak (gr_po_fully_received).

Tahap B — Goods Receipt (GR) — scan serial number oleh gudang​

  1. Buat GR dari PO (CreateGoodsReceipt) — nomor dari sequence purchasing.goods_receipt_seq, status draft, terikat warehouse. Satu PO hanya boleh punya satu GR draft aktif.
  2. 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 GR posted sebelumnya).
    • 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.
  3. Post GR (PostGoodsReceipt) — hanya dari draft:
    • Serial di-lock (serials_locked_at).
    • Buat product_inventory_items per serial number → unit masuk stok (status stock) dengan unit cost dari PO.
    • Status PO: partial_received (sebagian) atau received (lengkap).
  4. GR draft bisa di-cancel; posted tidak.

Tahap C — Purchase Invoice​

  1. Buat invoice dari PO/GR — nomor, tanggal, due date, currency + exchange rate; upload dokumen invoice vendor.
  2. State machine: draft → awaiting_approval → approved (+ void).
  3. Allocate Landed Cost — invoice biaya (ongkir/impor) dialokasikan ke unit inventory → mempengaruhi WAC (weighted average cost).
  4. Aging PO/invoice terpantau via report PO Aging.

Tahap D — Pay (Pembayaran ke Vendor)​

  1. Buat Vendor Payment (CreateVendorPayment) — nomor payment, vendor, tanggal, payment method, bank reference, currency + exchange rate, amount, bukti transfer (proof_file_url), notes → status pending.
  2. Payment application — satu payment bisa di-apply ke beberapa invoice sekaligus (vendor_payment_applications, amount_applied per invoice) — mendukung pembayaran parsial maupun gabungan.
  3. Post Payment (PostVendorPayment) — pending → posted, amount_paid tiap invoice ter-update:
    • amount_paid >= total_amount → invoice paid
    • amount_paid > 0 belum lunas → invoice partially_paid
  4. Void hanya dari pending.
  5. 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)​

TahapanEfek stok
Purchase OrderTidak berubah — dokumen komitmen beli ("on order" di PO Aging)
GR draft + scan serialBelum berubah — serial hanya progress di line item, bisa dihapus
Post GRBERTAMBAH — satu-satunya titik barang masuk; product_inventory_items per serial, status stock
Purchase InvoiceTidak berubah jumlah; Allocate Landed Cost mengubah nilai stok (WAC), bukan qty
Pay (Vendor Payment)Tidak berubah — murni finansial

Sisi penjualan (barang keluar)​

TahapanEfek stok
Sales Order + pembayaran merchantTidak berubah
Ready to ShipDicadangkan (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 + pairingTidak 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_received agar 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​

  1. Lahir di vendor — serial dicetak di label/barcode tiap unit; belum ada di sistem.
  2. Masuk sistem: scan saat GR — gudang scan barcode per unit; tersimpan progresif di serial_numbers[] line item GR; source scan/type/paste/api; duplikat ditolak lintas GR; bisa dihapus selama draft.
  3. Jadi identitas stok: Post GR — serial di-lock; satu row product_inventory_items per serial (status stock, warehouse, unit cost). Sejak ini serial = identitas unik unit seumur hidup.
  4. Dipilih untuk pesanan: alokasi & packing — Ready to Ship (allocated) → Mark Packed: admin scan/input serial lagi untuk mencocokkan unit fisik dengan shipping order (validasi stock & belum ter-link); serial ikut tercetak di label pengiriman.
  5. Terikat ke merchant: Dispatch — unit di-assign ke merchant via inventory_service; stok berkurang; in_transit → delivered setelah konfirmasi + foto barang.
  6. Aktivasi: scan QR pairing — QR perangkat berisi terminal_code/serial_number; backend lookup unit by serial milik merchant (listUnitsBySerial), validasi delivered/assigned → terminal active; response mengembalikan serial yang ter-pair.
  7. 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.
  8. 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​

TahapanPelakuRole yang berwenang (dari kode)
1. Registrasi userCalon merchantPublik (belum ada role)
2. Pendaftaran KYCCalon merchantUser terverifikasi OTP
3A. Review internalReviewer dashboardmerchantWriteRoles: superadmin, admin, operations
3B. Send to Bank + input NMID/MID/TIDAdmin acquirer relationsuperadmin, admin, operations
3C. Approve finalReviewer seniorsuperadmin, admin, operations
4. Sales order (buat pesanan, accept terms, bayar)Merchantowner only (Phase B.3 RBAC — financial action)
4. Verifikasi pembayaranTim sales/ops KeslessalesWriteRoles: superadmin, admin, operations
5. Ready to Ship, Pack, Print Label, Dispatch, ReturnedTim gudang/ops KeslessalesWriteRoles: superadmin, admin, operations
6. Confirm-receipt (foto barang)Merchantowner only (menentukan flag onboarding)
6. Pairing & operasional perangkatMerchantowner/staff dengan akses app
6. QRIS config (NMID/TID/qr_content)Admin Kesleswrite: superadmin + admin (paymentSettingsWriteRoles)
A. Purchase OrderTim purchasing/financepurchasingWriteRoles: superadmin, admin, finance
B. Goods Receipt + scan serialBagian gudangpurchasingReceiveRoles: superadmin, admin, finance, operations
C. Purchase InvoiceFinancepurchasingWriteRoles: superadmin, admin, finance
D. Pay (vendor payment)FinancepurchasingWriteRoles: superadmin, admin, finance
Master vendorFinance/adminvendorWriteRoles: superadmin, admin, finance
Report & exportManajemenread: superadmin, admin, finance; export CSV: superadmin, admin; planning/WAC drill-down: superadmin only

SOP per tahapan​

SOP Tahap 3 — Review Merchant (role: admin/operations)​

  1. Buka antrean pending_review, klik start-review agar status terkunci ke reviewer.
  2. 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.
  3. 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.
  4. Jika lolos: submit-acquirer — kirim email dokumen ke bank sesuai master ref_payment_service_provider, catat submission_ref di dashboard hari yang sama.
  5. 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.
  6. approve final hanya setelah acquirer_approved. Pastikan FCM approved terkirim (cek log notifikasi).

SOP Tahap 4–5 — Verifikasi Pembayaran & Pengiriman (role: admin/operations)​

  1. Cek notifikasi Slack order baru; verifikasi bukti transfer terhadap mutasi rekening sebelum set payment_received.
  2. Klik Ready to Ship hanya setelah pembayaran terverifikasi — aksi ini mengalokasikan unit dari stok.
  3. Saat packing: scan/input serial unit yang benar-benar masuk kardus. Sistem menolak serial yang bukan status stock atau sudah ter-link ke merchant lain — jangan di-bypass dengan memilih unit lain tanpa memperbarui fisik.
  4. Print Label setelah packed; tempel di paket; pastikan logo kurir dan alamat sesuai.
  5. Dispatch saat kurir pickup: isi kurir + nomor resi dengan benar — ini titik stok berkurang dan notifikasi FCM+WA ke merchant.
  6. 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)​

  1. Saat paket tiba, buka app → konfirmasi terima → foto barang dalam kondisi diterima (bukti untuk dispute).
  2. Scan QR di perangkat untuk pairing. Gagal pairing = cek perangkat memang dialamatkan ke akun ini (serial milik merchant lain akan ditolak).
  3. Pastikan notifikasi app aktif (push token terdaftar) agar notifikasi transaksi QRIS masuk.

SOP Tahap A–B — PO & Goods Receipt (role: finance/operations gudang)​

  1. PO dibuat finance/admin, approve internal, lalu sent ke vendor. GR tidak bisa dibuat sebelum PO sent.
  2. Saat barang datang, gudang (operations) buat GR draft dari PO terkait — satu PO satu GR draft aktif.
  3. Scan barcode serial tiap unit fisik satu per satu — gunakan scanner (source scan), hindari ketik manual kecuali barcode rusak (source type tercatat di audit). Jangan paste massal dari file vendor tanpa mencocokkan fisik.
  4. Serial duplikat ditolak sistem — jika terjadi, cek apakah unit pernah diterima di GR lain (indikasi kiriman dobel/salah label).
  5. Sebelum Post GR: hitung fisik = jumlah serial discan = qty PO line. Selisih → GR partial, sisanya tunggu kiriman berikut.
  6. 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)​

  1. Cocokkan invoice vendor dengan PO dan GR (qty, harga, currency) sebelum buat Purchase Invoice; upload dokumen asli.
  2. Submit → approve oleh approver berbeda dari pembuat (empat mata).
  3. Invoice biaya kirim/impor: jalankan Allocate Landed Cost agar WAC akurat sebelum penjualan unit batch tsb.
  4. Pembayaran: buat Vendor Payment, apply ke invoice yang dibayar (boleh parsial/gabungan), lampirkan bukti transfer + bank reference.
  5. Post payment hanya setelah dana benar keluar — posted tidak bisa di-void.

Referensi Kode Utama​

AreaLokasi
Registrasi & KYC mobileapps/mobile_user/lib/features/merchant/presentation/pages/
Registrasi backendmerchant_core_api/internal/httpapi/merchant_registration_*.go, internal/merchant/merchant.go
Review & acquirerservices/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 proofmerchant_core_api/internal/httpapi/merchant_sales_order_*.go, merchant_payment_proof_handler.go
Shipping & labelservices/dashboard_api/internal/app/dashboard_shipping_orders.go, apps/merchant_dashboard/.../shipping_panel.dart, shipping_label_pdf.dart
Confirm receipt + fotomerchant_core_api/internal/httpapi/merchant_terminal_receipt_handler.go
Pairingmerchant_core_api/internal/httpapi/merchant_terminal_pair_handler.go
QRIS configmerchant_core_api/internal/merchant/qris_config.go
PO / GR / Invoice / Paymentservices/inventory_service/internal/inventory/store_purchase_orders.go, store_goods_receipts.go, store_purchase_invoices.go, store_vendor_payments.go
GR serial entry dashboardservices/dashboard_api/internal/app/dashboard_goods_receipts.go