BAB 48. QR VALIDATION FLOW
Tujuan
Memastikan peserta benar-benar hadir secara fisik di lokasi pertemuan. QR Code digunakan sebagai lapisan validasi pertama sebelum GPS.
Komponen QR Code
Setiap sesi pertemuan memiliki QR Code unik yang berisi:
{
"meeting_id": 102,
"booking_id": 55,
"token": "a8f3b2c1d9e4f5a6",
"expired_at": "2026-08-10T14:10:00"
}
| Komponen | Deskripsi |
|---|---|
meeting_id | ID sesi pertemuan |
booking_id | ID booking terkait |
token | Token acak 64 karakter (UUID v4) |
expired_at | Waktu kadaluarsa QR (10 menit dari jadwal) |
Alur Validasi QR
flowchart TD
A["📷 Peserta Scan QR"] --> B["🔓 Decode QR"]
B --> C{{"Token ada\ndi database?"}}
C -->|Tidak| D["❌ Reject:\nQR Tidak Valid"]
C -->|Ya| E{{"QR Sudah\ndipakai?"}}
E -->|Ya| F["❌ Reject:\nQR Sudah Digunakan"]
E -->|Tidak| G{{"QR Masih\nBelum Expired?"}}
G -->|Expired| H["❌ Reject:\nQR Kadaluarsa"]
G -->|Valid| I["✅ Lanjut ke\nValidasi GPS"]
style D fill:#dc2626,color:#fff
style F fill:#dc2626,color:#fff
style H fill:#dc2626,color:#fff
style I fill:#059669,color:#fff
Aturan QR
| Aturan | Nilai |
|---|---|
| Masa berlaku | 10 menit dari waktu meeting |
| One-Time Use | Ya — setelah dipakai, token dinonaktifkan |
| Reuse | Tidak diizinkan |
| Generate ulang | Host dapat generate QR baru jika expired |
Implementasi Laravel
Generate QR saat Booking Dibuat
// app/Core/QR/QrCodeGenerator.php
class QrCodeGenerator
{
public function generateForMeeting(Meeting $meeting): string
{
$payload = json_encode([
'meeting_id' => $meeting->id,
'booking_id' => $meeting->booking_id,
'token' => Str::uuid()->toString(),
'expired_at' => $meeting->booking->scheduled_at
->addMinutes(10)
->toIso8601String(),
]);
// Simpan token ke database
$meeting->update([
'qr_token' => base64_encode($payload),
'qr_expires_at' => now()->addMinutes(10),
]);
return $meeting->qr_token;
}
}
Validasi QR di Check-In Endpoint
// app/Modules/Meetings/Services/MeetingService.php
public function validateQr(string $qrToken): Meeting
{
$payload = json_decode(base64_decode($qrToken), true);
$meeting = Meeting::where('qr_token', $qrToken)->firstOrFail();
if ($meeting->qr_used) {
throw new QrAlreadyUsedException('QR Code sudah digunakan.');
}
if (now()->isAfter($meeting->qr_expires_at)) {
throw new QrExpiredException('QR Code sudah kadaluarsa.');
}
return $meeting;
}
Flutter — Scan QR
// features/meeting/presentation/pages/check_in_page.dart
// Menggunakan package: mobile_scanner
MobileScanner(
onDetect: (capture) {
final barcode = capture.barcodes.first;
final qrValue = barcode.rawValue;
if (qrValue != null) {
ref.read(checkInControllerProvider.notifier)
.checkIn(qrToken: qrValue);
}
},
)