Receiver is a drop-in webhook handling library for Laravel.

Overview

Receiver

Receiver is a drop-in webhook handling library for Laravel.

Webhooks are a powerful part of any API lifecycle. Receiver aims to make handling incoming webhooks in your Laravel app a consistent and easy process.

Out of the box, Receiver has built in support for:

Of course, Receiver can receive webhooks from any source using custom providers.

Tests Latest Version on Packagist PHP from Packagist

Table of Contents

Installation

Requires:

  • PHP ^8.0
  • Laravel 8+
composer require hotmeteor/receiver

Optional:

Stripe support requires stripe/stripe-php

Receiving Webhooks

The Basics

Webhooks require an exposed endpoint to POST to. Receiver aims to make this a one-time setup that supports any of your incoming webhooks.

  1. Create a controller and route for the webhooks you expect to receive.
  2. Receive the webhook and handle it, as necessary:
    <?php
    
    namespace App\Http\Controllers\Webhooks;
    
    use App\Http\Controllers\Controller;
    use Illuminate\Http\Request;
    
    class WebhooksController extends Controller
    {
       public function store(Request $request)
       {
           Receiver::driver('slack')
               ->receive($request)
               ->ok();
       }
    }

The methods being used are simple:

  • Define the driver that should process the webhook
  • receive the request for handling
  • Respond back to the sender with a 200 ok response

Receiving from multiple apps

Maybe you have webhooks coming in from multiple services -- handle them all from one controller with a driver variable from your route.

<?php

namespace App\Http\Controllers\Webhooks;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;

class WebhooksController extends Controller
{
   public function store(Request $request, string $driver)
   {
       Receiver::driver($driver)
           ->receive($request)
           ->ok();
   }
}

The provided ReceivesWebhooks trait will take care of this for you.

Note: you'll still need to create the route to this action.

<?php

namespace App\Http\Controllers\Webhooks;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Receiver\ReceivesWebhooks;

class WebhooksController extends Controller
{
   use ReceivesWebhooks;
}

Handling Webhooks

The Basics

Now that webhooks are being received they need to be handled. Receiver will look for designated Handler classes for each event type that comes in in the App\Http\Handlers\[Driver] namespace. Receiver does not provide these handlers -- they are up to you to provide as needed. If Receiver doesn't find a matching handler it simplies ignores the event and responds with a 200 status code.

For example, a Stripe webhook handler would be App\Http\Handlers\Stripe\CustomerCreated for the incoming customer.created event.

Each handler is constructed with the event (name of the webhook event) and data properties.

<?php

namespace App\Http\Handlers\Stripe;

class CustomerCreated
{
    public function __construct(public string $event, public array $data)
    {
    }

    public function handle()
    {
        // Your code here
    }
}

Queueing Handlers

Of course, since your app may be receiving a lot of webhooks it might be better practice to queue these handlers. That way your app can efficiently respond back to the service that the webhook was received and requests aren't being blocked as events are handled.

Receiver attempts to dispatch every handled event, so queueing handlers is simply a matter of setting them up like any Laravel queued job:

<?php

namespace App\Http\Handlers\Stripe;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

class CustomerCreated implements ShouldQueue
{
    use Dispatchable;
    use InteractsWithQueue;
    use Queueable;
    use SerializesModels;

    public function __construct(public string $event, public array $data)
    {
    }

    public function handle()
    {
        // Your code here
    }
}

Extending Receiver

As mentioned previously, Receiver can handle webhooks from any source. Even though there are a few providers distributed with the package, Receiver can easily be extended to work with other apps.

Adding Custom Providers

The easiest way to add a new provider is to use the included Artisan command:

php artisan receiver:make <name>

This command will generate a new provider with the name you defined. This class will be created in the App\Http\Receivers namespace.

If your provider needs to be able to verify webhook signatures simply add the --verified flag to the command:

php artisan receiver:make <name> --verified

Once you've created your new provider you can simply extend Receiver in your AppServiceProvider so that Receiver can use it:

