Se rendre au contenu

Consommer des webhooks dans Odoo 19 : le guide pratique

par
Pierre

Plutôt que d'interroger sans cesse vos services externes, laissez-les vous prévenir. Les webhooks permettent à Odoo 19 de réagir aux événements en temps réel — paiement validé, colis expédié, lead créé — sans cron ni polling. Voici comment les consommer proprement, et en toute sécurité.

Quand une passerelle de paiement confirme une transaction ou qu'un transporteur met à jour un suivi, vous avez deux options. Soit vous interrogez leur API toutes les quelques minutes en espérant tomber sur la bonne fenêtre, soit vous les laissez pousser l'information vers Odoo dès que l'événement se produit. La seconde approche, c'est le webhook : un simple appel HTTP entrant qui transforme votre instance en système réactif. Ce guide montre comment l'implémenter dans Odoo 19, du contrôleur de base jusqu'aux bonnes pratiques de production.

Webhook ou polling : pourquoi ça change tout

Un webhook n'est rien d'autre qu'une URL de votre application que des systèmes tiers appellent en POST lorsqu'un événement survient. Le corps de la requête contient les données de l'événement : identifiant de paiement, statut, numéro de commande, etc.

Comparé au polling via un cron, l'écart est net. Le polling introduit une latence (vous découvrez l'événement à la prochaine exécution planifiée), consomme des appels API inutiles la plupart du temps pour rien, et alourdit votre serveur. Le webhook, lui, délivre l'information instantanément, sans requête superflue. Pour des flux critiques comme l'encaissement ou l'expédition, cette réactivité fait la différence entre un client informé en quelques secondes et un client qui attend.

Créer un endpoint de réception dans Odoo 19

Consommer un webhook revient à exposer un contrôleur HTTP qui accepte les requêtes POST entrantes. Voici un exemple minimal qui reçoit un statut de paiement et met à jour la commande correspondante.

from odoo import http
from odoo.http import request


class PaymentWebhookController(http.Controller):

    @http.route('/webhook/paiement', type='json',
                auth='public', methods=['POST'], csrf=False)
    def recevoir_paiement(self, **kwargs):
        data = request.get_json_data()
        reference = data.get('payment_reference')
        statut = data.get('status')

        commande = request.env['sale.order'].sudo().search(
            [('x_payment_ref', '=', reference)], limit=1)
        if commande and statut == 'paid':
            commande.write({'state': 'sale'})
        return {'result': 'ok'}

Trois points méritent attention dans cette signature de route. Le type json indique qu'Odoo doit interpréter le corps comme du JSON et renvoyer une réponse JSON. L'authentification public rend l'endpoint accessible sans session utilisateur — indispensable puisque le système appelant n'a pas de compte Odoo. Enfin, sudo() permet d'écrire dans les enregistrements malgré l'absence d'utilisateur authentifié ; à utiliser avec discernement.

Désactiver la protection CSRF

Le paramètre csrf=False n'est pas optionnel ici. La protection anti-CSRF d'Odoo attend un jeton généré par un formulaire de l'application. Or un webhook provient d'un système externe qui ne dispose d'aucun jeton. Sans cette désactivation, Odoo rejettera systématiquement la requête. En contrepartie, vous devez sécuriser l'endpoint autrement, comme expliqué plus bas.

Lire et valider la charge utile

Selon l'émetteur, la donnée arrive sous forme de JSON structuré ou de payload brut. Dans Odoo 19, privilégiez request.get_json_data() pour récupérer un dictionnaire exploitable. Si vous devez accéder au corps brut — par exemple pour vérifier une signature calculée sur les octets exacts — utilisez request.httprequest.data.

Quelle que soit la source, ne faites jamais confiance aveuglément aux données reçues. Vérifiez la présence des champs attendus, contrôlez les types, et rejetez proprement une requête malformée plutôt que de laisser une exception remonter. Un webhook robuste répond toujours, même pour signaler une erreur.

Sécuriser un endpoint public

C'est le point le plus souvent négligé. Votre URL étant publique, n'importe qui peut tenter d'y envoyer des requêtes falsifiées. Trois lignes de défense sont à combiner.

La première est le secret partagé. L'émetteur envoie une clé dans un en-tête HTTP que vous comparez à une valeur connue.

secret = request.httprequest.headers.get('X-Webhook-Secret')
if secret != request.env['ir.config_parameter'].sudo()\
        .get_param('aldensync.webhook_secret'):
    return {'error': 'non autorise'}

Notez que le secret est stocké dans les paramètres système plutôt qu'en dur dans le code — une pratique élémentaire mais cruciale. La deuxième ligne est la vérification de signature : les passerelles sérieuses (Stripe, par exemple) signent leur payload avec HMAC, ce qui garantit à la fois l'authenticité et l'intégrité. La troisième est le filtrage par IP au niveau du reverse proxy, lorsque l'émetteur publie une plage d'adresses stable.

Ne pas surcharger la requête : déléguer le traitement

Une erreur classique consiste à exécuter un traitement lourd directement dans le webhook : génération de documents, appels en cascade vers d'autres API, recalculs massifs. Si le traitement dépasse le délai d'attente de l'émetteur, celui-ci considère l'appel comme échoué et le rejoue — vous voilà avec des doublons et une charge qui s'emballe.

La bonne pratique est de répondre vite et de différer le travail. Enregistrez le payload dans une file ou un modèle tampon, retournez immédiatement un succès, puis traitez en arrière-plan via un cron ou un système de file d'attente.

def recevoir_paiement(self, **kwargs):
    request.env['aldensync.webhook.queue'].sudo().create({
        'payload': json.dumps(request.get_json_data()),
        'received_at': fields.Datetime.now(),
    })
    return {'result': 'queued'}

L'émetteur reçoit sa confirmation en quelques millisecondes, et votre logique métier s'exécute tranquillement, avec la possibilité de rejouer un enregistrement en cas d'échec sans perdre de données.

Journaliser pour déboguer

Les incidents de webhook sont difficiles à diagnostiquer puisqu'ils naissent à l'extérieur de votre système. Tracez systématiquement les requêtes entrantes : un simple _logger.info("Webhook reçu : %s", data) vous fera gagner des heures lorsqu'un partenaire modifiera son format sans prévenir. Conservez de quoi reconstituer ce qui est arrivé, quand, et comment vous y avez répondu.

Conclusion

Bien implémentés, les webhooks rendent Odoo 19 plus rapide, plus léger et nativement connecté à votre écosystème : confirmations de paiement, mises à jour de commandes, synchronisation de stock ou création de leads CRM. Mais leur nature publique impose une discipline : validation des données, sécurité par secret ou signature, traitement différé et journalisation. Ces quatre réflexes séparent un endpoint de démonstration d'un endpoint de production.

Chez AldenSync, nous concevons et opérons ces intégrations temps réel pour des PME industrielles, du contrôleur Odoo jusqu'à la supervision de bout en bout. Discutons de vos flux et mettons en place des webhooks fiables, sécurisés et durables.