Eza POS Developer Documentation

Dokumentasi REST API & Panduan Aplikasi Kasir Offline-First

Selamat datang di dokumentasi resmi Eza POS REST API. Dirancang khusus bagi pengembang (Android Native Java/Kotlin, Flutter, React Native, mau pun Web App) untuk membangun aplikasi kasir Offline-First yang tangguh, super cepat, dan tidak terganggu oleh koneksi internet yang lambat.

Base URL API: https://pos.ezapedia.my.id/api/pos_api.php
Format Autentikasi: X-API-KEY: {username_kasir}

Konsep & Tatacara Membuat Aplikasi POS Offline-First

Panduan arsitektur bagaimana aplikasi berjalan secara offline tanpa lag internet.

1. Alur Masuk APK (Hanya Input Username)

Di aplikasi Android/Mobile, kasir tidak perlu menghafal token rumit. Pada halaman awal cukup sediakan input Username Kasir. Username ini langsung menjadi API Key untuk mengambil semua hak akses dan data toko yang bersangkutan.

2. Buka Shift (Sinkronisasi Online Sekali)

Saat kasir klik "Buka Shift" dan memasukkan modal awal, aplikasi secara real-time memanggil endpoint action=catalog untuk menarik seluruh produk, stok aktif, harga regular & reseller, deskripsi produk, voucher, dan metode pembayaran, lalu disimpan ke database lokal HP (SQLite/Room).

3. Transaksi 100% Offline (Instan & Bebas Lag)

Semua aktivitas kasir (tambah item ke cart, input note kecil pada produk, potong stok lokal, hitung diskon voucher, pembayaran cash/tunai, serta cetak struk Bluetooth thermal) diproses langsung dari SQLite HP. Kasir tidak akan merasakan lag sama sekali meskipun sinyal internet drop.

4. Yang Berjalan Online (Hanya Bila Diperlukan)

Hanya 3 proses yang membutuhkan internet:
• Pembayaran QRIS Eza Pedia (generate QRIS & cek status live).
• Notifikasi Lonceng Pesanan Self-Order pelanggan masuk.
• Sync Background transaksi offline ke cloud dan monitoring owner toko.

Fitur Tambah Stok Langsung di APK Offline:

Meskipun aplikasi berjalan offline, kasir tetap dapat menambah stok masuk barang langsung melalui menu pengaturan aplikasi (seperti fitur di menu Kasir Online) dengan memanggil endpoint action=update_stock saat perangkat terhubung internet.

Autentikasi Berbasis Username Kasir (API Key)

Bagaimana cara mengirim kredensial pada setiap permintaan API.

Untuk mempermudah integrasi pihak ketiga dan developer APK Android, sistem ini menggunakan Username Kasir sebagai API Key utama. Anda dapat mengirimkan username kasir melalui salah satu metode berikut:

Opsi 1: Custom HTTP Header (Sangat Direkomendasikan) X-API-KEY: kasir1 atau X-Cashier-Username: kasir1
Opsi 2: Query Parameter URL https://pos.ezapedia.my.id/api/pos_api.php?action=catalog&api_key=kasir1
Opsi 3: Authorization Bearer Header Authorization: Bearer kasir1

Panduan Pengaturan Printer Kasir Offline (Direct Bluetooth ESC/POS)

Standar pencetakan nota struk 100% lokal dari HP/Tablet kasir tanpa perantara web server.

Prinsip Kerja Pencetakan Kasir Offline:

Pada aplikasi kasir offline native (Android Kotlin / Java / Flutter / React Native), proses cetak struk nota WAJIB dieksekusi secara lokal (offline) dari smartphone/tablet langsung ke printer thermal thermal Bluetooth (atau kabel USB OTG) menggunakan protokol komunikasi byte ESC/POS.

⚠️ Aplikasi TIDAK BOLEH memanggil server web untuk melakukan perintah cetak, agar saat toko mati lampu atau internet padam, kasir tetap dapat mencetak struk belanja secara instan (kecepatan cetak < 100 milidetik).

1
Tarik Profil dari Server

Saat buka shift, aplikasi menarik pengaturan printer dari endpoint action=printer_settings (Nama Printer Bluetooth tersimpan, ukuran kertas 58mm/80mm, nama toko, alamat, footer struk).

2
Koneksi Bluetooth Native

Aplikasi Android membuka koneksi Bluetooth SPP socket (UUID: 00001101-0000-1000-8000-00805F9B34FB) ke printer thermal yang telah di-pairing di HP kasir.

3
Stream Byte ESC/POS

Aplikasi menyusun teks nota dalam memori HP, menghitung perataan kolom, mengonversinya menjadi byte array ESC/POS, lalu mengirimkannya via OutputStream.write().

