> ## Documentation Index
> Fetch the complete documentation index at: https://docs.quinnsambal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Payments

<style>
  {`
    [class*="max-w-none"][class*="table"] {
      display: block !important;
      overflow-x: auto !important;
      max-width: 100% !important;
      width: 100% !important;
      flex-grow: 0 !important;
    }
    [class*="max-w-none"][class*="table"] > table {
      width: 100% !important;
      max-width: 100% !important;
      table-layout: fixed !important;
    }
    .mermaid {
      max-width: 100% !important;
      overflow-x: auto !important;
    }
    article svg[role="img"] {
      max-width: 100% !important;
      height: auto !important;
    }
    article img, .prose img {
      max-width: 100% !important;
      height: auto !important;
    }
    img[src*="LOGO"], img[src*="logo"] {
      max-width: 120px !important;
      max-height: 40px !important;
      width: auto !important;
      height: auto !important;
      object-fit: contain !important;
    }
    `}
</style>

***

title: "Pembayaran"
description: "Sistem pembayaran lengkap — 6 metode pembayaran, split payment, verifikasi manual/otomatis, refund, dan B2B payment terms."
-----------------------------------------------------------------------------------------------------------------------------------------

# Pembayaran

<img src="https://mintcdn.com/quinnofspicy/e4f_upKhVWcjsUmM/docs/mintlify/screenshots/pos/pos-reports.png?fit=max&auto=format&n=e4f_upKhVWcjsUmM&q=85&s=fc77248a83ed91bcf3435978aa0a622b" alt="Pembayaran" width="1920" height="1080" data-path="docs/mintlify/screenshots/pos/pos-reports.png" />

Sistem pembayaran SNISHOP ERP mendukung berbagai metode pembayaran dengan fitur **split payment**, **verifikasi otomatis/manual**, **B2B payment terms**, dan **refund**. Setiap transaksi dicatat dengan detail lengkap untuk keperluan audit dan rekonsiliasi keuangan.

## Metode Pembayaran

| # | Metode | Verifikasi | Waktu | Fitur |
| - | - | - | - | - |
| 1 | **Cash** | Otomatis | Instant | Quick cash buttons, hitung kembalian |
| 2 | **Transfer Bank** | Manual | 1-30 menit | Upload bukti, multiple bank accounts |
| 3 | **QRIS** | Otomatis | \< 5 detik | Semua bank & e-wallet |
| 4 | **Debit Card** | Otomatis | \< 5 detik | Semua bank |
| 5 | **Credit Card** | Otomatis | \< 5 detik | Visa, Mastercard, JCB |
| 6 | **E-Wallet** | Otomatis | \< 5 detik | GoPay, OVO, Dana, ShopeePay, LinkAja |

### Detail per Metode

#### 1. Cash (Tunai)

| Fitur | Deskripsi |
| - | - |
| **Input Nominal** | Kasir input jumlah uang yang diterima |
| **Quick Buttons** | Rp 50K, Rp 100K, Rp 150K, Rp 200K, Uang Pas |
| **Kembalian** | Kalkulasi otomatis |
| **Cetak Receipt** | Otomatis setelah transaksi |

#### 2. Transfer Bank

| Fitur | Deskripsi |
| - | - |
| **Rekening Tujuan** | Pilih dari daftar rekening perusahaan |
| **Upload Bukti** | Customer upload screenshot/foto bukti transfer |
| **Verifikasi Manual** | Admin cek dan konfirmasi bukti transfer |
| **Multiple Banks** | Support BCA, BNI, BRI, Mandiri, dll |

#### 3. QRIS

| Fitur | Deskripsi |
| - | - |
| **Generate QR** | QR code otomatis muncul di layar |
| **Scan & Pay** | Customer scan dengan aplikasi e-wallet/bank |
| **Auto Verify** | Pembayaran langsung terverifikasi |
| **Universal** | Support semua bank dan e-wallet yang support QRIS |

#### 4-5. Debit & Credit Card

| Fitur | Deskripsi |
| - | - |
| **Swipe/Insert** | Baca kartu via EDC atau NFC |
| **PIN/Signature** | Verifikasi via PIN atau tanda tangan |
| **Auto Verify** | Pembayaran langsung terverifikasi |
| **Network** | Visa, Mastercard, JCB |

#### 6. E-Wallet

| Fitur | Deskripsi |
| - | - |
| **GoPay** | Scan QR atau input nomor telepon |
| **OVO** | Scan QR atau input nomor telepon |
| **Dana** | Scan QR |
| **ShopeePay** | Scan QR |
| **LinkAja** | Scan QR |

## Split Payment

Pelanggan bisa membayar dengan **kombinasi beberapa metode pembayaran**:

### Contoh Split Payment

