Skip to content

Tutorial: Creating Webhooks

Gowri edited this page Dec 9, 2021 · 20 revisions

One of the most common use cases of async events is to publish webhooks to external systems. In this tutorial we'll explore creating a simple webhook that is published to all subscribers when a sales order is placed.

Firstly, we'll need to define an asynchronous event so we can dispatch it. A definition of an asynchronous event describes the name of the event and the shape of the data that will be provided when the event is dispatched.

An advantage of this is that lot of the core Magento data interfaces that are used for web APIs can be reused. Which means, if those APIs ever get updated, you don't have to change a thing.

Defining Async Event and Payload

In your module's etc directory, create a new async_events.xml file.

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:module:Aligent_AsyncEvents:etc/async_events.xsd">
    <async_event name="sales.order.created">
        <service class="Magento\Sales\Api\OrderRepositoryInterface" method="get"/>
    </async_event>
</config>

The above code defines a sales.order.created. This asynchronous event will have some data that corresponds to the same interface as the orders API.

How do we know?

If you open Magento/Sales/etc/webapi.xml and find the web API definition for Get /V1/orders/:id we can see it's being powered by the same method of the same repository.

    <route url="/V1/orders/:id" method="GET">
        <service class="Magento\Sales\Api\OrderRepositoryInterface" method="get"/>
        <!-- ... -->
    </route>

Which means, you can pretty much turn any existing web API into a webhook. There are some caveats depending on the repositories, please see the warnings.

Creating subscribers

Once you have an asynchronous event defined, the next step is to create a subscription. Here we will use an HTTPs subscription. The REST API can be used to create a new subscription

curl --location --request POST 'https://magento.url/rest/V1/async_event' \
--header 'Authorization: Bearer TOKEN' \
--header 'Content-Type: application/json' \
--data-raw '{
    "asyncEvent": {
        "event_name": "sales.order.created",
        "recipient_url": "https://example.com/order_handler"
        "verification_token": "supersecret",
        "metadata": "http"
    }
}'

In the above example, we're creating a subscription for sales.order.created which is the one we just defined in the previous step. We want that to be delivered to https://example.com/order_handler. We also provide a secret (this is useful to sign payloads). Finally it specifies to use the http notifier.

Dispatching Async Event

With an asynchronous event and one subscription setup, the only thing left is to dispatch it. This is just simply publishing to the event queue.

We want to dispatch this event after an order is placed. Therefore we will leverage the built-in sales_order_save_commit_after event. Create a new event observer for this event. You could also use a plugin if you wanted to trigger this in a different place.

Then, inside our event handler we'll dispatch our asynchronous event with the required arguments to resolve that data.

public function execute(Observer $observer): void
{
    /** @var Order $object */
    $object = $observer->getEvent()->getData('order');

    $this->publisher->publish(QueueMetadataInterface::EVENT_QUEUE, [
        'sales.order.created',
        $this->json->serialize([
            'id' => $object->getId()
        ])
    ]);
}

This just simply delegates it to the async events. Note that we dispatch sales.order.created and we provide the id argument. Note that you are not publishing the entire payload into the queue! Just enough information that is required by the service method you defined in async_events.xml

Clone this wiki locally