<?php
 namespace App\Http\Middleware; use App\Models\Affiliate; use App\Models\AffiliateClick; use App\Models\AffiliateLink; use App\Models\AffiliateReferral; use App\Models\Client; use App\Models\Setting; use Closure; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; use Symfony\Component\HttpFoundation\Response; class TrackAffiliateReferral { public function handle(Request $request, Closure $next): Response { if (!Setting::get('enable_affiliate', false)) { return $next($request); } $refCode = $request->query('ref'); if ($refCode) { $this->processReferral($request, $refCode); } $this->setClientAttribution($request); return $next($request); } private function processReferral(Request $request, string $refCode): void { $affiliate = Affiliate::where('referral_code', $refCode) ->where('status', 'active') ->first(); if (!$affiliate) { return; } $linkId = $request->query('lid'); $affiliateLink = null; if ($linkId) { $affiliateLink = AffiliateLink::where('id', $linkId) ->where('affiliate_id', $affiliate->id) ->where('is_active', true) ->first(); } if (!$affiliateLink) { $affiliateLink = AffiliateLink::where('affiliate_id', $affiliate->id) ->where('is_active', true) ->orderBy('id') ->first(); } $cookieDuration = (int) Setting::get('affiliate_cookie_duration', 90); $cookieData = json_encode([ 'affiliate_id' => $affiliate->id, 'link_id' => $affiliateLink?->id, ]); cookie()->queue( cookie( 'affiliate_ref', $cookieData, $cookieDuration * 24 * 60, '/', null, $request->isSecure(), true, false, 'Lax' ) ); $this->logClick($request, $affiliateLink, $affiliate); app(\App\Core\Hook\HookBridge::class)->fireAction('AffiliateClickthru', [ 'affiliate_id' => $affiliate->id, 'client_id' => $affiliate->client_id, 'link_id' => $affiliateLink?->id, 'code' => $refCode, 'ref_code' => $refCode, ]); if ($affiliateLink && $affiliateLink->coupon_id) { session(['affiliate_coupon_id' => $affiliateLink->coupon_id]); } Log::info('TrackAffiliateReferral: Referral tracked', [ 'affiliate_id' => $affiliate->id, 'link_id' => $affiliateLink?->id, 'ref_code' => $refCode, 'ip' => $request->ip(), ]); } private function logClick(Request $request, ?AffiliateLink $link, Affiliate $affiliate): void { if (!$link) { return; } $ipAddress = $request->ip(); $isUnique = !DB::table('affiliate_clicks') ->where('affiliate_link_id', $link->id) ->where('ip_address', $ipAddress) ->where('created_at', '>=', now()->subHours(24)) ->exists(); AffiliateClick::create([ 'affiliate_link_id' => $link->id, 'ip_address' => $ipAddress, 'user_agent' => mb_substr($request->userAgent() ?? '', 0, 500), 'referrer_url' => mb_substr($request->header('referer', '') ?? '', 0, 500), 'country' => $this->detectCountry($request), 'is_unique' => $isUnique, ]); $link->increment('clicks'); if ($isUnique) { $link->increment('unique_clicks'); } } private function setClientAttribution(Request $request): void { if (!Auth::guard('client')->check()) { return; } $client = Auth::guard('client')->user(); if ($client->affiliate_id) { return; } $cookieData = $this->getAffiliateCookieData($request); if (!$cookieData || !isset($cookieData['affiliate_id'])) { return; } $affiliateId = (int) $cookieData['affiliate_id']; $affiliate = Affiliate::where('id', $affiliateId) ->where('status', 'active') ->first(); if (!$affiliate) { return; } if ($affiliate->client_id === $client->id) { return; } $client->update(['affiliate_id' => $affiliateId]); AffiliateReferral::firstOrCreate( [ 'affiliate_id' => $affiliateId, 'referred_client_id' => $client->id, ], [ 'ip_address' => $request->ip(), 'status' => 'signup', ] ); Log::info('TrackAffiliateReferral: Client attributed to affiliate', [ 'client_id' => $client->id, 'affiliate_id' => $affiliateId, ]); } private function getAffiliateCookieData(Request $request): ?array { $cookieValue = $request->cookie('affiliate_ref'); if (!$cookieValue) { return null; } $data = json_decode($cookieValue, true); if (!is_array($data)) { return null; } return $data; } private function detectCountry(Request $request): ?string { try { $geoIpHeader = $request->header('CF-IPCountry'); if ($geoIpHeader && strlen($geoIpHeader) === 2) { return strtoupper($geoIpHeader); } $xCountry = $request->header('X-Country'); if ($xCountry && strlen($xCountry) === 2) { return strtoupper($xCountry); } return null; } catch (\Exception $e) { return null; } } } 