Algumas boas práticas para implementar webhooks

Pontos e boas práticas que você deve levar em consideração quando for implementar webhooks de integração.

Algumas boas práticas para implementar webhooks

Uma das modalidades mais populares de integração se dá através da implementação de webhooks: neste modelo você disponibiliza um endpoint que será invocado por um sistema externo sempre que algum evento do seu interesse ocorrer.

É muito comum, por exemplo, na implementação de integração com meios de pagamento, quando você deseja ser notificado quando alguém fez uma compra em seu site. Precisei implementar recentemente uma integração com o Shopify e no processo preparei este guia com os pontos que você deve levar em consideração na implementação deste tipo de integração.

Use este post como seu checklist para garantir que esta integração seja realizada da melhor maneira possível.

Comece pelas perguntas que você deve (se) fazer

As perguntas que você deve fazer

A primeira boa prática é perguntar. Normalmente a maior parte dos problemas surge justamente por que elas não foram feitas de antemão.

💡
Não há como responder a estas questões neste post pois tudo depende do contexto. O importante é que você se lembre de fazê-las.

Naturalmente há outras perguntas que podem ser feitas, mas estas são, na minha experiência, as mais importantes que você deve levantar.

A ordem das perguntas a seguir não representa suas prioridades.

Em quanto tempo você deve responder à notificação recebida?

Pouca gente leva este ponto em consideração e ele é muito importante. A maior parte das plataformas espera que você envie uma resposta rápida. Do ponto de vista arquitetural faz sentido: seu papel é apenas lhe informar que algo ocorreu.

Caso você demore muito tempo para enviar uma resposta a plataforma pode interpretar como um erro de comunicação a integração e consequências ruins podem ocorrer tal como, por exemplo, desabilitar a integração.

💡
Sugiro que você execute o processamento em uma thread separada ou enfileire o conteúdo da notificação para uma fila para que seja processada de forma assíncrona.

O fundamental é responder rápido.

Como valido a identidade do autor da notificação?

Há como validar a identidade do autor da notificação?

Este é um ponto de segurança fundamental que deve ser respondido. Como o autor da notificação garante sua identidade? Como você pode verificar se as notificações recebidas realmente vieram da plataforma integradora (você é realmente o Shopify?)?

