Blog

Objectif

Les événements d’accroche internet aka « webhooks » permettent à des intégrations tierces (externe à l’application PHPReaction ERP) de recevoir automatiquement une notification lorsqu’une action se produit dans l’application de gestion PHPReaction ERP.

Par exemple, lorsqu’un produit est créé via l’interface de gestion ou l’API, votre service « tiers » sera notifié afin d’exécuter ses propres réaction (Ajouter le produit en ligne, mise à jour de la liste des produits dans la catalogue, synchronisation des inventaire, etc.).

Évènements d'éclancheur / Trigger

PHPReaction peut envoyer des notifications pour les événements déclencheur aka Trigger pris en charge :

  • Create → lorsqu’une ressource est créée

  • Update → lorsqu’une ressource est mise à jour

  • Delete → lorsqu’une ressource est supprimée

Activation des webhooks

Pour activer la fonctionnalité de webhook dans l’ERP, ajoutez la clé suivante à la configuration :

CléValeur
phpreaction.refs.activateWebHooking1

Une fois activée, l’ERP commencera à émettre des webhooks pour les événements définis.

Clé de signature du projet

Chaque projet PHPReaction dispose d’une clé secrète utilisée pour signer les webhooks et garantir leur authenticité.

Elle doit rester secrète (Confidentielité niveau 0), car elle est utilisée pour vérifier la signature des webhooks reçus.

La Confidentielité niveau 0 signifie qu’il est impossible de l’obtenir même pour nous !


La clé public de vérification permet de s’assurer que le webhook provient réellement du service attendu.

phpreaction.refs.webhookPublicKey


De plus, la signature permet aussi de vérifier que le contenu du webhook n’a pas été modifié en transit.
Si un attaquant interceptait la requête et modifiait ses données, la signature calculée ne correspondrait plus, et votre serveur détecterait la falsification.

Pour obtenir la clé « clé public » utiliser l’accés pour les KVS via API.

Call API POST

/keyvaluestore_keyvalues/manager/get

BODY request

{ "key": "phpreaction.refs.webhookPublicKey" }


Response

{ "value": "public_key" }

Abonnés aux événements (Event Subscribers)

Un Event Subscriber définit les conditions dans lesquelles un webhook doit être envoyé.

Les abonnés peuvent être consultés ou configurés via l’API :

/event_hooking/event_subscriber

Chaque abonné doit comporter les informations suivantes :

  • Classe d’entité : l’objet surveillé (ex. : Produit, Vente, Achat, Paiment, …)

  • Type d’événement : le type d’action à notifier (Create, Update, Delete)

  • URL de rappel (Callback URL) : l’adresse HTTP vers laquelle le webhook sera envoyé

Corps de la requête / Webhook body

Lorsqu’un événement se produit, PHPReaction envoie une requête POST à votre URL de rappel, contenant un corps JSON ayant un jeton JWT contenant les informations de la requête.

Exemple

{
    "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IndlYmhvb2stdjEifQ.eyJpc3MiOiJQSFBSZWFjdGlvbiIsImF1ZCI6Imh0dHBzOi8vd2ViaG9vay5zaXRlLzhmNzYwNzEzLWRmYjktNDA2NC1hZTM1LTk0YWY2ZjAwYzI1MyIsInN1YiI6IlBIUFJlYWN0aW9uXFxFbnRpdHlcXE5vdGVCdW5kbGVcXE5vdGUiLCJzaG9ydG5hbWUiOiJub3RlcyIsImV2ZW50VHlwZSI6InBvc3RVcGRhdGUiLCJyZXNvdXJjZUlkIjoxLCJyZXNvdXJjZUlyaSI6Ii9vcGVuLWFwaS92My9ub3Rlcy8xIiwiZXZlbnRTdWJzY3JpYmVySWQiOjEsImV2ZW50U3Vic2NyaWJlcklyaSI6Ii9vcGVuLWFwaS92My9ldmVudF9zdWJzY3JpYmVycy8xIiwiaWF0IjoxNzYyODg5NzA0LCJuYmYiOjE3NjI4ODk3MDQsImV4cCI6MTc2Mjg5MDAwNCwidGVuYW50IjoibG9jYWwifQ.PrxF0AXcpkU1BZom34JclJ9HrrEL1rIg3xaM8szcOeHNEyMgMhZdD3G9fHcDCitawlTAZZYKjI91zJGMa-QTCMAioDid_BVyZbGPZ3JYlZWBklX6TVg9JvDfUkQygMfd87wi_JFp3REq9CBRUIo9Byx59gBVR9ZLfeD_zuPaLaO-8upoElfEGHkhhs-Kb_8ZhagF4eMGkH39arW-HJ-XcIrmdJZseTeAAixM655SJHVvwsPdloWKrUzz7VB8gjQaLFqeEvtRQJuYh8cI27TdI7idxtXY93gx_WuJeNJaUCKz57LEzUDN79xU2DlmeWr3nMCrDVbQHmt-cv8b2Z6AgQ"
}

