Webhooks de paiement : le guide pour ne jamais perdre une transaction
Un webhook, c'est la seule source de vérité fiable pour savoir si un paiement a réussi. Le return_url ne l'est pas — un utilisateur peut manipuler l'URL, ou fermer son navigateur avant la redirection. Si votre code livre des produits sur la base d'un return_url, vous avez un bug de sécurité qui vous fera perdre de l'argent.
La règle absolue
On confirme une transaction quand — et seulement quand — un webhook signé est arrivé avec status=success. Point. Le reste est du signal UX.
Vérifier une signature HMAC-SHA256
Le webhook contient un header X-Aventopay-Signature qui est le HMAC-SHA256 du body brut, signé avec votre webhook_secret. Deux règles :
- 1. Signer le body BRUT — pas le body reparsé puis re-sérialisé (les espaces changent la signature)
- 2. Comparer en temps constant (crypto.timingSafeEqual en Node) — sinon vous êtes vulnérable aux timing attacks
L'idempotence en pratique
Aventopay retente en cas d'erreur, et le réseau lui-même peut dupliquer. Votre handler doit être idempotent. Le pattern simple : une table SQL avec la référence unique de la transaction en clé primaire, un INSERT ON DUPLICATE KEY UPDATE, et un skip si déjà présent.
Machine à états
Un paiement passe par plusieurs états, dans un ordre non garanti (les callbacks peuvent arriver dans le désordre). Prévoir explicitement les cas success, pending, cancelled — et documenter votre gestion pour votre équipe.
Tester en local
Utilisez un tunnel HTTPS temporaire (ngrok, cloudflared). Configurez callback_url avec l'URL du tunnel. Vous recevez les événements en temps réel sur votre localhost.
