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"]
Aturan hitung
Section titled “Aturan hitung”Dihitung di klien dengan computeTotals dan dihitung ulang di server:
subtotal = Σ harga × qtyservice = round((subtotal − diskon) × serviceBp / 10000)pajak = round((subtotal − diskon + service) × taxBp / 10000)atau, bila inklusif,round(gross × taxBp / (10000 + taxBp))dan tidak menambah totaltotal
Pengaturan pajak dan service berasal dari GET /v1/outlet/settings (di-cache; diperbarui tiap 2 menit) dan diubah di backoffice.
Validasi server
Section titled “Validasi server”| 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.