$username, 'password' => $password, 'language' => 'e', 'sender' => $sender, 'receiver' => $receiver, 'message' => $message ]; $response = curlCall("POST", $apiUrl, json_encode($payload), [ "Content-Type: application/json" ]); if ($response) { $decoded = json_decode($response, true); if (isset($decoded['message']) && $decoded['message'] === 'Success') { return true; } error_log("❌ [Kazumi OTP] API returned failure response: " . $response); } return false; } /** * Retrieve Nabeh JWT Bearer Token, caching it in Redis for 24 hours. * * @return string|null The Bearer token, or null on failure. */ function getNabehBearerToken(): ?string { global $redis; // 1. Try fetching from Redis first if ($redis) { try { $cachedToken = $redis->get('nabeh_bearer_token'); if ($cachedToken) { return $cachedToken; } } catch (Exception $e) { $msg = "⚠️ [Nabeh Auth Redis] Error reading token: " . $e->getMessage(); error_log($msg); } } // 2. Token not cached, authenticate via Nabeh Login API $email = getenv('NABEH_EMAIL'); $password = getenv('NABEH_PASSWORD'); if (!$email || !$password) { $msg = "⚠️ [Nabeh Auth] Missing NABEH_EMAIL or NABEH_PASSWORD environment variables."; $GLOBALS['last_otp_error'] = $msg; error_log($msg); return null; } $apiUrl = 'https://nabeh.intaleqapp.com/api/auth/login'; $payload = [ 'email' => $email, 'password' => $password ]; $response = curlCall("POST", $apiUrl, json_encode($payload), [ 'Content-Type: application/json' ]); $debugLog = "[ " . date('Y-m-d H:i:s') . " ]\n"; $debugLog .= "Request to: $apiUrl\n"; $debugLog .= "Payload: " . json_encode($payload) . "\n"; $debugLog .= "Response: " . $response . "\n"; file_put_contents(__DIR__ . '/../../../logs/nabeh_debug.txt', $debugLog, FILE_APPEND); if ($response) { $decoded = json_decode($response, true); $token = $decoded['token'] ?? $decoded['message']['token'] ?? $decoded['jwt'] ?? $decoded['access_token'] ?? null; if ($token) { // 3. Cache token in Redis for 24h if ($redis) { try { $redis->setex('nabeh_bearer_token', 86400, $token); $debugLog = "Token cached in Redis successfully.\n"; file_put_contents(__DIR__ . '/../../../logs/nabeh_debug.txt', $debugLog, FILE_APPEND); } catch (Exception $e) { $msg = "⚠️ [Nabeh Auth Redis Cache Save] Error saving token: " . $e->getMessage(); error_log($msg); } } return $token; } $msg = "❌ [Nabeh Auth Login Failed] Response: " . $response; $GLOBALS['last_otp_error'] = $msg; error_log($msg); } else { $msg = "❌ [Nabeh Auth Login Failed] Empty response from login API."; $GLOBALS['last_otp_error'] = $msg; error_log($msg); } return null; } /** * Send OTP via Nabeh JWT Auth Gateway (WhatsApp, Voice, etc.) * * @param string $receiver Recipient phone number * @param string $otp 3-digit verification code * @param string $method text | voice | image | whatsapp * @param string $user_type passenger | driver | admin | service * @return bool True if OTP was sent successfully */ function sendNabehOtp(string $receiver, string $otp, string $method = '', string $user_type = 'passenger'): bool { $bearerToken = getNabehBearerToken(); if (!$bearerToken) { if (empty($GLOBALS['last_otp_error'])) { $GLOBALS['last_otp_error'] = "⚠️ [Nabeh OTP] Failed to obtain dynamic JWT Bearer token."; } return false; } // Strip symbols for Nabeh endpoint $phoneRaw = preg_replace('/\D+/', '', $receiver); // Map method/type (Image OTP card is default for Nabeh) $type = ($method === 'text') ? 'text' : (($method === 'voice') ? 'voice' : 'image'); $appName = 'سيرو رايدر'; if ($user_type === 'driver') { $appName = 'سيرو درايفر'; } elseif ($user_type === 'admin') { $appName = 'سيرو الأدمن'; } elseif ($user_type === 'service') { $appName = 'سيرو للخدمات'; } $apiUrl = 'https://nabeh.intaleqapp.com/api/otp/send'; $payload = [ 'phone' => $phoneRaw, 'type' => $type, 'code' => $otp, 'message' => "رمز التحقق الخاص بك لتطبيق {$appName} هو: *{code}* \n الرجاء عدم مشاركته مع أي شخص." ]; $response = curlCall("POST", $apiUrl, json_encode($payload), [ 'Content-Type: application/json', "Authorization: Bearer $bearerToken" ]); if ($response) { $decoded = json_decode($response, true); error_log("ℹ️ [Nabeh OTP Response] " . $response); if ($decoded) { $statusStr = strtolower((string)($decoded['status'] ?? '')); $msgStr = strtolower((string)($decoded['message'] ?? '')); if ( !empty($decoded['success']) || in_array($statusStr, ['success', 'ok', 'true', '200', 'sent', 'queued', '1'], true) || ($decoded['status'] ?? false) === true || ($decoded['code'] ?? 0) === 200 || !empty($decoded['message_id']) || !empty($decoded['id']) || !empty($decoded['token']) || strpos($msgStr, 'success') !== false || strpos($msgStr, 'sent') !== false || strpos($msgStr, 'تم') !== false ) { return true; } } $msg = "❌ [Nabeh OTP Response Failed] Raw: " . $response; $GLOBALS['last_otp_error'] = $msg; error_log($msg); } else { $msg = "❌ [Nabeh OTP cURL Failed] Empty response from cURL."; $GLOBALS['last_otp_error'] = $msg; error_log($msg); } return false; } /** * Send OTP via Intaleq Static OTP Gateway (using body app_key parameter) * * @param string $receiver Recipient phone number * @param string $otp 3-digit verification code * @param string $method whatsapp | sms | voice | flash_call * @return bool True if OTP was sent successfully */ function sendIntaleqOtp(string $receiver, string &$otp, string $method = 'whatsapp'): bool { $appKey = getenv('NABEH_OTP_APP_KEY'); if (!$appKey) { error_log("⚠️ [Intaleq OTP] Missing NABEH_OTP_APP_KEY in environment."); return false; } // Normalize receiver to start with + $phoneWithPlus = (strpos($receiver, '+') === 0) ? $receiver : '+' . $receiver; $apiUrl = 'https://otp.intaleqapp.com/api/request-otp.php'; $payload = [ 'phone' => $phoneWithPlus, 'app_key' => $appKey ]; $response = curlCall("POST", $apiUrl, json_encode($payload), [ 'Content-Type: application/json' ]); if ($response) { $decoded = json_decode($response, true); if ($decoded && (!empty($decoded['success']) || ($decoded['status'] ?? '') === 'success')) { if (isset($decoded['otp'])) { $otp = (string)$decoded['otp']; } return true; } $msg = "❌ [Intaleq OTP] API returned failure response: " . $response; error_log($msg); } else { error_log("❌ [Intaleq OTP] Empty response or cURL failed."); } return false; } /** * Generic cURL execution helper */ function curlCall(string $method, string $url, string $data, array $headers): ?string { $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_CUSTOMREQUEST => $method, CURLOPT_POSTFIELDS => $data, CURLOPT_HTTPHEADER => $headers, CURLOPT_TIMEOUT => 15, CURLOPT_CONNECTTIMEOUT => 5 ]); $response = curl_exec($ch); $error = curl_error($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($error) { $msg = "⚠️ [OTP cURL] Error calling $url: $error"; error_log($msg); return null; } if ($httpCode !== 200) { $msg = "⚠️ [OTP cURL] Non-200 HTTP code $httpCode from $url. Response: $response"; error_log($msg); } return $response; }