Didacticiel : création d’un point de terminaison webhook à l’aide d’une URL de fonction Lambda - AWS Lambda

Didacticiel : création d’un point de terminaison webhook à l’aide d’une URL de fonction Lambda

Dans ce didacticiel, vous créez une URL de fonction Lambda pour implémenter un point de terminaison webhook. Un webhook est une communication légère pilotée par les événements qui envoie automatiquement des données entre applications via HTTP. Vous pouvez utiliser un webhook pour recevoir des mises à jour immédiates sur les événements survenant dans un autre système, par exemple lorsqu’un nouveau client s’inscrit sur un site Web, qu’un paiement est traité ou qu’un fichier est chargé.

Avec Lambda, les webhooks peuvent être implémentés à l’aide d’URL de fonction Lambda ou d’API Gateway. Les URL de fonction sont un bon choix pour les webhooks simples qui ne nécessitent pas de fonctionnalités comme l’autorisation avancée ou la validation des demandes.

Astuce

Si vous ne savez pas quelle est la solution la mieux adaptée à votre cas d’utilisation particulier, consultez Sélection d’une méthode pour invoquer votre fonction Lambda à l’aide d’une requête HTTP.

Prérequis

Pour terminer ce didacticiel, Python (version 3.8 ou ultérieure) ou Node.js (version 18 ou ultérieure) doit être installé sur votre machine locale.

Pour tester le point de terminaison à l’aide d’une requête HTTP, le didacticiel utilise curl, un outil de ligne de commande que vous pouvez utiliser pour transférer des données à l’aide de différents protocoles réseau. Reportez-vous à la documentation curl pour savoir comment installer l’outil si vous ne l’avez pas déjà.

Créer la fonction Lambda

Créez d’abord la fonction Lambda qui s’exécute lorsqu’une requête HTTP est envoyée à votre point de terminaison webhook. Dans cet exemple, l’application d’envoi envoie une mise à jour chaque fois qu’un paiement est soumis, et indique dans le corps de la requête HTTP si le paiement a été effectué avec succès. La fonction Lambda analyse la demande et agit en fonction du statut du paiement. Dans cet exemple, le code imprime simplement l’ID de commande pour le paiement, mais dans une application réelle, vous pouvez ajouter la commande à une base de données ou envoyer une notification.

La fonction implémente également la méthode d’authentification la plus couramment utilisée pour les webhooks, l’authentification des messages basée sur le hachage (HMAC). Avec cette méthode, les applications d’envoi et de réception partagent une clé secrète. L’application d’envoi utilise un algorithme de hachage pour générer une signature unique à l’aide de cette clé associée au contenu du message, et inclut la signature dans la demande de webhook sous forme d’en-tête HTTP. L’application réceptrice répète ensuite cette étape en générant la signature à l’aide de la clé secrète, et compare la valeur obtenue avec la signature envoyée dans l’en-tête de la demande. Si le résultat correspond, la demande est considérée comme légitime.

Créez la fonction à l’aide de la console Lambda avec l’environnement d'exécution Python ou Node.js.

Python
Créer la fonction Lambda
  1. Ouvrez la page Functions (Fonctions) de la console Lambda.

  2. Créez une fonction de type « Hello World » de base en procédant comme suit :

    1. Choisissez Créer une fonction.

    2. Sélectionnez Créer à partir de zéro.

    3. Sous Nom de la fonction, saisissez myLambdaWebhook.

    4. Pour l’environnement d’exécution, sélectionnez python3.13.

    5. Choisissez Créer une fonction.

  3. Dans le volet Source du code, remplacez le code existant en copiant et en collant ce qui suit :

    import json import hmac import hashlib import os def lambda_handler(event, context): # Get the webhook secret from environment variables webhook_secret = os.environ['WEBHOOK_SECRET'] # Verify the webhook signature if not verify_signature(event, webhook_secret): return { 'statusCode': 401, 'body': json.dumps({'error': 'Invalid signature'}) } try: # Parse the webhook payload payload = json.loads(event['body']) # Handle different event types event_type = payload.get('type') if event_type == 'payment.success': # Handle successful payment order_id = payload.get('orderId') print(f"Processing successful payment for order {order_id}") # Add your business logic here # For example, update database, send notifications, etc. elif event_type == 'payment.failed': # Handle failed payment order_id = payload.get('orderId') print(f"Processing failed payment for order {order_id}") # Add your business logic here else: print(f"Received unhandled event type: {event_type}") # Return success response return { 'statusCode': 200, 'body': json.dumps({'received': True}) } except json.JSONDecodeError: return { 'statusCode': 400, 'body': json.dumps({'error': 'Invalid JSON payload'}) } except Exception as e: print(f"Error processing webhook: {e}") return { 'statusCode': 500, 'body': json.dumps({'error': 'Internal server error'}) } def verify_signature(event, webhook_secret): """ Verify the webhook signature using HMAC """ try: # Get the signature from headers signature = event['headers'].get('x-webhook-signature') if not signature: print("Error: Missing webhook signature in headers") return False # Get the raw body (return an empty string if the body key doesn't exist) body = event.get('body', '') # Create HMAC using the secret key expected_signature = hmac.new( webhook_secret.encode('utf-8'), body.encode('utf-8'), hashlib.sha256 ).hexdigest() # Compare the expected signature with the received signature to authenticate the message is_valid = hmac.compare_digest(signature, expected_signature) if not is_valid: print(f"Error: Invalid signature. Received: {signature}, Expected: {expected_signature}") return False return True except Exception as e: print(f"Error verifying signature: {e}") return False
  4. Dans la section DÉPLOYER, choisissez Déployer pour mettre à jour le code de votre fonction.

