Lewati ke konten

Pembayaran (Retail)

Komponen bersama PayModal dan ReceiptModal dari @smash-pos/core.

flowchart TD
  A["Tekan Bayar"] --> B["PayModal terbuka"]
  B --> C["Ringkasan: subtotal, diskon, service, pajak, total"]
  C --> D{"Diskon?"}
  D -- "ya" --> E["Isi nominal + alasan (min 3 huruf)"]
  D -- "tidak" --> F
  E --> F{"Metode bayar"}
  F -- "Tunai" --> G["Uang diterima<br/>tombol cepat, kembalian"]
  F -- "QRIS / Transfer / Kartu" --> H["Referensi opsional<br/>konfirmasi sudah diterima"]
  G --> I["Bayar"]
  H --> I
  I --> J["checkout(): outbox + kirim"]
  J --> K["ReceiptModal<br/>Bagikan / WhatsApp"]

Dihitung di klien dengan computeTotals dan dihitung ulang di server:

  1. subtotal = Σ harga × qty
  2. service = round((subtotal − diskon) × serviceBp / 10000)
  3. pajak = round((subtotal − diskon + service) × taxBp / 10000) atau, bila inklusif, round(gross × taxBp / (10000 + taxBp)) dan tidak menambah total
  4. total

Pengaturan pajak dan service berasal dari GET /v1/outlet/settings (di-cache; diperbarui tiap 2 menit) dan diubah di backoffice.

Kondisi Hasil
totalMinor klien ≠ total server rejected TOTAL_MISMATCH
Jumlah pembayaran ≠ total rejected PAYMENT_MISMATCH
Tunai dengan uang diterima < jumlah rejected PAYMENT_INVALID
Non-tunai membawa receivedMinor rejected PAYMENT_INVALID
Diskon tanpa alasan rejected DISCOUNT_REASON_REQUIRED
Diskon > subtotal rejected DISCOUNT_EXCEEDS_SUBTOTAL

ReceiptModal menampilkan teks monospace 32 kolom dari buildReceiptText. Tombol Bagikan memakai Share API (web: navigator.share atau salin), WhatsApp membuka wa.me. Cetak printer thermal belum ada; buildReceiptText sudah memakai lebar 32 kolom agar siap dihubungkan ke printer ESC/POS.

packages/pos-core/src/components/PayModal.tsx, ReceiptModal.tsx, receipt.ts, totals.ts.