Sistem Keuangan Santri Latee

Aplikasi kartu/wallet cashless untuk mengelola keuangan santri di Pondok Pesantren Latee (Annuqayah): saldo santri, tagihan rutin (SPP dll), penarikan tunai dengan verifikasi sidik jari, dan top up saldo oleh wali santri lewat transfer/e-wallet (Midtrans). Dibangun sebagai satu aplikasi Laravel + Livewire, dengan API terpisah untuk kebutuhan aplikasi mobile wali. Ada juga halaman kios (/kios) — layar tap-kartu publik tanpa login untuk cek saldo santri & ajukan penarikan tunai.

Operasional produksi

Sebelum instalasi, perpindahan domain, atau migrasi provider hosting, ikuti halaman Deployment & Mitigasi Hosting. Panduan tersebut mencakup kontrak JSON web–mobile, smoke test, observabilitas, dan rollback.

Peran & Akses

RoleAkses
AdminAkses penuh: kelola pengguna, santri, kartu, tagihan, verifikasi transaksi, pengaturan
BendaharaOperasional keuangan: tagihan, transaksi, top up, penarikan, laporan keuangan, dan leger kas; tidak mengelola data kesantrian, kantin, pengguna, perangkat, backup, atau pengaturan sistem
PengasuhAkses baca saja — dashboard & laporan santri, tidak ada aksi yang mengubah data
WaliLihat saldo & tagihan anak (bisa lebih dari satu, otomatis terkelompok lewat No. KK), bayar tagihan dari saldo atau langsung via Midtrans, top up saldo, bayar kantin via QR, transfer saldo antar anak dalam satu KK — empat aksi pemindah saldo terakhir dari aplikasi mobile digerbangi PIN transaksi 6 digit (lihat PinService di halaman API Wali)
SantriLihat saldo, tagihan, riwayat transaksi, dan mengajukan request penarikan tunai
PengelolaAkun pemilik/pengelola satu unit usaha (kantin/koperasi) — portal self-service (/pengelola) untuk lihat dashboard, ajukan pencairan saldo, ajukan ganti rekening bank, dan tampilkan QR pembayaran
Dev (role ini)Akses ke dokumentasi internal aplikasi — tidak punya akses ke data operasional/keuangan

Alur Uang (Ringkas)

  • Saldo santri disimpan sebagai satu baris per santri (saldo_santris), tapi sumber kebenarannya adalah ledger transaksi (transaksis) — setiap perubahan saldo selalu tercatat sebagai baris ledger yang tidak bisa diubah/dihapus (immutable), lengkap dengan saldo sebelum & sesudah untuk audit.
  • Semua mutasi saldo wajib lewat App\Services\WalletService (credit()/debit()), yang mengunci baris saldo (row lock) dan membungkus perubahan saldo + pencatatan ledger dalam satu transaksi database — mencegah race condition saat ada beberapa transaksi bersamaan.
  • Seluruh penerimaan tunai wajib diproses petugas kios melalui sesi kas aktif. Admin hanya memantau transaksi dan memverifikasi sesi, sehingga tidak ada setoran tunai yang dapat melewati audit perangkat, petugas, dan uang laci.
  • Penarikan tunai tidak bisa dibuat langsung oleh petugas — harus diawali request dari santri (PenarikanRequest), baru petugas bisa memverifikasi & mencairkan. Aturan ini dipaksakan di dua lapis: alur kerja (PenarikanService) dan model event (Transaksi::creating) sebagai lapis pertahanan kedua.
  • Top up wali lewat Midtrans selalu masuk 100% ke saldo santri — tidak ada lagi pemotongan otomatis untuk tagihan (TopupWaliService::settle(), dipicu webhook Midtrans yang idempoten, aman jika notifikasi yang sama terkirim berkali-kali).
  • Membayar tagihan lewat dua opsi eksplisit yang wali pilih sendiri: dari saldo (TagihanService::bayarDariSaldo(), ditolak oleh SaldoFloorService kalau hasilnya akan membuat saldo santri di bawah batas minimum yang admin atur), atau langsung via Midtrans untuk nominal persis sisa tagihan tanpa menyentuh saldo sama sekali (TopupWaliService::createSnapTransactionForTagihan()).
  • Wali & santri saling terhubung otomatis lewat No. KK (nomor kartu keluarga) — satu akun wali dengan beberapa anak di pondok otomatis melihat semuanya, tanpa perlu ditautkan manual satu-satu (kecuali kasus khusus, yang bisa ditautkan manual oleh admin).
  • Akun wali tidak harus dibuat manual satu-satu: WaliAccountService bisa membuatkan akun default (No. KK sebagai login & kata sandi awal, wajib diganti saat login pertama) langsung dari form Tambah Santri, halaman Data Keluarga, atau massal untuk semua keluarga yang belum punya wali sekaligus (termasuk opsional saat Import Excel).
  • Santri bisa membayar di kantin/koperasi pondok lewat QR code (KantinPembayaranService, memotong saldo santri & mengkredit saldo_unit milik unit usaha bersangkutan secara atomik), atau wali bisa memindahkan saldo langsung ke saudaranya dalam satu KK (TransferSaldoService, tanpa persetujuan admin karena uangnya tidak pernah keluar dari pondok) — keduanya, sama seperti bayar tagihan dari saldo, ditegakkan SaldoFloorService dan mewajibkan PIN transaksi (PinService) di sisi wali.

