user(); $this->checkAndGrantFreeSubscription($user); $this->refreshMarketSubscriptionAfterResponse($user); $recent = RecentArt::query()->where('user_id', $user->id)->get()->map(function ($q) { return [ 'id' => $q->law?->id, 'name' => $q->law?->title, 'is_locked' => $q->law?->is_locked, ]; }); $laws = Law::orderBy('created_at')->get()->map(function ($q) { return [ 'id' => $q->id, 'name' => $q->title, 'is_locked' => $q->is_locked, ]; }); $categories = ['hagigi', 'kifari']; $lawsByCategory = []; foreach ($categories as $category) { $lawsByCategory[$category] = Law::whereHas('category', function ($q) use ($category) { $q->where('type', $category); })->get()->map(function ($q) { return [ 'id' => $q->id, 'title' => $q->title, 'is_locked' => $q->is_locked, 'category_id' => $q->category_id, 'price' => $q->price, 'image' => $q->image, 'type' => 'law', ]; }); } $free_law = Law::where('is_locked', false)->orderBy('created_at')->get()->map(function ($q) { return [ 'id' => $q->id, 'name' => $q->title, 'is_locked' => $q->is_locked, ]; }); $freeSubscription = $user->userSubscribers() ->whereHas('subscribe', function ($query) { $query->where('is_free', true); }) ->where('expired_at', '>=', now()) ->first(); $current_plan = null; if ($freeSubscription) { $current_plan = [ 'id' => $freeSubscription->id, 'name' => $freeSubscription->subscribe->name, 'price' => $freeSubscription->subscribe->price, 'expired_day' => $freeSubscription->expired_at->diffInDays(now()), 'is_free' => true, ]; } $latestSubscription = $user->userSubscribers() ->where('expired_at', '>=', now()) ->latest('expired_at') ->first(); $expiredDays = UserSubscriber::query()->where('user_id', $user->id)->where('expired_at', '>=', now())->get()->sum(function ($subscriber) { return $subscriber->expired_at->diffInDays(now()); }); if ($latestSubscription) { $current_plan = [ 'id' => $latestSubscription->id, 'name' => $latestSubscription->subscribe->name ?? 'Subscription', 'price' => $latestSubscription->subscribe->price ?? 100, 'expired_day' => $expiredDays, 'is_free' => (bool)$latestSubscription->is_free, ]; } $unread_notifications_count = Notification::unreadForUser($user->id)->count(); $homeData = [ 'recent' => $recent, 'laws' => $lawsByCategory, 'last_law' => $laws, 'free' => $free_law, 'current_plan' => $current_plan, 'unread_notifications_count' => $unread_notifications_count, ]; return $this->success([ 'home' => $homeData, 'hoqoqi_list' => $this->getCategoryList(1), 'keyfari_list' => $this->getCategoryList(2), ]); } public function homeV3() { $user = auth()->user(); $this->checkAndGrantFreeSubscription($user); $this->refreshMarketSubscriptionAfterResponse($user); $recent = RecentArt::query()->where('user_id', $user->id)->get()->map(function ($q) { return [ 'id' => $q->law?->id, 'name' => $q->law?->title, 'is_locked' => $q->law?->is_locked, ]; }); $laws = Law::orderBy('created_at')->get()->map(function ($q) { return [ 'id' => $q->id, 'name' => $q->title, 'is_locked' => $q->is_locked, ]; }); $categories = ['hagigi', 'kifari']; $lawsByCategory = []; foreach ($categories as $category) { $lawsByCategory[$category] = Law::whereHas('category', function ($q) use ($category) { $q->where('type', $category); })->get()->map(function ($q) { return [ 'id' => $q->id, 'title' => $q->title, 'is_locked' => $q->is_locked, 'category_id' => $q->category_id, 'price' => $q->price, 'image' => $q->image, 'type' => 'law', ]; }); } $free_law = Law::where('is_locked', false)->orderBy('created_at')->get()->map(function ($q) { return [ 'id' => $q->id, 'name' => $q->title, 'is_locked' => $q->is_locked, ]; }); // --- V3 FIXED LOGIC --- $activeSubscribers = $user->userSubscribers() ->with('subscribe') ->where('expired_at', '>=', now()) ->get(); $current_plan = null; if ($activeSubscribers->isNotEmpty()) { $latestSubscription = $activeSubscribers->sortByDesc('expired_at')->first(); // FIX 1: Never sum the days. Calculate distance to the furthest expiration date. $expiredDays = $latestSubscription->expired_at->diffInDays(now()); // FIX 2: Accurately check if the active plan is free $isFree = (bool) ( $latestSubscription->is_free || ($latestSubscription->subscribe && $latestSubscription->subscribe->is_free) ); $current_plan = [ 'id' => $latestSubscription->id, 'name' => $latestSubscription->subscribe->name ?? 'Subscription', 'price' => $latestSubscription->subscribe->price ?? 0, 'expired_day' => $expiredDays, 'is_free' => $isFree, ]; } $unread_notifications_count = Notification::unreadForUser($user->id)->count(); $homeData = [ 'recent' => $recent, 'laws' => $lawsByCategory, 'last_law' => $laws, 'free' => $free_law, 'current_plan' => $current_plan, 'unread_notifications_count' => $unread_notifications_count, ]; return $this->success([ 'home' => $homeData, 'hoqoqi_list' => $this->getCategoryList(1), 'keyfari_list' => $this->getCategoryList(2), ]); } private function getCategoryList(int $categoryId) { $type = $categoryId === 1 ? 'hagigi' : 'kifari'; $categories = Category::with(['laws'])->where('type', $type)->get()->pluck('id'); $items = Law::query()->whereIn('category_id', $categories)->get(); $data = []; $user = auth()->user(); foreach ($items as $item) { $data[] = [ 'id' => $item->id, 'title' => $item->title, 'number' => $item->number, 'type' => 'laws', 'is_locked' => $user->isSubscriber() !== false ? false : Law::where('id', $item->id)->first()?->is_locked, 'image' => $item?->image, 'law' => $item?->title, 'count_art' => $item->arts->count(), 'count_volums' => $item->volums->count(), 'price' => $item->price, ]; } return $data; } public function index() { $user = auth()->user(); $this->checkAndGrantFreeSubscription($user); $this->refreshMarketSubscriptionAfterResponse($user); $recent = RecentArt::query()->where('user_id', $user->id)->get()->map(function ($q) { return [ 'id' => $q->law?->id, 'name' => $q->law?->title, 'is_locked' => $q->law?->is_locked, ]; }); $laws = Law::orderBy('created_at')->get()->map(function ($q) { return [ 'id' => $q->id, 'name' => $q->title, 'is_locked' => $q->is_locked, ]; }); $categories = ['hagigi', 'kifari']; $lawsByCategory = []; foreach ($categories as $category) { $lawsByCategory[$category] = Law::whereHas('category', function ($q) use ($category) { $q->where('type', $category); })->get()->map(function ($q) { return [ 'id' => $q->id, 'title' => $q->title, 'is_locked' => $q->is_locked, 'category_id' => $q->category_id, 'price' => $q->price, 'image' => $q->image, 'type' => 'law', ]; }); } $free_law = Law::where('is_locked', false)->orderBy('created_at')->get()->map(function ($q) { return [ 'id' => $q->id, 'name' => $q->title, 'is_locked' => $q->is_locked, ]; }); $freeSubscription = $user->userSubscribers() ->whereHas('subscribe', function ($query) { $query->where('is_free', true); }) ->where('expired_at', '>=', now()) ->first(); $current_plan = null; if ($freeSubscription) { $current_plan = [ 'id' => $freeSubscription->id, 'name' => $freeSubscription->subscribe->name, 'price' => $freeSubscription->subscribe->price, 'expired_day' => $freeSubscription->expired_at->diffInDays(now()), 'is_free' => true, ]; } $latestSubscription = $user->userSubscribers() ->where('expired_at', '>=', now()) ->latest('expired_at') ->first(); $expiredDays = UserSubscriber::query()->where('user_id', $user->id)->where('expired_at', '>=', now())->get()->sum(function ($subscriber) { return $subscriber->expired_at->diffInDays(now()); }); if ($latestSubscription) { $current_plan = [ 'id' => $latestSubscription->id, 'name' => $latestSubscription->subscribe->name ?? 'Subscription', 'price' => $latestSubscription->subscribe->price ?? 100, 'expired_day' => $expiredDays, 'is_free' => (bool)$latestSubscription->is_free, ]; } $unread_notifications_count = Notification::unreadForUser($user->id)->count(); return $this->success([ 'recent' => $recent, 'laws' => $lawsByCategory, 'last_law' => $laws, 'free' => $free_law, 'current_plan' => $current_plan, 'unread_notifications_count' => $unread_notifications_count, ]); } /** * Helper to automatically assign a free plan safely. */ private function checkAndGrantFreeSubscription($user): void { // 1. Have they ever used a free plan? $hasUsedFreePlan = $user->userSubscribers() ->whereHas('subscribe', function ($query) { $query->where('is_free', true); })->exists(); // 2. Do they currently have an active PAID plan? $hasActivePaidPlan = $user->userSubscribers() ->where('expired_at', '>', now()) ->whereHas('subscribe', function ($query) { $query->where('is_free', false); })->exists(); // FIX 3: Only grant the free plan if they have never had one AND they aren't currently paying! if (!$hasUsedFreePlan && !$hasActivePaidPlan) { $freePlan = SubscribePlan::where('is_free', true)->where('is_active', true)->first(); if ($freePlan) { $user->userSubscribers()->create([ 'subscribe_plan_id' => $freePlan->id, 'expired_at' => now()->addDays($freePlan->expired_day + 1), ]); } } } private function refreshMarketSubscriptionAfterResponse($user): void { $cacheKey = "home-market-refresh-user-{$user->id}"; $this->refreshMarketSubscription($user); if (!Cache::add($cacheKey, true, now()->addHours(24))) { return; } app()->terminating(function () use ($user) { $this->refreshMarketSubscription($user); }); } private function refreshMarketSubscription($user): void { $bazaarSubscription = $user->userSubscribers() ->whereNotNull('purchase_token') ->whereNotNull('subscription_id') ->where(function ($query) { $query->where('market_provider', 'bazaar') ->orWhereNull('market_provider'); }) ->latest() ->first(); if ($bazaarSubscription && $this->shouldRefreshMarketSubscription($bazaarSubscription) && $this->marketVerifier->refreshBazaarSubscriber($bazaarSubscription)) { return; } $myketSubscription = $user->userSubscribers() ->where('market_provider', 'myket') ->whereNotNull('purchase_token') ->whereNotNull('product_id') ->latest() ->first(); if ($myketSubscription && $this->shouldRefreshMarketSubscription($myketSubscription)) { $this->marketVerifier->refreshMyketSubscriber($myketSubscription); } } private function shouldRefreshMarketSubscription(UserSubscriber $subscriber): bool { if ($subscriber->expired_at && $subscriber->expired_at->lessThanOrEqualTo(now())) { return true; } return !$subscriber->last_verified_at || $subscriber->last_verified_at->lessThan(now()->subHours(12)); } }