fix: Fast text WhatsApp OTP by default, 50s timeout for image mode with auto-fallback

This commit is contained in:
Hamza-Ayed
2026-08-26 23:04:25 +03:00
parent cee6265930
commit f3c5f4161e
2 changed files with 40 additions and 19 deletions
+4 -2
View File
@@ -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([
+36 -17
View File
@@ -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