```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Total Transaksi:    Rp 250.000
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Pembayaran:
  ├─ Cash:          Rp 100.000  ✅
  ├─ QRIS:          Rp  80.000  ✅
  └─ E-Wallet:      Rp  70.000  ✅
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Sisa:               Rp       0  ✅ SELESAI
```

### Flow Split Payment

```mermaid theme={null}
flowchart TD
    A[Total: Rp 250.000] --> B[Pilih Split Payment]
    B --> C[Input pembayaran 1: Cash Rp 100.000]
    C --> D[Sisa: Rp 150.000]
    D --> E[Input pembayaran 2: QRIS Rp 80.000]
    E --> F[Sisa: Rp 70.000]
    F --> G[Input pembayaran 3: E-Wallet Rp 70.000]
    G --> H{Sisa = 0?}
    H -->|Ya| I[Transaksi selesai]
    H -->|Tidak| J[Tambah pembayaran lagi]
    J --> G
```

## Payment Verification

### Verifikasi Otomatis

Berlaku untuk: QRIS, Debit, Credit Card, E-Wallet

```mermaid theme={null}
sequenceDiagram
    participant C as Customer
    participant S as Sistem
    participant G as Gateway
    
    C->>G: Bayar via QRIS/Debit/E-Wallet
    G-->>S: Konfirmasi pembayaran
    S->>S: Auto-verify ✅
    S->>S: Update status order → Paid
    S-->>C: Notifikasi berhasil
```

### Verifikasi Manual

Berlaku untuk: Transfer Bank

```mermaid theme={null}
sequenceDiagram
    participant C as Customer
    participant S as Sistem
    participant A as Admin
    
    C->>S: Upload bukti transfer
    S->>S: Status → Verifying
    S-->>A: Notifikasi bukti baru
    A->>S: Cek bukti transfer
    alt Valid
        A->>S: Verifikasi ✅
        S->>S: Status → Paid
    else Invalid
        A->>S: Tolak ❌
        S->>S: Status → Rejected
        S-->>C: Upload ulang
    end
```

## Payment Terms (B2B)

Untuk customer B2B, tersedia fitur payment term:

| Fitur | Deskripsi |
| - | - |
| **30 Hari Payment Term** | Jatuh tempo otomatis 30 hari dari tanggal BTT |
| **Invoice** | Generate invoice otomatis |
| **Reminder** | Notifikasi sebelum jatuh tempo |
| **Overdue Tracking** | Tracking tagihan yang sudah lewat jatuh tempo |
| **Partial Payment** | Customer bisa bayar sebagian |
| **Outstanding Balance** | Tracking sisa hutang otomatis |

## Receipt

### Isi Receipt

| Komponen | Deskripsi |
| - | - |
| **Logo** | Logo perusahaan |
| **Info Toko** | Nama, alamat, telepon |
| **Order ID** | Nomor invoice/transaksi |
| **Tanggal** | Tanggal & waktu transaksi |
| **Items** | Daftar produk, qty, harga, subtotal |
| **Total** | Total pembayaran |
| **Metode** | Metode pembayaran yang digunakan |
| **Kasir** | Nama kasir yang memproses |
| **Lokasi** | Outlet/cabang |
| **Pesan** | "Terima kasih" custom |

### Format Receipt

| Format | Cara |
| - | - |
| **Thermal Print** | Cetak via Bluetooth thermal printer (58mm/80mm) |
| **PDF** | Download sebagai file PDF |
| **Email** | Kirim ke email customer |
| **WhatsApp** | Kirim via WhatsApp |

## Refund

Proses refund untuk pembayaran yang sudah selesai:

| Langkah | Aksi | Efek |
| - | - | - |
| 1 | Pilih order yang akan di-refund | — |
| 2 | Pilih produk yang di-refund | Partial atau full |
| 3 | Pilih metode refund | Cash atau transfer |
| 4 | Konfirmasi refund | Stok otomatis kembali |
| 5 | Receipt refund di-generate | Jurnal pembalik otomatis |

## Laporan Pembayaran

| Metrik | Deskripsi |
| - | - |
| **Revenue per Metode** | Total pendapatan per metode pembayaran |
| **Transaction Count** | Jumlah transaksi per metode |
| **Average Transaction** | Rata-rata nilai transaksi |
| **Success Rate** | Persentase pembayaran berhasil |
| **Refund Rate** | Persentase transaksi yang di-refund |

| Filter | Opsi |
| - | - |
| **Metode** | Cash, Transfer, QRIS, Debit, Credit, E-Wallet |
| **Channel** | Offline, Marketplace, Website, WhatsApp, dll |
| **Lokasi** | Per outlet/cabang |
| **Kasir** | Per kasir |
| **Periode** | Harian, mingguan, bulanan, custom |

***

## Entity Relationship Diagram (ERD)

