DEV Community

Stephann
Stephann

Posted on

Entendendo webhooks

Introdução

A gente já chegou?

Se você já assistiu Shrek, deve lembrar da cena onde o Burro fica perguntando de tempos em tempos "A gente já chegou?", e isso é basicamente a mesma estratégia muitas plataformas utilizam para se comunicar com outras plataformas, elas ficam "tem informação nova aí pra mim?" de forma recorrente até encontrar o que procura, isso é chamado de polling. Isso pode trazer algumas consequências como:

  • Necessidade implementar e observar o funcionamento de uma rotina para buscar dados de tempos em tempos, definindo um intervalo entre suas execuções.
  • Esse intervalo pode não ser o ideal, fazendo consultas em um intervalo menor ou maior que o necessário.
  • Essas rotinas podem sobrecarregar a plataforma que pergunta e/ou a plataforma que responde, dependendo do volume de dados que precisam ser conferidos e da frequência das perguntas.

Isso pode ser evitado com Webhooks.

O problema

Pra contextualizar, vamos imaginar o seguinte cenário: Você está desenvolvendo um e-commerce, e precisa processar o pagamento de um usuário. Você utilizará uma plataforma provedora de pagamento (Stripe, Pagar.me, Iugu e etc.). A forma de pagamento escolhida foi Pix, então quando você consome o serviço da provedora de pagamento, ela te devolve um QR Code e você exibe esse QR Code pro usuário conseguir pagar. Nesse momento você não tem como saber se o usuário pagou, se ele vai pagar em 10 minutos, em 1 hora, em 5 horas ou se vai deixar o Pix expirar, mas você precisa saber o que vai acontecer com esse Pix para poder atualizar o status do pedido na sua plataforma.

Uma possível solução para esse problema acima: Criar uma rotina pra consumir a API da provedora de pagamento e verificar se o status do pagamento saiu de "Aguardando Pagamento" para "Pago" ou "Vencido". Daí é necessário decidir o intervalo dessa rotina. Se for de hora em hora, você pode criar um problema, pois o pix é instantâneo e o usuário vai ficar preocupado porque a plataforma não mudou o status do pedido dele mesmo depois de 30, 40 minutos após o pagamento.

Então você muda pra rotina executar a cada minuto, e percebe isso está sobrecarregando sua plataforma e a provedora de pagamento, pois agora você tem 1000 pagamentos pra conferir. Depois você descobre que uma transação de cartão de crédito pode ser estornada até 6 meses depois da data do pagamento e que sua plataforma precisará passar 6 meses conferindo se o status da transação mudou.

Assim como na cena do Shrek, o ideal para ambas as partes nessa situação seria seu e-commerce parar de perguntar com tanta frequência, ao mesmo tempo que a provedora de pagamento te garantisse que te avisará quando houver alterações nos dados. Uma forma de fazer esse "acordo" entre plataformas é utilizando Webhooks

Os webhooks

Webhooks basicamente invertem o conceito de API, podemos pensar como uma API inversa, ao invés do e-commerce consumir um serviço da provedora de pagamento, a provedora de pagamento é que vai consumir o serviço do e-commerce enviando os novos dados.

Na prática funciona assim: você implementa uma rota para receber as requisições relacionadas à mudança de status dos pagamentos, ex.: minhaloja.com.br/webhooks, e informa essa URL nas configurações da provedora de pagamento. Com isso configurado, toda vez que um pagamento mudar de status, a provedora de pagamentos irá enviar para a sua aplicação os dados atualizados dos pagamentos sem você precisar pedir de tempos em tempos. Um exemplo de dados que a provedora poderia enviar:

{
  id: "405", // id do webhook
  event: "status_changed", // tipo do evento
  transaction_id: "125095", // id do pagamento
  old_status: "pending", // status antigo
  status: "paid", // status atual
  ...
}
Enter fullscreen mode Exit fullscreen mode

Mas e a segurança?

Agora você deverá estar se perguntando: "Mas se alguém descobrir essa rota e enviar requisições se passando pela provedora de pagamento?". É uma dúvida bem pertinente, de fato você precisa proteger essa URL para que sua aplicação não processe dados de desconhecidos. Uma prática comum no uso de webhooks pra garantir que a requisição é confiável, é implementar a troca de códigos onde apenas duas plataformas saibam como gerar esses códigos.

Um exemplo dessa estratégia seria utilizar um algoritmo de chave simétrica: A provedora de pagamento envia no corpo ou no cabeçalho da requisição o código, por exemplo: 1213e67a3b34c2848f8317d29bcb8cbc9e0979b8, e pra você garantir que foi a provedora de pagamento mesmo que enviou essa requisição, você precisa criptografar o corpo do webhook com algum algoritmo específico utilizando sua chave da API, caso o resultado da criptografia seja igual ao do código enviado na requisição significa que a requisição é confiável e pode ser processada.

