# Porting API QRIS ke CI4

Pembaruan: 16 Oktober 2026.

`api/qris` dan alias `api/qris.php` sekarang memakai controller `QrisApi`. Endpoint memerlukan sesi login dan toko aktif, mengembalikan JSON tanpa redirect login, serta memakai Cache-Control: no-store. Method GET/POST tersedia; method lain menghasilkan 405.

Pemanggil kasir yang sudah ada (`api/qris.php?amount=...`) tetap bekerja. GET menerima amount pada query. POST juga menerima form atau object JSON; query amount menjadi prioritas bila dikirim. Request ini hanya membentuk payload dan tidak menulis transaksi, saldo, atau status pembayaran.

## Nominal dan konfigurasi

Amount wajib bilangan bulat rupiah antara 1 dan 9999999999999. Nilai desimal, notasi eksponen dalam query, angka negatif, nol, array, dan nilai di luar batas ditolak 400. Digit nol di depan diizinkan dan dinormalisasi.

Konten QRIS diambil dari `stores.qris_content` milik toko sesi; parameter toko dari caller tidak digunakan. Toko tidak aktif menghasilkan 403, QRIS belum dikonfigurasi atau konten tidak valid 422, sesi tidak tersedia 401. Isi QRIS di Pengaturan Pembayaran. Kesalahan database menghasilkan pesan generik 500.

Respons berhasil mempertahankan kontrak sumber:

```json
{"success":true,"qris_string":"...","amount":10000,"store_name":"Nama toko"}
```

## Pembentukan payload

Helper `generateDynamicQris` sekarang mendelegasikan pembentukan ke `App\Libraries\QrisPayload`. Parser memeriksa tag/length numerik, panjang nilai yang lengkap, tag duplikat, payload format 00=01, dan CRC yang valid serta berada di akhir. Input QRIS rusak tidak lagi diproses sebagian.

Tag 01 menjadi 12, tag nominal 54 diganti satu kali, mata uang 53 memakai 360, tag merchant lainnya dipertahankan, dan checksum dihitung ulang. Mata uang lain ditolak. Input yang sebelumnya diterima tanpa CRC atau dengan checksum salah sekarang harus diperbaiki dari konten QRIS asli provider. Validasi ini memeriksa struktur/checksum; tidak membuktikan identitas merchant, registrasi provider, atau penerimaan aplikasi pembayaran.

Kasir menyimpan nominal QRIS yang sedang ditampilkan. Bila total keranjang sudah berubah atau QRIS belum berhasil dibentuk, tombol konfirmasi membentuk ulang QRIS sebelum melanjutkan pembayaran. Pemeriksaan ini tidak mengubah alur konfirmasi manual sumber: tidak ada callback bank atau verifikasi otomatis dana diterima pada tahap ini.

## Verifikasi

- PHPUnit seluruh aplikasi: **260 tes, 2.119 assertion**, lulus.
- Termasuk **14 tes QRIS/inventaris API**: route/alias, sesi, nominal, toko aktif, konfigurasi, respons, POST JSON, CRC dengan known vector, penggantian nominal, preservasi data merchant, struktur rusak/tag duplikat/checksum salah, dan kesalahan database generik.
- Test inventaris memeriksa route CI4 untuk **seluruh 12 file `pos/api/*.php`**, beserta alias tanpa ekstensi dan `.php`.
- Lint **321 file PHP**: tanpa kesalahan sintaks. JavaScript QRIS kasir lolos `node --check`.
- Pengujian memakai koneksi database tiruan dan payload TLV sintetis yang bukan QR merchant untuk pembayaran. Tidak ada transaksi produksi, request provider, scan aplikasi pembayaran, atau transfer dana nyata yang dilakukan. Uji staging dan pemindaian QR merchant yang benar masih diperlukan.

## Status folder API

Seluruh dua belas file sumber sekarang mempunyai route CI4:

| Endpoint | Controller |
| --- | --- |
| api_create_sale, api_check_stock, api_get_invoice | StoreApi |
| webhook_shopee_order | ShopeeWebhook |
| webhook_jaknot_sync | JaknotWebhook |
| wablas_inbox, wablas_tracking | WablasWebhook |
| whatsapp_chat_api, crm_chat | ChatApi |
| qris | QrisApi |
| notifications, switch-outlet | Api |

Cakupan route tidak berarti seluruh integrasi sudah diverifikasi dengan layanan nyata. Batasan tiap endpoint dicatat pada dokumen tahap terkait, termasuk bundle, fallback pengiriman, auto-reply best effort, dan model saldo/stok.

Tahap berikutnya adalah audit/porting cron dan worker WhatsApp, kemudian halaman admin/CRM lain serta halaman publik yang belum dipindahkan. Porting seluruh aplikasi belum dinyatakan selesai.
