Receiving data over webhooks with Paystack in Laravel — Part 1
Have you ever been building an application and thought: “I can make requests to this service’s API, but is there a way for them to let my app know when X happens? You could try calling the API on a set interval. Take the response, compare it to the last, and go from there. This is polling, but it is inefficient and can be an easy way to hit rate limits. Instead, some APIs and services offer what's known as a webhook. Instead of contacting them, they contact you.

What is a webhook?

A webhook (also called a web callback or HTTP push API) is a way for an app to provide other applications with real-time information. Webhooks are a way to send a notification. They are essentially one-way. The sender doesn’t care what you do with it, or in some cases even who you are. At most they only want a response letting them know that your app received it. A webhook delivers data to other applications as it happens, meaning you get data immediately.Many services, like the Stripe API, Paystack API, GitHub API, and Twillio API offer the ability to notify you programmatically when an event occurs. The webhook makes a POST request to the URL of your choice with a payload containing details about the event.This makes webhooks much more efficient for both provider and consumer. The only drawback to webhooks is the difficulty of initially setting them up.

If you’ve ever built a REST API that your application or other apps use, you know nearly everything you need to get started.

For our example, the flow looks like this:

Web service(either Paystack API or Github API) sends payload to your app’s endpoint. Your app receives payload. Your app responds and confirms receipt. Your app acts upon the payload data.

If you want to test the shape of a webhook before building an application to consume it, you can use a service like Requestbin.

What we will build

In this tutorial we will build a project that listens to events from Paystack.

Prerequisites

Laravel v5.8 or greater

spatie/laravel-webhook-client

A Paystack account. You can sign up for a free account here.

Set up your project

For this example we’ll be using Laravel, but the concepts carry over to other languages and frameworks. If you’re adding webhook consumption to an existing project, skip over the setup below.

To get started, initialize a new Laravel project:

composer create-project --prefer-dist laravel/laravel learning-webhook

Install Spatie’s Laravel webhook client package

composer require spatie/laravel-webhook-client

This tutorial uses Spatie’s webhook client package to handle and process incoming webhook events and it also verifies events ain’t compromised.

Configure the package

You can publish the config file with:

php artisan vendor:publish --provider="Spatie\WebhookClient\WebhookClientServiceProvider" --tag="config"

This is the contents of the file that will be published at config/webhook-client.php :

return [

'configs' => [

[

/*

* This package support multiple webhook receiving endpoints. If you only have

* one endpoint receiving webhooks, you can use 'default'.

*/

'name' => 'default', /*

* We expect that every webhook call will be signed using a secret. This secret

* is used to verify that the payload has not been tampered with. For the purpose of this article we will be using our Paystack secret key. Make sure you add it to the .env file */

'signing_secret' => env('PAYSTACK_SECRET_KEY'), /*

* The name of the header containing the signature.

*/

'signature_header_name' => 'x-paystack-signature', /*

* This class will verify that the content of the signature header is valid.

*

* It should implement \Spatie\WebhookClient\SignatureValidator\SignatureValidator

*/

'signature_validator' => \Spatie\WebhookClient\SignatureValidator\DefaultSignatureValidator::class, /*

* This class determines if the webhook call should be stored and processed.

*/

'webhook_profile' => \Spatie\WebhookClient\WebhookProfile\ProcessEverythingWebhookProfile::class, /*

* This class determines the response on a valid webhook call.

*/

'webhook_response' => \Spatie\WebhookClient\WebhookResponse\DefaultRespondsTo::class, /*

* The classname of the model to be used to store call. The class should be equal

* or extend Spatie\WebhookClient\Models\WebhookCall.

*/

'webhook_model' => \Spatie\WebhookClient\Models\WebhookCall::class, /*

* The class name of the job that will process the webhook request.

*

* This should be set to a class that extends \Spatie\WebhookClient\ProcessWebhookJob.

*/

'process_webhook_job' => '',

],

],

N.B: I added Paystack’s signature header “x-paystack-signature” to the “signature_header_name” and “PAYSTACK_SECRET_KEY” as the signing_secret. For clarity, check out Paystack’s docs on setting up webhook here:

Preparing the database

By default, all webhook calls will get saved in the database.

To create the table that holds the webhook calls, you must publish the migration with:

php artisan vendor:publish --provider="Spatie\WebhookClient\WebhookClientServiceProvider" --tag="migrations"

After the migration has been published, you can create the webhook_calls table by running the migrations:

php artisan migrate

Taking care of routing

Next, let’s take care of the routing. Paystack gives you the leeway to add a webhook URL to your dashboard in API & Webhook section of the settings page.

Webhook URL as configured on my Paystack account

In the routes file of your app, you must pass that URL to Route::webhooks as seen below:

//routes/api.php Route::webhooks('paystack-webhook');

Behind the scenes, this will register a POST route to a controller provided by this package. Because the app that sends webhooks to you has no way of getting a csrf-token, you must add that route to the except array of the VerifyCsrfToken middleware:

// App/Http/Middleware/VerifyCsrfToken.php protected $except = [

'paystack-webhook',

];

