Lewati ke konten utama

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"
}
KomponenDeskripsi
meeting_idID sesi pertemuan
booking_idID booking terkait
tokenToken acak 64 karakter (UUID v4)
expired_atWaktu 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

AturanNilai
Masa berlaku10 menit dari waktu meeting
One-Time UseYa — setelah dipakai, token dinonaktifkan
ReuseTidak diizinkan
Generate ulangHost 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);
}
},
)