APNs苹果推送

发布时间 2023-10-27 15:09:53作者: 诗里刻画的影子

转载自:https://zhuanlan.zhihu.com/p/622475157

做App总少不了消息推送,公司的App为了开发敏捷方便都直接上的三方推送诸如某盟、某推、某光,安卓和苹果都一套代码,高效好维护,只要按时给推送vip付费就好了。最近心血来潮想手搓下苹果的消息推送,踩踩以前没踩过的坑。

看了下苹果官方文档感觉并不困难,文档详情戳这里

Sending Notification Requests to APNs | Apple Developer Documentation

恰逢chatGPT大火赶下时髦,直接问了ai代码怎么写

charGPT3.5的回答

ai回答得有模有样,我里面换上证书和相关参数尝试,然后发现死活不行,苹果那边没反应。

一查文档发现其实苹果这边服务器推送大致分了两种模式:

一种是基于ssl证书模式推送,另一种是根据p8证书生产jwt token授权来推送。

显然代码给的是第一种,然后看了苹果的官方文档2021年就已经废弃了第一种模式并且强行要求使用http2请求,看来chatGPT3.5的投喂数据截止于2021年所言非虚。

苹果官方强行要求http2

这里我踩了两个大坑:

第一个是苹果证书ssl模式下要pem证书,我拿到的是p12证书,只能先用OpenSSL转成pem公钥和私钥,后来发现要公钥和私钥放在一起合成一个pem第一张图的代码才能用。然后转为jwt授权模式后要的是p8证书,之前的pem证书生成的token死活过不了。

第二个是我这边的服务器环境curl并不支持http2,一直报错Unexpected HTTP/1.x request: POST /3/device/XXXX。后来我只好重新编译curl和php相关的扩展直接升级到了curl 8.1.0。参考连接

怎么让php curl发http2请求

怎么让curl支持http2

下面是推送和jwt生成demo

<?php
//jwt token模式下的苹果推送

$device_token = 'your_device_token';  //苹果给的设备推送token
$message = 'Hello, world!'; //要推送的消息

// Create the notification payload
$payload = array(
    'aps' => array(
        'alert' => $message,
        'sound' => 'default'
    )
);
$payload_json = json_encode($payload);

//正式环境推送地址
$url = 'https://api.push.apple.com:443/3/device/' . $device_token;

//沙盒推送地址
//$url = "https://api.sandbox.push.apple.com:443/3/device/".$device_token;

$headers = array(
    'Authorization: Bearer 你生成的jwt_token',
    'Content-Type: application/json'
);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload_json);
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); //这里很关键,以http2发送请求,如果你的curl不支持http2则需要看前面的步骤
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

// Check the response
if ($http_code == 200) {
    echo 'Notification sent successfully.';
} else {
    echo 'Notification failed with error code: ' . $http_code;
}

生成 jwt_token

<?php
//jwt token生成代码
$key_id = 'your_key_id';
$team_id = 'your_team_id';
$pkey_file = 'AuthKey_your_key_id.p8'; //p8证书文件,我在这里填的绝对地址

// token过期时间
$expires = time() + 3600;

// Generate the JWT header
$header = array(
    'alg' => 'ES256',
    'kid' => $key_id
);
$jwt_header = base64_encode(json_encode($header));

// Generate the JWT payload
$payload = array(
    'iss' => $team_id,
    'iat' => time(),
    'exp' => $expires,
    'aud' => 'https://appleid.apple.com',
    'sub' => 'your_bundle_id' //这里填你的App包名
);
$jwt_payload = base64_encode(json_encode($payload));

//载入证书文件
$pkey_contents = file_get_contents($pkey_file);
$pkey = openssl_get_privatekey($pkey_contents);

//JWT 签名
$signature = '';
openssl_sign($jwt_header . '.' . $jwt_payload, $signature, $pkey, 'sha256');
$jwt_signature = base64_encode($signature);

// Generate the final JWT
$jwt = $jwt_header . '.' . $jwt_payload . '.' . $jwt_signature;

// Print the JWT
echo $jwt;