<?php

namespace App\Providers;

use App\Http\Receivers\MailchimpProvider;
use App\Http\Receivers\MailgunProvider;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        // 
    }

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        $receiver = app('receiver');

        $receiver->extend('mailchimp', function ($app) {
            return new MailchimpProvider(
                config('services.mailchimp.webhook_secret')
            );
        });
        
        $receiver->extend('mailgun', function ($app) {
            return new MailgunProvider(
                config('services.mailgun.webhook_secret')
            );
        });
    }
}

Defining Attributes

Receiver needs two pieces of information to receive and handle webhook events:

  • The event name
  • The event data

Since these are found in different attributes or headers depending on the webhook, Receiver makes it simple ways to define them in your provider.

<?php

namespace Receiver\Providers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class CustomProvider extends AbstractProvider
{
    /**
     * @param Request $request
     * @return string
     */
    public function getEvent(Request $request): string
    {
        return $request->input('event.name');
    }
    
    /**
     * @param Request $request
     * @return array
     */
    public function getData(Request $request): array
    {
        return $request->all();
    }
}

The getEvent() method is used to return the name of the webhook event, ie. customer.created.

The getData() method is used to return the payload of data that can be used within your handler. By default this is set to $request->all().

Securing Webhooks

Many webhooks have ways of verifying their authenticity as they are received, most commonly through signatures or basic authentication. No matter the strategy, Receiver allows you to write custom verification code as necessary. Simply implement the verify method in your provider and return true or false if it passes.

A false return will result in a 401 response being returned to the webhook sender.

<?php

namespace App\Http\Controllers\Webhooks;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;

class WebhooksController extends Controller
{
    public function verify(Request $request): bool
    {
        // return result of verification
    }

    public function store(Request $request, string $driver)
    {
        Receiver::driver($driver)
            ->receive($request)
            ->ok();
    }
}

Handshakes

Some webhooks want to perform a "handshake" to check if your endpoint exists and returns a valid response when it's first set up. To facilitate this, implement the handshake method in your provider:

<?php

namespace App\Http\Controllers\Webhooks;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;

class WebhooksController extends Controller
{
    public function handshake(Request $request): array
    {
        // return result of handshake
    }

    public function store(Request $request, string $driver)
    {
        Receiver::driver($driver)
            ->receive($request)
            ->ok();
    }
}

Unlike the verify method, handshake expects an array to be returned, since many times the webhook sender is expecting a specific "challenge" response. The return of the handshake method is sent back to the webhook sender.

Credits

Made with contributors-img.

License

The MIT License (MIT). Please see License File for more information.

