enforce(RateLimiter::identifier(), 'api'); try { $transit_con = Database::get('transit'); } catch (Exception $e) { jsonError('Transit service unavailable', 503); } $rawPhone = filterRequest('phone'); if (!$rawPhone) jsonError('Phone is required'); $phone = normalizePhone($rawPhone); if (transitIsOtpLocked($phone)) { // نفس رسالة النجاح — لا نكشف أن الحساب مقفل jsonSuccess(null, 'OTP sent successfully'); } $phoneEnc = $encryptionHelper->encryptData($phone); // transit_org_admins يعيش في قاعدة المواصلات ولا يملك عمود فهرس بعد، // فتتم المطابقة على الرقم الأصلي بعد فك التشفير عند فشل المقارنة المباشرة. $phoneNorm = normalizePhone($phone); $st = $transit_con->prepare( "SELECT a.id, o.contract_status FROM transit_org_admins a JOIN transit_orgs o ON o.id = a.org_id WHERE a.phone = ? AND a.is_active = 1 LIMIT 1" ); $st->execute([$phoneEnc]); $admin = $st->fetch(); if (!$admin) { // تحت التشفير العشوائي لا تتطابق النصوص المشفّرة، فنقارن الأرقام الأصلية. $all = $transit_con->query( "SELECT a.id, a.phone, o.contract_status FROM transit_org_admins a JOIN transit_orgs o ON o.id = a.org_id WHERE a.is_active = 1" )->fetchAll(PDO::FETCH_ASSOC); foreach ($all as $row) { $plain = $encryptionHelper->decryptData($row['phone'] ?? null); if ($plain && normalizePhone($plain) === $phoneNorm) { $admin = $row; break; } } } // لا نكشف إن كان الهاتف موجوداً أم لا — نفس الرد دائماً if (!$admin || $admin['contract_status'] === 'terminated') { jsonSuccess(null, 'OTP sent successfully'); } transitSendAdminOtp($phone); jsonSuccess(null, 'OTP sent successfully');