ip().'|'.config('app.key')); $windowHours = (int) config('link.click_unique_window_hours', 24); $isUnique = ! LinkClick::query() ->where('short_link_id', $link->id) ->where('ip_hash', $ipHash) ->where('clicked_at', '>=', now()->subHours($windowHours)) ->exists(); $parsed = LinkUserAgentParser::parse((string) $request->userAgent()); DB::transaction(function () use ($link, $request, $ipHash, $isUnique, $parsed) { LinkClick::create([ 'short_link_id' => $link->id, 'ip_hash' => $ipHash, 'user_agent' => $this->truncate($request->userAgent(), 512), 'referer' => $this->truncate($request->header('referer'), 512), 'device_type' => $parsed['device_type'], 'browser' => $parsed['browser'], 'os' => $parsed['os'], 'country' => LinkGeoResolver::countryCode($request), 'is_unique' => $isUnique, 'clicked_at' => now(), ]); $link->increment('clicks_total'); if ($isUnique) { $link->increment('unique_clicks_total'); } $link->update(['last_clicked_at' => now()]); LinkWallet::query() ->where('user_id', $link->user_id) ->increment('clicks_total'); }); } private function truncate(?string $value, int $max): ?string { if ($value === null) { return null; } return mb_strlen($value) > $max ? mb_substr($value, 0, $max) : $value; } }