Perintah Standar Byte ESC/POS:
Fungsi Cetak Kode Hex ESC/POS Deskripsi & Penggunaan
Initialize Printer 1B 40 Wajib dikirim di awal untuk mereset buffer dan font default
Rata Tengah (Center) 1B 61 01 Untuk Judul Toko, Alamat, dan Ucapan Terima Kasih
Rata Kiri (Left) 1B 61 00 Untuk nama barang dan rincian transaksi
Rata Kanan (Right) 1B 61 02 Untuk subtotal harga dan nominal kembalian
Bold ON / OFF 1B 45 01 / 1B 45 00 Huruf tebal pada nama toko dan Total Belanja
Feed Baris Baru 0A Baris baru (Enter)
Potong Kertas (Auto Cut) 1D 56 42 00 Memotong kertas struk secara otomatis di akhir nota
Buka Laci Uang (Drawer) 1B 70 00 19 FA Membuka laci kasir (Cash Drawer RJ11) otomatis setelah bayar
Contoh Struk 58mm (Maks 32 Karakter per Baris):
EZA COFFEE & ROASTERY
Jl. Merdeka No. 12, Jakarta
WA: 0812-3456-7890
--------------------------------
No. Nota : TRX-20261026-0001
Kasir : Budi Santoso
Waktu : 26/10/2026 14:30
--------------------------------
Kopi Susu Gula Aren
2 x Rp 18.000 Rp 36.000
* Note: Es sedikit, manis sedang
Roti Bakar Coklat Keju
1 x Rp 15.000 Rp 15.000
--------------------------------
Subtotal Rp 51.000
Diskon Voucher -Rp 5.000
TOTAL Rp 46.000
Tunai (Cash) Rp 50.000
Kembalian Rp 4.000
--------------------------------
Terima kasih atas kunjungan Anda
Powered by Eza POS
Implementasi Kotlin (Android Native Socket):
// 1. Hubungkan ke Bluetooth Printer SPP
val device = bluetoothAdapter.getRemoteDevice(printerMacAddress)
val uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")
val socket = device.createRfcommSocketToServiceRecord(uuid)
socket.connect()
val out = socket.outputStream

// 2. Kirim Header (Reset, Center, Bold)
out.write(byteArrayOf(0x1B, 0x40))       // Init
out.write(byteArrayOf(0x1B, 0x61, 0x01)) // Center
out.write(byteArrayOf(0x1B, 0x45, 0x01)) // Bold ON
out.write("EZA COFFEE & ROASTERY\n".toByteArray())
out.write(byteArrayOf(0x1B, 0x45, 0x00)) // Bold OFF
out.write("Jl. Merdeka No. 12, Jakarta\n\n".toByteArray())

// 3. Garis Pembatas (32 karakter untuk 58mm)
out.write(byteArrayOf(0x1B, 0x61, 0x00)) // Left
out.write("--------------------------------\n".toByteArray())

// 4. Rincian Item Barang
out.write("Kopi Susu Gula Aren\n".toByteArray())
out.write(String.format("%-16s %15s\n", "2 x 18.000", "36.000").toByteArray())
out.write("  * Note: Es sedikit\n".toByteArray())

// 5. Total Belanja & Cut Kertas
out.write("--------------------------------\n".toByteArray())
out.write(String.format("%-16s %15s\n", "TOTAL", "Rp 36.000").toByteArray())
out.write("\n\n\n".toByteArray())
out.write(byteArrayOf(0x1D, 0x56, 0x42, 0x00)) // Auto Cut
out.flush()
socket.close()

Panduan Desain Tampilan UI/UX & Alur Fitur Kasir Offline

Panduan lengkap tata cara implementasi seluruh menu kasir online agar tersedia di aplikasi offline.

1 Layar Masuk Kasir (Hanya Input Username) Screen 1

Buat form sederhana yang meminta Username Kasir saja. Begitu kasir menekan tombol "Masuk", aplikasi memverifikasi akun ke API (action=profile&api_key={username}). Jika valid, simpan username tersebut di local storage sebagai API Key permanen perangkat.

2 Layar Buka Shift & Auto-Sync Screen 2

Tampilkan modal/halaman Buka Shift dengan input modal kas awal (kas kecil). Saat tombol "Buka Shift" ditekan, panggil action=open_shift dan langsung tarik seluruh katalog (action=catalog). Seluruh produk, stok, metode pembayaran, voucher, profil printer, dan pesanan ditahan langsung disimpan ke SQLite lokal.

3 Layar Utama POS & Keranjang Belanja Screen 3

Layout 2 kolom: Kiri berupa grid katalog produk dengan filter kategori horizontal & tombol scan barcode kamera; Kanan berupa keranjang belanja (list item, qty +/- stepper, subtotal, tombol diskon voucher, tombol modal bayar).

4 Simpan / Tunda & Lanjutkan Pesanan (Hold) Fitur Kasir

Sediakan tombol "Tunda Pesanan" di bawah keranjang belanja. Jika antrian pelanggan tertunda, kasir memasukkan nama pesanan (misal: "Meja 4 / Pak Anton"), lalu keranjang disimpan ke tabel SQLite held_orders (dan sinkron ke action=save_held_order). Kasir dapat melanjutkan pesanan kapan saja melalui menu daftar pesanan ditahan (action=resume_held_order).

5 Hapus Keranjang & Batal / Refund Nota Fitur Kasir

Sebelum Bayar: Tombol "Kosongkan Keranjang" untuk menghapus seluruh item yang salah input.
Setelah Bayar: Pada menu Riwayat Transaksi, kasir memiliki tombol "Batal / Refund" untuk mengajukan pembatalan transaksi dengan alasan (action=cancel_sale / action=request_refund). Admin toko otomatis menerima notifikasi WhatsApp permohonan refund.

6 Buku Telepon Kasir & Data Pelanggan Fitur Kasir

Sediakan tombol Buku Telepon di header atas aplikasi. Kasir dapat mencari kontak pelanggan lama, memilih pelanggan untuk otomatis mengisi data pembeli di struk, atau menambahkan kontak pelanggan baru langsung dari aplikasi kasir (action=add_contact).

7 Kelola & Tambah Stok Barang Masuk Fitur Kasir