E se minha aplicação sair do ar?

Outra dúvida que pode ter surgido na sua cabeça: "E se minha aplicação ficar fora do ar no momento que a provedora de pagamento estiver enviando webhooks?". Também há soluções pra isso e a mais comum são as retentativas. Caso a aplicação A falhe em receber os webhooks da aplicação B, a aplicação B tentará enviar o webhook outras vezes. Normalmente o que se considera "enviado/recebido com sucesso" é a plataforma devolver um status HTTP de sucesso (status 200, por exemplo) ao receber o webhook.

O que varia de plataforma para plataforma são as políticas de retentativas. Tem plataforma que vai tentar 3 vezes no máximo (o que considero pouco e não muito confiável), outras vão tentar 30 vezes (🥰 pra mim é o ideal). E também algumas plataformas mudam o intervalo entre retentativas, por exemplo: As 5 primeiras tentativas têm intervalo entre elas de 1 minuto, as 20 próximas tentativas serão de hora em hora, as 5 últimas serão 1 vez ao dia. Isso ajuda a nenhum webhook ficar perdido num eventual problema de comunicação, porque não adiantaria a plataforma fazer 30 retentativas num mesmo minuto, e calhar da sua aplicação ficar fora do ar justamente nesse minuto específico.

Algumas plataformas também disponibilizam uma API para você consultar os webhooks e dispará-los novamente. Eu encaro essa opção como uma última saída, caso você desconfie de ter perdido o recebimento de algum webhook.

Tratando os webhooks

Uma estratégia que venho testando e parece funcionar bem é: Não processar o webhook no momento do seu recebimento. Ou seja, no exemplo do e-commerce, não vá mudar o status do pedido durante o processamento da requisição do webhook, pois pode haver um erro na sua lógica e sua plataforma devolver um status de erro na requisição (status 500, por exemplo). Isso fará a provedora de pagamento enviar várias vezes o webhook desnecessariamente, talvez até causando alguma inconsistência nos seus dados dependendo de como foi implementado.

A ideia é armazenar esse webhook numa estrutura (numa tabela, por exemplo) e processá-lo posteriormente de forma assíncrona (como um job no Sidekiq). Dessa forma você retorna de forma rápida um status 200 pra plataforma parceira pois você não precisou processar naquele momento, e você tem os dados necessários pra posteriormente processar esse webhook. Caso dê algum problema no processamento, você consegue reprocessar, investigar, debugar aquele webhook com mais calma.

Qualidade de vida

Outro ponto que considero que ajudo bastante a utilização dos webhooks, principalmente durante a fase de desenvolvimento e manutenção, é que a URL que vai receber os webhooks e que você informa para a plataforma, possam ser configuradas de maneira automatizada e de forma individualizada.

Configuração de maneira automatizada significa que você não precisa ir manualmente nas configurações da plataforma pra alterar a URL de recebimento de webhook, isso pode ser feito via código, configuração do projeto, parâmetro na requisição e etc. Isso ajuda nos casos de ter muitas pessoas trabalhando na integração e não precisar ficar alterando manualmente a URL para uma URL de ambiente dev.

Já a configuração de forma individualizada permite que você defina uma URL pra diferentes recursos que você estiver lidando. No exemplo da provedora de pagamento, essa funcionalidade me permitiria, no momento que eu criar uma transação, enviar algo como "webhook_url": "someurl.com", e os webhooks relacionados àquela transação seriam enviados pra essa URL específica. Isso ajuda tanto no ponto anterior de configuração automatizada, quanto em casos de migração e refatoração, onde pros pagamentos novos você consegue apontar os webhooks pra uma nova rota ou nova plataforma sem interferir nos webhooks dos pagamentos antigos.

Conclusão

Os webhooks são um ótimo recurso para dar mais possibilidades de integração entre plataformas, otimizando o poder computacional e garantindo uma melhor consistência e agilidade na atualização dos dados, e em determinados tipos de plataforma são essênciais.

Se você estiver desenvolvendo uma plataforma que seja necessário utilizar webhooks ou planejando se integrar com alguma que forneça esse recurso, eu recomendo conferir todos os pontos que mencionei no artigo para garantir uma melhor developer experience (pro seu time ou pra quem vai se integrar com seu produto). Os pontos que considero principais são:

  • Política de segurança. Prover uma forma pra garantir que as requisições são de quem deveriam ser.
  • Política de retentativas. Idealmente com um número alto de retentativas intervaladas de forma progressiva.
  • Disponbilização de API para listagem e disparo de webhooks, principalmente se puder filtrar por status (ex.: listar webhooks pendentes) ou por recurso (ex.: listar os webhooks relacionados a pagamento XYZ).

Com esses pontos garantidos, todo mundo sai ganhando, ALEGRIA!!!

Top comments (0)