Skip to Content
BackendIntegrationsPHPReaction Event Hooking (Webhooks)

PHPReaction Event Hooking (Webhooks)

Objectives

Allow third-party integrations to build apps which can receive a notification about an action that has been done to an entity, and do something with the information.

For example, when a product is updated, a third-party Shopify synchronization middleware can receive the webhook to a callback endpoint and proceed to synchronize the product on Shopify.

Supported events

  • Create (postPersist)
  • Update (postUpdate)
  • Delete (postRemove)

Toggleable feature on ERP

*All KVS are available from the quick add list

Webhooking must be enabled on the ERP using the following KeyValue:

phpreaction.refs.activateWebHooking => 1

Project secret key

The phpreaction.refs.projectKey key is to add the ERP’s security key for webhook signature. Use a secure string long string. This key has a clearance level of 0 (encrypted) and should not be available again, except if regenerated.

TODO generate project secret instead of user input…

EventSubscriber object

An EventSubscriber is a webhook definition.

List available at /event_hooking/event_subscriber.

Each event subscriber needs these properties:

  • Entity class: object that we want to have notifications from
  • Event type: the action that we want to have notifications from
  • Callback URL: the url to send the webhook data to
  • Event key: additional secret string to add to the signature secret TODO remove eventKey, should be generated and stored in a clearance level 0 (encrypted) KVS instead.

Webhook Request Body

Request body fields explanation:

  • class: entity fully qualified name
  • shortname: API shortname for the entity
  • eventType: Type of the event (Create, Update, Delete), identified by it’s slug
  • resourceId: ID of the resource
  • resourceIri: API identifier of the resource
  • eventSubscriberId: ID of the EventSubscriber which triggered the webhook notification
  • eventSubscriberIri: API identifier of the EventSubscriber
  • timestamp: Webhook timestamp
  • tenant: Tenant which owns the data

Typically, you will often use shortname, eventType, resourceId and resourceIri.

Example

This is an example of webhook request body using the “Notes” entity:

{ "class": "PHPReaction\\Entity\\NoteBundle\\Note", "shortname": "notes", "eventType": "postUpdate", "resourceId": 1, "resourceIri": "/open-api/v3/notes/1", "eventSubscriberId": 1, "eventSubscriberIri": "/open-api/v3/event_subscribers/1", "timestamp": 1761699183, "tenant": "demo1" }

Webhook Signature Secret

Every webhook are signed using the project key and the event key as secret, concatenated using an underscore ”_”.

projectKey_eventKey

For example:

  • Project key: foo
  • Event key: bar

The secret key for the webhook signature will be: foo_bar

N.B : If the event key is not specified, only the project key is used as secret.

projectKey

Verify Webhook Signature

When you receive a webhook, ideally you want to confirm that it was indeed sent by the app.

To do so, you have to validate the Signature using these headers:

  • X-Webhook-Signature: For the signature to verify
  • X-Webhook-Timestamp: The timestamp of the request
  • X-Webhook-Version: The webhook version

You also need the webhook secret.

This is a PHP example on how to verify the webhook signature:

/** * Make signature hash using request body, secret and request timestamp. * * @param string $messageBody * @param string $secret * @param int|null $timestamp * @return string */ public static function cryptSignature(string $messageBody, string $secret, ?int $timestamp = null): string { if (null === $timestamp) { $date = new \DateTime(); $timestamp = strtotime($date->format('Y-m-d H:i:s')); } $hashedSignature = hash_hmac('sha256', $timestamp.'.'.$messageBody, $secret, true); return base64_encode($hashedSignature); } /** * Verify signature from PHPR. * Returns true if valid, else returns false. * * @param Request $request * @param string $secret * * @return bool */ public static function signatureVerify(Request $request, string $secret): bool { // Time when the request is received $verifiedDatetime = new \DateTime(); $signature = $request->headers->get('X-Webhook-Signature'); $timestamp = $request->headers->get('X-Webhook-Timestamp'); $encodedMessageBody = $request->getContent(); $verifiedSignature = SignatureHelper::cryptSignature($encodedMessageBody, $secret, $timestamp); // Time the request was received $datetime = new \DateTime(strtotime($timestamp)); $dateTimeDiff = date_diff($datetime, $verifiedDatetime); $secondsDelay = ($dateTimeDiff->h * 3600) + ($dateTimeDiff->i * 60) + $dateTimeDiff->s; // Validate signature, optionally validate that it was sent less than a minute ago. if (hash_equals($signature, $verifiedSignature) && $secondsDelay <= 60) { return true; } // Invalid return false; }
Last updated on