Diagram berikut menggambarkan relasi antar-entity yang terlibat dalam modul pembayaran SNISHOP ERP. Setiap transaksi POS (`CompanyPOSTransaction`) dapat memiliki banyak detail pembayaran (split payment), dan setiap pembayaran yang berhasil akan tercatat sebagai `FinancialRecord` serta mempengaruhi saldo pada `Account`.

```mermaid theme={null}
erDiagram
    CompanyPOSTransaction ||--o{ PaymentDetail : "memiliki (split payment)"
    CompanyPOSTransaction }o--o| Account : "pembayaran masuk ke"
    CompanyPOSTransaction }o--o| Customer : "dibeli oleh"
    CompanyPOSTransaction }o--|| User : "diproses oleh (kasir)"
    CompanyPOSTransaction ||--o| FinancialRecord : "menciptakan jurnal"
    CompanyPOSTransaction }o--|| CompanyPOSSettings : "menggunakan konfigurasi"
    Account ||--o{ FinancialRecord : "sumber/tujuan dana"
    Account ||--o{ BankAccount : "representasi rekening"
    FinancialRecord }o--|| TransactionCategory : "dikategorikan"
    PaymentTransaction }o--|| User : "pembayaran online"
    BankAccount }o--|| CompanyPOSSettings : "rekening tujuan transfer"

    CompanyPOSTransaction {
        string company_id FK
        string location_id FK
        string transaction_number UK
        string invoice_number UK
        datetime transaction_date
        array items "detail produk"
        number subtotal
        number discount_amount
        number tax_amount
        number total
        enum payment_method "14 nilai enum"
        array payments "split payment detail"
        enum payment_status "7 status"
        number paid_amount
        number remaining_amount
        number change_amount
        string source "10 channel"
        string sales_channel "11 channel"
    }

    PaymentDetail {
        string method "metode per-tender"
        number amount "nominal per-tender"
        number change_amount "kembalian per-tender"
        string account_id FK
        string reference "ref bukti/QR"
        string verified_by
        datetime verified_at
    }

    Account {
        string user_id FK
        string company_id FK
        string name "Kas / BCA / GoPay"
        enum type "cash|bank|e-wallet|other"
        string account_number
        string bank_name
        number current_balance "server-authoritative"
        boolean is_default_pos
        enum mode "personal|business"
    }

    FinancialRecord {
        string user_id FK
        string company_id FK
        string account_id FK
        enum type "income|expense|transfer"
        number amount
        string reference_id "ke POS txn"
        enum reference_type "pos_transaction|invoice_payment|..."
        enum source "pos|manual|manufacturing|..."
        number cogs_amount "HPP dibekukan"
        number tax_amount "PPN keluaran"
        string idempotency_key "anti-double"
    }

    BankAccount {
        string user_id FK
        string company_id FK
        enum account_type "bank|ewallet"
        string provider "BCA / GoPay / dll"
        string account_name
        string account_number
        boolean is_primary
        number balance
    }

    CompanyPOSSettings {
        string company_id FK
        object receipt_settings "logo, alamat, NPWP"
        string receipt_width "58mm|80mm"
        boolean print_auto_after_transaction
        string font_size "small|medium|large"
    }
```

***

## Entity Schema Tables

### CompanyPOSTransaction — Field Pembayaran

Tabel berikut merinci seluruh field pada entity `CompanyPOSTransaction` yang berkaitan langsung dengan pembayaran. Data ini bersumber dari schema JSONC entity.

| Field | Tipe | Enum / Default | Deskripsi |
| - | - | - | - |
| `payment_method` | `string` (enum) | — | Metode pembayaran utama. Lihat tabel enum di bawah. |
| `payment_amount` | `number` | — | Jumlah uang yang dibayarkan (total semua tender) |
| `payments` | `array<object>` | `null` | Detail split payment (multi-tender). Bila kosong, `payment_method` scalar menjadi sumber kebenaran. |
| `payments[].method` | `string` | — | Metode pembayaran per-tender (mis. `cash`, `qris`, `ewallet`) |
| `payments[].amount` | `number` | — | Nominal yang dibayar per-tender |
| `payments[].change_amount` | `number` | `0` | Kembalian untuk tender ini (biasanya hanya pada cash) |
| `payments[].account_id` | `string` | — | FK ke `Account` — rekening/kantong tujuan dana |
| `payments[].reference` | `string` | — | No. referensi bukti transfer / QRIS / approval code |
| `payments[].verified_by` | `string` | — | Nama user yang memverifikasi tender ini |
| `payments[].verified_at` | `datetime` | — | Waktu verifikasi tender ini |
| `payment_status` | `string` (enum) | `"pending"` | Status pembayaran keseluruhan (7 nilai) |
| `paid_amount` | `number` | `0` | Nominal aktual yang telah diverifikasi |
| `remaining_amount` | `number` | `0` | Sisa kekurangan tagihan |
| `excess_amount` | `number` | `0` | Kelebihan nominal transfer (uang lebih) |
| `change_amount` | `number` | `0` | Total kembalian |
| `payment_reference` | `string` | — | No. referensi bukti transfer/QRIS (non-tunai) |
| `payment_verified_by` | `string` | — | Nama user yang memverifikasi pembayaran |
| `payment_verified_at` | `datetime` | — | Waktu verifikasi pembayaran |

