isAdminUser()) { return $next($request); } // Get the user's active subscription $subscription = $this->getActiveSubscription(); if (! $subscription) { return $this->handleNoSubscription($request); } // Get the plan with features $plan = $subscription->plan->load(['planFeatureLimits.planFeature']); if (! $plan) { Log::error('No plan found for subscription', ['subscription_id' => $subscription->id]); return $next($request); } // Check if we're enforcing a specific feature if ($featureName) { return $this->enforceSpecificFeature($request, $next, $plan, $subscription, $featureName); } // General usage tracking for common features $this->trackGeneralUsage($request, $plan, $subscription); return $next($request); } /** * Enforce limits for a specific feature */ private function enforceSpecificFeature( Request $request, Closure $next, Plan $plan, Subscription $subscription, string $featureName ): Response { // Check if user can use the feature $currentUsage = $this->getCurrentUsage($subscription->id, $featureName); $isOnTrial = (bool) ($subscription?->onTrial() ?? false); if (! $plan->canUseFeature($featureName, $currentUsage, $isOnTrial)) { return $this->handleFeatureLimitExceeded($request, $plan, $featureName, $currentUsage); } // Track usage if this is a usage-generating request if ($this->isUsageGeneratingRequest($request)) { $this->incrementUsage($subscription->id, $featureName); } return $next($request); } /** * Get user's active subscription */ private function getActiveSubscription(): ?Subscription { return Auth::user()->subscriptions() ->whereIn('status', ['active', 'trialing']) ->where('ends_at', '>', now()) ->with('plan') ->first(); } /** * Handle requests from users with no subscription */ private function handleNoSubscription(Request $request): Response { // Allow access to non-protected routes $allowedRoutes = [ 'pricing', 'checkout.*', 'login', 'register', 'home', 'dashboard.pricing', ]; foreach ($allowedRoutes as $route) { if ($request->routeIs($route)) { return $next($request); } } // Redirect to pricing page for other routes if ($request->expectsJson()) { return response()->json([ 'success' => false, 'error' => 'Subscription required', 'message' => 'Please subscribe to access this feature.', 'redirect_url' => route('pricing'), ], 402); } return redirect()->route('pricing') ->with('error', 'Please subscribe to access this feature.'); } /** * Handle when feature limit is exceeded */ private function handleFeatureLimitExceeded( Request $request, Plan $plan, string $featureName, float $currentUsage ): Response { $feature = $plan->planFeatureLimits ->whereHas('planFeature', function ($query) use ($featureName) { $query->where('name', $featureName); }) ->first(); $featureDisplayName = $feature?->planFeature->display_name ?? $featureName; $limit = $feature?->limit_value ?? 'Unknown'; $remaining = max(0, ($limit ?? 0) - $currentUsage); Log::info('Feature limit exceeded', [ 'user_id' => Auth::id(), 'plan_id' => $plan->id, 'feature' => $featureName, 'current_usage' => $currentUsage, 'limit' => $limit, ]); if ($request->expectsJson()) { return response()->json([ 'success' => false, 'error' => 'Feature limit exceeded', 'message' => "You have reached your limit for {$featureDisplayName}. Current usage: {$currentUsage}, Limit: {$limit}", 'feature' => $featureName, 'current_usage' => $currentUsage, 'limit' => $limit, 'remaining' => $remaining, 'upgrade_url' => $this->getUpgradeUrl($plan), ], 429); } return back()->with('error', "You have reached your limit for {$featureDisplayName}. Current usage: {$currentUsage}, Limit: {$limit}. Upgrade your plan to increase limits."); } /** * Track general usage for common features */ private function trackGeneralUsage(Request $request, Plan $plan, Subscription $subscription): void { // Track API calls if ($request->is('api/*') && $plan->hasFeature('api_access')) { $this->incrementUsage($subscription->id, 'api_access'); } // Track email operations if ($this->isEmailOperation($request) && $plan->hasFeature('email_sending')) { $this->incrementUsage($subscription->id, 'email_sending'); } // Track advanced filters usage if ($this->isFilterOperation($request) && $plan->hasFeature('advanced_filters')) { $this->incrementUsage($subscription->id, 'advanced_filters'); } } /** * Check if request generates usage */ private function isUsageGeneratingRequest(Request $request): bool { // POST, PUT, PATCH requests typically generate usage return in_array($request->method(), ['POST', 'PUT', 'PATCH']); } /** * Check if this is an email operation */ private function isEmailOperation(Request $request): bool { return $request->is(['api/emails/*', 'emails/*']) || str_contains($request->path(), 'email') || $request->has('to') || $request->has('subject'); } /** * Check if this is a filter operation */ private function isFilterOperation(Request $request): bool { return $request->has('filter') || $request->has('filters') || str_contains($request->path(), 'filter'); } /** * Get current usage for a feature */ private function getCurrentUsage(int $subscriptionId, string $featureName): float { $usage = PlanUsage::where('subscription_id', $subscriptionId) ->whereHas('planFeature', function ($query) use ($featureName) { $query->where('name', $featureName); }) ->where('period_type', 'monthly') ->whereMonth('created_at', now()->month) ->whereYear('created_at', now()->year) ->sum('usage_value'); return (float) $usage; } /** * Increment usage for a feature */ private function incrementUsage(int $subscriptionId, string $featureName): void { $subscription = Subscription::find($subscriptionId); $feature = $subscription->plan->planFeatureLimits ->whereHas('planFeature', function ($query) use ($featureName) { $query->where('name', $featureName); }) ->first(); if (! $feature) { return; } PlanUsage::updateOrCreate([ 'subscription_id' => $subscriptionId, 'plan_id' => $subscription->plan_id, 'user_id' => Auth::id(), 'plan_feature_id' => $feature->plan_feature_id, 'period_type' => $feature->limit_type === 'boolean' ? 'total' : 'monthly', 'created_at' => now()->startOfMonth(), ], [ 'usage_value' => \DB::raw('usage_value + 1'), 'updated_at' => now(), ]); } /** * Check if user is admin (bypasses limits) */ private function isAdminUser(): bool { return Auth::user() && Auth::user()->level >= 10; } /** * Get upgrade URL for plan */ private function getUpgradeUrl(Plan $currentPlan): string { $upgradePaths = $currentPlan->getUpgradePath(); return count($upgradePaths) > 0 ? route('pricing') : route('pricing'); } }