Comments
  • Support for fallback handlers when event-specific class doesn't exist

    Support for fallback handlers when event-specific class doesn't exist

    Thanks for this great package. I notice that right now, for any given driver if a webhook is sent with an event/topic that doesn't map to a handler class in that driver's namespace, the code just quietly finishes and returns success.

    I wonder if it would be helpful to allow the definition of a fallback class that would be used when no event-specific class is available?

    I could imagine implementing this by hardcoding a class name that would be used (e.g. "NoEventHandler.php") or by allowing users of the package to define a fallback class in the receiver definition, e.g. through getFallbackClass(): string or similar.

    opened by ChrisHardie 3
  • Stripe Driver Not Recognized?

    Stripe Driver Not Recognized?

    I've installed the latest version running on Laravel 9 and PHP 8.1.

    I'm getting this error message when trying to use the stripe driver:

    InvalidArgumentException: Driver [stripe] not supported.
    

    Not sure what I'm doing wrong or if this is a bug.

    My implementation:

    <?php
    
    namespace App\Http\Controllers\Webhooks;
    
    use Illuminate\Http\Request;
    use Receiver\Facades\Receiver;
    use App\Http\Controllers\Controller;
    
    class WebhooksController extends Controller
    {
        public function store(Request $request)
        {
            Receiver::driver('stripe')
                ->receive($request)
                ->ok();
        }
    }
    
    opened by nkeena 3
  • IDE: Undefined method 'receive'

    IDE: Undefined method 'receive'

    VS Code with PHP Intelephense is complaining about receive function (or any other function after the first one).

    Screen Shot 2022-09-06 at 10 44 17

    Code runs fine. Just IDE is complaining.

    opened by hmaesta 2
  • Unclear documentation

    Unclear documentation

    In you README, it states:

    Simply implement the verify method in your provider and return true or false if it passes.

    Likewise, it states:

    To facilitate this, implement the handshake method in your provider

    However, both of the examples for these two methods show them being on the controller, rather than the provider.

    If both of these methods are on the controller, it doesn't make it possible to group the verification and handshake with the provider itself. In any case, the documentation is confusing.

    opened by goodevilgenius 1
  • Fixed Stripe signature header

    Fixed Stripe signature header

    The "HTTP" prefix is not required when using Illuminate\Http\Request. Due to this the signature header from Stripe can not be read at all and all verifications fail.

    See https://www.php.net/manual/de/reserved.variables.server.php vs. https://laravel.com/docs/9.x/requests#request-headers

    opened by xolf 1
  • Fixed AbstractProvider::getClass()

    Fixed AbstractProvider::getClass()

    For events in event.name notation the method searched for a class App\Http\Handlers\Provider\Event.Name.

    Fixed the method so it searches for App\Http\Handlers\Provider\EventName.

    opened by xolf 0
  • Fix invalid method name

    Fix invalid method name

    There is just a mistake in the ReceiverManager on the postmark driver. The method ends with Provider instead of Driver. This fixes it.

    The test is included in the fix.

    opened by jankremlacek 0
  • Multiple events in single request

    Multiple events in single request

    I have been evaluating if this package would work with Xero webhooks.

    One thing I would like to know is if there is a way in a custom provider to handle multiple events in the one request. This is how Xero handles webhooks by sending an array of events that you then loop through.

    Example payload:

    {
       "events": [
          {
             "resourceUrl": "https://api.xero.com/api.xro/2.0/Contacts/717f2bfc-c6d4-41fd-b238-3f2f0c0cf777",
             "resourceId": "717f2bfc-c6d4-41fd-b238-3f2f0c0cf777",
             "eventDateUtc": "2017-06-21T01:15:39.902",
             "eventType": "Update",
             "eventCategory": "CONTACT",
             "tenantId": "c2cc9b6e-9458-4c7d-93cc-f02b81b0594f",
             "tenantType": "ORGANISATION"
          }
       ],
       "lastEventSequence": 1,
       "firstEventSequence": 1,
       "entropy": "S0m3r4Nd0mt3xt"
    
    }
    
    opened by ashleyhood 2
  • Extract providers out from core package to be installable independently

    Extract providers out from core package to be installable independently

    Receivers should be separated from the core package, much like how Socialite has https://socialiteproviders.com, a collection of Socialite providers maintained by many different contributors.

    The current providers in Receiver can stay, but any future ones will be built as independent packages. Additionally, we should create a package that will scaffold out a provider for contribution.

    feature 
    opened by hotmeteor 0
  • Authenticate postmark webhooks by IP

    Authenticate postmark webhooks by IP

    Is your feature request related to a problem? Please describe. For the Postmark provider it (annoyingly) authenticates the webhook using one of my app's users via HTTP basic auth. It's kinda silly to have to setup a special user in my app just to authenticate the webhooks. Then I would want to add logic to my login page's controller to exclude the possibility of logging in as that user on the main website for if those credentials where somehow leaked.

    Describe the solution you'd like In my opinion it makes much more sense to authenticate the webhook by IP address instead of the current method. And maybe add a config value to add additional IP's such as localhost or other special purpose IP's for testing.

    Describe alternatives you've considered I have extended receiver with my own Postmark provider to accomplish this for now.

    Additional context

    enhancement 
    opened by James4645 2
