# Tahap akhir: seluruh file publik yang tersisa

Tanggal: 17 Oktober 2026.

Tahap ini menyelesaikan 12 file terakhir dari daftar pada `PORTING_KATALOG_RENTAL.md`.

| File sumber `pos/` | Handler CI4 |
| --- | --- |
| `booking_api.php` | `BookingApi::index` |
| `checkout_rental.php` | `PublicRentalCheckout::index` |
| `checkout_rental_otp.php` | `PublicRentalCheckout::otp` |
| `checkout_rental_special.php` | `RentalSpecial::index` |
| `checkout_rental_success.php` | `PublicRentalCheckout::success` |
| `forgot_password.php` | `PublicAccount::forgot` |
| `order_meja.php` | `PublicTableOrder::index` |
| `pricing.php` | `Pricing::index` |
| `register.php` | `PublicAccount::register` |
| `tracking.php` | `WablasWebhook::tracking` |
| `upgrade.php` | `Upgrade::index` |
| `web1.php` | `PublicWeb::index` |

Route tersedia dengan dan tanpa `.php`. Alias root `index.php`, `login.php`, `logout.php`, `dashboard.php`, dan `kasir.php` juga disambungkan ke implementasi native CI. Redirect login menggunakan base URL aplikasi; sesi diperbarui setelah login.

## Rental dan booking

`RentalCheckout` memakai mekanisme challenge checkout produk: OTP berhash, bukti kepemilikan acak dalam sesi, masa berlaku 10 menit, maksimal lima percobaan, dan pembatasan permintaan. Sesi pending/selesai/seleksi rental dipisahkan dari keranjang dan pesanan barang biasa.

Harga dihitung dari database, berdasarkan satuan produk. Rental `hari` tetap dihitung sebagai hari meskipun input memuat jam; satuan `jam` dibulatkan ke atas per setengah jam. Total mencakup deposit. Tanggal rusak, periode terbalik, tanggal lampau, periode melebihi 366 hari, jumlah tidak valid, dan total di luar batas ditolak. Input tanggal lokal serta ISO dengan offset/UTC didukung.

Konfirmasi mengunci toko, pesanan/challenge, serta produk rental. Perubahan harga/deposit atau kapasitas yang tidak cukup membatalkan konfirmasi. Penyimpanan customer, transaksi `rental_transactions`, serta status online order dilakukan dalam satu transaksi; kegagalan tidak menghasilkan halaman sukses palsu. Konfirmasi ulang tidak membuat transaksi rental kedua. Rental tidak dimasukkan sebagai penjualan barang dan tidak mengurangi stok produk biasa. Pemberitahuan pemilik dikirim setelah commit.

`RentalAvailability` memeriksa kapasitas pada **dua** tabel reservasi: `rental_transactions` dan `booking_rentals`. Pemeriksaan memakai periode akhir eksklusif dan puncak jumlah unit yang terpakai, sehingga dua reservasi yang tidak bersamaan tidak dijumlahkan keliru. Booking `pending` menjadi hold 15 menit; booking terkonfirmasi/dibayar tetap dihitung. Pembuatan rental dari halaman staff juga memakai pemeriksaan dan urutan lock yang sama.

`booking_api` menerima POST ber-CSRF; tarif, deposit, dan toko diperoleh dari server. Pengiriman ulang booking dengan periode/jumlah sama dapat menggunakan ID booking yang memang tercatat dalam sesi pengunjung. Respons menyertakan token CSRF terbaru. Endpoint tidak lagi mengubah data melalui GET.

Checkout khusus hanya menerima ID yang dimiliki sesi (`my_booking_ids` atau `my_rental_order_ids`). Unggah bukti atau pilihan tunai mengonfirmasi reservasi, **tidak** menandai pembayaran lunas. Booking pending yang sudah kedaluwarsa tidak dapat dihidupkan kembali melalui unggahan. Bukti diperiksa MIME/ukuran (maksimal 5 MB, JPG/PNG/WEBP/PDF), memakai nama acak, dan dibersihkan jika penyimpanan database gagal. Rekening contoh dari template lama diganti informasi rekening toko.

`RentalCalendar` menggabungkan kedua tabel untuk detail rental, jadwal toko, dan `web1`; hanya interval yang memenuhi kapasitas unit ditandai penuh. Kalender tidak membuka identitas pelanggan. Kegagalan baca kalender menampilkan keadaan tidak diketahui; checkout tetap melakukan pemeriksaan server sendiri.