Teknologi

  • Backend: Laravel 13, PHP 8.4.1+
  • Frontend: Livewire 4 + Blade, Tailwind CSS v4
  • Database: MySQL
  • Auth web: Session — email/No. KK untuk wali, email untuk staf lain, NIS untuk santri (deteksi otomatis dari format input, lihat Auth/LoginForm)
  • Auth API: Laravel Sanctum (Bearer token, dengan ability per jenis klien — wali untuk aplikasi mobile wali, kiosk untuk perangkat kiosk pondok)
  • Payment gateway: Midtrans Snap (kredensial diatur admin lewat /admin/pengaturan/midtrans, tersimpan terenkripsi di database)
  • Import/export data besar: Laravel Excel (chunked, aman untuk data santri dalam jumlah besar)
  • Testing: Pest

Struktur Kode Penting

LokasiIsi
app/Services/Seluruh logika bisnis inti: WalletService, TagihanService, PenarikanService, TopupWaliService, SaldoFloorService (batas minimum saldo, dipakai bayar tagihan dari saldo/bayar kantin/transfer antar santri), PinService (PIN transaksi 6 digit + lockout untuk ketiga aksi pemindah saldo mobile), KantinPembayaranService + UnitUsahaWalletService (bayar kantin), TransferSaldoService (transfer saldo antar santri 1 KK), UnitUsahaPenarikanService + UnitUsahaRekeningService (pencairan & ganti rekening kantin), KeluargaLinkingService, WaliAccountService / PengelolaAccountService (buat akun wali/pengelola otomatis/massal), AppSettingsService (nama aplikasi/pondok/kontak dan logo yang diunggah admin lewat /admin/pengaturan/aplikasi — dipakai di seluruh layout web, halaman login, favicon, Kartu Santri, invoice/laporan PDF, dan diekspos ke aplikasi mobile lewat GET /api/wali/app-info), LaporanKeuanganService, LegerKasPondokService, DashboardService, BackupService, MidtransSettingsService, TrustedDeviceFingerprintVerifier
app/Models/Model Eloquent — lihat Transaksi untuk aturan ledger immutable, SaldoSantri untuk saldo per santri, UnitUsahaTransaksi untuk ledger kedua (kantin) dengan aturan immutable yang sama
app/Http/Middleware/EnsurePasswordIsChanged.phpMengunci user dengan must_change_password=true ke halaman /profil sampai kata sandinya diganti — lihat komentar di dalamnya soal kenapa deteksi request Livewire pakai header X-Livewire, bukan path URL
app/Livewire/Komponen UI per area: Admin/, Pengasuh/, Wali/, Santri/, Kios/ (halaman publik tanpa login), Profil/, Dev/ (halaman ini)
app/Http/Controllers/Api/Wali/Controller REST API untuk aplikasi mobile wali
app/Http/Controllers/Api/Kiosk/Controller REST API untuk perangkat kiosk fisik (cek saldo, verifikasi sidik jari) — beda dari halaman web /kios, lihat Dokumentasi API Kiosk
routes/web.phpRute web per role (/admin, /pengasuh, /wali, /santri, /dev) plus rute publik tanpa login: /login, /kios
routes/api.phpRute API (/api/wali/*, /api/kiosk/*)
tests/Feature/Test Pest — mencakup aturan integritas saldo, penarikan, Midtrans, penautan wali-santri, akun wali otomatis, dan API

Backup & Restore

Halaman /admin/backup (khusus role admin, tidak untuk bendahara) memakai spatie/laravel-backup. Satu backup berisi dump database penuh + seluruh berkas privat (storage/app/private — surat keterangan, foto santri), dikompres jadi satu file zip di disk backups (storage/app/backups, terpisah dari disk local supaya backup tidak ikut membackup dirinya sendiri).

  • Restore hanya mengembalikan bagian database, bukan berkas privat — keputusan sadar untuk menghindari kompleksitas mereplikasi struktur zip backup berkas milik spatie. Kalau berkas privat pernah perlu dipulihkan, ekstrak manual dari zip yang diunduh.
  • Restore wajib diketik ulang kata konfirmasi PULIHKAN (lihat BackupService::KODE_KONFIRMASI_PULIHKAN) sebelum dieksekusi.
  • Sebelum data diganti, sistem selalu membuat backup pengaman dari kondisi saat ini terlebih dahulu (tanpa syarat) — jadi restore tetap bisa dibatalkan meski salah pilih file.
  • Proses penggantian database dibungkus mode maintenance (artisan down/up) di dalam try/finally, supaya aplikasi tetap kembali menyala walau proses restore gagal di tengah jalan.
  • Nama file backup selalu disaring lewat basename() sebelum dipakai untuk unduh/hapus/restore, mencegah path traversal dari input pengguna.
  • Karena test suite Pest jalan di atas SQLite in-memory, round-trip mysqldump/mysql yang sesungguhnya tidak bisa diuji otomatis — sudah diverifikasi manual terhadap MySQL asli di lingkungan dev, termasuk mensimulasikan proses web server tanpa PATH shell interaktif.
  • Lokasi binary database: pada Linux/shared hosting, kosongkan DB_DUMP_BINARY_PATH agar sistem mencari mysqldump/mysql dari PATH dan lokasi umum server. Pada Windows/Laragon, isi dengan folder bin MySQL memakai forward slash. Setelah mengubah .env, jalankan php artisan optimize:clear lalu php artisan config:cache. Jika hosting memang tidak menyediakan kedua binary, halaman tetap siap dan otomatis memakai mode kompatibel PHP/PDO untuk membuat maupun memulihkan dump.

Batasan Saat Ini

Fitur berikut belum diimplementasikan penuh — arsitekturnya sudah disiapkan, tapi butuh perangkat keras/kebutuhan nyata untuk dilanjutkan:

  • Sidik jari fisik: sistem memakai FingerprintVerifier sebagai interface yang bisa diganti (TrustedDeviceFingerprintVerifier saat ini hanya mencocokkan referensi yang dikirim perangkat kiosk terhadap data kartu — pencocokan biometrik sungguhan terjadi di perangkat, bukan di server).
  • Multi-lembaga penuh: tabel lembagas sudah ada dan dipakai untuk pengelompokan tagihan, tapi belum ada UI pengelolaan lembaga lintas-institusi yang lengkap.
  • Reset PIN/kata sandi wali hanya bisa lewat admin (/admin/users) — belum ada alur self-service "lupa PIN"/"lupa kata sandi" dari aplikasi mobile itu sendiri.

Modul kantin (pembayaran QR, ledger unit usaha, pencairan & ganti rekening pengelola) sudah berjalan penuh, bukan lagi placeholder — lihat domain 8 di halaman Skema Database. Notifikasi push (Firebase Cloud Messaging) untuk tagihan baru & pengingat jatuh tempo juga sudah berjalan (PushNotificationService, WaliDeviceToken) — bukan lagi polling.

Dokumen Proyek

Product Requirements Document lengkap (ringkasan, arsitektur, seluruh kebutuhan fungsional, model data, keamanan, dan lampiran rute/endpoint) tersedia sebagai file yang bisa diunduh, selalu digenerate langsung dari halaman ini sehingga PDF dan Word tidak pernah berbeda isi satu sama lain:

Diagram ERD (Entity-Relationship Diagram) seluruh skema database - 25 tabel yang sudah berjalan ditandai hijau, tabel rencana pengembangan (mis. kwitansi resmi bernomor) ditandai biru: