Skip to main content

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-reviewsave-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-revisionrevision_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 pesananPOST /merchant/sales-order (RBAC: owner only, financial action) + accept-terms (persetujuan device fee).
  2. Alamat & ongkirPOST /merchant/shipping-address, GET /merchant/shipping-rate, GET /merchant/shipping-preview (rate via integrasi Komerce).
  3. Bayar & upload buktiPOST /merchant/sales-order/payment-proof → order pending → payment_review; tim internal dapat notifikasi Slack.
  4. Verifikasi pembayaran adminpayment_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 (_printLabelShippingLabelPdf) — 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/deliveredreturned, 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 pairingPOST /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 baikreturnUnits(..., "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_transitdelivered 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