# Mekanisme Pencegahan Race Condition - Pelunasan Faktur

Dokumen ini menjelaskan arsitektur dan logika sistem untuk mencegah pelunasan ganda (*double payment*) pada faktur yang sama dengan metode **Short-Lived Pessimistic Lock + Heartbeat Mechanism**.

---

## 📌 Alur Kerja Utama (Workflow)

Sistem menggunakan pendekatan *stateless session lock* berbasis waktu (TTL) pendek yang diperpanjang secara otomatis oleh browser selama pengguna aktif di halaman pelunasan.

1. **Pengguna A** membuka halaman Faktur X -> Sistem menerbitkan kunci (Lock) dengan TTL 60 detik dan memberikan token unik ke browser Pengguna A.
2. **Pengguna B** mencoba membuka Faktur X saat kunci masih aktif -> Sistem menolak akses karena faktur sedang dikunci.
3. Selama halaman terbuka, browser **Pengguna A** mengirim AJAX *Heartbeat* setiap 30 detik untuk memperbarui masa berlaku kunci (reset kembali ke 60 detik).
4. Jika **Pengguna A** menekan tombol "Batal" atau menyelesaikan pembayaran -> Kunci langsung dihapus (Unlock) saat itu juga.
5. Jika **Pengguna A** menutup browser tanpa *logout* -> Sinyal *heartbeat* terhenti, dan kunci akan otomatis lepas sendiri dalam 60 detik.

---

## 🔄 Rincian Mekanisme Kondisi

### 1. Pembukaan Halaman (Inisiasi Lock)
* Saat Pengguna A membuka halaman pelunasan Faktur X, sistem memeriksa status kunci di database.
* **Syarat Kunci Didapatkan:** Faktur belum dikunci **ATAU** masa berlaku kunci sebelumnya (`locked_until`) sudah melewati waktu sekarang (kedaluwarsa).
* Jika syarat terpenuhi, sistem mengunci faktur dengan menyematkan `ID User`, waktu `sekarang + 60 detik`, serta sebuah `Lock Token` (string acak unik) yang dikirim ke browser Pengguna A.
* Jika faktur sedang dikunci oleh orang lain dan belum kedaluwarsa, Pengguna B akan langsung ditolak dan dialihkan kembali ke halaman daftar faktur dengan pesan peringatan.

### 2. Mempertahankan Kunci (Heartbeat Mechanism)
* Karena protokol web bersifat *stateless*, sistem tidak tahu jika pengguna menutup browser tanpa *logout*.
* **Solusi:** Browser Pengguna A wajib mengirimkan sinyal AJAX (*heartbeat*) secara otomatis setiap **30 detik** ke server.
* Request AJAX wajib membawa `ID Faktur` dan `Lock Token`.
* Jika token cocok, server akan memperbarui masa berlaku kunci (`locked_until`) menjadi `waktu sekarang + 60 detik`.

### 3. Pelepasan Kunci (Unlock)
Mekanisme pelepasan kunci dibagi menjadi 3 skenario:

* **Skenario A: Pembayaran Berhasil**
  Ketika Pengguna A menekan tombol "Bayar", server memvalidasi `Lock Token`. Jika valid, transaksi diproses dan seluruh kolom penguncian langsung dihapus (`NULL`).
* **Skenario B: Pengguna Membatalkan Proses**
  Jika Pengguna A menekan tombol "Batal", sebuah skrip JavaScript akan menembak API *Unlock* via AJAX untuk menghapus kunci di database secara instan sebelum halaman berpindah. Faktur langsung tersedia untuk orang lain tanpa jeda.
* **Skenario C: Pengguna Menutup Browser / Mati Lampu (Abrupt Exit)**
  Jika pengguna keluar tanpa *logout* atau menutup *tab* browser, sinyal *heartbeat* otomatis terhenti. Dalam waktu maksimal **60 detik**, masa berlaku kunci akan habis (expired). Pengguna lain (Pengguna B) secara otomatis dapat mengambil alih kunci saat mereka memuat ulang halaman faktur tersebut.

---

## ⚠️ Proteksi Lapisan Akhir (Garda Final)

Untuk menghindari celah di mana Pengguna A mendiamkan formulir sangat lama lalu menekan tombol "Bayar" tepat ketika kuncinya baru saja diambil alih oleh Pengguna B, sistem menerapkan validasi ganda pada fungsi *submit*:

1. Server akan memeriksa apakah `Lock Token` yang dikirim dari formulir masih cocok dengan yang ada di database.
2. Server memeriksa apakah waktu `locked_until` belum kedaluwarsa.
3. Jika token tidak cocok atau sudah kedaluwarsa, proses pembayaran langsung **dibatalkan** demi keamanan data, dan pengguna diminta memuat ulang halaman.
