

# Tutorial: Creación de un punto de conexión de webhook mediante la URL de una función de Lambda
<a name="urls-webhook-tutorial"></a>

En este tutorial, creará la URL de una función de Lambda para implementar un punto de conexión de webhook. Un webhook es una comunicación ligera y basada en eventos que envía automáticamente datos entre aplicaciones mediante HTTP. Puede usar un webhook para recibir actualizaciones inmediatas sobre los eventos que ocurren en otro sistema, como cuando un nuevo cliente se registra en un sitio web, se procesa un pago o se carga un archivo.

Con Lambda, los webhooks se pueden implementar mediante URL de funciones de Lambda o API Gateway. Las URL de funciones son una buena opción para los webhooks sencillos que no requieren características como la autorización avanzada o la validación de solicitudes.

**sugerencia**  
Si no está seguro de cuál es el mejor método para su caso específico, consulte [Selección de un método para invocar una función de Lambda mediante una solicitud HTTP](furls-http-invoke-decision.md).

## Requisitos previos
<a name="urls-webhook-tutorial-prereqs"></a>

Para completar este tutorial, debe tener Python (versión 3.8 o posterior) o Node.js (versión 18 o posterior) instalados en su máquina local.

Para probar el punto de conexión mediante una solicitud HTTP, el tutorial utiliza [curl](https://curl.se/), una herramienta de línea de comandos que puede utilizar para transferir datos mediante varios protocolos de red. Consulte la [Documentación de curl](https://curl.se/docs/install.html) para obtener información sobre cómo instalar la herramienta si aún no la tiene.

## Crear la función de Lambda
<a name="urls-webhook-tutorial-function"></a>

Primero cree la función de Lambda que se ejecuta cuando se envía una solicitud HTTP a su punto de conexión de webhook. En este ejemplo, la aplicación remitente envía una actualización cada vez que se envía un pago e indica en el cuerpo de la solicitud HTTP si el pago se ha realizado correctamente. La función de Lambda analiza la solicitud y actúa de acuerdo con el estado del pago. En este ejemplo, el código solo imprime el ID del pedido para el pago, pero en una aplicación real, puede añadir el pedido a una base de datos o enviar una notificación.

La función también implementa el método de autenticación más común utilizado para los webhooks, la autenticación de mensajes basada en hash (HMAC). Con este método, tanto las aplicaciones de envío como las de recepción comparten una clave secreta. La aplicación de envío utiliza un algoritmo de hash para generar una firma única utilizando esta clave junto con el contenido del mensaje, e incluye la firma en la solicitud de webhook como un encabezado HTTP. Luego, la aplicación receptora repite este paso, genera la firma con la clave secreta y compara el valor resultante con la firma enviada en el encabezado de la solicitud. Si el resultado coincide, la solicitud se considera legítima. 

Cree la función mediante la consola de Lambda con el tiempo de ejecución de Python o Node.js.

------
#### [ Python ]

**Crear la función de Lambda**

1. Abra la [página de Funciones](https://console.aws.amazon.com/lambda/home#/functions) en la consola de Lambda.

1. Cree una función básica “Hello world” de la siguiente manera:

   1. Elija **Crear función**.

   1. Seleccione **Crear desde cero**.

   1. En **Nombre de la función**, introduzca **myLambdaWebhook**.

   1. En **Tiempo de ejecución**, seleccione **python 3.14**.

   1. Seleccione **Creación de función**.

1. En el panel **Código fuente**, sustituya el código existente al copiar y pegar lo siguiente:

   ```
   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
   ```

1. En la sección **IMPLEMENTAR**, elija **Implementar** para actualizar el código de la función.

------
#### [ Node.js ]

**Crear la función de Lambda**

1. Abra la página de [Funciones](https://console.aws.amazon.com/lambda/home#/functions) en la consola de Lambda.

1. Cree una función básica “Hello world” de la siguiente manera:

   1. Seleccione **Creación de función**.

   1. Seleccione **Crear desde cero**.

   1. En **Nombre de la función**, introduzca **myLambdaWebhook**.

   1. En **Tiempo de ejecución**, seleccione **nodejs24.x**.

   1. Seleccione **Creación de función**.

1. En el panel **Código fuente**, sustituya el código existente al copiar y pegar lo siguiente:

   ```
   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;
       }
     };
   ```

1. En la sección **IMPLEMENTAR**, elija **Implementar** para actualizar el código de la función.

------

## Crear la clave secreta
<a name="urls-webhook-tutorial-key"></a>

Para que la función de Lambda autentique la solicitud de webhook, utiliza una clave secreta que comparte con la aplicación que realiza la llamada. En este ejemplo, la clave se almacena en una variable del entorno. En aplicaciones de producción, no incluya información confidencial como contraseñas en el código de la función. En su lugar, [cree un secreto de AWS Secrets Manager](https://docs.aws.amazon.com/secretsmanager/latest/userguide/create_secret.html) y, a continuación, [utilice la extensión de Lambda AWS Parameters and Secrets](with-secrets-manager.md) para recuperar las credenciales en su función de Lambda.

**Crear y almacenar la clave secreta del webhook**

1. Genere una cadena larga y aleatoria utilizando un generador de números aleatorios criptográficamente seguro. Puede usar los siguientes fragmentos de código en Python o Node.js para generar e imprimir un secreto de 32 caracteres, o usar el método que prefiera.

------
#### [ Python ]

**Example código para generar un secreto**  

   ```
   import secrets
   webhook_secret = secrets.token_urlsafe(32)
   print(webhook_secret)
   ```

------
#### [ Node.js ]

**Example código para generar un secreto (formato de módulo ES)**  

   ```
   import crypto from 'crypto';
   let webhookSecret = crypto.randomBytes(32).toString('base64');
   console.log(webhookSecret)
   ```

------

1. Guarde la cadena generada como una variable de entorno para su función de la siguiente manera:

   1. En la sección **Configuración** de su función, seleccione **Variables de entorno**.

   1. Seleccione **Editar**.

   1. Elija **Add environment variable** (Añadir variable de entorno).

   1. En **Clave**, ingrese **WEBHOOK\$1SECRET**, y en **Valor**, ingrese el secreto que generó en el paso anterior.

   1. Seleccione **Save**.

Tome nota de este secreto ahora, ya que tendrá que usarlo más adelante en el tutorial para probar la función.

## Crear el punto de conexión de la URL de función
<a name="urls-webhook-tutorial-furl"></a>

Cree un punto de conexión de webhook mediante la URL de una función de Lambda. Dado que utiliza el tipo de autenticación `NONE` para crear un punto de conexión con acceso público, cualquier persona que disponga de la URL puede invocar su función. Para obtener más información sobre cómo controlar el acceso a las URL de las funciones, consulte [Control de acceso a las URL de las funciones de Lambda](urls-auth.md). Si necesita opciones de autenticación más avanzadas para su webhook, considere usar API Gateway.

**Crear el punto de conexión de la URL de función**

1. En la pestaña **Configuración** de su función, seleccione **URL de función**.

1. Elija **Create function URL** (Crear URL de función).

1. Para el **Tipo de autenticación**, seleccione **NINGUNO**.

1. Seleccione **Save**.

El punto de conexión de la URL de función que acaba de crear aparece en el panel **URL de función**. Copie el punto de conexión para usarlo más adelante en el tutorial.

## Probar la función en la consola
<a name="urls-webhook-tutorial-test-console"></a>

Antes de utilizar una solicitud HTTP para invocar la función mediante el punto de conexión de la URL, pruébela en la consola para confirmar que el código funciona según lo previsto.

Para verificar la función en la consola, primero debe calcular la firma de un webhook utilizando el secreto que generó anteriormente en el tutorial con la siguiente carga útil de JSON de prueba:

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

Use uno de los siguientes ejemplos de código de Python o Node.js para calcular la firma del webhook con su propio secreto.

------
#### [ Python ]

**Calcular la firma del webhook**

1. Guarde el siguiente código en un archivo denominado `calculate_signature.py`. Reemplace el secreto del webhook en el código por su propio valor.

   ```
   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)
   ```

1. Calcule la firma mediante la ejecución del siguiente comando desde el directorio donde haya guardado el código. Copie la firma que genera el código.

   ```
   python calculate_signature.py
   ```

------
#### [ Node.js ]

**Calcular la firma del webhook**

1. Guarde el siguiente código en un archivo denominado `calculate_signature.mjs`. Reemplace el secreto del webhook en el código por su propio valor.

   ```
   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);
   ```

1. Calcule la firma mediante la ejecución del siguiente comando desde el directorio donde haya guardado el código. Copie la firma que genera el código.

   ```
   node calculate_signature.mjs
   ```

------

Ahora puede probar el código de función mediante una solicitud HTTP de prueba en la consola.

**Probar la función en la consola**

1. Seleccione la pestaña **Código** para su función.

1. En la sección **EVENTOS DE PRUEBA**, elija **Crear nuevo evento de prueba**.

1. Para **Event name (Nombre de evento)**, escriba **myEvent**.

1. En el panel **Evento JSON**, sustituya el código JSON existente al copiar y pegar lo siguiente. Reemplace la firma del webhook por el valor que calculó en el paso anterior.

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

1. Seleccione **Save**.

1. Elija **Invoke (Invocar)**.

   Debería ver una salida similar a esta:

------
#### [ 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
   ```

------

## Probar la función mediante una solicitud HTTP
<a name="urls-webhook-tutorial-test-curl"></a>

Use la herramienta de línea de comandos curl para probar su punto de conexión de webhook.

**Probar la función mediante solicitudes HTTP**

1. En un programa de terminal o intérprete de comandos, ejecute el siguiente comando curl. Sustituya la URL por el valor del punto de conexión de la URL de función y sustituya la firma del webhook por la firma que calculó con su propia clave secreta.

   ```
   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"}'
   ```

   Debería ver los siguientes datos de salida:

   ```
   {"received": true}
   ```

1. Inspeccione los registros de CloudWatch de su función para confirmar que analizó la carga útil correctamente de la siguiente manera:

   1. En la consola de Amazon CloudWatch, abra la página [Grupos de registro](https://console.aws.amazon.com/cloudwatch/home#logsV2:log-groups).

   1. Seleccione el grupo de registro de la función (`/aws/lambda/myLambdaWebhook`).

   1. Seleccione el flujo de registros más reciente.

      Debería ver una salida similar a la siguiente en los registros de su función:

------
#### [ 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
      ```

------

1. Ejecute el siguiente comando curl para confirmar que el código detecta una firma no válida. Sustituya la URL por el punto de conexión de su propia URL de función.

   ```
   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"}'
   ```

   Debería ver los siguientes datos de salida:

   ```
   {"error": "Invalid signature"}
   ```

## Eliminación de sus recursos
<a name="urls-webhook-tutorial-cleanup"></a>

A menos que desee conservar los recursos que creó para este tutorial, puede eliminarlos ahora. Si elimina los recursos de AWS que ya no utiliza, evitará gastos innecesarios en su Cuenta de AWS.

**Cómo eliminar la función de Lambda**

1. Abra la [página de Funciones](https://console.aws.amazon.com/lambda/home#/functions) en la consola de Lambda.

1. Seleccione la función que ha creado.

1. Elija **Acciones**, **Eliminar**.

1. Escriba **confirm** en el campo de entrada de texto y elija **Delete**(Eliminar).

Cuando creó una función de Lambda en la consola, Lambda también creó un [rol de ejecución](lambda-intro-execution-role.md) para la función.

**Cómo eliminar el rol de ejecución**

1. Abra la página [Roles](https://console.aws.amazon.com/iam/home#/roles) en la consola de IAM.

1. Seleccione el rol de ejecución que creó Lambda. El rol tiene el formato de nombre `myLambdaWebhook-role-<random string>`.

1. Elija **Eliminar**.

1. Si desea continuar, escriba el nombre del rol en el campo de entrada de texto y elija **Delete** (Eliminar).