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

Payload not arriving in chrome

Open musaffar-patel opened this issue 4 years ago • 16 comments

Hi

It seems the payload arrives as null in event.data on the client side after a push message has been recieved.

Testing with the following code:

require __DIR__ . '/vendor/autoload.php';

use Minishlink\WebPush\WebPush;
use Minishlink\WebPush\Subscription;

$auth = [
    'VAPID' => [
        'subject' => 'htts://testpush.test/',
        'publicKey' => 'BBFwt4l1ZvIoCkH2Cp9DSQKGN6MUQ5mFXrT3OLKNsoqSTweloz87hZtIA64Zgx2zROgxPWN1l61q8ezM0QkD2Dg',
        'privateKey' => 'XXXX',
    ],
];

$notifications = [
    [
        'subscription' => Subscription::create([
            'endpoint' => 'https://fcm.googleapis.com/fcm/send/f1azG0YpefY:APA91bEgPUCksrDdHc3vpd8JZUsiLnf6sphqgw8TdyPVyTMaviZGvBY76RPJ2zFmMFpvhxoL3hvbvMSJ_O8Ct5nqKmDYozsZh-IU-gm2DWqeTVRRgW9k6Hq58vxnYi75RehmdXbEr_yb', // Chrome
            'contentEncoding' => 'aes128gcm',
            'publicKey' => 'BBFwt4l1ZvIoCkH2Cp9DSQKGN6MUQ5mFXrT3OLKNsoqSTweloz87hZtIA64Zgx2zROgxPWN1l61q8ezM0QkD2Dg',
            'privateKey' => 'XXXX',
            'authToken' => '',
            'payload' => '{"msg":"Hello World!"}' // optional (defaults null),
        ]),
    ],
];

$webPush = new WebPush($auth);

// send multiple notifications with payload
foreach ($notifications as $notification) {
    $webPush->queueNotification(
        $notification['subscription'],
        $notification['payload'] // optional (defaults null)
    );
}

/**
 * Check sent results
 * @var MessageSentReport $report
 */
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()}";
    }
}

The message is successfully sent, and notification appears on the client, however the event.data is always empty. Couldf you please help with the above?

Musaffar

musaffar-patel avatar Jun 21 '21 11:06 musaffar-patel

Hey, above you have a bug,

your payload should be inside your notification array, not your subscription object


$notifications = [
    [
        'subscription' => Subscription::create([
            'endpoint' => 'https://fcm.googleapis.com/fcm/send/f1azG0YpefY:APA91bEgPUCksrDdHc3vpd8JZUsiLnf6sphqgw8TdyPVyTMaviZGvBY76RPJ2zFmMFpvhxoL3hvbvMSJ_O8Ct5nqKmDYozsZh-IU-gm2DWqeTVRRgW9k6Hq58vxnYi75RehmdXbEr_yb', // Chrome
            'contentEncoding' => 'aes128gcm',
            'publicKey' => 'BBFwt4l1ZvIoCkH2Cp9DSQKGN6MUQ5mFXrT3OLKNsoqSTweloz87hZtIA64Zgx2zROgxPWN1l61q8ezM0QkD2Dg',
            'privateKey' => 'XXXX',
            'authToken' => '',
        ]),
        'payload' => '{"msg":"Hello World!"}' // optional (defaults null),
    ],
];

andrewiankidd avatar Jul 19 '21 21:07 andrewiankidd

Any solution to this problem? I spent a while trying to make it work. It's not working, "data" field is null

sadektouati avatar Nov 24 '21 02:11 sadektouati

@sadektouati Did you find a solution or did you give up?

richard-llmnn avatar Dec 17 '21 16:12 richard-llmnn

@sadektouati Did you find a solution or did you give up?

I sort of gave up. How about you

sadektouati avatar Dec 17 '21 16:12 sadektouati

@sadektouati Did you find a solution or did you give up?

I sort of gave up. How about you

I am not much further, but I will update / notify you if it works for me.

richard-llmnn avatar Dec 17 '21 16:12 richard-llmnn

@richard-llmnn Thank you very much

sadektouati avatar Dec 17 '21 16:12 sadektouati

