Brancher des formulaires · Publié le · Emmanuel Sauvage
Webhook Symfony : envoyer et recevoir des webhooks proprement
Dans une application Symfony, les webhooks interviennent dans deux sens : envoyer un événement à un service externe (notification, e-mail, CRM) et recevoir les événements d’un partenaire (paiement, signature électronique…). Voici des exemples prêts à adapter, et les bonnes pratiques qui évitent les mauvaises surprises en production.
Envoyer un webhook avec HttpClient
Stockez l’URL dans .env.local (WEBHOOKY_URL=…), jamais en dur dans le code ni dans le dépôt.
// src/Webhook/WebhookySender.php
namespace App\Webhook;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Contracts\HttpClient\HttpClientInterface;
final class WebhookySender
{
public function __construct(
private HttpClientInterface $client,
#[Autowire(env: 'WEBHOOKY_URL')] private string $url,
) {}
public function send(array $payload): void
{
$response = $this->client->request('POST', $this->url, [
'headers' => ['Accept' => 'application/json'],
'json' => $payload,
'timeout' => 10,
]);
if ($response->getStatusCode() >= 400) {
throw new \RuntimeException('Webhook refusé : HTTP '.$response->getStatusCode());
}
}
} Passer en asynchrone avec Messenger
Un appel HTTP synchrone ralentit la réponse et échoue si le service distant est indisponible. Avec Messenger, le contrôleur publie un message, et un worker effectue l’appel avec nouvelles tentatives automatiques.
// src/Message/ContactSubmitted.php
final class ContactSubmitted
{
public function __construct(public readonly array $payload) {}
}
// src/MessageHandler/ContactSubmittedHandler.php
#[AsMessageHandler]
final class ContactSubmittedHandler
{
public function __construct(private WebhookySender $sender) {}
public function __invoke(ContactSubmitted $message): void
{
$this->sender->send($message->payload);
}
}
// Dans le contrôleur
$bus->dispatch(new ContactSubmitted([
'name' => $form->get('name')->getData(),
'email' => $form->get('email')->getData(),
'message' => $form->get('message')->getData(),
])); Recevoir un webhook et vérifier sa signature
Pour un webhook entrant, vérifiez toujours son authenticité. Beaucoup d’émetteurs signent le corps de la requête avec un secret partagé (HMAC). Le nom de l’en-tête et l’algorithme dépendent du service : reportez-vous à sa documentation.
#[Route('/webhook/partner', methods: ['POST'])]
public function __invoke(Request $request): Response
{
$secret = $_ENV['PARTNER_WEBHOOK_SECRET'];
$signature = (string) $request->headers->get('X-Signature');
$expected = hash_hmac('sha256', $request->getContent(), $secret);
if (!hash_equals($expected, $signature)) {
return new Response('Signature invalide', Response::HTTP_UNAUTHORIZED);
}
$data = $request->toArray();
// Traitement rapide ou dispatch Messenger, puis accusé de réception
return new Response('', Response::HTTP_NO_CONTENT);
}
Pour les services courants, regardez aussi le composant symfony/webhook, qui fournit une structure
standard (RequestParser, RemoteEvent, consumers).
Bonnes pratiques
- Idempotence : stockez l’identifiant de l’événement reçu pour ignorer les doublons.
- Réponse rapide : répondez 2xx immédiatement, traitez en arrière-plan.
- Timeouts courts côté envoi, et journalisation des erreurs.
- Secrets dans les variables d’environnement ou le coffre de secrets Symfony.
Déléguer les intégrations à Webhooky
Plutôt que d’installer un SDK par fournisseur (e-mail, SMS, Slack…), votre application envoie un seul événement à Webhooky, qui exécute les actions configurées et journalise chaque exécution. Changer de fournisseur SMS devient une modification de configuration, pas un déploiement. Voir l’intégration Symfony et la page Webhooky pour les développeurs.
Questions fréquentes
Symfony a-t-il un composant Webhook ?
Oui. Depuis Symfony 6.3, les composants Webhook et RemoteEvent aident à recevoir et à parser des webhooks (avec des parsers fournis pour certains services) et à en envoyer. Pour un besoin simple, un contrôleur et HttpClient suffisent.
Faut-il envoyer les webhooks en asynchrone ?
C’est recommandé : avec Messenger et un transport asynchrone, la requête utilisateur n’attend pas le service distant et les échecs réseau peuvent être retentés.
Comment tester l’envoi ?
Utilisez MockHttpClient dans vos tests unitaires, et un webhook Webhooky en mode brouillon pour vérifier le payload réellement reçu.