遗留版FCM通知无法送达安卓设备问题求助
解决Web Push类型FCM订阅的推送问题
看起来你混淆了传统FCM设备令牌和Web Push订阅对象——你数据库里存的是Web Push格式的订阅信息,不是那种纯字符串的FCM令牌,这就是为什么你用传统FCM推送方式没效果的原因。Web Push协议要求消息必须加密,不能直接用FCM的/fcm/send接口传整个订阅对象,得用专门的Web Push逻辑来处理。
下面给你一步步的解决方向和代码示例:
1. 先搞清楚核心区别
你拿到的这个格式:
{"endpoint":" https://fcm.googleapis.com/fcm/send/fn2AxrA1DLI:APA...","expirationTime":null,"keys":{"p256dh":"BGwqbu2...","auth":"xasG3yCzA..."}}
是Web Push的订阅对象,常见于Web应用、WebView中的页面,或者某些用了Web Push SDK的Android应用。这种订阅需要加密消息内容才能推送,和传统FCM直接发明文消息的逻辑完全不同。
2. 用PHP Web Push库简化加密逻辑
自己实现Web Push的加密算法非常繁琐,推荐用成熟的第三方库minishlink/web-push来处理,它会帮你搞定密钥生成、消息加密和请求发送的所有细节。
步骤1:安装库
用Composer安装:
composer require minishlink/web-push
步骤2:编写推送脚本
把下面的代码改成你自己的配置:
require __DIR__ . '/vendor/autoload.php'; use Minishlink\WebPush\WebPush; use Minishlink\WebPush\Subscription; // 替换成你Firebase项目的VAPID公钥和私钥(就是你生成的那对) $vapidPublicKey = '你的VAPID公钥'; $vapidPrivateKey = '你的VAPID私钥'; // 从数据库取出的订阅数据 $subscriptionData = [ "endpoint" => "https://fcm.googleapis.com/fcm/send/fn2AxrA1DLI:APA*************************************G31", "expirationTime" => null, "keys" => [ "p256dh" => "BGwqbu2**************************************vCqSenne4KABWuGN6x3w", "auth" => "xasG3yCzA*************l7hA" ] ]; // 创建订阅对象 $subscription = Subscription::create($subscriptionData); // 初始化WebPush客户端 $webPush = new WebPush([ 'VAPID' => [ 'subject' => 'mailto:your-email@example.com', // 必须是邮箱或URL,用于FCM识别你的推送源 'publicKey' => $vapidPublicKey, 'privateKey' => $vapidPrivateKey, ], ]); // 准备要发送的通知内容 $notificationPayload = json_encode([ 'title' => 'Hello World', 'body' => '这是一条测试通知', // 可选:添加图标、点击跳转链接等 // 'icon' => '/path/to/icon.png', // 'data' => ['url' => 'https://example.com'] ]); // 发送通知 $webPush->sendOneNotification($subscription, $notificationPayload); // 检查推送结果 foreach ($webPush->flush() as $report) { $endpoint = $report->getRequest()->getUri()->__toString(); if ($report->isSuccess()) { echo "✅ 通知成功发送到: {$endpoint}"; } else { echo "❌ 通知发送失败到 {$endpoint}: {$report->getReason()}"; } }
3. 关键注意事项
- VAPID密钥必须匹配:你生成的VAPID公钥,必须和Android设备上注册订阅时用的公钥完全一致(比如如果是WebView里的网页注册推送,网页里用的公钥要和这里的一样),否则会被FCM拒绝。
- 检查设备状态:确保你的Android设备网络正常,没有禁用通知权限,并且注册订阅的应用/网页处于活跃状态(或者后台允许接收推送)。
- 排查错误日志:如果还是失败,看脚本输出的错误信息,或者去Firebase控制台的「Cloud Messaging」→「报告」里查看推送记录,里面会有详细的失败原因(比如密钥不匹配、订阅过期等)。
为什么你之前的脚本只输出"to"?
你之前应该是尝试用传统FCM的API,把整个Web Push订阅对象当成to字段的值传进去了,FCM接口不识别这种格式,导致请求异常,所以只输出了"to"这个字段名。现在换成Web Push的方式就没问题了。
内容的提问来源于stack exchange,提问作者Reggie S




