diff --git a/backend/app/Controllers/TestController.php b/backend/app/Controllers/TestController.php index 8baa271..e0ff2bc 100644 --- a/backend/app/Controllers/TestController.php +++ b/backend/app/Controllers/TestController.php @@ -93,12 +93,14 @@ class TestController $cleanPhone = '962' . substr($cleanPhone, 1); } $testOtp = (string)random_int(100000, 999999); + $type = $queryParams['type'] ?? 'text'; // Fast text mode by default try { $nabeh = new NabehService(); - $sendResult = $nabeh->sendOtp($cleanPhone, $testOtp, 'image', 'فحص منصة صَقِل'); + $sendResult = $nabeh->sendOtp($cleanPhone, $testOtp, $type, 'فحص منصة صَقِل'); $results['live_otp_test'] = [ 'phone' => $cleanPhone, 'code_sent' => $testOtp, + 'type_used' => $type, 'send_result' => $sendResult ]; } catch (\Exception $e) { @@ -108,7 +110,7 @@ class TestController ]; } } else { - $results['live_otp_test'] = 'To test sending an actual WhatsApp OTP, add ?phone=96279XXXXXXX to this URL'; + $results['live_otp_test'] = 'To test sending an actual WhatsApp OTP, add ?phone=96279XXXXXXX&type=text to this URL'; } $response->json([ diff --git a/backend/app/Services/NabehService.php b/backend/app/Services/NabehService.php index 0bd95f0..d34fe68 100644 --- a/backend/app/Services/NabehService.php +++ b/backend/app/Services/NabehService.php @@ -10,13 +10,15 @@ class NabehService private string $sendUrl; private string $email; private string $password; + private string $defaultType; public function __construct() { - $this->authUrl = (string)getenv('NABEH_AUTH_URL'); - $this->sendUrl = (string)getenv('NABEH_SEND_URL'); - $this->email = (string)getenv('NABEH_EMAIL'); - $this->password = (string)getenv('NABEH_PASSWORD'); + $this->authUrl = (string)getenv('NABEH_AUTH_URL'); + $this->sendUrl = (string)getenv('NABEH_SEND_URL'); + $this->email = (string)getenv('NABEH_EMAIL'); + $this->password = (string)getenv('NABEH_PASSWORD'); + $this->defaultType = (string)(getenv('NABEH_OTP_TYPE') ?: 'text'); $missing = []; if (empty($this->authUrl)) $missing[] = 'NABEH_AUTH_URL'; @@ -60,6 +62,7 @@ class NabehService CURLOPT_CONNECTTIMEOUT => 10, CURLOPT_HTTPHEADER => ['Content-Type: application/json'], CURLOPT_SSL_VERIFYPEER => false, + CURLOPT_SSL_VERIFYHOST => false, ]); $response = curl_exec($ch); @@ -94,9 +97,10 @@ class NabehService } /** - * Send OTP via Nabeh JWT Auth Gateway (WhatsApp Image/Text OTP) + * Send OTP via Nabeh JWT Auth Gateway (WhatsApp text/image OTP) + * Text mode is ultra-fast (1-2s). Image mode generates dynamic card (10-25s). */ - public function sendOtp(string $receiver, string $otp, string $method = 'image', string $appName = 'منصة صَقِل'): array + public function sendOtp(string $receiver, string $otp, ?string $method = null, string $appName = 'منصة صَقِل'): array { $bearerToken = $this->getBearerToken(); if (!$bearerToken) { @@ -114,22 +118,26 @@ class NabehService $phoneRaw = '962' . $phoneRaw; } - $type = in_array($method, ['text', 'voice', 'image'], true) ? $method : 'image'; + // Selected type: provided method > defaultType > 'text' + $selectedType = $method ?: $this->defaultType; + if (!in_array($selectedType, ['text', 'voice', 'image'], true)) { + $selectedType = 'text'; + } - // 1. First attempt with image - $result = $this->attemptSend($phoneRaw, $type, $otp, $appName, $bearerToken); + // 1. First attempt with selected type (text or image) + $result = $this->attemptSend($phoneRaw, $selectedType, $otp, $appName, $bearerToken); if ($result['success']) { return $result; } - // 2. Fallback to text if image fails - if ($type === 'image') { - error_log("ℹ️ [Nabeh OTP Fallback] Image failed, retrying with text type for phone {$phoneRaw}..."); + // 2. If image failed or timed out, fallback to instant text OTP + if ($selectedType === 'image') { + error_log("ℹ️ [Nabeh OTP Fallback] Image mode failed/timed out for {$phoneRaw}. Retrying instantly with Text mode..."); $textResult = $this->attemptSend($phoneRaw, 'text', $otp, $appName, $bearerToken); if ($textResult['success']) { + $textResult['note'] = 'Image OTP timed out, delivered via fast text fallback'; return $textResult; } - // Return text attempt with details return $textResult; } @@ -145,36 +153,43 @@ class NabehService 'message' => "رمز التحقق الخاص بك لمنصة {$appName} هو: *{$otp}* \n الرجاء عدم مشاركته مع أي شخص لحماية حسابك.", ]); + $timeout = ($type === 'image') ? 50 : 25; // 50s for image rendering, 25s for text + $ch = curl_init($this->sendUrl); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_POSTFIELDS => $payload, CURLOPT_RETURNTRANSFER => true, - CURLOPT_TIMEOUT => 35, + CURLOPT_TIMEOUT => $timeout, CURLOPT_CONNECTTIMEOUT => 10, CURLOPT_SSL_VERIFYPEER => false, + CURLOPT_SSL_VERIFYHOST => false, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', "Authorization: Bearer {$bearerToken}", ], ]); + $startTime = microtime(true); $response = curl_exec($ch); + $duration = round(microtime(true) - $startTime, 2); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $curlError = curl_error($ch); curl_close($ch); if ($curlError) { - error_log("❌ [Nabeh OTP cURL Error] " . $curlError); + error_log("❌ [Nabeh OTP cURL Error (type={$type}, duration={$duration}s)] " . $curlError); return [ 'success' => false, + 'type_used' => $type, + 'duration' => "{$duration}s", 'http_code' => $httpCode, - 'error' => 'cURL Connection Error: ' . $curlError, + 'error' => 'cURL Error: ' . $curlError, 'response' => null ]; } - error_log("ℹ️ [Nabeh OTP Response (HTTP {$httpCode})] " . $response); + error_log("ℹ️ [Nabeh OTP Response (type={$type}, duration={$duration}s, HTTP {$httpCode})] " . $response); if ($httpCode === 200 && $response) { $decoded = json_decode($response, true); @@ -198,6 +213,8 @@ class NabehService ) { return [ 'success' => true, + 'type_used' => $type, + 'duration' => "{$duration}s", 'http_code' => 200, 'message' => 'تم إرسال رمز التحقق بنجاح عبر الواتساب', 'raw' => $decoded @@ -208,6 +225,8 @@ class NabehService return [ 'success' => false, + 'type_used' => $type, + 'duration' => "{$duration}s", 'http_code' => $httpCode, 'error' => 'Nabeh Gateway rejected request (HTTP ' . $httpCode . ')', 'response' => $response