156 lines
5.2 KiB
PHP
156 lines
5.2 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use Illuminate\Support\Facades\Cache;
|
|
use Illuminate\Support\Facades\Http;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
class FCMService
|
|
{
|
|
/**
|
|
* Generate OAuth2 Access Token for Firebase Messaging.
|
|
* Caches the token for 50 minutes to avoid overhead.
|
|
*
|
|
* @return string|null
|
|
*/
|
|
public static function getAccessToken()
|
|
{
|
|
return Cache::remember('fcm_access_token', 3000, function () {
|
|
try {
|
|
$credentialsPath = base_path('migrant-fe5a7-firebase-adminsdk-fbsvc-f8b365ecd0.json');
|
|
|
|
if (!file_exists($credentialsPath)) {
|
|
Log::error("FCM Service Account JSON not found at: {$credentialsPath}");
|
|
return null;
|
|
}
|
|
|
|
$credentials = json_decode(file_get_contents($credentialsPath), true);
|
|
if (!$credentials) {
|
|
Log::error("FCM Service Account JSON is invalid or empty.");
|
|
return null;
|
|
}
|
|
|
|
$privateKey = $credentials['private_key'];
|
|
$clientEmail = $credentials['client_email'];
|
|
|
|
$header = json_encode(['alg' => 'RS256', 'typ' => 'JWT']);
|
|
|
|
$now = time();
|
|
$payload = json_encode([
|
|
'iss' => $clientEmail,
|
|
'scope' => 'https://www.googleapis.com/auth/firebase.messaging',
|
|
'aud' => 'https://oauth2.googleapis.com/token',
|
|
'exp' => $now + 3600,
|
|
'iat' => $now,
|
|
]);
|
|
|
|
$base64UrlHeader = self::base64UrlEncode($header);
|
|
$base64UrlPayload = self::base64UrlEncode($payload);
|
|
|
|
$signatureInput = $base64UrlHeader . '.' . $base64UrlPayload;
|
|
$signature = '';
|
|
|
|
if (!openssl_sign($signatureInput, $signature, $privateKey, OPENSSL_ALGO_SHA256)) {
|
|
Log::error("Failed to sign JWT with private key using openssl_sign.");
|
|
return null;
|
|
}
|
|
|
|
$base64UrlSignature = self::base64UrlEncode($signature);
|
|
$jwtToken = $signatureInput . '.' . $base64UrlSignature;
|
|
|
|
$response = Http::asForm()->post('https://oauth2.googleapis.com/token', [
|
|
'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
|
|
'assertion' => $jwtToken,
|
|
]);
|
|
|
|
if ($response->failed()) {
|
|
Log::error("FCM OAuth2 request failed: " . $response->body());
|
|
return null;
|
|
}
|
|
|
|
$data = $response->json();
|
|
return $data['access_token'] ?? null;
|
|
|
|
} catch (\Exception $e) {
|
|
Log::error("FCM Access Token Generation Error: " . $e->getMessage());
|
|
return null;
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Send a Push Notification to a specific FCM token.
|
|
*
|
|
* @param string $token
|
|
* @param string $title
|
|
* @param string $body
|
|
* @param array $data
|
|
* @return bool
|
|
*/
|
|
public static function sendPushNotification($token, $title, $body, array $data = [])
|
|
{
|
|
if (empty($token)) {
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
$accessToken = self::getAccessToken();
|
|
if (!$accessToken) {
|
|
Log::error("Cannot send push notification: FCM Access Token is null.");
|
|
return false;
|
|
}
|
|
|
|
// Load credentials to fetch the correct project_id
|
|
$credentialsPath = base_path('migrant-fe5a7-firebase-adminsdk-fbsvc-f8b365ecd0.json');
|
|
$credentials = json_decode(file_get_contents($credentialsPath), true);
|
|
$projectId = $credentials['project_id'] ?? 'migrant-fe5a7';
|
|
|
|
// Convert all data array values to strings as required by FCM v1
|
|
$formattedData = [];
|
|
foreach ($data as $key => $value) {
|
|
$formattedData[(string)$key] = (string)$value;
|
|
}
|
|
|
|
$payload = [
|
|
'message' => [
|
|
'token' => $token,
|
|
'notification' => [
|
|
'title' => $title,
|
|
'body' => $body,
|
|
],
|
|
]
|
|
];
|
|
|
|
if (!empty($formattedData)) {
|
|
$payload['message']['data'] = $formattedData;
|
|
}
|
|
|
|
$response = Http::withToken($accessToken)
|
|
->post("https://fcm.googleapis.com/v1/projects/{$projectId}/messages:send", $payload);
|
|
|
|
if ($response->failed()) {
|
|
Log::error("FCM Push Notification failed: " . $response->body());
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
|
|
} catch (\Exception $e) {
|
|
Log::error("FCM Notification Dispatch Failure: " . $e->getMessage());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Base64 Url Encode helper.
|
|
*
|
|
* @param string $data
|
|
* @return string
|
|
*/
|
|
private static function base64UrlEncode($data)
|
|
{
|
|
return str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($data));
|
|
}
|
|
}
|