enforce(RateLimiter::identifier(), 'login'); requireFoodFields(['phone', 'password']); $phone = normalizePhone(filterRequest('phone')); $password = filterRequest('password'); try { $food_con = Database::get('food'); } catch (Exception $e) { jsonError('Food service unavailable', 503); } $st = $food_con->prepare( "SELECT id, merchant_id, password_hash, is_active FROM food_merchant_users WHERE phone=? LIMIT 1" ); $st->execute([$phone]); $user = $st->fetch(); if (!$user || !$user['is_active'] || !password_verify($password, $user['password_hash'])) { appLog("[FOOD][MERCHANT_LOGIN] failed attempt for phone hash " . hash('sha256', $phone), 'WARNING'); jsonError('Invalid credentials', 401); } $merchantSt = $food_con->prepare("SELECT status FROM food_merchants WHERE id=?"); $merchantSt->execute([$user['merchant_id']]); $merchant = $merchantSt->fetch(); if (!$merchant || !in_array($merchant['status'], ['active', 'paused'], true)) { jsonError('Merchant account is not active', 403); } $token = foodCreateMerchantSession((int)$user['id'], (int)$user['merchant_id']); jsonSuccess(['session_token' => $token, 'merchant_id' => (int)$user['merchant_id']], 'Login successful');