“A long-exposure shot of a road in Ottawa with red light trails from the passing cars” by Marc-Olivier Jodoin on Unsplash

We often have to implement webhooks when we integrate third party services. Webhooks are where one web application sends data or events to other web applications via HTTP requests. For example most email services like mailgun and spark post will send messages related to hard bounces or spam notices as they deliver your applications emails.

It’s pretty easy to receive webhook messages by simply creating a controller and it’s nearly as simple to send them through Laravel’s amazing notification framework. How we’ll do this is creating a custom notification channel.

Installing Our Dependencies

First we’re going to need to install a library for sending HTTP requests. The one we’re going to use here is Guzzle which is pretty common in used by most PHP web applications in one way or another.

We can install this by using the composer.

composer require guzzlehttp/guzzle:~6.0

After doing so we can start making our new Webhook Notification channel.

Notification Channels

A notification channel in Laravel sets out a mechanism of sending structured messages to users, teams, anything you find fit over a multitude of “channels” which could be an email, a slack channel or SMS Message. Those channels themselves often send HTTP requests so this isn’t really something new we’re doing here.

First we’ll create a app/Channels folder and inside that we’ll make a WebhookChannel class. This class will implement a send method that takes a Notifiable object and a Notification object.

Because of how channels are used by the framework we’ll also put some dependencies in our new class by having a constructor that takes a Guzzle client and a logger instance into our channel. The logger is mainly there to make our lives a bit easier in being sure our messages get sent.

So at this point our WebhookChannel should look like this:

We’ll implement the actual methods later on but for now we’ll make our own notifiable trait to standardise how and what can receive notifications via webhook.

A Webhook notifable trait

At this point much like the Notifiable trait we’re going to make a trait for objects which can be notified by a webhook. This isn’t exactly necessary but it’s a nice way to have methods set out which are easy add to any class we want.

We’ll now create a new trait called WebhookNotifiable in the app folder which has two methods, getSigningKey() and getWebhookUrl() . Both of these methods will be required as part of our new webhook process. The methods will just access properties of the object using the traits, in this case a webhook_url and api_key property, the former to be the destination for the notification and the latter api_key will be used for giving the end user a way of verifying any HTTP requests that we send them.

Making a user who can received Webhook notifications

The next step is making our user work with the trait. This will only take a second by modifying the user migration table and creating fields to store an api_key and webhook_url . While at the same time we’ll add the trait to the User class found in app/User.php . Not how the user is already using the Notifiable trait and we simply add this extra one.

Finishing up our Webhook Channel

Now that we have a webhook notifiable trait we can write out the send method for delivering our notification through our WebhookChannel . We’ll edit the send method, first checking if the notification has a toWebHook() method and if it doesn’t then it’ll call toArray() instead. Laravel makes notifications with toArray() by default but it’s nice to have options as multiple channels might use this method. This allows us to get the content from the notification to be sent to our notifiable User . This will become the HTTP body for our webhook request.

We’re then going to generate some headers for the webhook request. We do this so that the users receiving them can verify that the notification came from us and no one else. We do this by hashing a random string and a timestamp using the signing key of the User . Providing a timestamp is a good way to make sure requests from hooks aren’t stale.

After that we’re going to use the Guzzle client to send our notification message. In the event we don’t get a HTTP 200 status code we’re going to throw an exception. In this case I made a simple WebhookFailedException just to standardise the process. We also make sure that if Guzzle throws any exceptions that we catch them and re-throw them as a WebhookFailedException . We use exceptions because if a notification is queued and delivered in a background process the exception will cause the notification to be re-queued for a further attempt. Note that we also use the logger just to make it clear that the request worked or failed by having it written to the log file.

An example notification

Now that we’ve gone through the process of setting up a mechanism for sending data via webhooks we’ll make a quick example notification.

This can be done by using the artisan command php artisan make:notification SomethingHappenedNotification which makes a class in the file app/Notifications/SomethingHappenedNotification.php .

We’ll give the notification a $message argument, this is just for the example, to show the kind of data that might be passed into the notification.

We can remove the toMail() method because we won’t be using this. Instead we’ll add a toWebhook($notification) method which will return an array. In this case we’ll simply put a few values for the sake of testing it.

To finish things up change the via() method return a array containing the WebhookChannel class. This will tell Laravel that this notification will need to be delivered via the WebhookChannel . We’ve also added that the notification should be queued using the ShouldQueue interface. This is really useful as it means that it can be handled on a queue so Laravel will take care of it as a background process if you implement a queue. This can be useful as HTTP requests might timeout or hold up any response you’re sending to your user.

Testing it all out

Obviously this can all be a bit difficult to go from implementing to having a working example so I’ve built my own little project that’s available on GitHub that you can play with. There’s some basic instructions in the readme that you can follow to get started with it.