183 lines
5.9 KiB
PHP
183 lines
5.9 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
/**
|
|
* خدمة إشعارات Firebase (FCM Service)
|
|
*
|
|
* الغرض من الملف:
|
|
* إرسال التنبيهات (Push Notifications) للهواتف الذكية عبر خدمة Google Firebase.
|
|
*
|
|
* كيفية العمل:
|
|
* 1. يتصل بخوادم Google باستخدام مفاتيح الوصول (API Keys).
|
|
* 2. يرسل التنبيه للراكب أو السائق بناءً على الرمز (Token) الخاص بجهازه.
|
|
* 3. يدير إرسال البيانات الإضافية (مثل معرف الرحلة) داخل التنبيه لتسهيل التفاعل معه داخل التطبيق.
|
|
*/
|
|
class FcmService
|
|
{
|
|
private ?string $accessToken = null;
|
|
private string $credentialsPath;
|
|
private string $cachePath;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->credentialsPath = config('intaleq.fcm_credentials_path');
|
|
$this->cachePath = config('intaleq.fcm_cache_path');
|
|
}
|
|
|
|
/**
|
|
* Send FCM notification to a specific device token
|
|
*/
|
|
public function sendToDevice(string $token, string $title, string $body, array $data = [], string $category = ''): array
|
|
{
|
|
if (empty($token)) {
|
|
return ['status' => 'error', 'message' => 'Empty token'];
|
|
}
|
|
|
|
// Add category to data payload
|
|
if ($category) {
|
|
$data['category'] = $category;
|
|
}
|
|
|
|
// Convert non-string data values
|
|
$stringData = [];
|
|
foreach ($data as $key => $value) {
|
|
$stringData[$key] = is_array($value) ? json_encode($value) : (string) $value;
|
|
}
|
|
|
|
$payload = [
|
|
'message' => [
|
|
'token' => $token,
|
|
'notification' => [
|
|
'title' => $title,
|
|
'body' => $body,
|
|
],
|
|
'data' => $stringData,
|
|
'android' => [
|
|
'priority' => 'high',
|
|
],
|
|
'apns' => [
|
|
'payload' => [
|
|
'aps' => [
|
|
'sound' => 'default',
|
|
'badge' => 1,
|
|
],
|
|
],
|
|
],
|
|
],
|
|
];
|
|
|
|
return $this->sendRequest($payload);
|
|
}
|
|
|
|
/**
|
|
* Send to FCM topic
|
|
*/
|
|
public function sendToTopic(string $topic, string $title, string $body, array $data = []): array
|
|
{
|
|
$payload = [
|
|
'message' => [
|
|
'topic' => $topic,
|
|
'notification' => [
|
|
'title' => $title,
|
|
'body' => $body,
|
|
],
|
|
'data' => array_map('strval', $data),
|
|
],
|
|
];
|
|
|
|
return $this->sendRequest($payload);
|
|
}
|
|
|
|
private function sendRequest(array $payload): array
|
|
{
|
|
$accessToken = $this->getAccessToken();
|
|
if (!$accessToken) {
|
|
return ['status' => 'error', 'message' => 'Failed to get access token'];
|
|
}
|
|
|
|
$credentials = json_decode(file_get_contents($this->credentialsPath), true);
|
|
$projectId = $credentials['project_id'] ?? '';
|
|
$url = "https://fcm.googleapis.com/v1/projects/{$projectId}/messages:send";
|
|
|
|
$ch = curl_init($url);
|
|
curl_setopt_array($ch, [
|
|
CURLOPT_POST => true,
|
|
CURLOPT_HTTPHEADER => [
|
|
"Authorization: Bearer {$accessToken}",
|
|
'Content-Type: application/json',
|
|
],
|
|
CURLOPT_POSTFIELDS => json_encode($payload),
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_TIMEOUT => 10,
|
|
]);
|
|
|
|
$result = curl_exec($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
|
|
if ($httpCode === 200) {
|
|
return ['status' => 'success', 'response' => json_decode($result, true)];
|
|
}
|
|
|
|
Log::error("[FCM] Error {$httpCode}: {$result}");
|
|
return ['status' => 'error', 'code' => $httpCode, 'response' => $result];
|
|
}
|
|
|
|
private function getAccessToken(): ?string
|
|
{
|
|
// Check cache
|
|
if (file_exists($this->cachePath)) {
|
|
$cached = json_decode(file_get_contents($this->cachePath), true);
|
|
if ($cached && ($cached['expires_at'] ?? 0) > time() + 60) {
|
|
return $cached['token'];
|
|
}
|
|
}
|
|
|
|
if (!file_exists($this->credentialsPath)) return null;
|
|
|
|
$credentials = json_decode(file_get_contents($this->credentialsPath), true);
|
|
$clientEmail = $credentials['client_email'];
|
|
$privateKey = $credentials['private_key'];
|
|
|
|
$now = time();
|
|
$header = rtrim(strtr(base64_encode(json_encode(['alg' => 'RS256', 'typ' => 'JWT'])), '+/', '-_'), '=');
|
|
$claim = rtrim(strtr(base64_encode(json_encode([
|
|
'iss' => $clientEmail,
|
|
'scope' => 'https://www.googleapis.com/auth/firebase.messaging',
|
|
'aud' => 'https://oauth2.googleapis.com/token',
|
|
'exp' => $now + 3600,
|
|
'iat' => $now,
|
|
])), '+/', '-_'), '=');
|
|
|
|
$signature = '';
|
|
openssl_sign("{$header}.{$claim}", $signature, $privateKey, 'SHA256');
|
|
$jwt = "{$header}.{$claim}." . rtrim(strtr(base64_encode($signature), '+/', '-_'), '=');
|
|
|
|
$ch = curl_init('https://oauth2.googleapis.com/token');
|
|
curl_setopt_array($ch, [
|
|
CURLOPT_POST => true,
|
|
CURLOPT_POSTFIELDS => http_build_query([
|
|
'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
|
|
'assertion' => $jwt,
|
|
]),
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
]);
|
|
$res = curl_exec($ch);
|
|
curl_close($ch);
|
|
|
|
$token = json_decode($res, true)['access_token'] ?? null;
|
|
|
|
if ($token) {
|
|
file_put_contents($this->cachePath, json_encode([
|
|
'token' => $token,
|
|
'expires_at' => time() + 3500,
|
|
]));
|
|
}
|
|
|
|
return $token;
|
|
}
|
|
}
|