Update Siro integration: NABEH_API_KEY, nabeh/ folder paths, /api/siro/ routes
This commit is contained in:
@@ -3,166 +3,72 @@
|
|||||||
namespace App\Core\Flows;
|
namespace App\Core\Flows;
|
||||||
|
|
||||||
use App\Services\GeminiService;
|
use App\Services\GeminiService;
|
||||||
|
use App\Services\SiroService;
|
||||||
use App\Models\DriverOcrData;
|
use App\Models\DriverOcrData;
|
||||||
use App\Models\ChatbotRule;
|
use App\Models\ChatbotRule;
|
||||||
use App\Models\DriverReminder;
|
use App\Models\DriverReminder;
|
||||||
|
use App\Core\Database;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DriverRegistrationFlow
|
* DriverRegistrationFlow
|
||||||
* Handles step-by-step driver and vehicle registration using Gemini OCR.
|
* Handles step-by-step driver and vehicle registration using Gemini OCR.
|
||||||
|
* Integrates with Siro platform for driver registration across Syria, Jordan, and Egypt.
|
||||||
*/
|
*/
|
||||||
class DriverRegistrationFlow extends BaseFlow
|
class DriverRegistrationFlow extends BaseFlow
|
||||||
{
|
{
|
||||||
private array $prompts = [
|
private array $prompts = [];
|
||||||
"id_front" => <<<EOT
|
private string $country = 'syria';
|
||||||
You are an OCR expert for Syrian national ID cards (green card).
|
|
||||||
|
|
||||||
### TASK
|
private array $stepToDocType = [
|
||||||
Analyse the **front side** of the ID and return **raw JSON only** with exactly these keys:
|
'id_front' => 'id_front',
|
||||||
|
'id_back' => 'id_back',
|
||||||
{
|
'driving_license_front' => 'driver_license_front',
|
||||||
"full_name": "", // الاسم الثلاثي أو الرباعي
|
'driving_license_back' => 'driver_license_back',
|
||||||
"national_number": "", // الرقم الوطني (LATIN digits only)
|
'vehicle_license_front' => 'car_license_front',
|
||||||
"dob": "YYYY-MM-DD", // تاريخ الميلاد
|
'vehicle_license_back' => 'car_license_back',
|
||||||
"address": "" // العنوان
|
'criminal_record' => 'criminal_record',
|
||||||
}
|
|
||||||
|
|
||||||
### RULES
|
|
||||||
* Read the red number on the bottom of the card.
|
|
||||||
* Convert any Eastern-Arabic digits (٠١٢٣٤٥٦٧٨٩) to Western-Arabic digits (0-9).
|
|
||||||
* `national_number` must contain **Latin digits only, no spaces or other characters**.
|
|
||||||
* If a field is missing, set it to **null**.
|
|
||||||
* Convert the birth date to ISO `YYYY-MM-DD`.
|
|
||||||
* Return valid JSON only — no extra keys, no markdown.
|
|
||||||
EOT,
|
|
||||||
"id_back" => <<<EOT
|
|
||||||
أنت خبير OCR مختص ببطاقات الهوية السورية (الوجه الخلفي).
|
|
||||||
|
|
||||||
### المطلوب
|
|
||||||
حلّل صورة الوجه الخلفي للهوية السورية وأعد **JSON صِرف** يحتوي المفاتيح التالية فقط:
|
|
||||||
|
|
||||||
{
|
|
||||||
"governorate": "", // المحافظة (مثال: دمشق)
|
|
||||||
"address": "", // العنوان التفصيلي (حيّ، بلدة …)
|
|
||||||
"gender": "", //Male or Female
|
|
||||||
"issue_date": "YYYY-MM-DD"// تاريخ الإصدار بصيغة ISO
|
|
||||||
}
|
|
||||||
|
|
||||||
### القواعد
|
|
||||||
1. حوّل أي أرقام عربية شرقية (٠١٢٣٤٥٦٧٨٩) إلى أرقام لاتينية (0-9).
|
|
||||||
2. أعدّ تاريخ الإصدار بالتقويم الميلادي بصيغة `YYYY-MM-DD`.
|
|
||||||
3. استخدم أحرف لاتينية كبيرة لزمرة الدم مع رمز `+` أو `-` فقط.
|
|
||||||
4. إذا كان أحد الحقول غير موجود مطلقًا، أعد قيمته **null**.
|
|
||||||
5. لا تُرجع أي مفاتيح إضافية أو شروح أو Markdown — JSON صالح فقط.
|
|
||||||
EOT,
|
|
||||||
"driving_license_front" => <<<EOT
|
|
||||||
You are an OCR expert for Syrian documents.
|
|
||||||
|
|
||||||
### TASK
|
|
||||||
Analyse the **front side of a Syrian driving licence** and return **clean JSON only** with the following keys (no extra keys, no markdown):
|
|
||||||
|
|
||||||
{
|
|
||||||
"name_arabic": "", // الاسم الثلاثي أو الرباعي بالعربية
|
|
||||||
"birth_place": "", // المحافظة أو المنطقة المكتوبة بعد كلمة الولادة
|
|
||||||
"birth_year": "", // سنة الميلاد فقط (أربعة أرقام)
|
|
||||||
"national_number": "",
|
|
||||||
"civil_registry": "", // سطر "القيد" (مثال: سهوة 3)
|
|
||||||
"blood_type": "" // زمرة الدم بالشكل: A+ , A- , B+ , B- , AB+ , AB- , O+ , O-
|
|
||||||
}
|
|
||||||
|
|
||||||
### RULES
|
|
||||||
* إذا كانت القيمة مفقودة تمامًا اكتب **null**.
|
|
||||||
* لا تُغيّر ترتيب المفاتيح.
|
|
||||||
* لا تُرسل أى شرح أو أسطر إضافية – JSON خالص فقط.
|
|
||||||
EOT,
|
|
||||||
"driving_license_back" => <<<EOT
|
|
||||||
You are an OCR expert for Syrian driving licences.
|
|
||||||
|
|
||||||
### TASK
|
|
||||||
Analyse the **back side** of a Syrian driving licence and return **raw JSON only** with exactly these keys:
|
|
||||||
|
|
||||||
{
|
|
||||||
"issue_date": "YYYY-MM-DD", // تاريخ المنح
|
|
||||||
"expiry_date": "YYYY-MM-DD", // صالحة لغاية
|
|
||||||
"license_number": "", // رقم الإجازة
|
|
||||||
"license_category": "" // D1, D2, D3 … (as printed after "UNIVERSAL DRIVING LICENCE")
|
|
||||||
}
|
|
||||||
|
|
||||||
### RULES
|
|
||||||
* If a value is totally absent, set it to **null**.
|
|
||||||
* Convert all dates to ISO `YYYY-MM-DD` (Gregorian).
|
|
||||||
* Do **NOT** add extra keys, comments, or markdown — return valid JSON only.
|
|
||||||
EOT,
|
|
||||||
"vehicle_license_front" => <<<EOT
|
|
||||||
You are an OCR expert specialized in analyzing Syrian vehicle registration cards (الرخصة البرتقالية).
|
|
||||||
|
|
||||||
Your task is to extract structured data from the **front side** of the Syrian orange vehicle card and return **raw JSON only** with the following exact fields:
|
|
||||||
|
|
||||||
{
|
|
||||||
"car_plate": "", // رقم المركبة الكامل مع اسم المحافظة، مأخوذ من الجهة اليسرى في السطر الأول (مثال: "155186 درعا")
|
|
||||||
"owner": "", // اسم المالك الكامل
|
|
||||||
"vin": "", // رقم الهيكل
|
|
||||||
"color": "", // اللون بالعربية أو الإنجليزية (مثال: "أبيض" أو "White")
|
|
||||||
"color_hex": "", // كود اللون بصيغة Hex (مثال: "#FFFFFF") أو #27332F إن تعذّر
|
|
||||||
"issue_date": "YYYY-MM-DD", // تاريخ المنح بصيغة ISO
|
|
||||||
"inspection_date": "YYYY-MM-DD" // تاريخ الفحص القادم بصيغة ISO
|
|
||||||
}
|
|
||||||
|
|
||||||
### Instructions & Rules:
|
|
||||||
|
|
||||||
1. Do **not** extract the "رمز المركبة" (on the right side of the first line) — use only the **left side** of the first line for `car_plate`.
|
|
||||||
2. Convert any Arabic dates (like `2024/05/13`) into ISO format `YYYY-MM-DD`.
|
|
||||||
3. If any value is missing or unreadable, return `null` for it.
|
|
||||||
4. Maintain Arabic encoding (e.g., owner name, city name, color).
|
|
||||||
5. Never guess — extract only what's visually found on the card.
|
|
||||||
6. Never include any explanation or extra output — return the JSON only.
|
|
||||||
|
|
||||||
Example of valid `car_plate`:
|
|
||||||
- "155186 درعا"
|
|
||||||
- "45291 دمشق"
|
|
||||||
- "122334 حمص"
|
|
||||||
EOT,
|
|
||||||
"vehicle_license_back" => <<<EOT
|
|
||||||
You are an OCR expert for Syrian vehicle registration cards (orange card).
|
|
||||||
|
|
||||||
### TASK
|
|
||||||
Analyse the **back side** of the card and return **raw JSON only** with exactly these keys (no more, no less):
|
|
||||||
|
|
||||||
{
|
|
||||||
"make": "", // الصانع (Hyundai …)
|
|
||||||
"model": "", // الطراز (H1 …)
|
|
||||||
"year": "", // سنة الصنع بالأرقام اللاتينية (e.g. "2019")
|
|
||||||
"fuel": "", // نوع الوقود (بنزين، ديزل …) أو بالإنجليزية (Petrol, Diesel,electric)
|
|
||||||
"chassis": "" // رقم الهيكل (VIN)
|
|
||||||
}
|
|
||||||
|
|
||||||
### RULES
|
|
||||||
* Convert any Eastern-Arabic digits (٠١٢٣٤٥٦٧٨٩) to Western digits (0-9).
|
|
||||||
* Normalise color names to standard English if possible, then map to a common Hex code
|
|
||||||
• "أبيض / White" → **#FFFFFF**
|
|
||||||
• "أسود / Black" → **#000000**
|
|
||||||
• "أحمر / Red" → **#FF0000**
|
|
||||||
• "أزرق / Blue" → **#0000FF**
|
|
||||||
• … (use the closest basic colour); if no match, set **color_hex = null**.
|
|
||||||
* If any field is unreadable or absent, set its value to **null**.
|
|
||||||
* Do **NOT** include extra keys, comments, or markdown — output valid JSON only.
|
|
||||||
EOT
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->prompts = SiroService::getDocumentPrompts($this->country);
|
||||||
|
}
|
||||||
|
|
||||||
public function handleStep(string $step, array $messageData, array &$context): FlowResult
|
public function handleStep(string $step, array $messageData, array &$context): FlowResult
|
||||||
{
|
{
|
||||||
$text = isset($messageData['body']) ? trim($messageData['body']) : '';
|
$text = isset($messageData['body']) ? trim($messageData['body']) : '';
|
||||||
$phone = $messageData['phone'];
|
$phone = $messageData['phone'];
|
||||||
$companyId = $context['company_id'] ?? 1;
|
$companyId = $context['company_id'] ?? 1;
|
||||||
|
|
||||||
|
// Detect country from phone number
|
||||||
|
$this->country = SiroService::detectCountry($phone);
|
||||||
|
$context['country'] = $this->country;
|
||||||
|
|
||||||
|
// Set country-specific prompts
|
||||||
|
$this->prompts = SiroService::getDocumentPrompts($this->country);
|
||||||
|
|
||||||
|
// Country name in Arabic for messages
|
||||||
|
$countryNames = [
|
||||||
|
'syria' => 'سوريا',
|
||||||
|
'jordan' => 'الأردن',
|
||||||
|
'egypt' => 'مصر',
|
||||||
|
];
|
||||||
|
$countryName = $countryNames[$this->country] ?? 'سوريا';
|
||||||
|
|
||||||
|
// App name based on country
|
||||||
|
$appNames = [
|
||||||
|
'syria' => 'سيرو',
|
||||||
|
'jordan' => 'سيرو',
|
||||||
|
'egypt' => 'سيرو',
|
||||||
|
];
|
||||||
|
$appName = $appNames[$this->country] ?? 'سيرو';
|
||||||
|
|
||||||
// If currently postponed and user sends a message, resume the flow
|
// If currently postponed and user sends a message, resume the flow
|
||||||
if ($step === 'postponed') {
|
if ($step === 'postponed') {
|
||||||
// Cancel active reminder
|
|
||||||
$activeReminder = DriverReminder::findActive($companyId, $phone);
|
$activeReminder = DriverReminder::findActive($companyId, $phone);
|
||||||
if ($activeReminder) {
|
if ($activeReminder) {
|
||||||
DriverReminder::update($activeReminder['id'], ['status' => 'cancelled']);
|
DriverReminder::update($activeReminder['id'], ['status' => 'cancelled']);
|
||||||
}
|
}
|
||||||
// Restore previous step
|
|
||||||
$step = $context['previous_step'] ?? 'ask_name';
|
$step = $context['previous_step'] ?? 'ask_name';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,7 +94,6 @@ EOT
|
|||||||
$context['postpone_count'] = $postponeCount;
|
$context['postpone_count'] = $postponeCount;
|
||||||
$context['previous_step'] = $step;
|
$context['previous_step'] = $step;
|
||||||
|
|
||||||
// Schedule reminder
|
|
||||||
$scheduledAt = date('Y-m-d H:i:s', strtotime("+{$hours} hours"));
|
$scheduledAt = date('Y-m-d H:i:s', strtotime("+{$hours} hours"));
|
||||||
DriverReminder::saveReminder([
|
DriverReminder::saveReminder([
|
||||||
'company_id' => $companyId,
|
'company_id' => $companyId,
|
||||||
@@ -209,7 +114,7 @@ EOT
|
|||||||
switch ($step) {
|
switch ($step) {
|
||||||
case 'start':
|
case 'start':
|
||||||
return new FlowResult(
|
return new FlowResult(
|
||||||
"أهلاً بك كابتن في خدمة تسجيل كباتن تطبيق انطلق 🚖.\nيرجى إرسال اسمك الثلاثي الكامل للبدء:",
|
"أهلاً بك كابتن في خدمة تسجيل كباتن تطبيق {$appName} في {$countryName} 🚖.\nيرجى إرسال اسمك الثلاثي الكامل للبدء:",
|
||||||
"ask_name"
|
"ask_name"
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -306,6 +211,17 @@ EOT
|
|||||||
return new FlowResult("عذراً، فشل حفظ الصورة. الرجاء إعادة إرسال صورة الوثيقة:", "criminal_record");
|
return new FlowResult("عذراً، فشل حفظ الصورة. الرجاء إعادة إرسال صورة الوثيقة:", "criminal_record");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Upload criminal record to Siro
|
||||||
|
$fullPath = __DIR__ . '/../../../../public' . $imageUrl;
|
||||||
|
$criminalSiroUrl = SiroService::uploadDocument(
|
||||||
|
$this->country,
|
||||||
|
SiroService::formatPhone($phone, $this->country),
|
||||||
|
'criminal_record',
|
||||||
|
$fullPath,
|
||||||
|
$messageData['imageMimeType']
|
||||||
|
);
|
||||||
|
$context['criminal_record_siro_url'] = $criminalSiroUrl;
|
||||||
|
|
||||||
// Securely save registration data to local database
|
// Securely save registration data to local database
|
||||||
try {
|
try {
|
||||||
DriverOcrData::saveSecure([
|
DriverOcrData::saveSecure([
|
||||||
@@ -332,11 +248,83 @@ EOT
|
|||||||
return new FlowResult("عذراً، حدث خطأ أثناء حفظ طلبك في قاعدة البيانات. يرجى المحاولة مرة أخرى لاحقاً.", "criminal_record");
|
return new FlowResult("عذراً، حدث خطأ أثناء حفظ طلبك في قاعدة البيانات. يرجى المحاولة مرة أخرى لاحقاً.", "criminal_record");
|
||||||
}
|
}
|
||||||
|
|
||||||
return new FlowResult(
|
// Register driver in Siro with Siro-hosted URLs
|
||||||
"شكراً لك كابتن، لقد تم استلام كافة المستندات والتحقق منها بنجاح. سيقوم فريق خدمة عملاء انطلق بمراجعة طلبك وتفعيل حسابك بأسرع وقت ممكن. يومك سعيد! 🚖",
|
$docUrls = [
|
||||||
"finished",
|
'id_front' => $context['id_front_siro_url'] ?? '',
|
||||||
true
|
'id_back' => $context['id_back_siro_url'] ?? '',
|
||||||
);
|
'driving_license_front' => $context['driving_license_front_siro_url'] ?? '',
|
||||||
|
'driving_license_back' => $context['driving_license_back_siro_url'] ?? '',
|
||||||
|
'vehicle_license_front' => $context['vehicle_license_front_siro_url'] ?? '',
|
||||||
|
'vehicle_license_back' => $context['vehicle_license_back_siro_url'] ?? '',
|
||||||
|
'criminal_record' => $criminalSiroUrl ?? '',
|
||||||
|
];
|
||||||
|
|
||||||
|
$idOcr = $context['id_front_ocr'] ?? [];
|
||||||
|
$vlOcr = $context['vehicle_license_front_ocr'] ?? [];
|
||||||
|
$vlbOcr = $context['vehicle_license_back_ocr'] ?? [];
|
||||||
|
$dlOcr = $context['driving_license_front_ocr'] ?? [];
|
||||||
|
|
||||||
|
$formattedPhone = SiroService::formatPhone($phone, $this->country);
|
||||||
|
$driverId = 'DRV' . date('YmdHis') . rand(100, 999);
|
||||||
|
|
||||||
|
$driverData = [
|
||||||
|
'phone' => $formattedPhone,
|
||||||
|
'password' => substr(md5($formattedPhone . time()), 0, 12),
|
||||||
|
'first_name' => explode(' ', $context['name'] ?? '')[0] ?? $context['name'],
|
||||||
|
'last_name' => implode(' ', array_slice(explode(' ', $context['name'] ?? ''), 1)) ?: $context['name'],
|
||||||
|
'name_arabic' => $context['name'] ?? '',
|
||||||
|
'national_number' => $idOcr['national_number'] ?? '',
|
||||||
|
'birthdate' => $idOcr['dob'] ?? '',
|
||||||
|
'address' => $idOcr['address'] ?? '',
|
||||||
|
'id' => $driverId,
|
||||||
|
];
|
||||||
|
|
||||||
|
$carData = [
|
||||||
|
'vin' => $vlbOcr['chassis'] ?? $vlOcr['vin'] ?? '',
|
||||||
|
'car_plate' => $vlOcr['car_plate'] ?? '',
|
||||||
|
'make' => $vlbOcr['make'] ?? '',
|
||||||
|
'model' => $vlbOcr['model'] ?? '',
|
||||||
|
'year' => $vlbOcr['year'] ?? '',
|
||||||
|
'color' => $vlOcr['color'] ?? '',
|
||||||
|
'color_hex' => $vlOcr['color_hex'] ?? '#000000',
|
||||||
|
'owner' => $vlOcr['owner'] ?? '',
|
||||||
|
'fuel' => $vlbOcr['fuel'] ?? '',
|
||||||
|
'expiration_date' => $vlOcr['issue_date'] ?? '',
|
||||||
|
'vehicle_category_id' => 1,
|
||||||
|
];
|
||||||
|
|
||||||
|
$syroResult = SiroService::registerDriver($driverData, $carData, $docUrls, $this->country);
|
||||||
|
|
||||||
|
$appNames = [
|
||||||
|
'syria' => 'سيرو',
|
||||||
|
'jordan' => 'سيرو',
|
||||||
|
'egypt' => 'سيرو',
|
||||||
|
];
|
||||||
|
$appName = $appNames[$this->country] ?? 'سيرو';
|
||||||
|
|
||||||
|
if ($syroResult && ($syroResult['status'] ?? '') === 'success') {
|
||||||
|
DriverOcrData::saveSecure([
|
||||||
|
'company_id' => $companyId,
|
||||||
|
'phone' => $phone,
|
||||||
|
'name' => $context['name'],
|
||||||
|
'status' => 'registered'
|
||||||
|
]);
|
||||||
|
|
||||||
|
return new FlowResult(
|
||||||
|
"شكراً لك كابتن، تم تسجيلك بنجاح في تطبيق {$appName} 🚖✅\nسيتم مراجعة طلبك من قبل فريق الخدمة وتفعيل حسابك قريباً. يومك سعيد!",
|
||||||
|
"finished",
|
||||||
|
true
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$errMsg = $syroResult['message'] ?? 'خطأ غير معروف';
|
||||||
|
error_log("[Registration Flow] Siro registration failed: " . json_encode($syroResult));
|
||||||
|
|
||||||
|
return new FlowResult(
|
||||||
|
"تم حفظ مستنداتك بنجاح في نظامنا. لكن حدث تأخير في تسجيلك على تطبيق {$appName}. سيقوم فريقنا بمراجعة بياناتك وتفعيل حسابك في أقرب وقت. شكراً لصبرك! 🙏",
|
||||||
|
"finished",
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return new FlowResult("خطأ في تحديد خطوة المسار.", "finished", true);
|
return new FlowResult("خطأ في تحديد خطوة المسار.", "finished", true);
|
||||||
@@ -365,6 +353,23 @@ EOT
|
|||||||
return new FlowResult("عذراً، فشل حفظ الصورة. الرجاء إعادة المحاولة وإرسال الصورة:", $step);
|
return new FlowResult("عذراً، فشل حفظ الصورة. الرجاء إعادة المحاولة وإرسال الصورة:", $step);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Upload to Siro and store the signed URL
|
||||||
|
$fullPath = __DIR__ . '/../../../../public' . $imageUrl;
|
||||||
|
$siroUrl = SiroService::uploadDocument(
|
||||||
|
$this->country,
|
||||||
|
SiroService::formatPhone($messageData['phone'], $this->country),
|
||||||
|
$this->stepToDocType[$step] ?? $step,
|
||||||
|
$fullPath,
|
||||||
|
$messageData['imageMimeType']
|
||||||
|
);
|
||||||
|
if ($siroUrl) {
|
||||||
|
$context[$step . '_siro_url'] = $siroUrl;
|
||||||
|
error_log("[DriverRegistrationFlow] Uploaded {$step} to Siro: {$siroUrl}");
|
||||||
|
} else {
|
||||||
|
$context[$step . '_siro_url'] = null;
|
||||||
|
error_log("[DriverRegistrationFlow] Warning: Failed to upload {$step} to Siro, using local URL");
|
||||||
|
}
|
||||||
|
|
||||||
$companyId = $context['company_id'] ?? 1;
|
$companyId = $context['company_id'] ?? 1;
|
||||||
|
|
||||||
// Check subscription limit for OCR
|
// Check subscription limit for OCR
|
||||||
|
|||||||
520
backend/app/Services/SiroService.php
Normal file
520
backend/app/Services/SiroService.php
Normal file
@@ -0,0 +1,520 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Services;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SiroService
|
||||||
|
* Handles communication between Nabeh and the Siro ride-hailing platform APIs.
|
||||||
|
* Supports Syria, Jordan, and Egypt with country detection from phone numbers.
|
||||||
|
*/
|
||||||
|
class SiroService
|
||||||
|
{
|
||||||
|
private const COUNTRY_SYRIA = 'syria';
|
||||||
|
private const COUNTRY_JORDAN = 'jordan';
|
||||||
|
private const COUNTRY_EGYPT = 'egypt';
|
||||||
|
|
||||||
|
private static array $countryPrefixes = [
|
||||||
|
'963' => self::COUNTRY_SYRIA,
|
||||||
|
'00963' => self::COUNTRY_SYRIA,
|
||||||
|
'962' => self::COUNTRY_JORDAN,
|
||||||
|
'00962' => self::COUNTRY_JORDAN,
|
||||||
|
'20' => self::COUNTRY_EGYPT,
|
||||||
|
'0020' => self::COUNTRY_EGYPT,
|
||||||
|
];
|
||||||
|
|
||||||
|
private static array $countryPhonePrefixes = [
|
||||||
|
self::COUNTRY_SYRIA => '963',
|
||||||
|
self::COUNTRY_JORDAN => '962',
|
||||||
|
self::COUNTRY_EGYPT => '20',
|
||||||
|
];
|
||||||
|
|
||||||
|
private static array $countryEnvKeys = [
|
||||||
|
self::COUNTRY_SYRIA => 'SIRO_API_URL_SYRIA',
|
||||||
|
self::COUNTRY_JORDAN => 'SIRO_API_URL_JORDAN',
|
||||||
|
self::COUNTRY_EGYPT => 'SIRO_API_URL_EGYPT',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect country from phone number
|
||||||
|
*/
|
||||||
|
public static function detectCountry(string $phone): string
|
||||||
|
{
|
||||||
|
$cleaned = preg_replace('/[^0-9]/', '', $phone);
|
||||||
|
foreach (self::$countryPrefixes as $prefix => $country) {
|
||||||
|
if (strpos($cleaned, $prefix) === 0) {
|
||||||
|
return $country;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return self::COUNTRY_SYRIA;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get country-specific API base URL
|
||||||
|
*/
|
||||||
|
public static function getApiUrl(string $country): string
|
||||||
|
{
|
||||||
|
$envKey = self::$countryEnvKeys[$country] ?? self::$countryEnvKeys[self::COUNTRY_SYRIA];
|
||||||
|
return rtrim(getenv($envKey) ?: 'https://api-syria.siromove.com/siro', '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format phone number to international format for the given country
|
||||||
|
*/
|
||||||
|
public static function formatPhone(string $phone, string $country): string
|
||||||
|
{
|
||||||
|
$cleaned = preg_replace('/[^0-9]/', '', $phone);
|
||||||
|
$prefix = self::$countryPhonePrefixes[$country] ?? '963';
|
||||||
|
|
||||||
|
if (strpos($cleaned, $prefix) === 0) {
|
||||||
|
return $cleaned;
|
||||||
|
}
|
||||||
|
if (strpos($cleaned, '00' . $prefix) === 0) {
|
||||||
|
return $prefix . substr($cleaned, strlen($prefix) + 2);
|
||||||
|
}
|
||||||
|
if (strpos($cleaned, '0') === 0) {
|
||||||
|
return $prefix . substr($cleaned, 1);
|
||||||
|
}
|
||||||
|
return $prefix . $cleaned;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upload a document image to Siro and return the signed URL
|
||||||
|
*/
|
||||||
|
public static function uploadDocument(
|
||||||
|
string $country,
|
||||||
|
string $driverId,
|
||||||
|
string $docType,
|
||||||
|
string $imagePath,
|
||||||
|
string $imageMimeType
|
||||||
|
): ?string {
|
||||||
|
$apiUrl = self::getApiUrl($country);
|
||||||
|
$apiKey = getenv('NABEH_API_KEY') ?: '';
|
||||||
|
$uploadUrl = rtrim($apiUrl, '/') . '/nabeh/upload_document.php';
|
||||||
|
|
||||||
|
$ch = curl_init();
|
||||||
|
curl_setopt($ch, CURLOPT_URL, $uploadUrl);
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_POST, true);
|
||||||
|
|
||||||
|
$headers = [
|
||||||
|
'X-API-Key: ' . $apiKey,
|
||||||
|
];
|
||||||
|
|
||||||
|
$body = [
|
||||||
|
'driver_id' => $driverId,
|
||||||
|
'doc_type' => $docType,
|
||||||
|
];
|
||||||
|
|
||||||
|
if (file_exists($imagePath)) {
|
||||||
|
$body['file'] = new \CURLFile($imagePath, $imageMimeType);
|
||||||
|
} else {
|
||||||
|
error_log("[SiroService] Image not found: {$imagePath}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||||
|
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
|
||||||
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||||
|
|
||||||
|
$res = curl_exec($ch);
|
||||||
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
if ($httpCode !== 200) {
|
||||||
|
error_log("[SiroService] Upload failed for {$docType}: HTTP {$httpCode} - {$res}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = json_decode($res, true);
|
||||||
|
$fileUrl = $data['message']['file_url'] ?? null;
|
||||||
|
|
||||||
|
if (!$fileUrl) {
|
||||||
|
error_log("[SiroService] Upload response missing file_url: {$res}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return $fileUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a driver in Siro via the simplified Nabeh integration endpoint
|
||||||
|
*/
|
||||||
|
public static function registerDriver(array $driverData, array $carData, array $documentUrls, string $country): ?array
|
||||||
|
{
|
||||||
|
$apiUrl = self::getApiUrl($country);
|
||||||
|
$apiKey = getenv('NABEH_API_KEY') ?: '';
|
||||||
|
$registerUrl = $apiUrl . '/nabeh/register.php';
|
||||||
|
|
||||||
|
$payload = array_merge($driverData, $carData, [
|
||||||
|
'id_front_url' => $documentUrls['id_front'] ?? '',
|
||||||
|
'id_back_url' => $documentUrls['id_back'] ?? '',
|
||||||
|
'driver_license_front_url' => $documentUrls['driving_license_front'] ?? '',
|
||||||
|
'driver_license_back_url' => $documentUrls['driving_license_back'] ?? '',
|
||||||
|
'vehicle_license_front_url' => $documentUrls['vehicle_license_front'] ?? '',
|
||||||
|
'vehicle_license_back_url' => $documentUrls['vehicle_license_back'] ?? '',
|
||||||
|
'criminal_record_url' => $documentUrls['criminal_record'] ?? '',
|
||||||
|
'profile_picture' => $documentUrls['profile_picture'] ?? '',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$ch = curl_init();
|
||||||
|
curl_setopt($ch, CURLOPT_URL, $registerUrl);
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_POST, true);
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||||
|
'X-API-Key: ' . $apiKey,
|
||||||
|
'Content-Type: application/json',
|
||||||
|
]);
|
||||||
|
curl_setopt($ch, CURLOPT_TIMEOUT, 60);
|
||||||
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||||
|
|
||||||
|
$res = curl_exec($ch);
|
||||||
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
if ($httpCode !== 200) {
|
||||||
|
error_log("[SiroService] Registration failed: HTTP {$httpCode} - {$res}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return json_decode($res, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check driver registration status in Siro
|
||||||
|
*/
|
||||||
|
public static function checkDriverStatus(string $phone, string $country): ?array
|
||||||
|
{
|
||||||
|
$apiUrl = self::getApiUrl($country);
|
||||||
|
$apiKey = getenv('NABEH_API_KEY') ?: '';
|
||||||
|
$statusUrl = $apiUrl . '/nabeh/driver_status.php';
|
||||||
|
|
||||||
|
$ch = curl_init();
|
||||||
|
curl_setopt($ch, CURLOPT_URL, $statusUrl . '?phone=' . urlencode($phone));
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||||
|
'X-API-Key: ' . $apiKey,
|
||||||
|
]);
|
||||||
|
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
|
||||||
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||||
|
|
||||||
|
$res = curl_exec($ch);
|
||||||
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
if ($httpCode !== 200) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return json_decode($res, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query Siro platform for driver info, trips, stats, or trip details
|
||||||
|
*/
|
||||||
|
public static function querySiro(string $country, array $params): ?array
|
||||||
|
{
|
||||||
|
$apiUrl = self::getApiUrl($country);
|
||||||
|
$apiKey = getenv('NABEH_API_KEY') ?: '';
|
||||||
|
$queryUrl = $apiUrl . '/nabeh/query.php';
|
||||||
|
|
||||||
|
$ch = curl_init();
|
||||||
|
curl_setopt($ch, CURLOPT_URL, $queryUrl);
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_POST, true);
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($params));
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||||
|
'X-API-Key: ' . $apiKey,
|
||||||
|
'Content-Type: application/json',
|
||||||
|
]);
|
||||||
|
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
|
||||||
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||||
|
|
||||||
|
$res = curl_exec($ch);
|
||||||
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
if ($httpCode !== 200) {
|
||||||
|
error_log("[SiroService] Query failed: HTTP {$httpCode} - {$res}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return json_decode($res, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get OCR prompts for a specific country
|
||||||
|
*/
|
||||||
|
public static function getDocumentPrompts(string $country): array
|
||||||
|
{
|
||||||
|
$syriaPrompts = [
|
||||||
|
"id_front" => <<<EOT
|
||||||
|
You are an OCR expert for Syrian national ID cards (green card).
|
||||||
|
|
||||||
|
### TASK
|
||||||
|
Analyse the **front side** of the ID and return **raw JSON only** with exactly these keys:
|
||||||
|
|
||||||
|
{
|
||||||
|
"full_name": "",
|
||||||
|
"national_number": "",
|
||||||
|
"dob": "YYYY-MM-DD",
|
||||||
|
"address": ""
|
||||||
|
}
|
||||||
|
|
||||||
|
### RULES
|
||||||
|
* Read the red number on the bottom of the card.
|
||||||
|
* Convert any Eastern-Arabic digits to Western-Arabic digits.
|
||||||
|
* Return valid JSON only — no extra keys, no markdown.
|
||||||
|
EOT,
|
||||||
|
"id_back" => <<<EOT
|
||||||
|
أنت خبير OCR مختص ببطاقات الهوية السورية (الوجه الخلفي).
|
||||||
|
|
||||||
|
### المطلوب
|
||||||
|
حلّل صورة الوجه الخلفي للهوية السورية وأعد **JSON صِرف**:
|
||||||
|
|
||||||
|
{
|
||||||
|
"governorate": "",
|
||||||
|
"address": "",
|
||||||
|
"gender": "",
|
||||||
|
"issue_date": "YYYY-MM-DD"
|
||||||
|
}
|
||||||
|
|
||||||
|
### القواعد
|
||||||
|
1. حوّل أي أرقام عربية شرقية إلى أرقام لاتينية.
|
||||||
|
2. لا تُرجع أي مفاتيح إضافية أو شروح.
|
||||||
|
EOT,
|
||||||
|
"driving_license_front" => <<<EOT
|
||||||
|
You are an OCR expert for Syrian documents.
|
||||||
|
|
||||||
|
### TASK
|
||||||
|
Analyse the **front side of a Syrian driving licence** and return **clean JSON only**:
|
||||||
|
|
||||||
|
{
|
||||||
|
"name_arabic": "",
|
||||||
|
"birth_place": "",
|
||||||
|
"birth_year": "",
|
||||||
|
"national_number": "",
|
||||||
|
"civil_registry": "",
|
||||||
|
"blood_type": ""
|
||||||
|
}
|
||||||
|
EOT,
|
||||||
|
"driving_license_back" => <<<EOT
|
||||||
|
You are an OCR expert for Syrian driving licences.
|
||||||
|
|
||||||
|
### TASK
|
||||||
|
Analyse the **back side** of a Syrian driving licence:
|
||||||
|
|
||||||
|
{
|
||||||
|
"issue_date": "YYYY-MM-DD",
|
||||||
|
"expiry_date": "YYYY-MM-DD",
|
||||||
|
"license_number": "",
|
||||||
|
"license_category": ""
|
||||||
|
}
|
||||||
|
EOT,
|
||||||
|
"vehicle_license_front" => <<<EOT
|
||||||
|
You are an OCR expert for Syrian orange vehicle cards.
|
||||||
|
|
||||||
|
### TASK
|
||||||
|
Analyse the **front side**:
|
||||||
|
|
||||||
|
{
|
||||||
|
"car_plate": "",
|
||||||
|
"owner": "",
|
||||||
|
"vin": "",
|
||||||
|
"color": "",
|
||||||
|
"color_hex": "",
|
||||||
|
"issue_date": "YYYY-MM-DD",
|
||||||
|
"inspection_date": "YYYY-MM-DD"
|
||||||
|
}
|
||||||
|
EOT,
|
||||||
|
"vehicle_license_back" => <<<EOT
|
||||||
|
You are an OCR expert for Syrian orange vehicle cards.
|
||||||
|
|
||||||
|
### TASK
|
||||||
|
Analyse the **back side**:
|
||||||
|
|
||||||
|
{
|
||||||
|
"make": "",
|
||||||
|
"model": "",
|
||||||
|
"year": "",
|
||||||
|
"fuel": "",
|
||||||
|
"chassis": ""
|
||||||
|
}
|
||||||
|
EOT,
|
||||||
|
];
|
||||||
|
|
||||||
|
$jordanPrompts = [
|
||||||
|
"id_front" => <<<EOT
|
||||||
|
You are an OCR expert for Jordanian national ID cards.
|
||||||
|
|
||||||
|
### TASK
|
||||||
|
Analyse the **front side** of the Jordanian ID and return **raw JSON only**:
|
||||||
|
|
||||||
|
{
|
||||||
|
"full_name": "",
|
||||||
|
"national_number": "",
|
||||||
|
"dob": "YYYY-MM-DD",
|
||||||
|
"address": ""
|
||||||
|
}
|
||||||
|
|
||||||
|
Extract the national number from the ID. Return valid JSON only.
|
||||||
|
EOT,
|
||||||
|
"id_back" => <<<EOT
|
||||||
|
You are an OCR expert for Jordanian national ID cards.
|
||||||
|
|
||||||
|
### TASK
|
||||||
|
Analyse the **back side** of the Jordanian ID:
|
||||||
|
|
||||||
|
{
|
||||||
|
"governorate": "",
|
||||||
|
"gender": "",
|
||||||
|
"issue_date": "YYYY-MM-DD",
|
||||||
|
"expiry_date": "YYYY-MM-DD"
|
||||||
|
}
|
||||||
|
EOT,
|
||||||
|
"driving_license_front" => <<<EOT
|
||||||
|
You are an OCR expert for Jordanian driving licences.
|
||||||
|
|
||||||
|
### TASK
|
||||||
|
Analyse the **front side**:
|
||||||
|
|
||||||
|
{
|
||||||
|
"name_arabic": "",
|
||||||
|
"license_number": "",
|
||||||
|
"issue_date": "YYYY-MM-DD",
|
||||||
|
"expiry_date": "YYYY-MM-DD",
|
||||||
|
"license_category": ""
|
||||||
|
}
|
||||||
|
EOT,
|
||||||
|
"driving_license_back" => <<<EOT
|
||||||
|
You are an OCR expert for Jordanian driving licences.
|
||||||
|
|
||||||
|
### TASK
|
||||||
|
Analyse the **back side**:
|
||||||
|
|
||||||
|
{
|
||||||
|
"blood_type": "",
|
||||||
|
"birth_place": "",
|
||||||
|
"notes": ""
|
||||||
|
}
|
||||||
|
EOT,
|
||||||
|
"vehicle_license_front" => <<<EOT
|
||||||
|
You are an OCR expert for Jordanian vehicle registration cards.
|
||||||
|
|
||||||
|
### TASK
|
||||||
|
Analyse the **front side**:
|
||||||
|
|
||||||
|
{
|
||||||
|
"car_plate": "",
|
||||||
|
"owner": "",
|
||||||
|
"vin": "",
|
||||||
|
"color": "",
|
||||||
|
"make": "",
|
||||||
|
"model": "",
|
||||||
|
"year": ""
|
||||||
|
}
|
||||||
|
EOT,
|
||||||
|
"vehicle_license_back" => <<<EOT
|
||||||
|
You are an OCR expert for Jordanian vehicle cards.
|
||||||
|
|
||||||
|
### TASK
|
||||||
|
Analyse the **back side**:
|
||||||
|
|
||||||
|
{
|
||||||
|
"fuel": "",
|
||||||
|
"chassis": "",
|
||||||
|
"inspection_date": "YYYY-MM-DD"
|
||||||
|
}
|
||||||
|
EOT,
|
||||||
|
];
|
||||||
|
|
||||||
|
$egyptPrompts = [
|
||||||
|
"id_front" => <<<EOT
|
||||||
|
You are an OCR expert for Egyptian national ID cards.
|
||||||
|
|
||||||
|
### TASK
|
||||||
|
Analyse the **front side** of the Egyptian ID:
|
||||||
|
|
||||||
|
{
|
||||||
|
"full_name": "",
|
||||||
|
"national_number": "",
|
||||||
|
"dob": "YYYY-MM-DD",
|
||||||
|
"address": ""
|
||||||
|
}
|
||||||
|
EOT,
|
||||||
|
"id_back" => <<<EOT
|
||||||
|
You are an OCR expert for Egyptian national ID cards.
|
||||||
|
|
||||||
|
### TASK
|
||||||
|
Analyse the **back side**:
|
||||||
|
|
||||||
|
{
|
||||||
|
"governorate": "",
|
||||||
|
"gender": "",
|
||||||
|
"issue_date": "YYYY-MM-DD",
|
||||||
|
"expiry_date": "YYYY-MM-DD"
|
||||||
|
}
|
||||||
|
EOT,
|
||||||
|
"driving_license_front" => <<<EOT
|
||||||
|
You are an OCR expert for Egyptian driving licences.
|
||||||
|
|
||||||
|
### TASK
|
||||||
|
Analyse the **front side**:
|
||||||
|
|
||||||
|
{
|
||||||
|
"name_arabic": "",
|
||||||
|
"license_number": "",
|
||||||
|
"issue_date": "YYYY-MM-DD",
|
||||||
|
"expiry_date": "YYYY-MM-DD",
|
||||||
|
"license_category": ""
|
||||||
|
}
|
||||||
|
EOT,
|
||||||
|
"driving_license_back" => <<<EOT
|
||||||
|
You are an OCR expert for Egyptian driving licences.
|
||||||
|
|
||||||
|
### TASK
|
||||||
|
Analyse the **back side**:
|
||||||
|
|
||||||
|
{
|
||||||
|
"blood_type": "",
|
||||||
|
"address": "",
|
||||||
|
"birth_place": ""
|
||||||
|
}
|
||||||
|
EOT,
|
||||||
|
"vehicle_license_front" => <<<EOT
|
||||||
|
You are an OCR expert for Egyptian vehicle registration cards.
|
||||||
|
|
||||||
|
### TASK
|
||||||
|
Analyse the **front side**:
|
||||||
|
|
||||||
|
{
|
||||||
|
"car_plate": "",
|
||||||
|
"owner": "",
|
||||||
|
"vin": "",
|
||||||
|
"color": "",
|
||||||
|
"make": "",
|
||||||
|
"model": "",
|
||||||
|
"year": ""
|
||||||
|
}
|
||||||
|
EOT,
|
||||||
|
"vehicle_license_back" => <<<EOT
|
||||||
|
You are an OCR expert for Egyptian vehicle cards.
|
||||||
|
|
||||||
|
### TASK
|
||||||
|
Analyse the **back side**:
|
||||||
|
|
||||||
|
{
|
||||||
|
"fuel": "",
|
||||||
|
"chassis": "",
|
||||||
|
"registration_date": "YYYY-MM-DD"
|
||||||
|
}
|
||||||
|
EOT,
|
||||||
|
];
|
||||||
|
|
||||||
|
return match ($country) {
|
||||||
|
self::COUNTRY_JORDAN => $jordanPrompts,
|
||||||
|
self::COUNTRY_EGYPT => $egyptPrompts,
|
||||||
|
default => $syriaPrompts,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -160,6 +160,155 @@ $router->post('/api/integrations/woocommerce/disconnect', [\App\Controllers\WooC
|
|||||||
$router->post('/api/webhooks/woocommerce', [\App\Controllers\WooCommerceController::class, 'webhook']);
|
$router->post('/api/webhooks/woocommerce', [\App\Controllers\WooCommerceController::class, 'webhook']);
|
||||||
|
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// Siro Integration API Endpoints
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
// Siro Driver Info - Returns real-time driver data to Siro
|
||||||
|
$router->post('/api/siro/driver-info', function ($request, $response) {
|
||||||
|
$apiKey = getenv('NABEH_API_KEY');
|
||||||
|
$incomingKey = $request->getHeader('x-api-key') ?? '';
|
||||||
|
|
||||||
|
if (empty($apiKey) || $incomingKey !== $apiKey) {
|
||||||
|
$response->status(401)->json([
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Unauthorized'
|
||||||
|
]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$body = $request->getBody();
|
||||||
|
$phone = $body['phone'] ?? '';
|
||||||
|
|
||||||
|
if (empty($phone)) {
|
||||||
|
$response->status(400)->json([
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Missing phone number'
|
||||||
|
]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find driver OCR data
|
||||||
|
$hash = \App\Core\Security::blindIndex($phone);
|
||||||
|
$record = \App\Core\Database::selectOne(
|
||||||
|
"SELECT * FROM driver_ocr_data WHERE phone_hash = ? LIMIT 1",
|
||||||
|
[$hash]
|
||||||
|
);
|
||||||
|
|
||||||
|
$response->json([
|
||||||
|
'status' => 'success',
|
||||||
|
'data' => $record ? \App\Models\DriverOcrData::decryptRecord($record) : null
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Siro Registration Status Check
|
||||||
|
$router->get('/api/siro/registration-status', function ($request, $response) {
|
||||||
|
$apiKey = getenv('NABEH_API_KEY');
|
||||||
|
$incomingKey = $request->getHeader('x-api-key') ?? '';
|
||||||
|
|
||||||
|
if (empty($apiKey) || $incomingKey !== $apiKey) {
|
||||||
|
$response->status(401)->json([
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Unauthorized'
|
||||||
|
]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$phone = $request->get('phone') ?? '';
|
||||||
|
|
||||||
|
if (empty($phone)) {
|
||||||
|
$response->status(400)->json([
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Missing phone parameter'
|
||||||
|
]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$hash = \App\Core\Security::blindIndex($phone);
|
||||||
|
$record = \App\Core\Database::selectOne(
|
||||||
|
"SELECT id, name, status, created_at, updated_at FROM driver_ocr_data WHERE phone_hash = ? LIMIT 1",
|
||||||
|
[$hash]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!$record) {
|
||||||
|
$response->json([
|
||||||
|
'status' => 'success',
|
||||||
|
'data' => null,
|
||||||
|
'message' => 'No registration found for this phone'
|
||||||
|
]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$response->json([
|
||||||
|
'status' => 'success',
|
||||||
|
'data' => $record
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Siro Webhook - Receives driver activation confirmations from Siro
|
||||||
|
$router->post('/api/siro/webhook', function ($request, $response) {
|
||||||
|
$apiKey = getenv('NABEH_API_KEY');
|
||||||
|
$incomingKey = $request->getHeader('x-api-key') ?? '';
|
||||||
|
|
||||||
|
if (empty($apiKey) || $incomingKey !== $apiKey) {
|
||||||
|
$response->status(401)->json([
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Unauthorized'
|
||||||
|
]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$body = $request->getBody();
|
||||||
|
$phone = $body['phone'] ?? '';
|
||||||
|
$syroDriverId = $body['driver_id'] ?? '';
|
||||||
|
$event = $body['event'] ?? '';
|
||||||
|
$status = $body['status'] ?? '';
|
||||||
|
|
||||||
|
if (empty($phone) || empty($event)) {
|
||||||
|
$response->status(400)->json([
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Missing required fields: phone, event'
|
||||||
|
]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
error_log("[Siro Webhook] Event: {$event}, Phone: {$phone}, DriverID: {$syroDriverId}, Status: {$status}");
|
||||||
|
|
||||||
|
$hash = \App\Core\Security::blindIndex($phone);
|
||||||
|
|
||||||
|
if ($event === 'driver_activated' && $status === 'actives') {
|
||||||
|
\App\Core\Database::execute(
|
||||||
|
"UPDATE driver_ocr_data SET status = 'registered', syro_driver_id = ? WHERE phone_hash = ?",
|
||||||
|
[$syroDriverId, $hash]
|
||||||
|
);
|
||||||
|
|
||||||
|
$response->json([
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Driver status updated'
|
||||||
|
]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($event === 'driver_rejected') {
|
||||||
|
\App\Core\Database::execute(
|
||||||
|
"UPDATE driver_ocr_data SET status = 'rejected' WHERE phone_hash = ?",
|
||||||
|
[$hash]
|
||||||
|
);
|
||||||
|
|
||||||
|
$response->json([
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Driver rejected'
|
||||||
|
]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$response->json([
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Event received'
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
// Mock External API for Entaleq Driver Info (Used to fetch real-time driver data)
|
// Mock External API for Entaleq Driver Info (Used to fetch real-time driver data)
|
||||||
$router->post('/api/external/driver-info', function ($request, $response) {
|
$router->post('/api/external/driver-info', function ($request, $response) {
|
||||||
$body = $request->getBody();
|
$body = $request->getBody();
|
||||||
|
|||||||
Reference in New Issue
Block a user