

# Tutorial: criar um endpoint de webhook usando um URL de função do Lambda
<a name="urls-webhook-tutorial"></a>

Neste tutorial, você cria um URL de função do Lambda para implementar um endpoint de webhook. Um webhook é uma comunicação leve e orientada por eventos que envia dados automaticamente entre aplicações usando HTTP. Você pode usar um webhook para receber atualizações imediatas sobre eventos que acontecem em outro sistema, como quando um novo cliente se cadastra em um site, um pagamento é processado ou um arquivo é carregado.

Com o Lambda, os webhooks podem ser implementados usando URLs de funções do Lambda ou API Gateway. URLs de funções são uma boa opção para webhooks simples que não exigem atributos como autorização avançada ou validação de solicitações.

**dica**  
Se não tiver certeza de qual é a melhor solução para seu caso de uso, consulte [Seleção de um método para invocar a função do Lambda usando uma solicitação HTTP](furls-http-invoke-decision.md).

## Pré-requisitos
<a name="urls-webhook-tutorial-prereqs"></a>

Para concluir este tutorial, você deve ter Python (versão 3.8 ou posterior) ou Node.js (versão 18 ou posterior) instalado em sua máquina local.

Para testar o endpoint usando uma solicitação HTTP, o tutorial usa [curl](https://curl.se/), uma ferramenta de linha de comando que você pode usar para transferir dados usando vários protocolos de rede. Consulte a [documentação do curl](https://curl.se/docs/install.html) para saber como instalar a ferramenta, caso ainda não o tenha.

## Criar a função do Lambda
<a name="urls-webhook-tutorial-function"></a>

Primeiro, crie a função do Lambda que será executada quando uma solicitação HTTP for enviada para seu endpoint de webhook. Neste exemplo, a aplicação de envio envia uma atualização sempre que um pagamento é enviado e indica no corpo da solicitação HTTP se o pagamento foi bem-sucedido. A função do Lambda analisa a solicitação e age de acordo com o status do pagamento. Neste exemplo, o código apenas imprime o ID do pedido para o pagamento, mas em uma aplicação real, você pode adicionar o pedido a um banco de dados ou enviar uma notificação.

A função também implementa o método de autenticação mais comum usado para webhooks, a autenticação de mensagens baseada em hash (HMAC). Com esse método, as aplicações de envio e recebimento compartilham uma chave secreta. A aplicação de envio usa um algoritmo de hash para gerar uma assinatura exclusiva usando essa chave junto com o conteúdo da mensagem e inclui a assinatura na solicitação do webhook como um cabeçalho HTTP. A aplicação recebedora então repete essa etapa, gerando a assinatura usando a chave secreta, e compara o valor resultante com a assinatura enviada no cabeçalho da solicitação. Se o resultado corresponder, a solicitação será considerada legítima. 

Crie a função usando o console do Lambda com o runtime do Python ou do Node.js.

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

**Criar a função do Lambda**

1. Abra a [página Funções](https://console.aws.amazon.com/lambda/home#/functions) do console do Lambda.

1. Crie uma função básica "Hello world" fazendo o seguinte:

   1. Escolha a opção **Criar função**.

   1. Selecione **Criar do zero**.

   1. Em **Function name** (Nome da função), insira **myLambdaWebhook**.

   1. Em **Runtime**, escolha **Python3.14**.

   1. Escolha a opção **Criar função**.

1. No painel **Código-fonte**, substitua o código existente copiando e colando o seguinte:

   ```
   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. Na seção **DEPLOY**, escolha **Implantar** para atualizar o código da função.

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

**Criar a função do Lambda**

1. Abra a [página Funções](https://console.aws.amazon.com/lambda/home#/functions) do console do Lambda.

1. Crie uma função básica "Hello world" fazendo o seguinte:

   1. Escolha a opção **Criar função**.

   1. Selecione **Criar do zero**.

   1. Em **Function name** (Nome da função), insira **myLambdaWebhook**.

   1. Em **Runtime**, selecione **nodejs24.x**.

   1. Escolha a opção **Criar função**.

1. No painel **Código-fonte**, substitua o código existente copiando e colando o seguinte:

   ```
   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. Na seção **DEPLOY**, escolha **Implantar** para atualizar o código da função.

------

## Criar a chave secreta
<a name="urls-webhook-tutorial-key"></a>

Para que a função do Lambda autentique a solicitação do webhook, ela usa uma chave secreta que compartilha com a aplicação de chamada. Neste exemplo, a chave é armazenada em um variável de ambiente. Não inclua informações confidenciais, como senhas, no código de função, em uma aplicação em produção. Em vez disso, [crie um segredo do AWS Secrets Manager](https://docs.aws.amazon.com/secretsmanager/latest/userguide/create_secret.html) e [use a extensão do Lambda AWS Parameters and Secrets](with-secrets-manager.md) para recuperar suas credenciais na função do Lambda.

**Criar e armazenar a chave secreta do webhook**

1. Gere uma string longa e aleatória usando um gerador de números aleatórios criptograficamente seguro. Você pode usar os seguintes trechos de código em Python ou Node.js para gerar e imprimir um segredo de 32 caracteres, ou usar seu próprio método preferido.

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

**Example código para gerar um segredo**  

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

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

**Example código para gerar um segredo (formato de módulo ES)**  

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

------

1. Armazene sua string gerada como uma variável de ambiente para sua função fazendo o seguinte:

   1. Na guia **Configurações** para sua função, selecione **Variáveis de ambiente**.

   1. Escolha **Editar**.

   1. Escolha **Add environment variable (Adicionar variável de ambiente)**.

   1. Para **Chave**, insira **WEBHOOK\$1SECRET**, e para **Valor**, insira o segredo que você gerou na etapa anterior.

   1. Escolha **Salvar**.

Você precisará usar esse segredo novamente em outro momento no tutorial para testar sua função, então anote-o agora.

## Criar o endpoint do URL da função
<a name="urls-webhook-tutorial-furl"></a>

Criar um endpoint para seu webhook usando um URL da função do Lambda. Como você usa o tipo de autenticação `NONE` para criar um endpoint com acesso público, qualquer pessoa com o URL pode invocar sua função. Para saber mais sobre como controlar o acesso aos URLs das funções, consulte [Controlar o acesso aos URLs de função do Lambda](urls-auth.md). Se você precisar de opções de autenticação mais avançadas para seu webhook, considere usar o API Gateway.

**Criar o endpoint do URL da função**

1. Na guia **Configuração** para sua função, selecione **URL da função**.

1. Escolha **Create function URL** (Criar URL de função).

1. Para **Tipo de autenticação**, selecione **NONE**.

1. Escolha **Salvar**.

O endpoint do URL da função que você acabou de criar é exibido no painel **URL da função**. Copie o endpoint para usar posteriormente no tutorial.

## Testar a função no console
<a name="urls-webhook-tutorial-test-console"></a>

Antes de usar uma solicitação HTTP para invocar sua função usando o endpoint do URL, teste-a no console para confirmar se o código está funcionando conforme o esperado.

Para verificar a função no console, primeiro calcule uma assinatura de webhook usando o segredo gerado anteriormente no tutorial com a seguinte carga útil JSON de teste:

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

Use um dos exemplos de código Python ou Node.js a seguir para calcular a assinatura do webhook usando seu próprio segredo.

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

**Calcular a assinatura do webhook**

1. Salve o código a seguir como um arquivo chamado `calculate_signature.py`. Substitua o segredo do webhook no código pelo seu próprio 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 a assinatura executando o comando a seguir no diretório onde você salvou o código. Copie a assinatura que o código gera.

   ```
   python calculate_signature.py
   ```

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

**Calcular a assinatura do webhook**

1. Salve o código a seguir como um arquivo chamado `calculate_signature.mjs`. Substitua o segredo do webhook no código pelo seu próprio 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 a assinatura executando o comando a seguir no diretório onde você salvou o código. Copie a assinatura que o código gera.

   ```
   node calculate_signature.mjs
   ```

------

Agora você pode testar o código da função usando uma solicitação HTTP de teste no console.

**Testar a função no console**

1. Selecione a guia **Code** para sua função.

1. Na seção **TEST EVENTS**, selecione **Create new test event**

1. Em **Event Name (Nome do evento)**, insira **myEvent**.

1. Substitua o JSON existente copiando e colando o seguinte no painel **JSON do evento**. Substitua a assinatura do webhook pelo valor calculado na etapa anterior.

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

1. Escolha **Salvar**.

1. Escolha **Invoke (Invocar)**.

   Você deve ver uma saída semelhante a:

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

------

## Testar a função usando uma solicitação HTTP
<a name="urls-webhook-tutorial-test-curl"></a>

Use a ferramenta de linha de comando curl para testar seu endpoint de webhook.

**Testar a função usando solicitações HTTP**

1. Em um terminal ou programa shell, execute o seguinte comando curl. Substitua o URL pelo valor do endpoint do URL da sua própria função e substitua a assinatura do webhook pela assinatura que você calculou usando sua própria chave 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"}'
   ```

   A seguinte saída deverá ser mostrada:

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

1. Inspecione os logs do CloudWatch da sua função para confirmar se ela analisou a carga útil corretamente, fazendo o seguinte:

   1. No console do Amazon CloudWatch, abra a página [Grupo de logs](https://console.aws.amazon.com/cloudwatch/home#logsV2:log-groups).

   1. Selecione o grupo de logs da função (`/aws/lambda/myLambdaWebhook`).

   1. Selecione o stream de logs mais recente.

      Você deve ver uma saída semelhante à seguinte nos logs da sua função:

------
#### [ 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. Confirme se seu código detecta uma assinatura inválida executando o seguinte comando curl. Substitua o URL pelo endpoint do URL da sua própria função.

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

   A seguinte saída deverá ser mostrada:

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

## Limpe os recursos
<a name="urls-webhook-tutorial-cleanup"></a>

Agora você pode excluir os recursos criados para este tutorial, a menos que queira mantê-los. Excluindo os recursos da AWS que você não está mais usando, você evita cobranças desnecessárias em sua Conta da AWS.

**Como excluir a função do Lambda**

1. Abra a página [Functions](https://console.aws.amazon.com/lambda/home#/functions) (Funções) no console do Lambda.

1. Selecione a função que você criou.

1. Selecione **Actions**, **Delete**.

1. Digite **confirm** no campo de entrada de texto e escolha **Delete** (Excluir).

Quando você cria uma função do Lambda no console, o Lambda também cria um [perfil de execução](lambda-intro-execution-role.md) para essa função.

**Para excluir a função de execução**

1. Abra a página [Roles](https://console.aws.amazon.com/iam/home#/roles) (Funções) no console do IAM.

1. Selecione o perfil de execução que o Lambda criou. O perfil tem o formato de nome `myLambdaWebhook-role-<random string>`.

1. Escolha **Excluir**.

1. Insira o nome do perfil no campo de entrada de texto e escolha **Delete** (Excluir).