### payment\_method Enum — 14 Nilai

Field `payment_method` pada `CompanyPOSTransaction` mendukung 14 nilai enum berikut. Nilai-nilai ini mencakup metode pembayaran offline (POS) maupun online (gateway).

| # | Nilai Enum | Kategori | Deskripsi |
| - | - | - | - |
| 1 | `cash` | Offline | Tunai di kasir |
| 2 | `card` | Offline | Debit/kredit card via EDC |
| 3 | `transfer` | Offline | Transfer bank manual (upload bukti) |
| 4 | `ewallet` | Offline | GoPay, OVO, Dana, ShopeePay, LinkAja |
| 5 | `qris` | Offline | QRIS universal (semua bank & e-wallet) |
| 6 | `saldo` | Offline | Potong saldo member/internal |
| 7 | `mayar` | Online | Payment gateway Mayar |
| 8 | `debt` | B2B | Hutang / tempo (B2B payment terms) |
| 9 | `manual_transfer` | Online | Transfer bank manual (online order) |
| 10 | `midtrans` | Online | Payment gateway Midtrans |
| 11 | `tripay` | Online | Payment gateway Tripay |
| 12 | `stripe` | Online | Payment gateway Stripe (international) |
| 13 | `paypal` | Online | PayPal (international) |

### payment\_status Enum — 7 Nilai

| # | Nilai | Trigger | Deskripsi |
| - | - | - | - |
| 1 | `pending` | Default saat create | Transaksi baru, belum ada pembayaran |
| 2 | `pending_verification` | Upload bukti transfer | Menunggu admin verifikasi bukti |
| 3 | `partially_paid` | Split payment belum lunas | Sebagian sudah terbayar |
| 4 | `paid` | Semua tender verified | Pembayaran lengkap & terverifikasi |
| 5 | `failed` | Gateway timeout/error | Pembayaran gagal diproses |
| 6 | `refunded` | Refund diproses | Dana dikembalikan ke customer |
| 7 | `rejected` | Admin tolak bukti | Bukti transfer ditolak |

### payments\[] Sub-Object Schema

Setiap elemen dalam array `payments` merepresentasikan satu "tender" (satu metode pembayaran dalam split payment):

```jsonc theme={null}
{
  "method": "cash",           // Metode pembayaran tender ini
  "amount": 100000,           // Nominal yang dibayar
  "change_amount": 0,         // Kembalian (biasanya hanya cash)
  "account_id": "acc_123",    // FK → Account (rekening tujuan)
  "reference": "TRX-ABC",     // Referensi bukti/QR/approval
  "verified_by": "admin_budi",// User yang verifikasi
  "verified_at": "2025-01-15T10:30:00Z"
}
```

### Account — Rekening/Kantong Pembayaran

Entity `Account` merepresentasikan rekening atau "kantong" dana yang menjadi tujuan pembayaran POS.

| Field | Tipe | Enum / Default | Deskripsi |
| - | - | - | - |
| `name` | `string` | — | Nama rekening (mis. "Kas Outlet 1", "BCA Utama") |
| `type` | `string` (enum) | `"cash"` | `cash` \| `bank` \| `e-wallet` \| `other` |
| `account_number` | `string` | — | Nomor rekening (opsional) |
| `bank_name` | `string` | — | Nama bank (untuk `type=bank`) |
| `initial_balance` | `number` | `0` | Saldo awal saat pembuatan |
| `current_balance` | `number` | `0` | **Server-authoritative** — diupdate oleh POS sales, invoice payment, expense, void |
| `currency` | `string` | `"IDR"` | Mata uang |
| `is_default_pos` | `boolean` | `false` | Rekening default untuk POS Kasir |
| `is_active` | `boolean` | `true` | Status aktif rekening |
| `mode` | `string` (enum) | `"personal"` | `personal` \| `business` |

### FinancialRecord — Jurnal Keuangan dari POS

Setiap transaksi POS yang berhasil menciptakan `FinancialRecord` secara otomatis sebagai jurnal pendapatan.

