Webhooks
| Receive events | |
| Verify signatures | |
| Consume | |
| Retries |
Receive events
A webhook is a plain POST request sent by an outside service when something happens: a payment completes, a build finishes, a form is submitted. Your job is small: accept the POST on a dedicated route, parse the body, and answer fast with a status code.
use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Attribute\Route; class AcmeWebhookController { #[Route("/webhook/acme", methods: ["POST"])] public function receive(Request $request): JsonResponse { $payload = $request->getContent(); $data = json_decode($payload, true); if (!is_array($data)) { return new JsonResponse(["error" => "Bad JSON"], 400); } return new JsonResponse(["status" => "accepted"]); } }
Verify signatures
Anyone can POST to your URL, so verify the signature header before trusting the body. The sender signs the payload with a shared secret, you recompute the signature with hash_hmac(), and you compare the two with hash_equals(). Reject anything that does not match.
$signature = $request->headers->get("X-Acme-Signature", ""); $expected = hash_hmac("sha256", $payload, $secret); if (!hash_equals($expected, $signature)) { return new JsonResponse(["error" => "Bad signature"], 401); }
Consume
Answer the webhook at once and do the slow work later in a worker. Wrap the event in a message and handle it outside the request, so a slow database never times out the sender and retries stay possible.
use Symfony\Component\RemoteEvent\RemoteEvent; class AcmeEventHandler { public function __invoke(RemoteEvent $event): void { $name = $event->getName(); $payload = $event->getPayload(); // update the local order from $payload } }
Retries
When handling fails, Messenger retries the message with a growing delay instead of dropping it. Three retries with a pause fix most short outages, and only a message that keeps failing lands in the failure queue for review.
messenger: retry_strategy: max_retries 3, delay 1000
Failed webhook handling is retried 3 times, then stored for review
Next: Asset Mapper
Article author: Arthur Isaev