@sadektouati It works on my localhost :+1: after a bit of try and error. Please make sure that you have the publicKey (p265dh), the endpoint and the authToken (auth). My backend send notification code looks like this:

            $subscription = [
                'endpoint' => $credential->getEndpoint(),
                'publicKey' => $credential->getPublicKey(),
                'authToken' => $credential->getAuthtoken(),
            ];

            $payload = [
                "title" => "Hallo Push-Welt!",
                "body" => "Hi! Dies ist eine Testnachricht!",
                "badge" => "/assets/img/test1.svg",
                "icon" => "/assets/img/test2.svg",
                "image" => "https://unterricht.cloud/wp-content/themes/bueffel/assets/img/unterricht-logo.png",
                "tag" => "test",
                "data" => [
                    "show1" => "/test1",
                    "show2" => "/test2",
                    "default" => "/test3"
                ],
            ];

            $message = [
                'subscription' => Subscription::create($subscription),
                'payload' => json_encode($payload),
            ];
            // $this->webpushConnection is the WebPush object (Minishlink\WebPush\WebPush)
            $this->webpushConnection->queueNotification(
                $message['subscription'],
                $message['payload']
            );

        foreach ($this->webpushConnection->flush() as $report) {

        }

richard-llmnn avatar Dec 17 '21 17:12 richard-llmnn

@richard-llmnn I'm happy to hear that.

Can you please tell me what goes where ?

Here's the object I get from the subscription: { "keys": {"auth": "string", "p256dh": "string"}, "endpoint": "string", "expirationTime": null }

`$subscription = [

            'endpoint' => $credential->getEndpoint(),

            'publicKey' => $credential->getPublicKey(),

            'authToken' => $credential->getAuthtoken(),

        ];

`

sadektouati avatar Dec 18 '21 04:12 sadektouati

@sadektouati On the client you get the subscription object. In my project I send the endpoint, keys.auth and keys.p265d to my backend api. The backend saves the data to a mysql database. The structrue looks like:

Webpush_ID | endpoint | publicKey | authToken

1 | https://fcm.googleapis.com/fcm/sen.... | BMrfFtMtL9IWl9vchDbbbYzJlbQwpl... | pbs9Y6r5TscHC64Ce9... // example data

So, my backend saves the data in the database as follows:

  • endpoint (from client) -> DB endpoint
  • keys.p265d (from client) -> DB publicKey
  • keys.auth (from client) -> DB authToken

Now if you want to send a message to the users that has subscripted to your webpush you have to do the following:

  1. Fetch the data from the database (I use for this symfony/doctrine orm, so all rows are saved in a own object. My name for the row object is $credential and with the getter methods I get the data for the row.)
  2. use the code from my comment: https://github.com/web-push-libs/web-push-php/issues/334#issuecomment-996893924

Some good tutorials are:

  • https://www.bjoern.info/2020/05/web-push-notifications-mit-php-und-js/ (German)
  • https://www.attheminute.com/article/sending-a-web-push-notification-tutorial (English)
  • https://felixgerschau.com/web-push-notifications-tutorial/ (English)

richard-llmnn avatar Dec 18 '21 11:12 richard-llmnn

@richard-llmnn Thank you very much, now it's working. My problem was that intead of this: Subscription::create([ 'endpoint' => $endpoint, 'publicKey' => $p256dh, 'authToken' => $keys_auth, 'contentEncoding' => 'aesgcm', ]), I was doing this: Subscription::create([ 'endpoint' => $endpoint, $p256dh, $keys_auth, 'contentEncoding' => 'aesgcm', ]), I'll open a new issue for this.

Make sure you update the subscription on your server each time it's changed on the client. I did something similar to this: https://medium.com/@madridserginho/how-to-handle-webpush-api-pushsubscriptionchange-event-in-modern-browsers-6e47840d756f

sadektouati avatar Dec 18 '21 14:12 sadektouati

me too, I cannot send push.. Even if all reports are successful, I cannot see a push notification, neither in the browser on my laptop, nor in the mobile phone (Android). I use Lumen

        $payload = [
            'title' => 'test',
            'body' => 'Messaggio di test',
            'vibrate' => [100, 50, 100],
            'silent' => false,
        ];

        $notifications = Subscription::all()->map(function($item) use ($payload) {
            $subsData = json_decode($item->subscription, true);
            return [
                'subscription' => Subs::create($subsData),
                'payload' => json_encode($payload)
            ];
        })->toArray();

        $auth = Config::get('vapid');
        $webPush = new WebPush($auth);

        // send multiple notifications with payload
        foreach ($notifications as $n) {
            $webPush->queueNotification(
                $n['subscription'],
                $n['payload'] // optional (defaults null)
            );
        }

        $results = [];

        foreach ($webPush->flush() as $report) {
//            $endpoint = $report->getRequest()->getUri()->__toString();

            if (!$report->isSuccess()) {
                $results[] = $report;
            }
        }