Releases(v0.1.14)
  • v0.1.14(Dec 26, 2022)

  • v0.1.13(Dec 3, 2022)

  • v0.1.12(Dec 2, 2022)

  • v0.1.11(Dec 2, 2022)

  • v0.1.10(Oct 27, 2022)

  • v0.1.9(Oct 27, 2022)

    What's Changed

    • Docs: provide example route for receiving webhooks from multiple services by @ChrisHardie in https://github.com/hotmeteor/receiver/pull/12

    New Contributors

    • @ChrisHardie made their first contribution in https://github.com/hotmeteor/receiver/pull/12

    Full Changelog: https://github.com/hotmeteor/receiver/compare/v0.1.8...v0.1.9

    Source code(tar.gz)
    Source code(zip)
  • v0.1.8(Oct 15, 2022)

    What's Changed

    • #10 by @hotmeteor in https://github.com/hotmeteor/receiver/pull/11

    New Contributors

    • @hotmeteor made their first contribution in https://github.com/hotmeteor/receiver/pull/11

    Full Changelog: https://github.com/hotmeteor/receiver/compare/v0.1.7...v0.1.8

    Source code(tar.gz)
    Source code(zip)
  • v0.1.7(Sep 30, 2022)

    What's Changed

    • Fixed AbstractProvider::getClass() by @xolf in https://github.com/hotmeteor/receiver/pull/6

    Full Changelog: https://github.com/hotmeteor/receiver/compare/v0.1.6...v0.1.7

    Source code(tar.gz)
    Source code(zip)
  • v0.1.6(Sep 20, 2022)

    What's Changed

    • Fixed Stripe signature header by @xolf in https://github.com/hotmeteor/receiver/pull/5

    New Contributors

    • @xolf made their first contribution in https://github.com/hotmeteor/receiver/pull/5

    Full Changelog: https://github.com/hotmeteor/receiver/compare/v0.1.5...v0.1.6

    Source code(tar.gz)
    Source code(zip)
  • v0.1.5(Sep 8, 2022)

    What's Changed

    • Fix getData() return in README + add getData() to strubs by @hmaesta in https://github.com/hotmeteor/receiver/pull/4

    New Contributors

    • @hmaesta made their first contribution in https://github.com/hotmeteor/receiver/pull/4

    Full Changelog: https://github.com/hotmeteor/receiver/compare/v0.1.4...v0.1.5

    Source code(tar.gz)
    Source code(zip)
  • v0.1.4(Sep 7, 2022)

  • v0.1.3(Aug 24, 2022)

    What's Changed

    • Fix invalid method name by @jankremlacek in https://github.com/hotmeteor/receiver/pull/1

    New Contributors

    • @jankremlacek made their first contribution in https://github.com/hotmeteor/receiver/pull/1

    Full Changelog: https://github.com/hotmeteor/receiver/compare/v0.1.2...v0.1.3

    Source code(tar.gz)
    Source code(zip)
  • v0.1.1(Aug 23, 2022)

Owner
Adam Campbell
I build software that scratches my own itches, and then love to share it with others.
Adam Campbell
Laradeploy offers you to automate deployment using a GitHub webhook.

Introduction Laradeploy offers you to automate deployment using a GitHub webhook. Simple and fast just make a git push to GitHub deploy the new modifi

Gentrit Abazi 10 Feb 21, 2022
A Discord Webhook Application with the Coinbase API

Ein PHP-Skript um einen Kryptowährungspreis in Discord darzustellen. Installation Windows Voraussetzungen Docker vollständig installiert Discord und e

Nevah 3 Oct 15, 2022
An easy code to send messages on a discord text channel with webhook.

Status Webhook-Discord An easy code to send messages on a discord text channel with webhook. Don't forget to check the latest version of Webhook-Disco

Victor 1 Dec 3, 2021
Provides simple interfaces to implement a webhook-based tweeting system

webhook-tweeter This package aims to provide simple interfaces to implement a webhook-based tweeting system. This can, for example, be used to tweet a