| Field | Tipe | Enum / Default | Deskripsi |
| - | - | - | - |
| `type` | `string` (enum) | — | `income` \| `expense` \| `transfer` |
| `amount` | `number` | — | Jumlah transaksi (gross revenue) |
| `account_id` | `string` | — | FK ke `Account` — sumber/tujuan dana |
| `reference_id` | `string` | — | ID referensi ke `CompanyPOSTransaction` |
| `reference_type` | `string` (enum) | — | `pos_transaction` \| `invoice_payment` \| `expense` \| `manual` \| `transfer` \| `production_order` \| `distribution_shipment` \| `distribution_return` \| `batch_recall` \| `stock_opname` |
| `source` | `string` (enum) | `"manual"` | `manual` \| `ai_text` \| `ai_scan` \| `pos` \| `manufacturing` \| `distribution` \| `recall` \| `stock_opname` |
| `cogs_amount` | `number` | `0` | HPP/COGS yang dibekukan saat transaksi POS — untuk P\&L laba kotor |
| `tax_amount` | `number` | `0` | PPN keluaran yang termasuk dalam `amount` |
| `channel_fee` | `number` | `0` | Potongan biaya marketplace/platform |
| `idempotency_key` | `string` | — | Kunci idempoten — mencegah double-entry |

***

## Payment Verification Lifecycle (State Machine)

Diagram berikut menunjukkan seluruh state yang mungkin dilalui oleh status pembayaran (`payment_status`) dalam sebuah transaksi POS, beserta event pemicu transisinya.

```mermaid theme={null}
stateDiagram-v2
    [*] --> pending : Transaksi dibuat

    pending --> pending_verification : Upload bukti transfer
    pending --> paid : Bayar tunai / auto-verify (QRIS, Debit, Credit, E-Wallet)
    pending --> failed : Gateway timeout / error
    pending --> cancelled : Transaksi dibatalkan

    pending_verification --> paid : Admin verifikasi valid
    pending_verification --> rejected : Admin tolak bukti
    pending_verification --> pending_verification : Admin minta upload ulang

    rejected --> pending_verification : Customer upload bukti baru
    rejected --> cancelled : Customer batalkan

    pending --> partially_paid : Split payment — sebagian tender verified
    partially_paid --> partially_paid : Tambah tender baru (belum lunas)
    partially_paid --> paid : Semua tender verified & total = required

    paid --> refunded : Refund diproses (partial/full)
    refunded --> paid : Refund dibatalkan

    failed --> pending : Retry pembayaran
    failed --> [*] : Transaksi gagal final

    paid --> [*] : Selesai
    cancelled --> [*] : Dibatalkan
```

### Penjelasan Tiap Transisi

| Dari | Ke | Event / Trigger | Kondisi |
| - | - | - | - |
| `[*]` | `pending` | `CompanyPOSTransaction` di-create | Default state saat transaksi baru dibuat |
| `pending` | `pending_verification` | Kasir pilih "Transfer Bank" & customer upload bukti | `payment_method = transfer` |
| `pending` | `paid` | Pembayaran langsung verified | Cash (instant), QRIS/Debit/Credit/E-Wallet (auto-verify dari gateway) |
| `pending` | `failed` | Gateway tidak merespon | Timeout > 30 detik atau error dari payment gateway |
| `pending` | `partially_paid` | Split payment: 1+ tender verified tapi total \< required | `paid_amount < total` |
| `pending_verification` | `paid` | Admin klik "Verifikasi" | Bukti transfer valid, nominal cocok |
| `pending_verification` | `rejected` | Admin klik "Tolak" | Bukti tidak valid / nominal tidak cocok |
| `rejected` | `pending_verification` | Customer upload bukti baru | Setelah admin minta upload ulang |
| `partially_paid` | `paid` | Tender terakhir verified | `SUM(payments[].amount) >= total` |
| `paid` | `refunded` | Kasir/Admin proses refund | Partial atau full refund |
| `failed` | `pending` | Kasir retry pembayaran | Customer pilih metode lain atau coba lagi |

***

## Sequence Diagram — Split Payment Flow

Diagram urutan berikut menggambarkan detail flow split payment dari awal hingga transaksi selesai, termasuk interaksi dengan `Account` untuk update saldo.

```mermaid theme={null}
sequenceDiagram
    participant C as Customer
    participant K as Kasir (POS UI)
    participant S as Server (SNISHOP API)
    participant ACC as Account Service
    participant FR as FinancialRecord

    C->>K: Pilih produk & checkout
    K->>K: Hitung total: Rp 250.000
    K->>C: Tampilkan total & pilih "Split Payment"

    Note over K: Tender 1 — Cash Rp 100.000
    C->>K: Serahkan cash Rp 100.000
    K->>S: POST /payments {method:"cash", amount:100000}
    S->>S: Validasi: 100000 ≤ 250000 ✓
    S->>ACC: Update Kas Account (+100.000)
    S->>S: paid_amount = 100.000, remaining = 150.000
    S-->>K: payment_status → partially_paid

    Note over K: Tender 2 — QRIS Rp 80.000
    K->>S: POST /payments {method:"qris", amount:80000}
    S->>S: Generate QR code
    C->>S: Scan & bayar Rp 80.000
    S->>S: Gateway callback: PAID ✓
    S->>ACC: Update QRIS Account (+80.000)
    S->>S: paid_amount = 180.000, remaining = 70.000
    S-->>K: payment_status → partially_paid

    Note over K: Tender 3 — E-Wallet Rp 70.000
    K->>S: POST /payments {method:"ewallet", amount:70000}
    S->>S: Gateway request
    C->>S: Bayar Rp 70.000 via GoPay
    S->>S: Gateway callback: PAID ✓
    S->>ACC: Update E-Wallet Account (+70.000)
    S->>S: paid_amount = 250.000, remaining = 0

    S->>FR: Create FinancialRecord (income, Rp 250.000)
    S->>S: payment_status → paid
    S-->>K: Transaksi SELESAI
    K->>C: Cetak receipt (3 tender)
```