Creating our own signature validator

What we need to do next is to create a custom signature validator, why do we need to create a signature validator?

It is important to verify that events originate from Paystack to avoid delivering value based on a countefeit event.

The quoted statement above is the reason why we need to create a custom signature validator which implements Spatie\WebhookClient\WebhookClient\SignatureValidator\SignatureValidator interface.

//App/Handler/CustomSignatureValidator.php <?php namespace App\Handler; use Illuminate\Http\Request; use Spatie\WebhookClient\Exceptions\WebhookFailed; use Spatie\WebhookClient\WebhookConfig; use Spatie\WebhookClient\SignatureValidator\SignatureValidator; class PaystackSignature implements SignatureValidator{

public function isValid(Request $request, WebhookConfig $config): bool{ $signature = $request->header($config->signatureHeaderName); if (! $signature) { return false;

} $signingSecret = $config->signingSecret; if (empty($signingSecret)) { throw WebhookFailed::signingSecretNotSet();

} $computedSignature = hash_hmac('sha512', $request->getContent(), $signingSecret); return hash_equals($signature, $computedSignature);

}

}

What is needed to be done after this is to update the file in this path config/laravel-webhook-client.php with the name of the class handling the signature verification of the incoming webhook response so as to truly ascertain that it is not a counterfeited response.

/* * This class will verify that the content of the signature header is valid. * * It should implement \Spatie\WebhookClient\SignatureValidator\SignatureValidator */ 'signature_validator' => App\Handler\CustomSignatureValidator::class,

Storing and processing webhooks

Since that’s out of the way, we can now focus on handling our webhook response. To do that, we need to create a folder called Handler and place it in our App directory.

After creating the folder, we then need to create a file called ProcessWebhook.php in the folder we created above to handle(store and process) the webhook response.

//App/Handler/ProcessWebhook.php

<?php namespace App\Handler;

use \Spatie\WebhookClient\ProcessWebhookJob; //The class extends "ProcessWebhookJob" class as that is the class //that will handle the job of processing our webhook before we have //access to it. class ProcessWebhook extends ProcessWebhookJob

{ public function handle(){

$data = json_decode($this->webhookCall, true);

//Do something with the event

logger($data['payload']);

http_response_code(200); //Acknowledge you received the response }

}

What is needed to be done after this is to update the file in this path config/laravel-webhook-client.php with the name of the class handling the webhook response gotten from Paystack or any other service you might be using(say Github API, Stripe API, etc).

/*

* The class name of the job that will process the webhook request.

*

* This should be set to a class that extends \Spatie\WebhookClient\ProcessWebhookJob.

*/

'process_webhook_job' => App\Handler\ProcessWebhook::class,

],

Conclusion

That was fun, right? In a very short time, we were able to setup and configure a webhook-client package to handle incoming events from Paystack. For further readings, you can read more about the package we used for this project at its official documentation too. You can also check out the full source code of this project on GitHub.

In the next part of this article, we will see how we can test our webhook locally with ngrok before going LIVE!

If you liked this, click the💚 below so other people will see this here on Medium.

Twitter: @samsss_a
['Samson Adejoro']
['Backend Development', 'Web Development', 'Technology', 'Paystack', 'Laravel']