web-push-php icon indicating copy to clipboard operation
web-push-php copied to clipboard

Issue with push notification to chrome

Open Ashutoshsaxena97 opened this issue 5 years ago • 12 comments

I have implemented the web push notification using the web-push-PHP library and it was working well but now an issue has occurred Users can be subscribed successfully but not able to send the notification. I have print the response of $webPush->sendNotification( $notification['subscription'], $notification['payload'] // optional (defaults null) ); and its return 1 even though notification not push to chrome browser, if I push notification manually from crome-developer mode / application/service-worker then notification will push.

operating system: Linux PHP Version: 7.3

Ashutoshsaxena97 avatar Jan 14 '20 09:01 Ashutoshsaxena97

When you send a push using package do you see any errors on chrome? Usually if you keep the website open with the console opened it shows up a message if anything goes wrong (or right). Can you check it and post it here? It would help as well if you could show your service worker file or at least the part where renders the push.

b14r avatar Jan 14 '20 22:01 b14r

Thanks for the response. When you send a push using package do you see any errors on chrome? Ans- No.

For reference, please check the video URL: https://www.loom.com/share/b08148f79406429385af1e8fee8a25e6

Code for the manifest file

self.addEventListener('push', function(event) { event.waitUntil( self.registration.pushManager.getSubscription().then(function(sub){ subscription_id=sub.endpoint.split("/").slice(-1)[0]; url='data_json.php?sub_id='+subscription_id+'&param='+Math.random();

}).then(function(obj){

    fetch(url).then(function(response) {
        return response.text();
    }).then(function(ret_content) {
    data = JSON.parse(ret_content);
    var title = data.notification.title;
    var message = data.notification.message;
    var icon = data.notification.icon;
    return self.registration.showNotification(title, {

      body: message,
      icon: icon,
      tag:'removeNotificationQueue',
      data: {
        url: data.notification.url
      }
    });
  }).catch(function(err) {
  });
})

); });

code for send notification to chrome:

        $Subscriber_data = $this->getModel()->getUserData($sub_ids);
        foreach($Subscriber_data as $data){
            $auth = array(
                'VAPID' => array(
                    'subject' => "subject",
                    'publicKey' => $data['publickey'],
                    'privateKey' => $data['privatekey'], // in the real world, this would be in a secret file
                ),
            );
            $push_info['title'] = $data['title'];
            $push_info['body'] = $data['message'];
            $push_info['target_url'] = $data['url'];
            $push_info['icon'] = $data['icon'];
            // echo"<pre>";print_r($data);die;

            $webPush = new WebPush($auth);
            $webPush->sendNotification(
                $data['endpoint'],
                json_encode($push_info), // optional (defaults null)
                $data['userpublickey'], // optional (defaults null)
                $data['secretkey'] // optional (defaults null)
            );
            $res = $webPush->flush();

Ashutoshsaxena97 avatar Jan 16 '20 07:01 Ashutoshsaxena97

So, i see a problem here:

          $webPush = new WebPush($auth);
            $webPush->sendNotification(
                $data['endpoint'],
                json_encode($push_info), // optional (defaults null)
                $data['userpublickey'], // optional (defaults null)
                $data['secretkey'] // optional (defaults null)
            );

you see, the first two arguments of the method sendNotification should be a Subscription object and the JSON payload.

something like this should do the trick:

$push_info['title'] = $data['title'];
$push_info['message'] = $data['message'];
$push_info['url'] = $data['url'];
$push_info['icon'] = $data['icon'];

$subscription = Subscription::create([
    'endpoint' => $data['endpoint'],
    'publicKey' => $data['userpublickey'],
    'authToken' => $data['secretkey']
]);

$webPush = new WebPush($auth);
$webPush->sendNotification(
    $subscription,
    json_encode($push_info)
);
$res = $webPush->flush();

Let me know what happens, see if you can check any errors in the php logs or something.

b14r avatar Jan 16 '20 22:01 b14r

Also, just to boost performance, I would leave the loop to do what REALLY needs to be looped. if you devices need different VAPID keys, I would suggest you to find a way to group and loop subscribers that use the same VAPID keys, this way only this section will need to be inside the loop:

$subscription = Subscription::create([
    'endpoint' => $data['endpoint'],
    'publicKey' => $data['userpublickey'],
    'authToken' => $data['secretkey']
]);

$webPush->sendNotification(
    $subscription,
    $push_info_json
);

Assign the $auth,$webPush variables, json_encoding, and flushing could be outside the loop. Of course I do not know what are the requirements of your projects, these are all just tips about conventions that are usually followed.

b14r avatar Jan 17 '20 09:01 b14r

Hi, I'm currenty facing the same issue. I noticed that the push-event of Chrome's service worker is not triggered as long as a payload is supplied although the message seems to be submitted successfully according to this loop:

foreach ($webPush->flush() as $report) {
    $endpoint = $report->getRequest()->getUri()->__toString();
    if ($report->isSuccess()) {
        echo "[v] Message sent successfully for subscription {$endpoint}.";
    } else {
        echo "[x] Message failed to sent for subscription {$endpoint}: {$report->getReason()}";
    }
}

As soon as the payload is removed the push-event will be triggered but of course with no data. Also formatting the payload in different ways changed nothing for me. I'm guessing Chrome's push service has some sort of rule related to the payload but even though I've been searching the internet for information for two days now I can't seem to find anything related other than this issue and that Chrome used to force you to fetch the content of a notification from within your SW's push-event. But from what I read this isn't supposed to be the case anymore because it used up unneccessary amounts of data.

edeljokaa avatar Jan 17 '20 09:01 edeljokaa

@edeljokaa You can send payload as long as you use VAPID keys (as far as I know, I might be wrong), so if you are not using VAPID the payload (title and body of the push, among the other properties) will not go through.

b14r avatar Jan 17 '20 10:01 b14r

Thanks for your response.

From what I understand this is how you use VAPID keys, right?

$payload = array(
    "title" => "Testing",
    "msg" => "The best test of the world!",
);

$options = array(
    'TTL' => 60,
);

$auth = array(
    'VAPID' => array(
        'subject' => 'mailto:******@**********',
        'publicKey' => $this->publicKey,
        'privateKey' => $this->privateKey,
    ),
);

$webPush = new WebPush($auth,$options);

$subscription = Subscription::create(
    array(
        'endpoint' => $endpoint,
        'publicKey' => $this->publicKey,
        'authToken' => $this->privateKey,
    )
);

if (!is_array($payload)) {
    $payload = array($payload);
}
if (count($payload) > 0) {
    $payload = json_encode($payload);
}

$webPush->sendNotification(
    $subscription,
    $payload,
    true
);

I hid the e-mailaddress in order to post but it is a valid one. The public and private key were generated using the three openssl commands found in the VAPID-section.

edeljokaa avatar Jan 17 '20 10:01 edeljokaa

@edeljokaa Your implementation seems to be correct, I tested the second snippet you posted and it is working for me, something weird happened tho: When I used true for the flush param on sendNotification the push notification would only be deployed if I looped through the response of it. Like this:

$response = $webPush->sendNotification(
            $subscription,
            $payload,
            true
        );

        foreach($response as $result){
            dd($result->isSuccess());
        }

Without that last foreach the push never reaches the browser, I'm not sure if that's a bug or something that I got wrong. Now, when I used the $webPush->flush() method inside a loop (like in the package examples) everything seemed to work just fine. I'm not really sure what is causing your issue...

b14r avatar Jan 17 '20 11:01 b14r

@bemontibeller Thanks for your input. At least I know I'm using everything in the correct way. I'll keep searching but since it worked for you, I'm guessing it has something to do with our development environment which is a bit unique.

edeljokaa avatar Jan 17 '20 11:01 edeljokaa

@bemontibeller thanks for the response,

The problem is only with the desktop Chrome web browser. Web push perfectly works with Mozilla(Firefox), chrome with Android device. I am not understanding if it working with firefox, mobile chrome browser then why is not working with desktop chrome web browser.

Ashutoshsaxena97 avatar Jan 20 '20 05:01 Ashutoshsaxena97

Also, just to boost performance, I would leave the loop to do what REALLY needs to be looped. if you devices need different VAPID keys, I would suggest you to find a way to group and loop subscribers that use the same VAPID keys, this way only this section will need to be inside the loop:

$subscription = Subscription::create([
    'endpoint' => $data['endpoint'],
    'publicKey' => $data['userpublickey'],
    'authToken' => $data['secretkey']
]);

$webPush->sendNotification(
    $subscription,
    $push_info_json
);

Assign the $auth,$webPush variables, json_encoding, and flushing could be outside the loop. Of course I do not know what are the requirements of your projects, these are all just tips about conventions that are usually followed.

@bemontibeller thanks for the response,

The problem is only with the desktop Chrome web browser. Web push perfectly works with Mozilla(Firefox), chrome with Android device. I am not understanding if it working with firefox, mobile chrome browser then why is not working with desktop chrome web browser.

Ashutoshsaxena97 avatar Jan 22 '20 10:01 Ashutoshsaxena97

@edeljokaa Your implementation seems to be correct, I tested the second snippet you posted and it is working for me, something weird happened tho: When I used true for the flush param on sendNotification the push notification would only be deployed if I looped through the response of it. Like this:

$response = $webPush->sendNotification(
            $subscription,
            $payload,
            true
        );

        foreach($response as $result){
            dd($result->isSuccess());
        }

Without that last foreach the push never reaches the browser, I'm not sure if that's a bug or something that I got wrong. Now, when I used the $webPush->flush() method inside a loop (like in the package examples) everything seemed to work just fine. I'm not really sure what is causing your issue...

I think it's intended behavior because the sendNotifications method returns a generator. Methods that yield values must be called before they are able to exhaust their contents. So, the notifications in the flush can't send to the endpoints unless they're triggered by a loop

nmeri17 avatar Nov 27 '20 16:11 nmeri17