Kasir dapat membuka menu "Kelola Stok" untuk melihat sisa stok produk. Saat ada pasokan baru tiba dari supplier/gudang, kasir dapat langsung menambah jumlah stok barang masuk melalui endpoint action=update_stock (mode add atau set).

8 Modal Pembayaran & Eza Pedia QRIS Screen 4

Pilihan metode pembayaran dipisahkan secara cerdas:
• Tunai / Transfer Manual / QRIS Statis / EDC: Diproses 100% offline di SQLite lokal dan langsung mencetak struk.
• QRIS Eza Pedia Gateway (Online): Aplikasi memanggil action=create_qris, menampilkan QR dinamis di layar HP kasir, dan melakukan polling action=check_qris_status sampai lunas.

Spesifikasi Endpoint REST API

Daftar endpoint lengkap untuk integrasi sinkronisasi, kasir, dan pelaporan.

GET ?action=catalog
Sync Down Seluruh Data Master

Mengambil seluruh data kategori, produk yang diizinkan untuk kasir ini, stok terkini, deskripsi produk, voucher yang berlaku, metode pembayaran toko, serta profil toko. Digunakan saat pertama kali buka aplikasi atau saat Buka Shift.

Header:
X-API-KEY: username_kasir
Contoh Response (200 OK):
{
  "status": "success",
  "success": true,
  "message": "Data katalog lengkap, stok, dan pengaturan berhasil disinkronkan.",
  "data": {
    "sync_timestamp": 1729938000,
    "categories": [
      { "id": 1, "name": "Makanan Utama" },
      { "id": 2, "name": "Minuman Segar" }
    ],
    "products": [
      {
        "id": 101,
        "name": "Kopi Susu Gula Aren",
        "description": "Espresso robusta dengan susu segar dan gula aren asli",
        "category_id": 2,
        "category_name": "Minuman Segar",
        "base_price": 8000,
        "regular_price": 18000,
        "reseller_price": 15000,
        "stock_quantity": 45,
        "image": "kopi.jpg",
        "image_url": "https://domain.com/uploads/kopi.jpg",
        "type": "physical",
        "is_pack": false
      }
    ],
    "vouchers": [
      {
        "id": 5,
        "code": "DISKON10",
        "description": "Diskon 10% minimal belanja 50rb",
        "type": "percentage",
        "value": 10,
        "min_purchase": 50000,
        "max_discount": 15000
      }
    ],
    "payment_methods": {
      "cash": { "enabled": true, "label": "Tunai / Cash" },
      "transfer": { "enabled": true, "label": "Transfer Bank" },
      "qris": { "enabled": true, "label": "QRIS Eza Pedia" }
    },
    "store_info": {
      "store_name": "Kedai Kopi Mantap",
      "cashier_name": "Kasir Pagi",
      "store_address": "Jl. Merdeka No. 12, Jakarta",
      "coordinates": "-6.200000,106.816666",
      "footer_note": "Terima kasih atas kunjungan Anda"
    }
  }
}
GET ?action=profile
Ambil Data Profil Kasir & Toko

Mengambil informasi detail akun kasir yang sedang aktif dan identitas toko/merchant.

{
  "status": "success",
  "data": {
    "cashier": {
      "id": 3,
      "username": "kasir_utama",
      "nama_lengkap": "Budi Santoso",
      "role": "kasir"
    },
    "store": {
      "admin_id": 1,
      "store_name": "Resto Nusantara",
      "owner_name": "Mas Eza",
      "store_address": "Jl. Pemuda No. 45",
      "coordinates": "-6.175392,106.827153",
      "store_logo": "https://domain.com/uploads/logo.png",
      "qris_image": "https://domain.com/uploads/qris.jpg"
    }
  }
}
GET ?action=settings
Ambil Pengaturan Operasional Kasir

Mengambil konfigurasi nota struk, mode stok (harian/berkelanjutan), aturan delivery self-order, dan channel pembayaran.

GET ?action=payment_methods
Tarik Data Metode Pembayaran Toko

Mengembalikan rincian seluruh metode pembayaran aktif toko yang dipisahkan antara Offline Methods (Tunai, Transfer Manual, QRIS Statis, EDC, Kasbon) dan Online Gateway (Eza Pedia Real-Time QRIS / Virtual Account).

Contoh Response JSON:
{
  "status": "success",
  "data": {
    "offline_methods": [
      {
        "id": "cash",
        "name": "Tunai (Cash)",
        "type": "cash",
        "is_enabled": true,
        "is_offline": true,
        "requires_change_calc": true,
        "description": "Pembayaran tunai langsung dari pelanggan, diproses 100% offline"
      },
      {
        "id": "transfer",
        "name": "Transfer Bank Manual",
        "type": "bank_transfer",
        "is_enabled": true,
        "is_offline": true,
        "bank_details": {
          "name": "BCA",
          "account_name": "Toko Eza POS",
          "account_number": "8801234567"
        }
      },
      {
        "id": "qris",
        "name": "QRIS Statis / Manual",
        "type": "static_qris",
        "is_enabled": true,
        "is_offline": true,
        "qris_image_url": "https://domain.com/uploads/qris.jpg"
      },
      {
        "id": "edc",
        "name": "Mesin EDC / Debit / Kredit",
        "type": "edc",
        "is_enabled": true,
        "is_offline": true
      }
    ],
    "online_gateway": {
      "provider": "Eza Pedia Payment Gateway",
      "is_active": true,
      "channels": [
        {
          "id": "ezapedia_qris",
          "code": "QRIS",
          "name": "QRIS Dinamis (Eza Pedia)",
          "type": "qris",
          "is_online": true,
          "create_endpoint": "action=create_qris",
          "status_endpoint": "action=check_qris_status",
          "fee": 0
        }
      ],
      "instructions": "Metode pembayaran online ini membutuhkan koneksi internet saat pelanggan memilihnya untuk generate QRIS dinamis."
    }
  }
}
GET POST ?action=printer_settings
Ambil & Simpan Profil Printer Thermal

