求助:Firebase FCM从Legacy API迁移至HTTP v1 API的PHP脚本实现
适配FCM HTTP v1 API的纯PHP实现(无第三方依赖)
核心差异说明
FCM HTTP v1 API和旧版Legacy API的核心区别在于两点:
- 身份验证从固定服务器密钥改为OAuth 2.0访问令牌
- 请求体格式更严格,需嵌套在
message字段下,API端点包含项目ID
完整实现代码
1. 获取OAuth 2.0访问令牌
从Firebase控制台下载服务账号JSON密钥,提取client_email和private_key字段,用以下函数生成有效期1小时的访问令牌:
function getFcmAccessToken($clientEmail, $privateKey) { $tokenUrl = "https://oauth2.googleapis.com/token"; // 构造JWT Header $header = json_encode(["alg" => "RS256", "typ" => "JWT"]); // 构造JWT Payload $now = time(); $payload = json_encode([ "iss" => $clientEmail, "scope" => "https://www.googleapis.com/auth/firebase.messaging", "aud" => $tokenUrl, "exp" => $now + 3600, "iat" => $now ]); // Base64URL编码Header和Payload $base64Header = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($header)); $base64Payload = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($payload)); // 生成签名 $signatureInput = $base64Header . "." . $base64Payload; openssl_sign($signatureInput, $signature, $privateKey, "SHA256"); $base64Signature = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($signature)); $jwt = $base64Header . "." . $base64Payload . "." . $base64Signature; // 请求获取访问令牌 $curl = curl_init($tokenUrl); curl_setopt($curl, CURLOPT_POST, true); curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query([ "grant_type" => "urn:ietf:params:oauth:grant-type:jwt-bearer", "assertion" => $jwt ])); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_HTTPHEADER, ["Content-Type: application/x-www-form-urlencoded"]); $response = curl_exec($curl); curl_close($curl); $responseData = json_decode($response, true); return $responseData["access_token"] ?? null; }
2. 发送FCM通知
使用获取到的访问令牌,构造符合v1 API格式的请求:
function sendFcmNotification($accessToken, $projectId, $deviceToken, $title, $body) { $apiUrl = "https://fcm.googleapis.com/v1/projects/{$projectId}/messages:send"; $requestBody = json_encode([ "message" => [ "token" => $deviceToken, "notification" => [ "title" => $title, "body" => $body ], // 可选:添加自定义数据负载 "data" => [ "order_id" => "12345", "status" => "shipped" ] ] ]); $curl = curl_init($apiUrl); curl_setopt($curl, CURLOPT_POST, true); curl_setopt($curl, CURLOPT_POSTFIELDS, $requestBody); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_HTTPHEADER, [ "Authorization: Bearer {$accessToken}", "Content-Type: application/json" ]); // 建议开启SSL验证,避免安全风险 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true); $response = curl_exec($curl); $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); curl_close($curl); return [ "http_code" => $httpCode, "response" => json_decode($response, true) ]; }
3. 调用示例
// 替换为你的实际信息 $clientEmail = "your-service-account@your-project.iam.gserviceaccount.com"; $privateKey = "-----BEGIN PRIVATE KEY-----\nYOUR_PRIVATE_KEY_CONTENTS\n-----END PRIVATE KEY-----"; $projectId = "your-firebase-project-id"; $deviceToken = "target-user-fcm-token"; // 获取访问令牌 $accessToken = getFcmAccessToken($clientEmail, $privateKey); if (!$accessToken) { die("获取访问令牌失败"); } // 发送通知 $result = sendFcmNotification($accessToken, $projectId, $deviceToken, "新订单提醒", "您的订单已发货"); print_r($result);
重要注意事项
- 私钥格式:确保私钥包含完整的首尾标记,换行符保留正确格式
- 令牌缓存:访问令牌有效期1小时,建议缓存令牌减少重复请求
- SSL验证:不要随意关闭
CURLOPT_SSL_VERIFYPEER,若托管环境有证书问题,联系服务商解决 - 错误排查:若请求失败,检查HTTP状态码,常见问题包括令牌过期、设备令牌无效、项目ID错误
内容的提问来源于stack exchange,提问作者FreddyBoy
相关产品推荐
相关产品推荐

