Update: 2026-07-12 05:40:28
This commit is contained in:
@@ -28,6 +28,12 @@ class Database
|
||||
'user' => 'DB_RIDE_USER',
|
||||
'pass' => 'DB_RIDE_PASS',
|
||||
],
|
||||
'transit' => [
|
||||
'name' => 'DB_TRANSIT_NAME',
|
||||
'host' => 'DB_TRANSIT_HOST',
|
||||
'user' => 'DB_TRANSIT_USER',
|
||||
'pass' => 'DB_TRANSIT_PASS',
|
||||
],
|
||||
];
|
||||
|
||||
public static function get(string $name = 'main'): PDO
|
||||
|
||||
@@ -120,6 +120,55 @@ class FcmService
|
||||
: ['status' => 'error', 'code' => $httpCode, 'response' => $result];
|
||||
}
|
||||
|
||||
// ── إرسال إشعار لـ FCM Topic (قناة المواصلاتي وغيرها) ──
|
||||
public function sendToTopic(
|
||||
string $topic,
|
||||
string $title,
|
||||
string $body,
|
||||
array $data = []
|
||||
): array {
|
||||
$accessToken = $this->getAccessToken();
|
||||
if (!$accessToken) return ['status' => 'error', 'message' => 'No access token'];
|
||||
|
||||
if (!file_exists($this->serviceAccountFile)) {
|
||||
return ['status' => 'error', 'message' => 'Service account file missing'];
|
||||
}
|
||||
|
||||
$creds = json_decode(file_get_contents($this->serviceAccountFile), true);
|
||||
$projectId = $creds['project_id'];
|
||||
$fcmUrl = "https://fcm.googleapis.com/v1/projects/{$projectId}/messages:send";
|
||||
|
||||
$processedData = array_map(
|
||||
fn($v) => is_array($v) || is_object($v) ? json_encode($v, JSON_UNESCAPED_UNICODE) : (string)$v,
|
||||
array_merge($data, ['title' => $title, 'body' => $body])
|
||||
);
|
||||
|
||||
$payload = [
|
||||
'message' => [
|
||||
'topic' => $topic,
|
||||
'notification' => ['title' => $title, 'body' => $body],
|
||||
'data' => $processedData,
|
||||
'android' => ['priority' => 'HIGH'],
|
||||
],
|
||||
];
|
||||
|
||||
$ch = curl_init($fcmUrl);
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_HTTPHEADER => ["Authorization: Bearer $accessToken", 'Content-Type: application/json; charset=UTF-8'],
|
||||
CURLOPT_POSTFIELDS => json_encode($payload, JSON_UNESCAPED_UNICODE),
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_TIMEOUT => 5,
|
||||
]);
|
||||
$result = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
return $httpCode === 200
|
||||
? ['status' => 'success']
|
||||
: ['status' => 'error', 'code' => $httpCode, 'response' => $result];
|
||||
}
|
||||
|
||||
// ── Access Token مع Redis Cache ─────────────────────────
|
||||
private function getAccessToken(): ?string
|
||||
{
|
||||
|
||||
@@ -17,15 +17,16 @@ class OtpService
|
||||
}
|
||||
|
||||
// ── توليد وحفظ OTP ─────────────────────────────────────
|
||||
public function generate(string $phone): string
|
||||
// $digits: عدد الأرقام — الافتراضي 6، يمكن تمرير 3 لـ transit (100–999)
|
||||
public function generate(string $phone, int $digits = 3): string
|
||||
{
|
||||
// OTP آمن (6 أرقام عشوائية)
|
||||
$otp = str_pad((string)random_int(100000, 999999), 6, '0', STR_PAD_LEFT);
|
||||
$min = (int)str_pad('1', $digits, '0'); // digits=3 → 100 | digits=6 → 100000
|
||||
$max = (int)str_pad('9', $digits, '9'); // digits=3 → 999 | digits=6 → 999999
|
||||
$otp = str_pad((string)random_int($min, $max), $digits, '0', STR_PAD_LEFT);
|
||||
|
||||
if ($this->redis) {
|
||||
$key = "otp:{$phone}";
|
||||
$this->redis->setex($key, self::OTP_TTL, password_hash($otp, PASSWORD_BCRYPT));
|
||||
// إعادة تعيين عداد المحاولات
|
||||
$this->redis->del("otp:attempts:{$phone}");
|
||||
}
|
||||
|
||||
|
||||
@@ -234,3 +234,32 @@ function getInternalSocketKey(): string
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* تطبيع رقم الهاتف إلى الصيغة الدولية بدون + (E.164 بدون +)
|
||||
* ناتج ثابت لأي مدخل من JO/SY/EG:
|
||||
* الأردن → 9627XXXXXXXX
|
||||
* سوريا → 9639XXXXXXX
|
||||
* مصر → 20XXXXXXXXXX
|
||||
* يُستخدم دائماً قبل التشفير وقبل الاستعلام.
|
||||
*/
|
||||
function normalizePhone(string $phone): string
|
||||
{
|
||||
$d = preg_replace('/\D+/', '', $phone);
|
||||
|
||||
// سوريا: 09X → 963X | 9X (9 أرقام) → 963X | 963... مكتمل
|
||||
if (strlen($d) === 10 && str_starts_with($d, '09')) return '963' . substr($d, 1);
|
||||
if (strlen($d) === 9 && str_starts_with($d, '9')) return '963' . $d;
|
||||
if (strlen($d) === 12 && str_starts_with($d, '963')) return $d;
|
||||
|
||||
// الأردن: 07X → 962X | 7X (9 أرقام) → 962X | 962... مكتمل
|
||||
if (strlen($d) === 10 && str_starts_with($d, '07')) return '962' . substr($d, 1);
|
||||
if (strlen($d) === 9 && str_starts_with($d, '7')) return '962' . $d;
|
||||
if (strlen($d) === 12 && str_starts_with($d, '962')) return $d;
|
||||
|
||||
// مصر: 01X → 20X | 201... مكتمل
|
||||
if (strlen($d) === 11 && str_starts_with($d, '01')) return '20' . substr($d, 1);
|
||||
if (strlen($d) === 13 && str_starts_with($d, '20')) return $d;
|
||||
|
||||
return $d; // رقم خارج النطاق — يُعاد كما هو
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user