Discovering Symfony’s Secret Weapon: The Ultimate Guide to the Webhook Component

Jakub Skowron (skowron.dev)
3 min readSep 5, 2023

--

Photo by Grace To on Unsplash

Modern web applications increasingly use webhooks, which allow communication between various services. For Symfony developers, managing webhooks has now become much simpler thanks to the new Webhook Component, which was presented during the SymfonyCon 2022 conference.

What are webhooks?

A webhook is a mechanism that allows an external service to send events to our application in the form of HTTP requests. For example, the Stripe service sends webhooks informing about the completion of a shopping session. To receive such events, we must configure the appropriate URL of our application in Stripe.

Installing the Webhook Component

To install the Webhook Component in your Symfony project, use the following command:

composer require symfony/webhook

The Webhook Component supports Symfony version 5.2 and newer.

Repository and Documentation

Using the component: Implementing Stripe payments

To integrate with Stripe, we need to create a parser that will handle webhook requests from Stripe and handle RemoteEvent events.

  1. Creating a parser for Stripe

The parser will be responsible for validating the request from Stripe and converting it into a RemoteEvent object. To achieve this, we must implement the RequestParserInterface.

namespace App\Webhook;

use Symfony\Component\Mailer\Bridge\Stripe\Webhook\StripeRequestParser;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Mailer\Bridge\Stripe\Webhook\RejectWebhookException;
use Symfony\Component\Webhook\RequestParserInterface;
final class CustomStripeRequestParser extends StripeRequestParser implements RequestParserInterface
{
public function parse(Request $request, string $secret): ?RemoteEvent
{
// Validate the request from Stripe
if (!$this->isValidRequest($request)) {
throw new RejectWebhookException('Invalid Stripe request.');
}
// Convert the request into a RemoteEvent object
$data = $request->getContent();
$eventData = json_decode($data, true);
$remoteEvent = new StripeRemoteEvent($eventData);
return $remoteEvent;
}
private function isValidRequest(Request $request): bool
{
// Logic for verifying the request from Stripe
// ...
return true;
}
}
  1. Handling RemoteEvent

After processing the request by the parser, the RemoteEvent is passed to the appropriate consumer.

namespace App\RemoteEvent;

use Symfony\Component\RemoteEvent\Attribute\AsRemoteEventConsumer;
use Symfony\Component\RemoteEvent\Event\StripePaymentEvent;
#[AsRemoteEventConsumer(name: 'stripe')]
class StripePaymentConsumer
{
public function consume(StripePaymentEvent $event): void
{
// Update the payment status in the database
$payment = $this->paymentRepository->findByStripeId($event->getStripeId());
if ($payment) {
$payment->setStatus($event->getStatus());
$this->entityManager->persist($payment);
$this->entityManager->flush();
}
}
}

Configuration

  1. config/routes.yaml

The routes.yaml file allows you to define paths for our webhooks. This way, we can specify the URL under which our application will listen for incoming webhook requests.

webhook_stripe:
resource: '@FrameworkBundle/Resources/config/routing/webhook.xml'
prefix: /webhook/stripe
  1. webhook.yaml

The webhook.yaml file is used to configure our parser and consumer. It specifies which services are to be used to handle specific webhook requests. This allows us to easily manage different webhooks in one place.

framework:
webhook:
routing:
stripe:
service: App\Webhook\CustomStripeRequestParser

Testing the Webhook Component

Many of you will be wondering how to approach webhook testing in Symfony. It is very simple. The most important thing in wehbook testing is to simulate real HTTP requests, in our case with Stripe. Let’s see what a simple test might look like:

public function testStripeWebhook(): void
{
$client = static::createClient();
$data = ['id' => 'evt_test', 'type' => 'payment_intent.succeeded'];

$client->request('POST', '/webhook/stripe', [], [], [], json_encode($data));

$this->assertEquals(200, $client->getResponse()->getStatusCode());
}

Conclusion

The Webhook Component in Symfony represents a significant leap forward in the realm of seamless integrations with external services. This innovative feature not only simplifies the intricate process of handling webhook events but also ensures a secure and efficient integration into our core business logic. As the digital landscape continues to evolve, tools like these empower developers to craft more responsive, interconnected, and dynamic applications. Embracing the Webhook Component is a testament to Symfony’s commitment to providing cutting-edge solutions for today’s challenges in web development. You have to try it yourself, it’s a game changer for every developer whom struggle with intergrations.

--

--

Jakub Skowron (skowron.dev)

Poland based PHP/Python Web Backend dev. Love to work with Symfony and FastAPI frameworks. In spare time totally gearhead.