## Registrasi dan reset password

`PublicIdentity` menyimpan challenge di database: hash OTP, hash bukti sesi, batas percobaan, waktu kedaluwarsa, verifikasi, dan pemakaian. Password pendaftaran sudah dihash sebelum dimasukkan ke payload pending; tidak ada password atau OTP terbuka dalam sesi. Pengiriman memakai `CheckoutMessenger`, tanpa log isi OTP dan tanpa retry otomatis pada hasil pengiriman yang tidak pasti.

Pengiriman dibatasi satu per menit dan lima per jam per identitas/nomor. Reset hanya dapat diselesaikan setelah verifikasi challenge yang sama, masih berlaku, dan belum dipakai. Nomor akun diperiksa lagi sebelum password diubah. Permintaan untuk identitas yang tidak ditemukan menggunakan tampilan generik dan tidak mengirim pesan. Resend mengganti challenge dan tidak mewariskan verifikasi sebelumnya.

Pendaftaran memeriksa format, kecocokan password, email/username/telepon, dan kode referal. Penyelesaian diserialkan untuk mencegah identitas ganda pada skema lama tanpa indeks unik yang memadai. Toko, user pemilik dengan role **Super Admin**, role Admin/Kasir beserta menu, unit, kategori, serta outlet/paket Basic/subscription bila tabelnya tersedia dibuat secara atomik. `check_stock` toko baru tetap 0 bila kolom tersedia, sesuai sumber.

Notifikasi referal dipertahankan setelah commit. Nomor pribadi developer yang semula hard-coded tidak disalin sebagai tujuan otomatis; tujuan tambahan hanya digunakan bila `REGISTER_NOTIFY_PHONE` dikonfigurasi. Contohnya ditambahkan ke `env`; `.env` nyata tidak diubah.

## Pesan meja, paket, dan tracking

QR meja memperoleh toko, outlet, dan meja hanya dari token meja aktif. Produk dan harga/promo diperiksa ulang dari database; data `store_id`, harga, atau outlet dari browser tidak dipercaya. Penyimpanan sales/detail dan kunci pengulangan dilakukan atomik. Pesanan berstatus `pending_otp`/`baru` dan sumber `qr_meja`; stok baru bergerak ketika staff mengonfirmasi. Kunci request mencegah submit ulang menghasilkan pesanan kedua. Konfirmasi staff mengunci status pesanan terlebih dahulu untuk menghindari pengurangan stok dua kali. Ringkasan HTML keranjang meng-escape nama produk.

`pricing` menampilkan paket/fitur aktif. `upgrade` tetap memerlukan login, membatasi pembayaran ke toko sesi, menghitung harga server (diskon tahunan 15%), dan membuat subscription/payment pending tanpa menandai lunas. Permintaan pending ganda ditolak; bukti dapat ditambahkan kemudian pada pembayaran pending milik toko yang sama. Bukti menggunakan validasi file yang sama dengan checkout rental.

`tracking.php` menggunakan handler webhook tracking yang sudah diporting, termasuk autentikasi wajib `WABLAS_WEBHOOK_TOKEN`, validasi payload, pemeriksaan pesan/toko, serta penolakan external ID ambigu. Mengonfigurasi callback baru tidak dilakukan oleh proses porting ini.

## Skema dan validasi

Tabel tambahan yang dibuat oleh alur terkait sebelum transaksi: `online_checkout_challenges`, `booking_rentals` bila belum ada, `public_identity_locks`, `public_identity_challenges`, dan `qr_table_requests`. Kolom bukti pembayaran booking ditambahkan bila hilang; `booking_rentals.hours` dan `rental_transactions.duration` berbentuk integer dapat dinaikkan ke DECIMAL agar sewa setengah jam tidak kehilangan durasi. Helper kolom sales/snapshot dijalankan sebelum transaksi. Skema sumber seperti toko, produk/rental, customer, user, role/menu, dan tabel transaksi tetap diperlukan.

Validasi otomatis dan keterbatasan akhir dicatat di [PORTING_SELESAI.md](PORTING_SELESAI.md). Inventaris route menyeluruh tersedia di [PORTING_INVENTARIS.md](PORTING_INVENTARIS.md).