Node.js
Créer la fonction Lambda
  1. Ouvrez la page Functions (Fonctions) de la console Lambda.

  2. Créez une fonction de type « Hello World » de base en procédant comme suit :

    1. Choisissez Créer une fonction.

    2. Sélectionnez Créer à partir de zéro.

    3. Sous Nom de la fonction, saisissez myLambdaWebhook.

    4. Pour Exécution, sélectionnez nodejs22.x.

    5. Choisissez Créer une fonction.

  3. Dans le volet Source du code, remplacez le code existant en copiant et en collant ce qui suit :

    import crypto from 'crypto'; export const handler = async (event, context) => { // Get the webhook secret from environment variables const webhookSecret = process.env.WEBHOOK_SECRET; // Verify the webhook signature if (!verifySignature(event, webhookSecret)) { return { statusCode: 401, body: JSON.stringify({ error: 'Invalid signature' }) }; } try { // Parse the webhook payload const payload = JSON.parse(event.body); // Handle different event types const eventType = payload.type; switch (eventType) { case 'payment.success': { // Handle successful payment const orderId = payload.orderId; console.log(`Processing successful payment for order ${orderId}`); // Add your business logic here // For example, update database, send notifications, etc. break; } case 'payment.failed': { // Handle failed payment const orderId = payload.orderId; console.log(`Processing failed payment for order ${orderId}`); // Add your business logic here break; } default: console.log(`Received unhandled event type: ${eventType}`); } // Return success response return { statusCode: 200, body: JSON.stringify({ received: true }) }; } catch (error) { if (error instanceof SyntaxError) { // Handle JSON parsing errors return { statusCode: 400, body: JSON.stringify({ error: 'Invalid JSON payload' }) }; } // Handle all other errors console.error('Error processing webhook:', error); return { statusCode: 500, body: JSON.stringify({ error: 'Internal server error' }) }; } }; // Verify the webhook signature using HMAC const verifySignature = (event, webhookSecret) => { try { // Get the signature from headers const signature = event.headers['x-webhook-signature']; if (!signature) { console.log('No signature found in headers:', event.headers); return false; } // Get the raw body (return an empty string if the body key doesn't exist) const body = event.body || ''; // Create HMAC using the secret key const hmac = crypto.createHmac('sha256', webhookSecret); const expectedSignature = hmac.update(body).digest('hex'); // Compare expected and received signatures const isValid = signature === expectedSignature; if (!isValid) { console.log(`Invalid signature. Received: ${signature}, Expected: ${expectedSignature}`); return false; } return true; } catch (error) { console.error('Error during signature verification:', error); return false; } };
  4. Dans la section DÉPLOYER, choisissez Déployer pour mettre à jour le code de votre fonction.

Créer la clé de secret

Pour que la fonction Lambda authentifie la demande de webhook, elle utilise une clé secrète qu’elle partage avec l’application appelante. Dans cet exemple, la clé est stockée dans une variable d’environnement. Dans une application de production, n’incluez pas d’informations sensibles telles que des mots de passe dans votre code de fonction. Créez plutôt un secret AWS Secrets Manager, puis utilisez l’extension Lambda AWS Parameters and Secrets pour récupérer vos informations d’identification dans votre fonction Lambda.

Créer et stocker la clé secrète du webhook
  1. Générez une longue chaîne aléatoire à l’aide d’un générateur de nombres aléatoires sécurisé par cryptographie. Vous pouvez utiliser les extraits de code suivants en Python ou Node.js pour générer et imprimer un secret de 32 caractères, ou utiliser votre propre méthode préférée.

    Python
    Exemple code pour générer un secret
    import secrets webhook_secret = secrets.token_urlsafe(32) print(webhook_secret)
    Node.js
    Exemple code pour générer un secret (format du module ES)
    import crypto from 'crypto'; let webhookSecret = crypto.randomBytes(32).toString('base64'); console.log(webhookSecret)
  2. Stockez la chaîne générée en tant que variable d’environnement pour votre fonction en procédant comme suit :

    1. Dans l’onglet Configuration de votre fonction, sélectionnez Variables d’environnement.

    2. Choisissez Modifier.

    3. Choisissez Ajouter une variable d’environnement.

    4. Pour Clé, saisissez WEBHOOK_SECRET, puis pour Valeur, saisissez le secret que vous avez généré à l’étape précédente.

    5. Choisissez Enregistrer.

Vous devrez réutiliser ce secret ultérieurement dans le didacticiel pour tester votre fonction. Veillez donc à le noter dès maintenant.

Créer le point de terminaison d’URL de fonction

Créez un point de terminaison pour votre webhook à l’aide d’une URL de fonction Lambda. Comme vous utilisez le type d’authentification NONE pour créer un point de terminaison avec accès public, toute personne disposant de l’URL peut invoquer votre fonction. Pour en savoir plus sur le contrôle de l’accès aux URL de fonction, consultez Contrôle d’accès aux URL de fonctions Lambda. Si vous avez besoin d’options d’authentification plus avancées pour votre webhook, pensez à utiliser API Gateway.

Créer le point de terminaison d’URL de fonction
  1. Dans l’onglet Configuration de votre fonction, sélectionnez URL de la fonction.

  2. Choisissez Create function URL (Créer une URL de fonction).

  3. Pour Type d’authentification, sélectionnez NONE.

  4. Choisissez Enregistrer.

Le point de terminaison de l’URL de fonction que vous venez de créer s’affiche dans le volet URL de fonction. Copiez le point de terminaison ; vous l’utiliserez ultérieurement dans le didacticiel.

Tester la fonction dans console

Avant d’utiliser une requête HTTP pour invoquer votre fonction à l’aide du point de terminaison d’URL, testez-la dans la console pour vérifier que votre code fonctionne comme prévu.

Pour vérifier la fonction dans la console, vous devez d’abord calculer une signature de webhook à l’aide du secret que vous avez généré plus tôt dans le didacticiel avec les données utiles JSON de test suivantes :

{ "type": "payment.success", "orderId": "1234", "amount": "99.99" }

Utilisez l’un des exemples de code Python ou Node.js suivants pour calculer la signature du webhook à l’aide de votre propre secret.

Python
Calculer la signature du webhook
  1. Enregistrez le code suivant dans un fichier nommé calculate_signature.py. Remplacez le secret de webhook dans le code par votre propre valeur.

    import secrets import hmac import json import hashlib webhook_secret = "arlbSDCP86n_1H90s0fL_Qb2NAHBIBQOyGI0X4Zay4M" body = json.dumps({"type": "payment.success", "orderId": "1234", "amount": "99.99"}) signature = hmac.new( webhook_secret.encode('utf-8'), body.encode('utf-8'), hashlib.sha256 ).hexdigest() print(signature)
  2. Calculez la signature en exécutant la commande suivante à partir du répertoire où vous avez enregistré le code. Copiez la signature émise par le code.

    python calculate_signature.py
Node.js
Calculer la signature du webhook
  1. Enregistrez le code suivant dans un fichier nommé calculate_signature.mjs. Remplacez le secret de webhook dans le code par votre propre valeur.

    import crypto from 'crypto'; const webhookSecret = "arlbSDCP86n_1H90s0fL_Qb2NAHBIBQOyGI0X4Zay4M" const body = "{\"type\": \"payment.success\", \"orderId\": \"1234\", \"amount\": \"99.99\"}"; let hmac = crypto.createHmac('sha256', webhookSecret); let signature = hmac.update(body).digest('hex'); console.log(signature);
  2. Calculez la signature en exécutant la commande suivante à partir du répertoire où vous avez enregistré le code. Copiez la signature émise par le code.

    node calculate_signature.mjs

Vous pouvez désormais tester le code de votre fonction à l’aide d’une requête HTTP de test dans la console.

Tester la fonction dans console
  1. Sélectionnez l’onglet Code correspondant à votre fonction.

  2. Dans la section ÉVÉNEMENTS DE TEST, choisissez Créer un nouvel événement de test.

  3. Dans Event Name (Nom de l'événement), saisissez myEvent.

  4. Remplacez le JSON existant en copiant et en collant ce qui suit dans le volet JSON d’événement. Remplacez la signature de webhook par la valeur calculée à l’étape précédente.

    { "headers": { "Content-Type": "application/json", "x-webhook-signature": "2d672e7a0423fab740fbc040e801d1241f2df32d2ffd8989617a599486553e2a" }, "body": "{\"type\": \"payment.success\", \"orderId\": \"1234\", \"amount\": \"99.99\"}" }
  5. Choisissez Enregistrer.

  6. Sélectionnez Invoke.

    Vous devez voir des résultats similaires à ce qui suit :

    Python
    Status: Succeeded Test Event Name: myEvent Response: { "statusCode": 200, "body": "{\"received\": true}" } Function Logs: START RequestId: 50cc0788-d70e-453a-9a22-ceaa210e8ac6 Version: $LATEST Processing successful payment for order 1234 END RequestId: 50cc0788-d70e-453a-9a22-ceaa210e8ac6 REPORT RequestId: 50cc0788-d70e-453a-9a22-ceaa210e8ac6 Duration: 1.55 ms Billed Duration: 2 ms Memory Size: 128 MB Max Memory Used: 36 MB Init Duration: 136.32 ms
    Node.js
    Status: Succeeded Test Event Name: myEvent Response: { "statusCode": 200, "body": "{\"received\":true}" } Function Logs: START RequestId: e54fe6c7-1df9-4f05-a4c4-0f71cacd64f4 Version: $LATEST 2025-01-10T18:05:42.062Z e54fe6c7-1df9-4f05-a4c4-0f71cacd64f4 INFO Processing successful payment for order 1234 END RequestId: e54fe6c7-1df9-4f05-a4c4-0f71cacd64f4 REPORT RequestId: e54fe6c7-1df9-4f05-a4c4-0f71cacd64f4 Duration: 60.10 ms Billed Duration: 61 ms Memory Size: 128 MB Max Memory Used: 72 MB Init Duration: 174.46 ms Request ID: e54fe6c7-1df9-4f05-a4c4-0f71cacd64f4

Tester la fonction avec une requête HTTP

Utilisez l’outil de ligne de commande curl pour tester votre point de terminaison webhook.

Tester la fonction avec des requêtes HTTP
  1. Dans un terminal ou un programme shell, exécutez la commande curl suivante. Remplacez l’URL par la valeur du point de terminaison de votre URL de fonction et remplacez la signature du webhook par la signature que vous avez calculée à l’aide de votre propre clé secrète.

    curl -X POST https://ryqgmbx5xjzxahif6frvzikpre0bpvpf.lambda-url.us-west-2.on.aws/ \ -H "Content-Type: application/json" \ -H "x-webhook-signature: d5f52b76ffba65ff60ea73da67bdf1fc5825d4db56b5d3ffa0b64b7cb85ef48b" \ -d '{"type": "payment.success", "orderId": "1234", "amount": "99.99"}'

    Vous devriez voir la sortie suivante :

    {"received": true}
  2. Consultez les journaux CloudWatch de votre fonction pour vérifier qu’elle a correctement analysé les données utiles en procédant comme suit :

    1. Dans la console Amazon CloudWatch, ouvrez la page Groupe de journaux.

    2. Sélectionnez le groupe de journaux de votre fonction (/aws/lambda/myLambdaWebhook).

    3. Sélectionnez le flux de journaux le plus récent.

      Vous devez voir des résultats similaires à ce qui suit dans les journaux de votre fonction :

      Python
      Processing successful payment for order 1234
      Node.js
      2025-01-10T18:05:42.062Z e54fe6c7-1df9-4f05-a4c4-0f71cacd64f4 INFO Processing successful payment for order 1234
  3. Confirmez que votre code détecte une signature non valide en exécutant la commande curl suivante. Remplacez l’URL par le point de terminaison de l’URL de votre propre fonction.

    curl -X POST https://ryqgmbx5xjzxahif6frvzikpre0bpvpf.lambda-url.us-west-2.on.aws/ \ -H "Content-Type: application/json" \ -H "x-webhook-signature: abcdefg" \ -d '{"type": "payment.success", "orderId": "1234", "amount": "99.99"}'

    Vous devriez voir la sortie suivante :

    {"error": "Invalid signature"}

Nettoyage de vos ressources

Vous pouvez maintenant supprimer les ressources que vous avez créées pour ce didacticiel, sauf si vous souhaitez les conserver. En supprimant des ressources AWS que vous n’utilisez plus, vous évitez les frais superflus pour votre Compte AWS.

Pour supprimer la fonction Lambda
  1. Ouvrez la page Functions (Fonctions) de la console Lambda.

  2. Sélectionnez la fonction que vous avez créée.

  3. Choisissez Actions, Delete.

  4. Saisissez confirm dans la zone de saisie de texte et choisissez Delete (Supprimer).

Lorsque vous avez créé la fonction Lambda dans la console, Lambda a également créé un rôle d’exécution pour votre fonction.

Pour supprimer le rôle d’exécution
  1. Ouvrez la page Roles (Rôles) de la console IAM.

  2. Sélectionnez le rôle d’exécution que Lambda a créé. Le nom du rôle est au format myLambdaWebhook-role-<random string>.

  3. Sélectionnez Delete (Supprimer).

  4. Saisissez le nom du rôle dans le champ de saisie de texte et choisissez Delete (Supprimer).