Exemple du « payload » ou « claims » du jeton JWT décodé:

{
    "iss": "PHPReaction",
    "aud": "https://webhook.site/8f760713-dfb9-4064-ae35-94af6f00c253",
    "sub": "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",
    "iat": 1762889704,
    "nbf": 1762889704,
    "exp": 1762890004,
    "tenant": "local"
}

Détails des champs du jeton JWT

ChampDescriptionExampleExplication
issÉmetteur du webhook (nom de l’application ou URL)https://api.example.comString
subNom complet (FQCN) de l’entité concernéePHPReaction\Entity\NoteBundle\NoteString FQCN
audAudience (URL de callback)https://client.app/webhookString (URL)
shortnameNom court de l’entité dans l’APInotesString
eventTypeType d’évènement (slug)createString (create, update, delete)
resourceIdIdentifiant interne de la ressource42Integer
resourceIriIdentifiant API de la ressource/api/notes/42String IRI
eventSubscriberIdIdentifiant de l’EventSubscriber déclencheur3Integer
eventSubscriberIriRéférence API de l’EventSubscriber/api/event_subscribers/3String IRI
iatHorodatage d’émission du webhook (issued at)1761699183Integer (Unix timestamp)
nbfHorodatage “not before”1761699183Integer (Unix timestamp)
expDate d’expiration du JWT (5 min après iat)1761699483Integer (Unix timestamp)
jtiIdentifiant unique du JWT (anti-replay)550e8400-e29b-41d4-a716-446655440000String (UUID)
tenantTenant propriétaire des donnéesdemo1String

Les champs les plus utilisés pour le traitement sont :
shortname, eventType, resourceId, et resourceIri.

Sécurité et vérification de la signature

Tous les webhooks émis par PHPReaction utilisent un jeton JWT signé afin de garantir qu’ils proviennent bien du système et qu’ils n’ont pas été modifiés.

Vérifier la signature (exemple en PHP)

Lorsque vous recevez un webhook, il est fortement recommandé de valider sa signature afin de vérifier son origine.
Dans ce cas, le JWT offre une méthode simple et fiable pour vérifier la signature et extraire le payload.

Vous pouvez d’abord valider la version de l’API à l’aide de l’en-tête suivant :

  • X-Webhook-Version : version de l’API Webhook

Cela permet de détecter d’éventuels changements dans la structure du payload ou dans la requête elle-même.

Vérification et décodage du JWT

Voici un exemple en PHP montrant comment vérifier un JWT de webhook :

Cet exemple utilise https://github.com/firebase/php-jwt pour simplifier le décodage.

*Vous pouvez utiliser la bibliothèque JWT de votre choix.

*Vous pouvez également utiliser la classe WebhookSignatureHelper du projet phpcreation-sdk-php.

/**
 * @param string $jwt The JWT token
 * @param string $publicKey The Public Key (PEM format)
 * @return array The decoded payload or false if signature is invalid.
 */
public static function verify(string $jwt, string $publicKey): array|false
{
    try {
        $decoded = JWT::decode($jwt, new Key($publicKey, 'RS256'));
    } catch (\Throwable $ex) {
        return false;
    }
    
    // Cast decoded claims to array
    $claims = (array) $decoded;
    
    // Validate claims
    if ('PHPReaction' !== $claims['iss']) {
        return false;
    }
    
    if ('tenant' !== $claims['tenant']) {
        return false;
    }
    
    // (...) Any other private claim you want to validate
    
    return $claims;
}