This commit is contained in:
2026-03-03 16:30:57 +07:00
parent a13304e40e
commit c253e1a370
7569 changed files with 1324841 additions and 0 deletions
@@ -0,0 +1,141 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
/**
* FCM HTTP v1 API helper (Firebase Cloud Messaging).
* Uses OAuth2 access token from service account credentials (env: FCM_CREDENTIALS_PATH or FCM_CREDENTIALS_JSON).
* Requires FCM_PROJECT_ID. Optional: FCM_LIMIT_PER_HOUR, FCM_LIMIT_PER_DAY for quota (used by callers).
*/
if (!function_exists('fcm_v1_get_credentials')) {
function fcm_v1_get_credentials()
{
$json = (defined('FCM_CREDENTIALS_JSON') ? FCM_CREDENTIALS_JSON : '');
if ($json !== '') {
return json_decode($json, true);
}
$path = (defined('FCM_CREDENTIALS_PATH') ? FCM_CREDENTIALS_PATH : '');
if ($path !== '' && is_readable($path)) {
return json_decode(file_get_contents($path), true);
}
return null;
}
}
if (!function_exists('fcm_v1_get_access_token')) {
/**
* Get OAuth2 access token for FCM using service account JWT.
* @return string|null Access token or null on failure
*/
function fcm_v1_get_access_token()
{
$cred = fcm_v1_get_credentials();
if (!$cred || empty($cred['client_email']) || empty($cred['private_key'])) {
log_message('error', 'FCM v1: Missing FCM_CREDENTIALS_JSON or FCM_CREDENTIALS_PATH');
return null;
}
$now = time();
$jwt = array(
'iss' => $cred['client_email'],
'sub' => $cred['client_email'],
'aud' => 'https://oauth2.googleapis.com/token',
'iat' => $now,
'exp' => $now + 3600,
);
$jwt_b64 = fcm_v1_base64url_encode(json_encode(array('alg' => 'RS256', 'typ' => 'JWT')))
. '.' . fcm_v1_base64url_encode(json_encode($jwt));
$sig = '';
$key = openssl_pkey_get_private($cred['private_key']);
if (!$key) {
log_message('error', 'FCM v1: Invalid private key');
return null;
}
openssl_sign($jwt_b64, $sig, $key, OPENSSL_ALGO_SHA256);
openssl_free_key($key);
$jwt_b64 .= '.' . fcm_v1_base64url_encode($sig);
$resp = @file_get_contents('https://oauth2.googleapis.com/token', false, stream_context_create(array(
'http' => array(
'method' => 'POST',
'header' => "Content-Type: application/x-www-form-urlencoded",
'content' => 'grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=' . $jwt_b64,
),
)));
if ($resp === false) {
log_message('error', 'FCM v1: Token request failed');
return null;
}
$data = json_decode($resp, true);
return isset($data['access_token']) ? $data['access_token'] : null;
}
}
if (!function_exists('fcm_v1_base64url_encode')) {
function fcm_v1_base64url_encode($data)
{
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
}
if (!function_exists('fcm_v1_send')) {
/**
* Send FCM message via HTTP v1 API.
*
* @param string $target Device token or topic name (without /topics/ prefix)
* @param array $data Key-value data payload (values will be stringified)
* @param bool $is_topic True if $target is a topic name
* @param array $options Optional: 'title', 'body' for notification; 'priority' => 'high' for Android
* @return string|false Response body or false on failure
*/
function fcm_v1_send($target, $data, $is_topic = false, $options = array())
{
$project_id = '';
if (defined('FCM_PROJECT_ID') && FCM_PROJECT_ID !== '') {
$project_id = FCM_PROJECT_ID;
}
if ($project_id === '') {
log_message('error', 'FCM v1: FCM_PROJECT_ID not set');
return false;
}
$token = fcm_v1_get_access_token();
if (!$token) {
return false;
}
$data_str = array();
foreach ($data as $k => $v) {
$data_str[$k] = (string) $v;
}
$message = array(
'data' => $data_str,
);
if ($is_topic) {
$message['topic'] = $target;
} else {
$message['token'] = $target;
}
if (!empty($options['title']) || !empty($options['body'])) {
$message['notification'] = array(
'title' => isset($options['title']) ? (string) $options['title'] : '',
'body' => isset($options['body']) ? (string) $options['body'] : '',
);
}
if (isset($options['android']) && is_array($options['android'])) {
$message['android'] = $options['android'];
} else {
$message['android'] = array('priority' => 'HIGH');
}
$body = json_encode(array('message' => $message));
$ctx = stream_context_create(array(
'http' => array(
'method' => 'POST',
'header' =>
"Content-Type: application/json\r\n" .
"Authorization: Bearer " . $token . "\r\n",
'content' => $body,
'ignore_errors' => true,
),
));
$url = 'https://fcm.googleapis.com/v1/projects/' . $project_id . '/messages:send';
$resp = @file_get_contents($url, false, $ctx);
return $resp !== false ? $resp : false;
}
}