***

## Sequence Diagram — Verifikasi per Metode Pembayaran

### Cash — Verifikasi Otomatis (Instant)

```mermaid theme={null}
sequenceDiagram
    participant C as Customer
    participant K as Kasir
    participant S as Server
    participant ACC as Kas Account

    C->>K: Serahkan uang tunai
    K->>K: Input nominal received
    K->>K: Quick button: Rp 50K / 100K / 150K / 200K / Uang Pas
    K->>S: POST /finalize {method:"cash", amount:X}
    S->>S: Kalkulasi kembalian = amount - total
    S->>ACC: current_balance += total
    S->>S: payment_status → paid (instant)
    S-->>K: Return: paid ✅ + change_amount
    K->>C: Kembalikan uang + receipt
```

### Transfer Bank — Verifikasi Manual

```mermaid theme={null}
sequenceDiagram
    participant C as Customer
    participant K as Kasir
    participant S as Server
    participant A as Admin
    participant ACC as Bank Account

    C->>K: Pilih "Transfer Bank"
    K->>S: Create transaction → payment_status: pending
    S->>S: Generate virtual account / tampilkan rekening tujuan
    C->>S: Transfer via mobile banking
    C->>S: Upload bukti transfer (screenshot/foto)
    S->>S: payment_status → pending_verification
    S-->>A: Notifikasi: "Bukti transfer baru perlu verifikasi"

    A->>S: Review bukti transfer
    alt Bukti Valid
        A->>S: Klik "Verifikasi" ✅
        S->>ACC: current_balance += total
        S->>S: payment_status → paid
        S->>S: Set payment_verified_by, payment_verified_at
        S-->>K: Notifikasi: "Pembayaran terverifikasi"
    else Bukti Tidak Valid
        A->>S: Klik "Tolak" ❌
        S->>S: payment_status → rejected
        S-->>C: Notifikasi: "Upload ulang bukti transfer"
    end
```

### QRIS — Verifikasi Otomatis via Gateway

```mermaid theme={null}
sequenceDiagram
    participant C as Customer
    participant K as Kasir
    participant S as Server
    participant G as QRIS Gateway
    participant ACC as QRIS Account

    C->>K: Pilih "QRIS"
    K->>S: POST /payments {method:"qris"}
    S->>G: Request generate QR
    G-->>S: Return QR payload (NMRA format)
    S-->>K: Tampilkan QR code di layar
    K-->>C: "Silakan scan QR dengan aplikasi bank/e-wallet"

    C->>G: Scan & konfirmasi bayar
    G->>G: Validasi & proses (all banks + e-wallets)
    G-->>S: Webhook callback: status = PAID
    S->>S: Verify signature & amount match
    S->>ACC: current_balance += total
    S->>S: payment_status → paid (< 5 detik)
    S-->>K: Notifikasi: "Pembayaran berhasil"
    K-->>C: Receipt cetak otomatis
```

### Debit & Credit Card — Verifikasi via EDC/NFC

```mermaid theme={null}
sequenceDiagram
    participant C as Customer
    participant K as Kasir
    participant EDC as EDC Terminal
    participant S as Server
    participant ACC as Card Account

    C->>K: Pilih "Debit/Credit Card"
    K->>S: POST /payments {method:"card"}
    S->>EDC: Initiate card transaction
    C->>EDC: Swipe / Insert / Tap NFC
    EDC->>EDC: Read card data
    EDC-->>C: "Masukkan PIN" / "Tap to confirm"
    C->>EDC: Input PIN / signature
    EDC->>EDC: Authorize with bank network (Visa/MC/JCB)
    EDC-->>S: Approval code + status: APPROVED
    S->>ACC: current_balance += total
    S->>S: payment_status → paid (< 5 detik)
    S->>S: Store reference = approval_code
    S-->>K: Notifikasi: "Pembayaran berhasil"
    K-->>C: Receipt + slip EDC
```

### E-Wallet — Verifikasi Otomatis

