I have a project for setting up news websites, and I’ve decided not to rely on external Composer packages. I’ve already removed all dependencies except for Minishlink\WebPush, which I still need for push notifications.
So far, I’ve successfully:
- Generated the publicKey and privateKey for my website’s push notifications.
- Created the endpoint, p256dh, and auth keys for subscribers. Everything works fine, except for the final step — actually sending the notifications. Right now, I’m using Minishlink\WebPush for that, like this:
use Minishlink\WebPush\WebPush;
use Minishlink\WebPush\Subscription;
function sendDeviceNotification($email, $content, $email_subject, $userAvatar, $articlePath) {
global $pdo;
$query = "SELECT endpoint, p256dh, authKey FROM users_auth_tokens WHERE email = :email";
$stmt = $pdo->prepare($query);
$stmt->execute([':email' => $email]);
if ($stmt->rowCount() > 0) {
$auth = [
'VAPID' => [
'subject' => main_url,
'publicKey' => website_push_public_key,
'privateKey' => website_push_private_key,
],
];
$webPush = new WebPush($auth);
while ($subscriber = $stmt->fetch(PDO::FETCH_ASSOC)) {
$subscription = Subscription::create([
'endpoint' => $subscriber['endpoint'],
'keys' => [
'p256dh' => $subscriber['p256dh'],
'auth' => $subscriber['authKey'],
],
]);
$payload = json_encode([
'title' => $content,
'body' => $email_subject,
'icon' => $userAvatar,
'badge' => main_url . '/favicon.ico',
'extraData' => $articlePath,
]);
$webPush->queueNotification($subscription, $payload);
}
foreach ($webPush->flush() as $report) {
if ($report->isSuccess()) {
}
}
}
}
I’ve tried countless ways to replace Minishlink\WebPush with a custom solution, but nothing works. This is literally the last missing piece—once I solve this, my push notification system will be fully independent.
Has anyone successfully implemented Web Push notifications without Minishlink\WebPush?
Additional Info:
To help others, I’m also sharing how I generate and store the website’s VAPID keys (works perfectly):
if (strpos(main_url, 'https://') === 0 && empty(website_push_public_key) && empty(website_push_private_key)) {
function generateVAPIDKeys() {
$config = [
"private_key_type" => OPENSSL_KEYTYPE_EC,
"curve_name" => "prime256v1",
];
$res = openssl_pkey_new($config);
if (!$res) {
throw new Exception('The key pair cannot be created.');
}
if (!openssl_pkey_export($res, $privateKeyPem)) {
throw new Exception('The private key cannot be extracted.');
}
$keyDetails = openssl_pkey_get_details($res);
if (!$keyDetails || !isset($keyDetails['ec']['x']) || !isset($keyDetails['ec']['y']) || !isset($keyDetails['ec']['d'])) {
throw new Exception('The key details cannot be extracted.');
}
$publicKeyHex = '04' . bin2hex($keyDetails['ec']['x']) . bin2hex($keyDetails['ec']['y']);
$publicKey = hex2bin($publicKeyHex);
$publicKeyBase64 = rtrim(strtr(base64_encode($publicKey), '+/', '-_'), '=');
$privateKeyBase64 = rtrim(strtr(base64_encode($keyDetails['ec']['d']), '+/', '-_'), '=');
return [
'publicKey' => $publicKeyBase64,
'privateKey' => $privateKeyBase64,
];
}
try {
$vapidKeys = generateVAPIDKeys();
$updateKeys = [
'website_push_public_key' => $vapidKeys['publicKey'],
'website_push_private_key' => $vapidKeys['privateKey']
];
$sql = "UPDATE system_options SET display = :display WHERE setting_name = :setting_name";
$statement = $pdo->prepare($sql);
foreach ($updateKeys as $settingName => $keyValue) {
$statement->bindValue(':display', $keyValue, PDO::PARAM_STR);
$statement->bindValue(':setting_name', $settingName, PDO::PARAM_STR);
$statement->execute();
}
} catch (Exception $e) {
trigger_error($e->getMessage());
}
}
And this is how I store the user’s subscription keys (also working fine):
if (strpos(main_url, 'https://') === 0 && isset($data['push_subscriber']) && !empty($data['push_subscriber'])) {
$pushData = $data['push_subscriber'];
$endpoint = $pushData['endpoint'];
$p256dh = $pushData['keys']['p256dh'];
$authKey = $pushData['keys']['auth'];
$device_id_decrypted = decrypt($device_id, encryption_key);
$sql = "UPDATE users_auth_tokens SET endpoint = :endpoint, p256dh = :p256dh, authKey = :authKey WHERE email = :email AND device_id = :device_id";
$stmt = $pdo->prepare($sql);
$stmt->bindParam(':endpoint', $endpoint, PDO::PARAM_STR);
$stmt->bindParam(':p256dh', $p256dh, PDO::PARAM_STR);
$stmt->bindParam(':authKey', $authKey, PDO::PARAM_STR);
$stmt->bindParam(':email', $email, PDO::PARAM_STR);
$stmt->bindParam(':device_id', $device_id_decrypted, PDO::PARAM_STR);
$stmt->execute();
}
Would really appreciate any help in getting push notifications to work without Minishlink\WebPush! Thanks in advance.