GET: Mengambil nama printer Bluetooth tersimpan, ukuran kertas (58mm / 80mm), format teks header, dan footer struk.
POST: Menyimpan nama printer/MAC Address yang dipilih kasir ke server cloud agar tetap tersimpan saat berganti HP.

Payload POST (Simpan Printer):
{
  "printer_type": "bluetooth",
  "printer_name": "MPT-II (66:22:BB:11:00:AA)",
  "paper_width": "58mm", // "58mm" (32 kolom) atau "80mm" (48 kolom)
  "auto_print": "yes"
}
CRUD ?action=get_held_orders
Simpan / Tunda & Lanjutkan Pesanan Kasir

Digunakan untuk fitur Tunda Pesanan (Hold Order). Kasir dapat menyimpan keranjang belanja pelanggan yang antriannya tertahan, melayani pelanggan lain, lalu memulihkan (resume) pesanan kembali ke keranjang kasir.

1. Ambil Daftar Pesanan Ditahan (GET): https://pos.ezapedia.my.id/api/pos_api.php?action=get_held_orders
2. Tunda / Simpan Pesanan (POST): POST ?action=save_held_order
{
  "order_name": "Meja 5 / Pak Bambang",
  "cart_data": [
    { "id": 101, "name": "Kopi Susu", "quantity": 2, "price": 18000, "note": "Gula aren sedikit" }
  ]
}
3. Lanjutkan Pesanan (Resume - GET/POST): https://pos.ezapedia.my.id/api/pos_api.php?action=resume_held_order&id=12

Mengembalikan isi keranjang belanja dan secara otomatis menghapus antrian tunda tersebut.

4. Hapus Pesanan Ditahan (DELETE/POST): https://pos.ezapedia.my.id/api/pos_api.php?action=delete_held_order&id=12
CRUD ?action=contacts
Buku Telepon Kasir & Manajemen Pelanggan

Fitur buku telepon di kasir untuk mencari pelanggan, menambahkan kontak baru, memperbarui nomor WhatsApp, dan menghapus kontak.

1. Cari / Ambil Daftar Kontak (GET): https://pos.ezapedia.my.id/api/pos_api.php?action=contacts&q=budi
2. Tambah Kontak Pelanggan Baru (POST): POST ?action=add_contact
{
  "name": "Ibu Ratna",
  "phone": "081298765432"
}
3. Sinkron Massal Kontak dari HP (POST): POST ?action=sync_contacts

Mengirimkan array kontak dari smartphone kasir ke cloud sekaligus.

POST ?action=cancel_sale
Batalkan / Ajukan Refund Transaksi

Jika kasir salah menginput transaksi yang sudah terlanjur selesai, kasir dapat membatalkan atau mengajukan refund nota dengan menyertakan alasan jelas. Notifikasi WhatsApp otomatis dikirimkan ke Admin Toko untuk persetujuan (approval) di panel web.

Payload POST:
{
  "sale_id": "TRX-20261026-0001",
  "reason": "Salah input jumlah pesanan, pelanggan minta ganti menu"
}
POST ?action=update_stock
Tambah / Perbarui Stok Produk Langsung

Kasir dapat menambah atau mengatur jumlah stok produk secara langsung di pengaturan APK offline saat ada barang masuk (seperti fitur di menu Kasir Online).

Payload JSON (Single atau Batch):
// Opsi 1: Update 1 Produk
{
  "product_id": 101,
  "quantity": 25,
  "mode": "add",    // "add" (tambah ke stok lama) atau "set" (timpa total stok)
  "notes": "Restock kiriman dari gudang pusat"
}

// Opsi 2: Batch Update Banyak Produk Sekaligus
{
  "stocks": {
    "101": 50,
    "102": 30
  },
  "mode": "set",
  "notes": "Stok awal buka toko"
}
POST ?action=sync_up_sales
Sinkronisasi Penjualan Offline ke Cloud

Mengirimkan transaksi yang telah diproses secara offline saat perangkat mendeteksi sinyal internet. Mendukung pengiriman catatan produk (note kecil pensil), diskon voucher, dan pemotongan stok otomatis di server.

Payload JSON:
{
  "transactions": [
    {
      "id": "TRX-20261026-0001",
      "order_number_daily": 1,
      "subtotal": 36000,
      "voucher_code": "DISKON10",
      "voucher_discount": 3600,
      "total_belanja": 32400,
      "metode_pembayaran": "cash",
      "uang_bayar": 50000,
      "kembalian": 17600,
      "customer_name": "Pak Joko",
      "customer_phone": "08123456789",
      "transaction_time": "2026-10-26 10:15:30",
      "items": [
        {
          "id": 101,
          "name": "Kopi Susu Gula Aren",
          "price": 18000,
          "quantity": 2,
          "subtotal": 36000,
          "note": "Es sedikit, tidak terlalu manis"  // Catatan produk per-item
        }
      ]
    }
  ]
}
GET ?action=daily_sales&date=YYYY-MM-DD
Ambil Riwayat Penjualan Kasir

Mengambil daftar transaksi kasir per tanggal tertentu lengkap dengan total omzet, rincian pembayaran tunai vs non-tunai, dan detail item.