```mermaid theme={null}
sequenceDiagram
    participant C as Customer
    participant K as Kasir
    participant S as Server
    participant EW as E-Wallet API (GoPay/OVO/Dana/ShopeePay/LinkAja)
    participant ACC as E-Wallet Account

    C->>K: Pilih "E-Wallet"
    K->>S: POST /payments {method:"ewallet"}
    S->>EW: Request payment (phone number / QR scan)
    S-->>K: Tampilkan QR / minta input nomor HP

    alt Scan QR
        C->>EW: Scan QRIS / app-specific QR
    else Input Nomor
        C->>EW: Konfirmasi bayar di app
    end

    EW->>EW: Verify balance & process
    EW-->>S: Webhook: status = PAID
    S->>S: Verify amount match
    S->>ACC: current_balance += total
    S->>S: payment_status → paid (< 5 detik)
    S-->>K: Notifikasi: "Pembayaran berhasil"
    K-->>C: Receipt cetak
```

***

## Payment Method Comparison Table

Tabel perbandingan lengkap 6 metode pembayaran utama yang tersedia di POS SNISHOP ERP.

| Aspek | Cash | Transfer Bank | QRIS | Debit Card | Credit Card | E-Wallet |
| - | - | - | - | - | - | - |
| **Verifikasi** | Otomatis (instant) | Manual (admin) | Otomatis (gateway) | Otomatis (EDC) | Otomatis (EDC) | Otomatis (gateway) |
| **Waktu Verifikasi** | \< 1 detik | 1-30 menit | \< 5 detik | \< 5 detik | \< 5 detik | \< 5 detik |
| **Kembalian** | Ya (kalkulasi otomatis) | Tidak (exact amount) | Tidak | Tidak | Tidak | Tidak |
| **Quick Buttons** | Rp 50K, 100K, 150K, 200K, Uang Pas | — | — | — | — | — |
| **Bukti Fisik** | Receipt thermal | Upload screenshot + receipt | QR code + receipt | Slip EDC + receipt | Slip EDC + receipt | Receipt digital |
| **Split Payment** | Ya | Ya | Ya | Ya | Ya | Ya |
| **Refund** | Cash langsung | Transfer balik | Via gateway | Via EDC | Via EDC | Via gateway |
| **Risk** | Uang palsu | Bukti palsu | Minimal | Card fraud | Chargeback | Minimal |
| **Biaya Admin** | Tidak ada | Tidak ada | 0.3-0.7% per trx | 0.5-1.5% per trx | 1.5-3% per trx | 0.5-1.5% per trx |
| **Cocok Untuk** | Transaksi kecil, tunai | B2B, nominal besar | Universal, semua customer | Nasabah bank | International / premium | Digital-native customer |
| **Account Type** | `cash` | `bank` | `e-wallet` | `bank` | `bank` | `e-wallet` |
| **Enum Value** | `cash` | `transfer` / `manual_transfer` | `qris` | `card` | `card` | `ewallet` |
| **Support di B2B** | Ya | Ya (dengan PO) | Ya | Ya | Ya | Ya |
| **Offline Mode** | Ya (full) | Partial (upload nanti) | Tidak | Ya (EDC standalone) | Ya (EDC standalone) | Tidak |

***

## Split Payment Calculation — Contoh Perhitungan

### Konsep Dasar

Split payment memungkinkan satu transaksi dibayar dengan **kombinasi beberapa metode pembayaran**. Sistem menghitung secara real-time sisa tagihan setelah setiap tender (pembayaran parsial) diverifikasi.

**Rumus:**

```
remaining_amount = total - SUM(payments[].amount)
```

### Contoh 1 — Tiga Metode (Cash + QRIS + E-Wallet)

| Step | Metode | Amount | paid\_amount (kumulatif) | remaining\_amount | Status |
| - | - | - | - | - | - |
| — | — | — | 0 | 250.000 | `pending` |
| 1 | Cash | 100.000 | 100.000 | 150.000 | `partially_paid` |
| 2 | QRIS | 80.000 | 180.000 | 70.000 | `partially_paid` |
| 3 | E-Wallet | 70.000 | 250.000 | 0 | `paid` |

```
Total: Rp 250.000
├─ Cash:      Rp 100.000  ✅ verified
├─ QRIS:      Rp  80.000  ✅ verified
└─ E-Wallet:  Rp  70.000  ✅ verified
   ──────────────────────
   Total Paid: Rp 250.000
   Sisa:       Rp       0  ✅ SELESAI
```

### Contoh 2 — Cash dengan Kembalian + Transfer

| Step | Metode | Amount | Change | paid\_amount | remaining | Status |
| - | - | - | - | - | - | - |
| — | — | — | — | 0 | 180.000 | `pending` |
| 1 | Cash | 200.000 | 20.000 | 180.000 | 0 | `paid` |

```
Total: Rp 180.000
├─ Cash:      Rp 200.000  ✅ verified
│  └─ Kembalian: Rp 20.000
   ──────────────────────
   Total Paid: Rp 180.000 (net)
   Sisa:       Rp       0  ✅ SELESAI
```

