如何用PHP通过FCM HTTP v1发送推送通知?代码无输出排查
排查FCM HTTP v1 PHP推送无输出无响应问题
你的代码核心问题是错误使用服务账号私钥作为Bearer令牌——FCM HTTP v1 API要求使用OAuth2访问令牌,而非直接使用私钥或旧版的Server Key,这是v1与旧版API的核心差异。以下是具体修复步骤:
1. 先获取OAuth2访问令牌
需要通过服务账号私钥向Google令牌端点请求有效期1小时的访问令牌,实现逻辑如下:
function getFcmAccessToken($serviceAccount) { $privateKey = $serviceAccount['private_key']; $clientEmail = $serviceAccount['client_email']; $tokenUrl = 'https://oauth2.googleapis.com/token'; $jwtHeader = json_encode([ 'alg' => 'RS256', 'typ' => 'JWT' ]); $jwtClaimSet = json_encode([ 'iss' => $clientEmail, 'scope' => 'https://www.googleapis.com/auth/firebase.messaging', 'aud' => $tokenUrl, 'exp' => time() + 3600, 'iat' => time() ]); // 编码JWT的Header和Claim Set $encodedHeader = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($jwtHeader)); $encodedClaimSet = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($jwtClaimSet)); $signatureInput = $encodedHeader . '.' . $encodedClaimSet; // 用私钥生成签名 openssl_sign($signatureInput, $signature, $privateKey, 'SHA256'); $encodedSignature = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($signature)); $jwt = $encodedHeader . '.' . $encodedClaimSet . '.' . $encodedSignature; // 请求令牌 $ch = curl_init($tokenUrl); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([ 'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer', 'assertion' => $jwt ])); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = json_decode(curl_exec($ch), true); curl_close($ch); return $response['access_token'] ?? null; }
2. 修改FCM推送代码
调用上述函数获取令牌后,再发送推送请求,同时添加curl错误排查逻辑:
<?php $serviceAccount = json_decode(file_get_contents('privatekey.json'), true); $accessToken = getFcmAccessToken($serviceAccount); if (!$accessToken) { die('Failed to get access token'); } $projectId = $serviceAccount['project_id']; $data = [ 'message' => [ 'token' => 'd6dYpnaLSP2zRk4o6dFfFZ:APA91bG-ZB7XFB-01K54cEVAiyMuqbKyBGhYX-XeiR2QcC3QKv1-MXI4wAy_cwL-d1TLNOUkF-pqt0WQed4b2VcL8OeBxXOPVwrMiWv69TkDRRidOPUw_YjA-ISsgCNoxlWUIb5V_PV6', 'notification' => [ 'title' => 'Your Title', 'body' => 'Your Message', ], ], ]; $headers = [ 'Authorization: Bearer ' . $accessToken, 'Content-Type: application/json', ]; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, "https://fcm.googleapis.com/v1/projects/{$projectId}/messages:send"); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 建议开启SSL验证,生产环境不要关闭 // curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); $result = curl_exec($ch); if ($result === false) { echo 'Curl Error: ' . curl_error($ch); } else { echo $result; } curl_close($ch); // 令牌获取函数 function getFcmAccessToken($serviceAccount) { $privateKey = $serviceAccount['private_key']; $clientEmail = $serviceAccount['client_email']; $tokenUrl = 'https://oauth2.googleapis.com/token'; $jwtHeader = json_encode([ 'alg' => 'RS256', 'typ' => 'JWT' ]); $jwtClaimSet = json_encode([ 'iss' => $clientEmail, 'scope' => 'https://www.googleapis.com/auth/firebase.messaging', 'aud' => $tokenUrl, 'exp' => time() + 3600, 'iat' => time() ]); $encodedHeader = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($jwtHeader)); $encodedClaimSet = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($jwtClaimSet)); $signatureInput = $encodedHeader . '.' . $encodedClaimSet; openssl_sign($signatureInput, $signature, $privateKey, 'SHA256'); $encodedSignature = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($signature)); $jwt = $encodedHeader . '.' . $encodedClaimSet . '.' . $encodedSignature; $ch = curl_init($tokenUrl); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([ 'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer', 'assertion' => $jwt ])); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = json_decode(curl_exec($ch), true); curl_close($ch); return $response['access_token'] ?? null; } ?>
3. 额外排查要点
- 确认
privatekey.json路径正确,且文件内容完整无格式错误 - 确保服务账号拥有
Firebase Messaging Admin权限(在Google Cloud IAM中配置) - 生产环境不要关闭
CURLOPT_SSL_VERIFYPEER,避免安全风险 - 检查设备令牌是否有效,旧版API能成功不代表v1也能,确保令牌未过期或被撤销
内容的提问来源于stack exchange,提问作者Mohsin S S
相关产品推荐
相关产品推荐