Ricardo Boss 2 May 7, 2022
Contact Form7 - KeepinCRM connector via Webhook

Contact Form7 - KeepinCRM connector Плагін для відправки даних з форм на Contact Form7 до KeepinCRM через Webhook. Встановлення Вивантажити плагін арх

null 1 Aug 11, 2022
An easy-to-use API for Discord webhook

WebhookAPI An easy-to-use API for Discord webhook İmage: Example Add use use ayd1ndemirci\WebhookAPI; Usage: $webhook = "your_discord_webhook_url";

Aydın Demirci 5 Jun 7, 2023
MOP is a php query handling and manipulation library providing easy and reliable way to manipulate query and get result in a fastest way

Mysql Optimizer mysql optimizer also known as MOP is a php query handling and manipulation library providing easy and reliable way to manipulate query

null 2 Nov 20, 2021
html-sanitizer is a library aiming at handling, cleaning and sanitizing HTML sent by external users

html-sanitizer html-sanitizer is a library aiming at handling, cleaning and sanitizing HTML sent by external users (who you cannot trust), allowing yo

Titouan Galopin 381 Dec 12, 2022
Firebird-PHP: A library created to meet a work need when handling a Firebird database

Firebird-PHP: A library created to meet a work need when handling a Firebird database

Philipe  Lima 3 Jun 27, 2022
PHP library for handling sessions

Horizom Session PHP library for handling sessions. Requirements Installation Available Methods Quick Start Usage Tests TODO Changelog Contribution Spo

Horizom 1 Aug 29, 2022
The Workflow Package add Drag & Drop Workflows to your Laravel Application.

Workflows add Drag & Drop automation's to your Laravel application. The Workflow Package adds Drag & Drop Workflows to your Laravel Application. A Wor

42coders 196 Dec 29, 2022
An opinionated extension package for Laravel Orchid to extend its table handling capabilities, and some further useful helper methods.

OrchidTables An opinionated extension package for Laravel Orchid to extend its table handling capabilities, and some further useful helper methods. In

null 25 Dec 22, 2022
A drop-in replacement for the Magento 2 checkout.

Clean Checkout for Magento 2 A drop-in replacement for the Magento 2 checkout. Features Modules The project is divided into several modules: Overall c

Daniel Sloof 275 Sep 14, 2022
A faster drop in replacement for bin/magento cache:clean with file watcher

"You know, hope is a mistake. If you can't fix what's broken, you'll, uh... you'll go insane." - Max Rockatansky Magento 2 Cache Clean A faster drop i

mage2tv 460 Dec 26, 2022
Igbinary is a drop in replacement for the standard php serializer.

igbinary Igbinary is a drop in replacement for the standard php serializer. Instead of the time and space consuming textual representation used by PHP

Igbinary development 727 Dec 21, 2022
A Symfony2 bundle that integrates Select2 as a drop-in replacement for a standard entity field on a Symfony form.

select2entity-bundle Introduction This is a Symfony bundle which enables the popular Select2 component to be used as a drop-in replacement for a stand

Ross Keatinge 214 Nov 21, 2022
DropItem3D - PocketMine-MP plugin to be able to see drop items realistic.

DropItem3D - PocketMine-MP plugin to be able to see drop items realistic.

ぼい 12 Jun 2, 2022
File Upload widget with multiple file selection, drag&drop support, progress bar, validation and preview images, audio and video for jQuery

File Upload widget with multiple file selection, drag&drop support, progress bar, validation and preview images, audio and video for jQuery. Supports cross-domain, chunked and resumable file uploads. Works with any server-side platform (Google App Engine, PHP, Python, Ruby on Rails, Java, etc.) that supports standard HTML form file uploads.

Sebastian Tschan 31.1k Dec 30, 2022
Decimal handling as value object instead of plain strings.

Decimal Object Decimal value object for PHP. Background When working with monetary values, normal data types like int or float are not suitable for ex

Spryker 16 Oct 24, 2022