parseUserAgent((string) $request->userAgent()); $ipHash = $this->hashIp((string) $request->ip()); $windowHours = (int) config('qr.scan_unique_window_hours', 24); $isUnique = ! QrScanEvent::query() ->where('qr_code_id', $qrCode->id) ->where('ip_hash', $ipHash) ->where('scanned_at', '>=', now()->subHours($windowHours)) ->exists(); $event = QrScanEvent::create([ 'qr_code_id' => $qrCode->id, 'scanned_at' => now(), 'ip_hash' => $ipHash, 'user_agent' => Str::limit((string) $request->userAgent(), 500, ''), 'device_type' => $parsed['device_type'], 'browser' => $parsed['browser'], 'os' => $parsed['os'], 'referrer' => Str::limit((string) $request->headers->get('referer'), 255, ''), 'is_unique' => $isUnique, ]); $qrCode->increment('scans_total'); if ($isUnique) { $qrCode->increment('unique_scans_total'); } $qrCode->update(['last_scanned_at' => now()]); QrWallet::query() ->where('user_id', $qrCode->user_id) ->increment('scans_total'); return $event; } private function hashIp(string $ip): string { return hash('sha256', $ip . '|' . (string) config('app.key')); } /** * @return array{device_type: string, browser: string, os: string} */ private function parseUserAgent(string $ua): array { $uaLower = strtolower($ua); $device = str_contains($uaLower, 'mobile') || str_contains($uaLower, 'android') || str_contains($uaLower, 'iphone') ? 'mobile' : (str_contains($uaLower, 'tablet') || str_contains($uaLower, 'ipad') ? 'tablet' : 'desktop'); $browser = match (true) { str_contains($uaLower, 'edg/') => 'Edge', str_contains($uaLower, 'chrome/') && ! str_contains($uaLower, 'edg/') => 'Chrome', str_contains($uaLower, 'safari/') && ! str_contains($uaLower, 'chrome/') => 'Safari', str_contains($uaLower, 'firefox/') => 'Firefox', default => 'Other', }; $os = match (true) { str_contains($uaLower, 'iphone') || str_contains($uaLower, 'ipad') => 'iOS', str_contains($uaLower, 'android') => 'Android', str_contains($uaLower, 'windows') => 'Windows', str_contains($uaLower, 'mac os') || str_contains($uaLower, 'macintosh') => 'macOS', str_contains($uaLower, 'linux') => 'Linux', default => 'Other', }; return [ 'device_type' => $device, 'browser' => $browser, 'os' => $os, ]; } }