Esta é uma informação que normalmente está presente na própria documentação da plataforma. No caso do Shopify, por exemplo, é enviado um cabeçalho que contém a assinatura digital do payload recebido usando o algoritmo SHA256 (passo 2 neste link: https://shopify.dev/docs/apps/build/webhooks/subscribe/https#step-5-verify-the-webhook ).

Outra opção implementada por diversas plataformas é o fornecimento de uma API Key de identificação da plataforma sob a forma de um cabeçalho HTTP ou no próprio corpo da requisição.

Como proceder no caso de haver uma falha no recebimento das notificações?

Como proceder no caso de falhas?

Problemas acontecem: talvez seu sistema esteja indisponível justo na hora em que uma notificação foi enviada e você não pode perder nenhuma notificação. Neste caso, como proceder? As seguintes perguntas precisam ser respondidas:

  • A plataforma tem algum mecanismo de retentativa de envio em caso de falhas? Se sim, de quanto em quanto tempo será realizada uma nova tentativa? Quantas tentativas são realizadas?
  • Na plataforma existe algum mecanismo de reenvio de notificações que possa ser disparado por você manualmente? (infelizmente este é um recurso raro).
  • Caso na plataforma não exista um mecanismo de retentativas de envio ou um mecanismo de reenvio manual, é possível entrar em contato com o suporte da plataforma te pedindo para que isto seja feito?
  • Caso para os três pontos acima a resposta seja não você precisa pensar na implementação de algum mecanismo de reconciliação, ou seja, algum dispositivo que consulte a plataforma de integração por notificações que possam ter sido perdidas para que você de alguma forma as reimplemente na sua plataforma.
💡
Se prepare esperando que tudo dê errado. Uma estratégia interessante é persistir o conteúdo das notificações antes de qualquer processamento para que assim estratégias de reprocessamento possam ser pensadas.

Qual a versão da plataforma de integração?

Qual a versão da plataforma de integração?

Normalmente plataformas com mais anos de mercado podem ter mais de uma versão da sua plataforma de integração disponível, e algumas marcadas para serem descontinuadas. MUITA atenção ao último ponto para evitar implementar algo que pode parar de funcionar em breve (você foi avisado).

Sugiro que você evite versões instáveis da plataforma. Muita atenção à documentação, normalmente são listadas as versões disponíveis, tal como no print abaixo, extraído do site do Shopify.

Existe um ambiente sandbox?

Muitas plataformas de integração oferecem um ambiente de sandbox. Ele é usado para que você possa enviar notificações para sua plataforma sem que custos adicionais sejam incluídos na sua conta durante a implementação da sua integração.

Sempre verifique se há um ambiente de sandbox. Caso não exista, use a prática "Tenha uma notificação canônica" que é descrita mais adiante neste post.

💡
MUITO CUIDADO com o ambiente sandbox.

Não raro ele não reflete exatamente o ambiente de produção da plataforma de integração. Sempre podem haver algumas diferenças.

Você pode encontrar surpresinhas bem desagradáveis em ambientes sandbox: mais de uma vez já o vi sendo usado para testar novas funcionalidades da plataforma.

Finalmente, algumas práticas que adotamos

Práticas que adotamos

Jamais confie na documentação

Não é raro durante a implementação da integração encontrar diferenças entre o que foi documentado e o que de fato nos é enviado. Sendo assim a primeira prática é: desconfie sempre.

Atributos podem ser removidos ou inseridos, ou mesmo ter seus nomes alterados sem que você seja informado. Acabei criando alguns hábitos de programação que me ajudam a evitar este tipo de problema:

  • Se vou usar alguma biblioteca de parseamento, como o Jackson no caso do Java, evito usar configurações que disparem exceções caso campos inesperados surjam (aqui um exemplo).
  • Ter uma notificação canônica: receber ao menos uma notificação vinda do ambiente de produção, salvá-la em arquivo, junto com todos os cabeçalhos e usá-la como base na implementação e testes.

Health Checking obrigatório

Implemente health checks em suas integrações

Toda integração tem uma série de condições para que possa funcionar:

  • Consegue acessar o banco de dados?
  • Todas as variáveis de ambiente estão definidas?
  • Consigo acessar o local aonde persisto os payloads que recebi?
  • As credenciais usadas para acessar os serviços de apoio estão válidas?
  • Tenho memória suficiente?
  • As últimas notificações que recebi são válidas?
  • Faz MUITO tempo que não recebo notificações?

Implemente um endpoint de health checking que valide todas estas condições ou situações que podem impactar no funcionamento da sua integração. E mais importante: inclua este endpoint em alguma ferramenta de verificação, tal como Status Cake, Uptime Kuma, os próprios mecanismos que seu provedor cloud ofereça.

E ainda mais importante: se for receber notificações por e-mail, certifique-se de que estas notificações não sejam marcadas como SPAM!

É fundamental ter estes mecanismos em produção pois uma vez implementados, é muito comum as pessoas simplesmente SE ESQUECEREM de que implementaram estes webhooks. Vai por mim: seu Eu do futuro irá me agradecer por esta dica.

Verifique seu ambiente de produção com o Health Checking do Spring Boot Actuator
Aprenda a escrever health checks no Spring Boot usando o Actuator

Caso você use Spring Boot, neste texto te ensinamos a implementar um health checks usando o Actuator!

Audite TUDO

Audite TUDO!

Caso algo dê errado é fundamental que seja possível descobrir a causa do problema. Sendo assim é fundamental que todas as interações com seus webhooks sejam auditadas.

  • Persista em algum lugar os payloads, cabeçalhos (ou metadados) e a hora em que foram recebidas TODAS as notificações.
  • Registre a hora em que cada passo da integração foi realizada.
  • Gere log de tudo.
    • Aqui atenção especial para qual o tipo de log. Se for mais fácil para você inserir o registro de ações em um banco de dados relacional (cuidado para não estourar seu banco de dados com informações desnecessárias) para acesso rápido aos dados, faça isto.
  • Implemente sua auditoria de tal modo que seja fácil e rápido obter todas as informações relativas ao processamento para que possa ser possível reproduzir a situação.
  • A ação envolveu algum usuário? Registra quem foi.
💡
Custo operacional (OpEx) deve ser levado em consideração aqui. Para armazenar payloads, sugiro a adoção de soluções de storage como o S3 da AWS, que são bem mais baratos que as opções de bancos de dados relacionais.

Use seu computador local como receptor das notificações durante o desenvolvimento

Já vi diversos casos em que a construção de webhooks atrasa por que o ambiente de desenvolvimento é ruim. A pessoa escreve o webhook, o implanta em algum ambiente e, na sequência, fica esperando as notificações chegarem para ver se está tudo correto ou não.

E este é um sofrimento TOTALMENTE desnecessário!

Existem ferramentas como Ngrok que expõem seu computador à Internet. São super simples: o que fazem é essencialmente criar um endereço na Internet (o Ngrok inclusive cria este endereço com suporte a SSL/TLS !!!) que aponta para o endereço local da sua máquina.

Aí basta configurar a plataforma de integração para que envie as notificações para este endereço.

Nós temos uma receita neste site que te ensina a usar o Ngrok. Veja o link abaixo:

Receita – expondo seu localhost ao mundo com Ngrok
Em tempos de quarentena esta é uma receita bastante útil. Eis a situação: você desenvolve sua aplicação localmente e gostaria que outras pessoas a acessassem remotamente mas sem a necessidade de implantá-la em um servidor. Como fazer? Esta receita tem dois ingredientes: Linux e Ngrok, mas você pode substituir o

Ngrok salva sua pele!

Não quer usar o ngrok? Tudo bem! Há alternativas: Localtunnel e Serveo, por exemplo.

Tenha notificações canônicas

Lembra quando disse para não confiar na documentação? Então... comece por aqui.

Tendo expondo sua máquina local à Internet e configurado sua plataforma de integração para enviar notificações para este endereço, implemente um script simples que obtenha o conteúdo da notificação (payload e cabeçalhos) e os armazene em um arquivo local.

Você não precisa pensar muito para fazer isto. Se pedir para o Copilot, por exemplo, te implementar algo em Go, vai te retornar o código a seguir:

package main

import (
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "os"
)

func handler(w http.ResponseWriter, r *http.Request) {
    // Cria ou abre o arquivo para salvar os dados
    file, err := os.OpenFile("request_data.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
    if err != nil {
        log.Fatal(err)
    }
    defer file.Close()

    // Escreve os cabeçalhos no arquivo
    for name, values := range r.Header {
        for _, value := range values {
            _, err := file.WriteString(fmt.Sprintf("%s: %s\n", name, value))
            if err != nil {
                log.Fatal(err)
            }
        }
    }

    // Lê o corpo da requisição
    body, err := ioutil.ReadAll(r.Body)
    if err != nil {
        log.Fatal(err)
    }

    // Escreve o corpo da requisição no arquivo
    _, err = file.WriteString(fmt.Sprintf("\nBody: %s\n\n", string(body)))
    if err != nil {
        log.Fatal(err)
    }

    // Responde ao cliente
    fmt.Fprintf(w, "Dados salvos com sucesso!")
}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}
💡
O código acima é só um exemplo. Nem validei, apenas pedi ao Github Copilot para que o escrevesse pra mim como exemplo pra você.

Use por sua conta e risco! :D

Tendo esta notificação canônica, que deve vir de produção (se for um meio de pagamento, faça uma compra de R$ 1,00 pra validar!) é possível verificar se a documentação bate com a realidade.

E ainda melhor: você pode usar o que obter como base pra escrever seus testes, sem precisar ficar interagindo diretamente com a plataforma de integração o tempo inteiro.

💡
Esta prática pode te economizar uma verdadeira FORTUNA caso esteja implementando integração com meios de pagamento!

E é isto

Meu objetivo neste post foi te mostrar algumas práticas que podem te poupar um bom tempo na implementação de webhooks. Evitei por o óbvio aqui ("leia a documentação", "escreva testes", "use boas práticas de programação"....), apontando as dificuldades mais comuns que vejo sendo enfrentadas por outras pessoas que precisam implementar este tipo de integração.

Óbvio que há mais boas práticas que podem ser seguidas, assim como ainda mais questões, e gostaria de ouvir as suas. Caso as tenha, basta se inscrever aqui na itexto Insights e nos mandar, ok?

Mantido por itexto Consultoria