GET ?action=notifications
Notifikasi Lonceng & Pesanan Self-Order

Digunakan untuk fitur lonceng notifikasi pada aplikasi kasir. Memberikan informasi jika ada pesanan mandiri pelanggan (Self-Order Dine-in / Takeaway / Delivery) yang masuk ke kasir secara online.

POST ?action=open_shift / ?action=close_shift
Manajemen Shift & Settlement

Buka Shift: Merekam modal awal uang tunai laci (misal Rp 100.000).
Tutup Shift: Mengirimkan penghitungan uang fisik kasir, menghitung selisih kas otomatis, dan mengirimkan laporan closing shift ke WhatsApp Owner melalui WACS.

POST ?action=check_voucher
Validasi Voucher & Hitung Potongan

Memeriksa keabsahan kode voucher, syarat minimal belanja, dan mengembalikan nominal potongan diskon yang berhak diperoleh.

POST ?action=create_online_payment (alias: action=create_qris)
Eza Pedia Gateway Real-Time

Membangkitkan tagihan pembayaran online dan kode QRIS Dinamis resmi dari Eza Pedia Gateway. Aplikasi kasir (seperti APK Android SimpleApp) langsung menerima properti qr_image_url untuk ditampilkan ke pelanggan tanpa membuat data QR dummy lokal.

Payload Request (JSON POST):
{
  "amount": 50000,                            // Nominal belanja / tagihan (min. Rp 1.000)
  "order_id": "QRIS-1730000000000",          // ID Order unik dari APK / POS
  "channel": "QRIS",                          // "QRIS", "BCA", "BRI", "BNI", "MANDIRI", "DANA", "OVO", dll
  "payment_method": "QRIS",                   // Alias channel
  "customer_name": "Pelanggan POS"            // Nama pelanggan (opsional)
}
Contoh Response Tagihan (200 OK):
{
  "status": "success",
  "success": true,
  "message": "Tagihan pembayaran online Eza Pedia berhasil dibuat.",
  "data": {
    "order_id": "QRIS-1730000000000",
    "amount": 50000,
    "unique_code": 87,                                           // Kode unik acak 2 digit (11-99) atau nominal tetap
    "total_amount": 50087,                                       // Total tepat yang harus dibayar/ditransfer pelanggan
    "fee_amount": 87,
    "merchant_name": "TOKO BERKAH JAYA",                         // Nama Merchant Resmi hasil ekstraksi Tag 59 EMVCo QRIS
    "merchant_city": "JAKARTA",                                  // Kota Merchant Tag 60
    "nmid": "ID1020023456789",                                   // National Merchant ID Tag 26-51
    "postal_code": "12950",
    "payment_method": "QRIS",
    "channel": "QRIS",
    "qr_image_url": "https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=...", // Gambar QRIS dinamis bernominal tepat
    "qr_content": "00020101021226600016ID.CO.EZAPEDIA.WWW...",   // String EMVCo QRIS dinamis asli
    "va_number": "",                                             // Nomor Rekening / VA jika metode Bank / E-Wallet
    "account_name": "TOKO BERKAH JAYA",
    "status": "PENDING",                                         // Status awal
    "paid": false,
    "is_paid": false,
    "expired_at": "2026-10-30 15:30:00"
  }
}
GET POST ?action=get_merchant_info (alias: action=get_ezapedia_merchant)
Data Merchant Resmi QRIS Statis

Mengambil data merchant resmi hasil ekstraksi QRIS statis kasir/toko (Tag 59, 60, 26-51, 61) serta pengaturan biaya admin / kode unik. Endpoint ini digunakan oleh aplikasi Android untuk menampilkan nama merchant asli dan NMID tepat di atas bingkai QRIS dinamis yang di-generate.

Contoh Response Data Merchant (200 OK):
{
  "status": "success",
  "success": true,
  "message": "Data merchant resmi Eza Pedia Gateway berhasil diambil.",
  "data": {
    "merchant_name": "TOKO BERKAH JAYA",                         // Nama toko resmi dari QRIS
    "merchant_city": "JAKARTA",                                  // Kota asal toko
    "nmid": "ID1020023456789",                                   // NMID resmi
    "postal_code": "12950",                                      // Kode pos
    "source": "cashier",                                         // Sumber konfigurasi: "cashier" (mandiri), "store", atau "system"
    "fee_enabled": true,                                         // Status switch biaya admin/kode unik
    "fee_type": "random_2digit",                                 // "random_2digit" (Rp 11-99) atau "fixed"
    "fee_amount": 0,                                             // Nominal fee jika tipe fixed
    "has_static_qris": true
  }
}
GET POST ?action=get_cashier_gateway (alias: action=get_user_gateway, get_ezapedia_config)
Gateway Mandiri Kasir / Outlet

Mengambil seluruh data konfigurasi Eza Pedia Gateway yang diatur khusus untuk akun kasir/toko cabang dari menu admin/user_form.php?id=. Termasuk: String EMVCo QRIS statis kasir, metadata merchant asli (Tag 59, 60, NMID), daftar saluran pembayaran aktif (QRIS, 11 Bank, 5 E-Wallet), aturan biaya admin (2 digit acak vs nominal tetap), dan paket aplikasi listener terpantau (monitored apps).

Jika parameter amount disertakan (misal amount=4000), endpoint otomatis menghitung kode unik / admin fee dan menghasilkan QRIS dinamis langsung dengan nominal tepat yang siap diproses dan dipantau oleh aplikasi listener Android.

