$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 to read cached token from Redis (TTL 24 hours) if ($redis) { try { $cachedToken = $redis->get('nabeh_bearer_token'); if ($cachedToken) { return $cachedToken; } } catch (Exception $e) { error_log("⚠️ [Nabeh Auth Redis] Error reading token: " . $e->getMessage()); } } // 2. Token not cached, authenticate via Nabeh Login API $email = getenv('NABEH_EMAIL'); $password = getenv('NABEH_PASSWORD'); if (!$email || !$password) { error_log("⚠️ [Nabeh Auth] Missing NABEH_EMAIL or NABEH_PASSWORD environment variables."); 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' ]); if ($response) { $decoded = json_decode($response, true); $token = $decoded['token'] ?? $decoded['message']['token'] ?? $decoded['jwt'] ?? $decoded['access_token'] ?? null; if ($token) { // Cache token in Redis for 24 hours (86400 seconds) if ($redis) { try { $redis->setex('nabeh_bearer_token', 86400, $token); } catch (Exception $e) { error_log("⚠️ [Nabeh Auth Redis Cache Save] Error saving token: " . $e->getMessage()); } } return $token; } error_log("❌ [Nabeh Auth] Failed to extract token from login response: " . $response); } 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 * @return bool True if OTP was sent successfully */ function sendNabehOtp(string $receiver, string $otp, string $method = 'text'): bool { $bearerToken = getNabehBearerToken(); if (!$bearerToken) { error_log("⚠️ [Nabeh OTP] Failed to obtain dynamic JWT Bearer token."); return false; } // Strip symbols for Nabeh endpoint $phoneRaw = preg_replace('/\D+/', '', $receiver); // Map method/type $type = 'text'; if ($method === 'voice') { $type = 'voice'; } elseif ($method === 'image') { $type = 'image'; } // elseif ($method === 'flash_call') { // $type = 'flash_call'; // } $apiUrl = 'https://nabeh.intaleqapp.com/api/otp/send'; $payload = [ 'phone' => $phoneRaw, 'type' => $type, 'code' => $otp ]; $response = curlCall("POST", $apiUrl, json_encode($payload), [ 'Content-Type: application/json', "Authorization: Bearer $bearerToken" ]); if ($response) { $decoded = json_decode($response, true); if ($decoded && ($decoded['success'] ?? false)) { return true; } error_log("❌ [Nabeh OTP] API returned failure response: " . $response); } 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, 'device_type' => 'android', 'method' => $method, 'code' => $otp ]; $response = curlCall("POST", $apiUrl, json_encode($payload), [ 'Content-Type: application/json' ]); if ($response) { $decoded = json_decode($response, true); if ($decoded && ($decoded['success'] ?? false)) { return true; } error_log("❌ [Intaleq OTP] API returned failure response: " . $response); } 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) { error_log("⚠️ [OTP cURL] Error calling $url: $error"); return null; } if ($httpCode !== 200) { error_log("⚠️ [OTP cURL] Non-200 HTTP code $httpCode from $url. Response: $response"); } return $response; }