where $auth = Config::get('vapid'); returns something like this

[
    'VAPID' => [
        'subject' => 'myemail'
        'publicKey' => '<publicKey>'
        'privateKey' => '<secretKey>'
    ],
]

and $item->subscription is already a json of the form

{
   "endpoint":"endpointUrl",
   "expirationTime":null,
   "keys":{
      "p256dh":"pdkey",
      "auth":"authkey"
   }
}

falco442 avatar Dec 22 '21 12:12 falco442

@falco442 in my case it was a matter of making a subscription exactly as Subscription::create([ 'endpoint' => <endpoint>, 'publicKey' => <p256d>, 'authToken' => <keys_auth>, 'contentEncoding' => 'aesgcm', ]),

the endpoint, publicKey, authToken are the keys you received in the subscription object on the client.

sadektouati avatar Dec 22 '21 17:12 sadektouati

me too, I cannot send push.. Even if all reports are successful, I cannot see a push notification, neither in the browser on my laptop, nor in the mobile phone (Android). I use Lumen

        $payload = [
            'title' => 'test',
            'body' => 'Messaggio di test',
            'vibrate' => [100, 50, 100],
            'silent' => false,
        ];

        $notifications = Subscription::all()->map(function($item) use ($payload) {
            $subsData = json_decode($item->subscription, true);
            return [
                'subscription' => Subs::create($subsData),
                'payload' => json_encode($payload)
            ];
        })->toArray();

        $auth = Config::get('vapid');
        $webPush = new WebPush($auth);

        // send multiple notifications with payload
        foreach ($notifications as $n) {
            $webPush->queueNotification(
                $n['subscription'],
                $n['payload'] // optional (defaults null)
            );
        }

        $results = [];

        foreach ($webPush->flush() as $report) {
//            $endpoint = $report->getRequest()->getUri()->__toString();

            if (!$report->isSuccess()) {
                $results[] = $report;
            }
        }

where $auth = Config::get('vapid'); returns something like this

[
    'VAPID' => [
        'subject' => 'myemail'
        'publicKey' => '<publicKey>'
        'privateKey' => '<secretKey>'
    ],
]

and $item->subscription is already a json of the form

{
   "endpoint":"endpointUrl",
   "expirationTime":null,
   "keys":{
      "p256dh":"pdkey",
      "auth":"authkey"
   }
}

Do you get it working?

richard-llmnn avatar Dec 27 '21 20:12 richard-llmnn

@richard-llmnn I think the problem is in your $subsData = json_decode($item->subscription, true); it should be : $subsData = [ 'endpoint' => $endpoint, 'publicKey' => $p256dh, 'authToken' => $keys_auth, 'contentEncoding' => 'aesgcm', ]; json_decode($item->subscription, true) would give you [ 'endpoint' => $endpoint, 'keys' => ['p256dh' => $p256dh, 'auth' => $keys_auth]]; I hope this helps you

sadektouati avatar Dec 27 '21 22:12 sadektouati

You may have to modify the service worker JS which is listening for push event.

self.addEventListener('push', function (event) {
    if (!(self.Notification && self.Notification.permission === 'granted')) {
        return;
    }

    const sendNotification = body => {
        // you could refresh a notification badge here with postMessage API
        const title = "Web Push example";

        return self.registration.showNotification(title, {
            body,
        });
    };

   // Notice this part carefully. event.waitUntil is only getting called when event.data is non-empty.
    if (event.data) {
        console.log( event );
        const message = event.data.text();
        event.waitUntil(sendNotification(message));
    }
});

Using this script started showing the push notifications.

ankitrox avatar Feb 28 '22 13:02 ankitrox

Hello,

For me it was one of this key ('endpoint', 'keys', 'p256dh', 'auth', 'contentEncoding', 'publicKey', 'authToken') which was badly written by me, the optional keys in the table are not verified if they are well formatted (so treat as null), to get payload you need to have endpoint, publicKey and authToken OR endpoint, keys with ph256dh and auth.

Hope it save someone times XD

Alexandre-re-RE avatar Mar 16 '22 15:03 Alexandre-re-RE