
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.activateWebHooking | 1 |
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
| Champ | Description | Example | Explication |
|---|---|---|---|
| iss | Émetteur du webhook (nom de l’application ou URL) | https://api.example.com | String |
| sub | Nom complet (FQCN) de l’entité concernée | PHPReaction\Entity\NoteBundle\Note | String FQCN |
| aud | Audience (URL de callback) | https://client.app/webhook | String (URL) |
| shortname | Nom court de l’entité dans l’API | notes | String |
| eventType | Type d’évènement (slug) | create | String (create, update, delete) |
| resourceId | Identifiant interne de la ressource | 42 | Integer |
| resourceIri | Identifiant API de la ressource | /api/notes/42 | String IRI |
| eventSubscriberId | Identifiant de l’EventSubscriber déclencheur | 3 | Integer |
| eventSubscriberIri | Référence API de l’EventSubscriber | /api/event_subscribers/3 | String IRI |
| iat | Horodatage d’émission du webhook (issued at) | 1761699183 | Integer (Unix timestamp) |
| nbf | Horodatage “not before” | 1761699183 | Integer (Unix timestamp) |
| exp | Date d’expiration du JWT (5 min après iat) | 1761699483 | Integer (Unix timestamp) |
| jti | Identifiant unique du JWT (anti-replay) | 550e8400-e29b-41d4-a716-446655440000 | String (UUID) |
| tenant | Tenant propriétaire des données | demo1 | String |
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;
}