Parameter Request (GET / POST JSON):
{
  "id": 5,                               // ID Kasir (user_id dari admin/user_form.php?id=5)
  "cashier_username": "kasir_utama",     // Username kasir (opsional jika id tidak diisi)
  "amount": 4000,                        // Nominal belanja untuk kalkulasi & dynamic QRIS (opsional)
  "channel": "QRIS",                     // "QRIS", "BCA", "DANA", dll (opsional)
  "order_id": "QRIS-20261030-001",       // ID transaksi (opsional, jika ingin langsung dicatat di riwayat)
  "create_transaction": true             // Boolean jika ingin dicatat di gateway_transactions & online_transactions
}
Contoh Response (200 OK):
{
  "status": "success",
  "success": true,
  "message": "Data Eza Pedia Gateway kasir berhasil dimuat.",
  "data": {
    "user_id": 5,
    "cashier_id": 5,
    "admin_id": 1,
    "cashier_username": "kasir_utama",
    "store_name": "Toko Berkah Cabang 1",
    "gateway_enabled": true,
    "has_qris": true,
    "merchant_name": "Eza Pedia",
    "merchant_city": "Kota Tangerang",
    "nmid": "ID1026492947555",
    "postal_code": "15226",
    "static_qris_payload": "00020101021126570011ID.DANA.WWW...",
    "fee_enabled": true,
    "fee_type": "random_2digit",
    "fee_amount": 0,
    "channels": [
      { "code": "QRIS", "name": "QRIS Dinamis / Statis", "active": true },
      { "code": "BCA", "name": "Bank BCA", "account_number": "1234567890", "active": true }
    ],
    "monitored_apps": [
      "com.bca", "id.co.bri.brimo", "id.bmri.livin", "id.bni.mobile", "id.dana", "ovo.id"
    ],
    "calculation": {
      "original_amount": 4000,
      "fee_enabled": true,
      "fee_type": "random_2digit",
      "unique_code": 47,
      "total_amount": 4047,
      "dynamic_qris_payload": "00020101021226570011ID.DANA.WWW...54044047...",
      "dynamic_qris_image": "https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=..."
    },
    "webhook_url": "https://pos.ezapedia.my.id/api/gateway_receiver.php"
  }
}
POST GET ?action=check_online_payment (alias: action=check_qris_status)
Auto-Refresh Status Pembayaran (Polling 3s)

Aplikasi Android memanggil endpoint ini secara periodik (setiap 3 detik). Sistem memeriksa status dari tabel online_transactions lokal, sinkronisasi webhook otomatis, serta cek langsung ke gateway server. Ketika pelanggan selesai membayar, server langsung mengembalikan "status": "PAID" dan "paid": true sehingga aplikasi kasir langsung auto-refresh, menampilkan notifikasi lunas hijau, dan menyelesaikan transaksi.

Payload Request:
{
  "order_id": "QRIS-1730000000000"
}
Contoh Response Saat Sudah Lunas (200 OK):
{
  "status": "success",
  "success": true,
  "message": "Pembayaran online berhasil diverifikasi.",
  "data": {
    "order_id": "QRIS-1730000000000",
    "status": "PAID",        // HURUF BESAR: Langsung memicu checkRes.data.status === 'PAID'
    "paid": true,            // BOOLEAN: Memicu checkRes.data.paid === true
    "is_paid": true,
    "amount": 50000,
    "payment_method": "QRIS",
    "paid_at": "2026-10-29 15:05:12"
  }
}
POST /api/gateway_receiver.php
Webhook Receiver Mutasi Android

Endpoint yang dipanggil oleh aplikasi Android listener (seperti SimpleApp Eza POS) ketika mendeteksi notifikasi mutasi uang masuk dari bank atau e-wallet (BCA, Mandiri, BRI, BNI, DANA, GoPay, OVO, ShopeePay). Tidak perlu pairing code atau scan QR! Cukup sertakan header X-Cashier-Username: username_kasir atau field username pada JSON payload.

Payload Request Mutasi (JSON POST):
{
  "amount": 50000,                            // Nominal mutasi masuk
  "bank_name": "BCA",                         // "BCA", "BRI", "MANDIRI", "DANA", "GOPAY", "OVO", "SHOPEEPAY", dll
  "sender_name": "BUDI SANTOSO",              // Nama pengirim pada mutasi
  "description": "QRIS DARI BUDI SANTOSO",     // Keterangan / notifikasi lengkap
  "reference_id": "WS123456789",              // Ref / ID mutasi bank
  "idempotency_key": "idemp_bca_50000_...",   // Anti-double processing key
  "device_id": "and_dev_982312",              // ID perangkat HP kasir
  "device_name": "HP Kasir Meja 1",           // Nama perangkat HP
  "username": "kasir_utama"                   // USERNAME KASIR TOKO (Identitas unik toko)
}
Contoh Response (200 OK):
{
  "status": "success",
  "success": true,
  "message": "Mutasi Rp 50.000 cocok dan tagihan 'QRIS-1730000000000' berhasil ditandai LUNAS.",
  "data": {
    "matched": true,
    "order_id": "QRIS-1730000000000",
    "amount": 50000,
    "bank_name": "BCA"
  }
}
POST ?action=update_payment_status
Update Status Pembayaran & Nomor Invoice Sama

Digunakan ketika ada perubahan status pembayaran di aplikasi kasir Android (misal: pesanan Tempo/Kasbon yang dilunasi, transfer bank yang telah dikonfirmasi, atau perubahan metode pembayaran). Endpoint ini mempertahankan nomor invoice yang sama di database server tanpa menduplikasi baris transaksi atau memotong stok ulang.