### Contoh 3 — B2B Partial Payment (Hutang + Cash)

| Step | Metode | Amount | paid\_amount | remaining | Status |
| - | - | - | - | - | - |
| — | — | — | 0 | 500.000 | `pending` |
| 1 | Cash | 200.000 | 200.000 | 300.000 | `partially_paid` |
| 2 | Debt (hutang 30 hari) | 300.000 | 500.000 | 0 | `paid` |

```
Total: Rp 500.000
├─ Cash:      Rp 200.000  ✅ verified
└─ Debt:      Rp 300.000  ✅ (jatuh tempo 30 hari)
   ──────────────────────
   Total Paid: Rp 500.000
   Sisa:       Rp       0  ✅ SELESAI
   Outstanding: Rp 300.000 (tagihan B2B)
```

### Contoh 4 — Split 4 Metode (Cash + Debit + QRIS + E-Wallet)

| Step | Metode | Amount | Account Type | paid\_amount | remaining | Status |
| - | - | - | - | - | - | - |
| — | — | — | — | 0 | 420.000 | `pending` |
| 1 | Cash | 100.000 | `cash` | 100.000 | 320.000 | `partially_paid` |
| 2 | Debit | 150.000 | `bank` | 250.000 | 170.000 | `partially_paid` |
| 3 | QRIS | 120.000 | `e-wallet` | 370.000 | 50.000 | `partially_paid` |
| 4 | E-Wallet | 50.000 | `e-wallet` | 420.000 | 0 | `paid` |

```
Total: Rp 420.000
├─ Cash:      Rp 100.000  ✅ → Kas Account
├─ Debit:     Rp 150.000  ✅ → Bank Account
├─ QRIS:      Rp 120.000  ✅ → QRIS Account
└─ E-Wallet:  Rp  50.000  ✅ → E-Wallet Account
   ──────────────────────
   Total Paid: Rp 420.000
   Sisa:       Rp       0  ✅ SELESAI
```

### Validasi Split Payment

Sistem melakukan validasi berikut pada setiap step split payment:

| Validasi | Aturan | Error Jika Gagal |
| - | - | - |
| **Amount ≤ Remaining** | `tender.amount ≤ remaining_amount` | "Nominal melebihi sisa tagihan" |
| **Amount > 0** | `tender.amount > 0` | "Nominal harus lebih dari 0" |
| **Max 5 Tender** | `payments.length ≤ 5` | "Maksimal 5 metode pembayaran" |
| **No Duplicate Method** | Tidak ada 2 tender dengan `method` sama | "Metode pembayaran sudah digunakan" |
| **Cash Change Logic** | Hanya tender cash yang boleh punya `change_amount > 0` | "Kembalian hanya untuk tunai" |
| **Account Active** | `payments[].account_id` harus `is_active = true` | "Rekening tidak aktif" |

***

## Integrasi Pembayaran dengan Modul Keuangan

Setiap transaksi POS yang berstatus `paid` secara otomatis menciptakan `FinancialRecord` dengan konfigurasi berikut:

| Field FinancialRecord | Sumber Nilai | Deskripsi |
| - | - | - |
| `type` | `"income"` | Selalu income untuk transaksi POS |
| `amount` | `CompanyPOSTransaction.total` | Total pendapatan (gross) |
| `account_id` | `payments[].account_id` | Rekening tujuan (per-tender untuk split payment) |
| `reference_id` | `CompanyPOSTransaction._id` | Link ke transaksi POS asal |
| `reference_type` | `"pos_transaction"` | Tipe referensi |
| `source` | `"pos"` | Sumber otomatis dari POS |
| `cogs_amount` | Dihitung dari `items[].unit_cost × quantity` | HPP total untuk kalkulasi laba kotor |
| `tax_amount` | `CompanyPOSTransaction.tax_amount` | PPN yang termasuk dalam total |
| `channel_fee` | `0` (kecuali marketplace) | Biaya platform (jika via marketplace) |
| `idempotency_key` | `txn_{transaction_number}` | Mencegah double-entry saat retry |

### Flow Jurnal Otomatis

```mermaid theme={null}
flowchart LR
    A[Transaksi POS<br/>payment_status = paid] --> B{Split Payment?}
    B -->|Tidak| C[1 FinancialRecord<br/>amount = total]
    B -->|Ya| D[N FinancialRecord<br/>1 per tender]
    C --> E[Update Account.current_balance]
    D --> E
    E --> F[Update Laporan Keuangan]
    F --> G[Neraca & P&L ter-update]
```

Untuk split payment, sistem menciptakan **satu `FinancialRecord` per tender**, masing-masing dengan `account_id` yang berbeda sesuai rekening tujuan. Ini memastikan saldo setiap rekening akurat dan dapat direkonsiliasi secara independen.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.