validate([ 'per_page' => 'integer', 'mobile' => 'string', 'email' => 'string', 'package_name' => 'string', ]); $users = User::when(isset($data['package_name']), fn ($q) => $q->whereHas('packageNames', fn ($q2) => $q2->where('name', $data['package_name']))) ->when(isset($data['mobile']), fn ($q) => $q->where('mobile', $data['mobile'])) ->when(isset($data['email']), fn ($q) => $q->where('email', $data['email'])) ->paginate($data['per_page'] ?? 30); return $users; } public function getUserTransactions(Request $request, $identifier) { $data = $request->validate([ 'per_page' => 'integer' ]); if (!$user = $this->findUserByIdentifier($identifier)) { return response()->json([ 'message' => 'user not found' ], 404); } $transactions = $user->transactions()->paginate($data['per_page'] ?? 30); return $transactions; } public function getUserStatus(Request $request, $identifier) { $data = $request->validate([ 'package_name' => 'string' ]); if (isset($data['package_name']) && !$packageName = PackageName::with('products')->where('name', $data['package_name'])->first()) { return response()->json([ 'message' => 'package name not found' ], 404); } if (!$user = User::with([ 'products' => fn ($q) => $q->when(isset($data['package_name']), fn ($q1) => $q1->where('package_name_id', $packageName->id)), 'products.packageName' ])->where(function($query) use ($identifier) { $this->applyIdentifierCondition($query, $identifier); })->first()) { return response()->json([ 'message' => 'user not found' ], 404); } return $user; } public function getUserStatusV1(Request $request, $identifier) { $data = $request->validate([ 'package_name' => 'string' ]); $userIdentifier = $identifier; if (isset($data['package_name']) && !$packageName = PackageName::with('products') ->where('name', $data['package_name'])->first()) { return response()->json([ 'message' => 'package name not found' ], 404); } // For OldPurchase, we need to determine if identifier is mobile or email if ($this->isMobileNumber($identifier)) { $mobile = preg_replace('/09/', '989', $identifier, 1); $query = OldPurchase::when(isset($data['package_name']), fn ($q) => $q->where('package_name', $data['package_name'])) ->where('mobile', $mobile); } else { // Assuming OldPurchase might have email field or we need to find user first if (!$user = $this->findUserByIdentifier($identifier)) { return response()->json([ 'message' => 'user not found' ], 404); } $query = OldPurchase::when(isset($data['package_name']), fn ($q) => $q->where('package_name', $data['package_name'])) ->where('mobile', preg_replace('/09/', '989', $user->mobile, 1)); } if (!$query->first()) { return response()->json([ 'message' => 'user is not paid' ], 400); } return response()->json([ 'message' => 'user is paid' ]); } public function subscribeUser(Request $request, $identifier) { $data = $request->validate([ 'product_id' => 'integer|required' ]); if (!$user = $this->findUserByIdentifier($identifier)) { return response()->json([ 'message' => 'user not found' ], 404); } if (!$product = Product::where('id', $data['product_id'])->first()) { return response()->json([ 'message' => 'product not found' ], 404); } $product->buy($user); return response()->json([ 'message' => 'subscribed successfuly' ]); } /** * List users who reached the referral invite target (کد معرف) and have * not yet been granted their free-month subscription. */ public function pendingReferralRewards(Request $request) { $target = User::REFERRAL_SUBSCRIPTION_TARGET; $users = User::withCount('referrals') ->whereNull('referral_subscription_granted_at') ->having('referrals_count', '>=', $target) ->orderByDesc('referrals_count') ->get(['id', 'uuid', 'first_name', 'last_name', 'email', 'mobile', 'referral_code']); return response()->json([ 'subscription_target' => $target, 'data' => $users->map(fn ($u) => [ 'id' => $u->id, 'uuid' => $u->uuid, 'name' => trim($u->first_name . ' ' . $u->last_name), 'email' => $u->email, 'mobile' => $u->mobile, 'referral_code' => $u->referral_code, 'successful_invites' => $u->referrals_count, ]), ]); } /** * Grant the free-month subscription to a user who reached the referral * invite target. Defaults to the meditation package's monthly product, * overridable via product_id. Marks the grant so it happens only once. */ public function fulfillReferralReward(Request $request, $userId) { $data = $request->validate([ 'product_id' => 'integer|nullable', ]); if (!$user = User::find($userId)) { return response()->json(['message' => 'user not found'], 404); } if ($user->referral_subscription_granted_at) { return response()->json(['message' => 'referral subscription already granted'], 400); } if ($user->referrals()->count() < User::REFERRAL_SUBSCRIPTION_TARGET) { return response()->json(['message' => 'user has not reached the referral target yet'], 400); } // Resolve which monthly product to grant if (!empty($data['product_id'])) { $product = Product::where('id', $data['product_id'])->first(); } else { $package = PackageName::where('name', User::REFERRAL_PACKAGE_NAME)->first(); $product = $package ? Product::where('package_name_id', $package->id) ->where('type', Product::TYPES['monthly']) ->first() : null; } if (!$product) { return response()->json(['message' => 'monthly product not found'], 404); } $product->buy($user); $user->referral_subscription_granted_at = now(); $user->save(); return response()->json([ 'message' => 'referral subscription granted', 'user_id' => $user->id, 'product_id' => $product->id, ]); } public function unsubscribeUser(Request $request, $identifier) { $data = $request->validate([ 'product_id' => 'integer|required' ]); if (!$user = $this->findUserByIdentifier($identifier)) { return response()->json([ 'message' => 'user not found' ], 404); } if (!$product = Product::where('id', $data['product_id'])->first()) { return response()->json([ 'message' => 'product not found' ], 404); } $user->products()->updateExistingPivot($product, ['expire_at' => now()->format('Y-m-d H:i:s')]); return response()->json([ 'message' => 'unsubscribed successfuly' ]); } public function deleteUser(Request $request, $identifier) { if (!$user = $this->findUserByIdentifier($identifier)) { return response()->json([ 'message' => 'user not found' ], 404); } $user->delete(); return response()->json([ 'message' => 'user deleted' ]); } public function updateUserProfile(Request $request, $identifier) { $data = $request->validate([ 'first_name' => 'string', 'last_name' => 'string', 'email' => 'email', 'mobile' => [new MobileNumber, 'string'], 'avatar' => 'image' ]); if (!$subjectUser = $this->findUserByIdentifier($identifier)) { return response()->json([ 'message' => 'user not found' ], 404); } if (isset($data['email'])) { $user = User::where('email', $data['email'])->first(); if ($user && $data['email'] != $subjectUser->email) { return response()->json([ 'message' => 'user with given email exists' ], 400); } } if (isset($data['mobile'])) { $user = User::where('mobile', $data['mobile'])->first(); if ($user && $data['mobile'] != $subjectUser->mobile) { return response()->json([ 'message' => 'user with given mobile exists' ], 400); } } if (isset($data['avatar'])) { Storage::disk('public')->put("avatars/$subjectUser->uuid.png", file_get_contents($data['avatar']->path())); $data['avatar'] = "storage/avatars/$subjectUser->uuid.png"; } $subjectUser->update($data); return response()->json([ 'message' => 'user updated' ]); } /** * Helper method to find user by mobile or email */ private function findUserByIdentifier($identifier) { return User::where(function($query) use ($identifier) { $this->applyIdentifierCondition($query, $identifier); })->first(); } /** * Helper method to apply condition for mobile or email */ private function applyIdentifierCondition($query, $identifier) { if ($this->isMobileNumber($identifier)) { $query->where('mobile', $identifier); } else { $query->where('email', $identifier); } } /** * Helper method to check if identifier is a mobile number */ private function isMobileNumber($identifier) { // Simple check for mobile number pattern (starts with 09 or +98 or 989) return preg_match('/^(09|\+98|989)\d{9}$/', $identifier); } }