Payload JSON:
{
  "sale_id": "TRX-20261026-0001",           // Nomor Invoice / ID Transaksi
  "status": "Sudah Dibayar",                // "Sudah Dibayar", "Selesai", "Pending", dll
  "metode_pembayaran": "cash",              // "cash", "transfer", "qris", "edc", dll
  "uang_bayar": 50000,                      // Jumlah uang yang dibayarkan
  "kembalian": 14000,                       // Uang kembalian
  "customer_name": "Pak Joko Santoso",      // Nama pembeli (opsional)
  "customer_phone": "08123456789",          // No WA pembeli (opsional)
  "transfer_sender_name": "Joko S"          // Nama pengirim jika transfer (opsional)
}
Contoh Response (200 OK):
{
  "status": "success",
  "success": true,
  "message": "Status transaksi dan pembayaran invoice 'TRX-20261026-0001' berhasil diperbarui di server.",
  "data": {
    "sale_id": "TRX-20261026-0001",
    "order_number_daily": 1,
    "status": "Sudah Dibayar",
    "metode_pembayaran": "cash",
    "total_belanja": 36000,
    "uang_bayar": 50000,
    "kembalian": 14000,
    "customer_name": "Pak Joko Santoso",
    "customer_phone": "08123456789",
    "updated_at": "2026-10-27 08:50:00"
  }
}
POST ?action=update_product
Update Produk & Stok dari Aplikasi Android

Memungkinkan aplikasi Android luar jaringan untuk memperbarui informasi produk (nama, harga modal, harga jual normal, harga reseller, deskripsi) serta stok fisik produk secara real-time. Juga tersedia endpoint batch ?action=batch_update_products untuk banyak produk sekaligus.

Payload JSON Single Product:
{
  "product_id": 101,                        // ID Produk
  "name": "Kopi Susu Gula Aren Premium",    // Nama baru (opsional)
  "regular_price": 20000,                   // Harga jual normal
  "reseller_price": 17000,                  // Harga reseller / grosir
  "base_price": 9000,                       // Harga modal
  "stock_quantity": 60,                     // Atur jumlah stok fisik saat ini
  "description": "Espresso arabika blend dengan gula aren asli"
}
Payload JSON Batch Update Produk:
// POST ?action=batch_update_products
{
  "products": [
    { "id": 101, "regular_price": 20000, "stock_quantity": 50 },
    { "id": 102, "regular_price": 15000, "stock_quantity": 35 }
  ]
}
POST ?action=update_profile
Update Profil Kasir & Pengaturan
Proteksi Keamanan Username: Kasir DILARANG KERAS mengubah data username kasir. Jika ada percobaan pengiriman username yang berbeda, server otomatis menolak request dengan status 403 Forbidden. Username berfungsi sebagai API Key unik dan identitas akun permanen.

Kasir dapat memperbarui nama lengkap, ganti password, nama toko cabang, alamat, no telepon, dan konfigurasi printer thermal.

Payload JSON:
{
  "nama_lengkap": "Budi Santoso, S.Kom",    // Nama lengkap tampilan kasir
  "password": "password_baru123",           // Ganti password (opsional, min 4 karakter)
  "store_name": "Kedai Kopi Cabang 2",      // Nama toko cabang kasir
  "store_address": "Jl. Raya Barat No. 88", // Alamat toko
  "store_phone": "081298765432",            // Nomor WA toko
  "footer_note": "Terima kasih atas kunjungan Anda!",
  "printer_name": "MPT-II (Bluetooth)",
  "paper_width": "58mm"
}
POST / GET ?action=sync_all
Universal Sync-All Dua Arah (Bi-Directional Sync)

Endpoint all-in-one yang dirancang untuk sinkronisasi total dua arah antara aplikasi Android dan server cloud. Mengirimkan seluruh perubahan lokal (transaksi baru, pelunasan nota, stok, produk, kontak) sekaligus menarik seluruh data master terbaru toko (katalog produk lengkap, stok real-time, kategori, metode pembayaran, printer thermal, voucher, riwayat transaksi) dalam 1 kali network request.

