Aller au contenu

Prévenir un autre système

Un événement se produit chez vous et un autre logiciel doit le savoir : une facture est payée, un emprunt est en retard. Vous n’écrivez ni la boucle d’envoi, ni la signature, ni les réessais.

Un abonnement

from kewos.webhooks import register_subscription
register_subscription(
engine,
url="https://exemple.test/hook",
events=("loan.overdue",), # () = tous les événements
secret=secret, # partagé avec le receveur
)

Puis, quand ça arrive :

dispatcher.dispatch_event(event_name="loan.overdue", payload={"loan_id": 19, "days_late": 3})

Le noyau parcourt les abonnements actifs dont la liste d’événements correspond, signe le corps avec le secret de chacun, et poste.

Ce qui arrive chez le receveur

Exécuté contre un vrai serveur HTTP :

POST /hook
{"event":"loan.overdue","payload":{"days_late":3,"loan_id":19}}
X-Kewos-Event: loan.overdue
X-Kewos-Signature: fde0817d51fd55e21c613193...
X-Kewos-Delivery-Id: 9e01348d23e44f27b6b12f38...
  • X-Kewos-Event évite au receveur d’ouvrir le corps pour router.

  • X-Kewos-Signature est un HMAC SHA-256 du corps, sous le secret de l’abonnement. Le receveur le recalcule et compare :

    from kewos.webhooks import verify_signature
    verify_signature(corps, signature=signature, secret=secret) # True
    verify_signature(corps, signature=signature, secret="autre") # False

    La comparaison est à temps constant. Le noyau n’a qu’une implémentation HMAC, utilisée pour signer et pour vérifier : un désaccord entre les deux côtés est impossible.

  • X-Kewos-Delivery-Id est frappé une fois par envoi et partagé par tous les receveurs du même événement. Il sert deux fois : corréler un éventail d’envois côté exploitation, et dédupliquer un réessai côté receveur (même identifiant = même tentative).

Ce qui se passe quand ça rate

Le receveur est en panne, lent, ou répond 500. Trois mécanismes, aucun à écrire :

  1. Une ligne par tentative dans kws_webhook_delivery : statut, nombre d’essais, prochaine date de réessai. Un envoi n’est jamais un événement perdu dans un journal.
  2. Un rattrapage espacé : 1 min, 5 min, 30 min, 2 h, 12 h, 24 h. Un service qui redémarre récupère son événement ; un service mort ne noie pas le vôtre sous les tentatives.
  3. Une désactivation automatique au-delà du seuil d’échecs, avec un événement émis pour que l’application prévienne l’administrateur du tenant. Un abonnement mort qu’on continue d’appeler indéfiniment est une fuite de ressources, et un silence.

Et un rejeu : renvoyer une livraison enregistrée, resignée à la volée, depuis l’interface. C’est la réponse à « je n’ai rien reçu » qui ne demande pas de rejouer l’opération métier.