You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

遗留版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

火山引擎 最新活动