Mode Dua Arah (Bi-Directional Sync):
• Jika mengirim JSON Payload: Server memproses pembaruan lokal (sales, stock, status bayar), lalu mengembalikan snapshot data terbaru.
• Jika Request Kosong / GET: Server langsung mengembalikan seluruh snapshot data toko, produk, stok, metode pembayaran, dan riwayat transaksi secara instan tanpa perlu upload.
Payload JSON (Opsional jika ada data upload offline):
{
  "sales": [                                // Transaksi offline (otomatis upsert invoice)
    {
      "id": "TRX-20261027-0001",
      "order_number_daily": 1,
      "total_belanja": 45000,
      "metode_pembayaran": "cash",
      "status": "Sudah Dibayar",
      "items": [{ "id": 101, "quantity": 2, "price": 20000 }]
    }
  ],
  "payment_updates": [                      // Perubahan status pembayaran nota lama
    { "sale_id": "TRX-20261026-0005", "status": "Sudah Dibayar", "metode_pembayaran": "transfer" }
  ],
  "stocks": {                               // Pembaruan stok produk
    "101": 55,
    "102": 40
  },
  "contacts": [                             // Kontak pelanggan baru
    { "name": "Pak Rudi", "phone": "0812345678" }
  ]
}
Contoh Response Lengkap (200 OK):
{
  "status": "success",
  "success": true,
  "message": "Sinkronisasi total seluruh data kasir berhasil diproses di server. Data master toko, katalog produk, stok, metode pembayaran, dan riwayat berhasil dimuat.",
  "data": {
    "upload_summary": {                     // Ringkasan hasil upload data lokal
      "sales": { "synced": 1, "inserted": 1, "updated": 0, "errors": [] },
      "payment_updates": { "updated": 1 },
      "stocks": { "updated": 2 },
      "products": { "updated": 0 },
      "contacts": { "imported": 1 },
      "profile_updated": false
    },
    "store_info": {                         // Informasi lengkap profil toko & kasir
      "admin_id": 1,
      "kasir_id": 5,
      "username": "kasir",
      "store_name": "Toko Berkah Sejahtera",
      "cashier_name": "Kasir Utama",
      "store_address": "Jl. Merdeka No. 10",
      "store_phone": "08123456789",
      "footer_note": "Terima kasih atas kunjungan Anda"
    },
    "categories": [                         // Daftar seluruh kategori kasir
      { "id": 1, "name": "Makanan" },
      { "id": 2, "name": "Minuman" }
    ],
    "products": [                           // Daftar seluruh produk + stok + harga real-time
      {
        "id": 101,
        "name": "Kopi Susu Gula Aren",
        "category_id": 2,
        "category_name": "Minuman",
        "base_price": 8000,
        "regular_price": 18000,
        "reseller_price": 15000,
        "stock_quantity": 55,
        "image_url": "https://domain.com/uploads/kopi.jpg"
      }
    ],
    "stock_map": {                          // Mapping cepat product_id => stok
      "101": 55
    },
    "payment_methods": { ... },             // Metode pembayaran offline & Eza Pedia gateway
    "printer_settings": { ... },            // Konfigurasi printer thermal ESC/POS
    "vouchers": [ ... ],                    // Daftar voucher diskon aktif
    "daily_sales": [ ... ],                 // Riwayat transaksi kasir hari ini
    "contacts": [ ... ],                    // Buku telepon pelanggan
    "active_shift": { ... },                // Shift kerja kasir yang sedang aktif
    "server_time": "2026-10-27 09:20:00"
  }
}

Rekomendasi Skema Database SQLite Lokal (Untuk APK Android)

Struktur tabel lokal pada Room / SQLite / WatermelonDB agar aplikasi bekerja optimal secara offline.

-- 1. Tabel Produk Lokal (Disimpan di HP)
CREATE TABLE products (
    id INTEGER PRIMARY KEY,
    category_id INTEGER,
    name TEXT NOT NULL,
    description TEXT,
    base_price REAL,
    regular_price REAL,
    reseller_price REAL,
    stock_quantity INTEGER DEFAULT 0,
    image_url TEXT,
    type TEXT DEFAULT 'physical',
    is_pack INTEGER DEFAULT 0
);

-- 2. Tabel Kategori Lokal
CREATE TABLE categories (
    id INTEGER PRIMARY KEY,
    name TEXT NOT NULL
);

-- 3. Tabel Keranjang & Transaksi Offline
CREATE TABLE offline_sales (
    id TEXT PRIMARY KEY,               -- e.g. "TRX-20261026-0001"
    order_number_daily INTEGER,
    items_json TEXT NOT NULL,          -- JSON array item beserta catatan (note)
    subtotal REAL,
    voucher_code TEXT,
    voucher_discount REAL DEFAULT 0,
    total_belanja REAL NOT NULL,
    metode_pembayaran TEXT DEFAULT 'cash',
    uang_bayar REAL,
    kembalian REAL,
    customer_name TEXT,
    customer_phone TEXT,
    transaction_time TEXT NOT NULL,
    is_synced INTEGER DEFAULT 0        -- 0 = Belum Sync, 1 = Sudah Terkirim ke Cloud
);

-- 4. Tabel Shift Kasir Lokal
CREATE TABLE shifts (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    server_shift_id INTEGER,
    start_time TEXT,
    end_time TEXT,
    start_balance REAL,
    end_balance REAL,
    status TEXT DEFAULT 'open'
);

-- 5. Tabel Pesanan Ditahan Lokal (Held Orders)
CREATE TABLE held_orders (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    server_held_id INTEGER,
    order_name TEXT NOT NULL,
    cart_json TEXT NOT NULL,
    created_at TEXT NOT NULL
);

-- 6. Tabel Buku Telepon & Kontak Lokal
CREATE TABLE contacts (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    server_contact_id INTEGER,
    contact_name TEXT NOT NULL,
    contact_phone TEXT NOT NULL,
    created_at TEXT
);

-- 7. Tabel Pengaturan Printer Thermal Lokal
CREATE TABLE printer_settings (
    id INTEGER PRIMARY KEY DEFAULT 1,
    printer_type TEXT DEFAULT 'bluetooth',
    printer_name TEXT,
    paper_width TEXT DEFAULT '58mm',
    auto_print TEXT DEFAULT 'yes'
);

Live API Tester (Uji Coba Langsung)

Coba kirim permintaan langsung ke server dan lihat format JSON responsnya

Interactive Tool
Request Payload (JSON Body) GET (Kosong = Tarik Data)

* Tip: Untuk sync_all, jika JSON Body diisi data lokal, server akan meng-upload & memperbarui data; jika dikosongkan, server akan otomatis menarik seluruh master data toko & produk secara lengkap.

Hasil JSON Response Server: -
Klik tombol "Kirim Request Live" di atas untuk mencoba API.