Product

Um guia prático para usar os webhooks da Mailgun

Saiba como configurar webhooks, lidar com dados de seus e-mails transacionais e proteger os dados do webhook neste guest post de um cliente da Mailgun.
Imagem para Um guia prático para usar os webhooks da Mailgun

Este guest post é de Opeyemi Obembe, cliente da Mailgun com sede na Nigéria. Quando vimos o tutorial de Opeyemi sobre como configurar webhooks, sabíamos que precisávamos compartilhá-lo com você. A metodologia de Opeyemi não é apenas sólida, mas ele é o tipo de profissional de desenvolvimento que adoramos apoiar.

Opeyemi se cadastrou na Mailgun há quatro anos e, desde então, construiu coisas muito legais. Mais recentemente, ele desenvolveu o Suet, um projeto de código aberto que fornece análises detalhadas e relatórios para e-mails transacionais enviados pela Mailgun. Você pode usá-lo em conjunto com nossos novos recursos de análises para obter insights adicionais sobre o engajamento do seu e-mail.

E-mails transacionais são essenciais para a maioria dos aplicativos. Enviamos e-mails de boas-vindas, e-mails de recuperação de senha, notificações e muito mais. E quando fazemos isso, usamos provedores como a Mailgun. Enviar os e-mails é muito legal, mas e quanto à entrega e ao desempenho? O usuário recebeu aquele e-mail de redefinição de senha? Aquele e-mail de notificação de “expiração do cartão de crédito” foi aberto?

Embora esses dados estejam disponíveis em seu painel da conta Mailgun, outra maneira de obter atualizações sobre o que está acontecendo com suas mensagens transacionais na Mailgun é por meio de webhooksHá também a API, mas, diferentemente da API, em que você “solicita” essas atualizações (Poll), com webhooks, as atualizações são enviadas para você (Push). Tudo o que você precisa fazer é fornecer o URL de um script que possa lidar com os dados do evento via POST.

Não é preciso dizer que o push tem algumas vantagens sobre o poll.

  • Você não precisa fazer solicitações repetidas de API. Isso significa consumir menos recursos do servidor.
  • As atualizações são mais em tempo real porque são enviadas assim que ficam disponíveis no servidor.

Configuração de webhooks

Existem duas maneiras de configurar webhooks na Mailgun. Isso pode ser feito pelo painel da Mailgun ou API. A maneira mais direta de fazer isso é pelo painel. Após entrar no seu painel, um link de Webhooks está disponível na barra de navegação.

webhooks1

A página de webhooks lista os diferentes tipos de eventos para os quais você pode receber dados de eventos. Ao clicar no ícone de “+” na frente de cada evento, você pode definir o URL para onde os dados do evento serão enviados.

Lidando com os dados

Para lidar com os dados do evento enviados para o URL do nosso webhook, precisamos saber como esses dados serão. Os parâmetros enviados via POST estão disponíveis na documentação da API. Podemos ir um passo além e confirmar isso usando um URL de webhook de teste que registrará os dados da Mailgun. Podemos usar o Postbin da Mailgun ou requestb.in. Esses serviços gerarão um endpoint exclusivo que podemos usar no painel da Mailgun para obter dados de eventos de amostra. Recomendo o requestbin porque ele fornece mais detalhes, como os cabeçalhos da solicitação. Esses cabeçalhos são importantes porque você pode facilmente deixar passar o fato de que a Mailgun envia alguns dados usando o content-type [application/x-www-form-urlencoded] e outros como [multipart/form-data]. Perder esses pequenos detalhes muda tudo sobre como você obtém os dados do evento.

Vamos em frente e criar um endpoint de teste e ver como os dados do evento se parecem em comparação com o que está na documentação.

Screen-Shot-2019-04-12-at-3
  • Repita isso para todos os eventos nos quais você tem interesse.
  • Atualize a página do requestbin para ver os dados do evento enviados.
requestbin-webhooks-data

Se você observar atentamente os dados do requestbin, notará o que eu disse sobre alguns dados serem enviados como multipart/form-data.

Agora que sabemos quais são os parâmetros para cada tipo de evento e o tipo de conteúdo em que eles podem vir, é fácil escrever um código para lidar com os dados enviados. Aqui está um código simples que exibirá detalhes de reclamações e e-mails descartados. (Estou usando o multer para lidar com multipart/form-data.)

                                

                                    const express = require('express')  rn    , bodyParser = require('body-parser')rn    , multer = require('multer')rn    ;rnrnconst app = express();  rnapp.use(bodyParser.urlencoded({extended: false}));  rnapp.listen(process.env.PORT || 3000);rnrnapp.post('/webhook', multer().none(), function(req, res) {  rn  const email = req.body.recipient;rn  const event = req.body.event;rnrn  if (event == 'complained') {rn    console.log(<code data-enlighter-language="generic" class="EnlighterJSRAW">${email} complained about your mail</code>);rn  }rn  else if (event == 'dropped') {rn    console.log(<code data-enlighter-language="generic" class="EnlighterJSRAW">Mail to ${email} dropped. ${event.description}</code>);rn  }rn  else if (event == 'bounced') {rn    console.log(<code data-enlighter-language="generic" class="EnlighterJSRAW">Error ${event.code}: Mail to ${email} bounced. ${event.error}</code>);rn  }rnrn  res.end();rn});
                                
                            

Tornando-o seguro

Não há nada que impeça alguém que conheça o URL do nosso webhook de criar dados de eventos falsos e enviá-los para o URL. Felizmente, a Mailgun assina cada solicitação enviada e também publica os seguintes parâmetros:

  • timestamp (número de segundos decorridos desde 1º de janeiro de 1970)
  • token (string gerada aleatoriamente com tamanho 50)
  • signature (string hexadecimal gerada pelo algoritmo HMAC)

Para verificar o token, você precisa:

  • Concatenar os valores de timestamp e token.
  • Codificar a string resultante com HMAC, usando a chave de assinatura HTTP do seu webhook como chave e Sha256 como algoritmo.

O resultado deve ser igual à signature.

Veja como isso fica no Node.js:

                                

                                    const value = event_data_timestamp+event_data_token;  rnconst hash = crypto.createHmac('sha256', apikey)  rn                   .update(value)rn                   .digest('hex');rnif (hash !== event_data_signature) {  rn  console.log('Invalid signature');rn  return;rn}
                                
                            

Se adicionarmos isso ao nosso exemplo de código original, teremos algo assim:

                                

                                    const express = require('express')  rn    , crypto = require('crypto')rn    , multer = require('multer')rn    , bodyParser = require('body-parser')rn    ;rnrnconst app = express();  rnapp.use(bodyParser.urlencoded({extended: false}));  rnapp.listen(process.env.PORT || 3000);rnrnapp.get('/webhook', multer().none(), function(req, res) {  rn  // Validate signaturern  const value = req.body.timestamp+req.body.token;rn  const hash = crypto.createHmac('sha256',rn              process.env.API_KEY)rn                   .update(value)rn                   .digest('hex');rn  if (hash !== req.body.signature) {rn    console.log('Invalid signature');rn    return res.end();rn  }rnrn  // Log status of eventrn  const email = req.body.recipient;rn  const event = req.body.event;rnrn  if (event == 'complained') {rn    console.log(<code data-enlighter-language="generic" class="EnlighterJSRAW">${email} complained about your mail</code>);rn  }rn  else if (event == 'dropped') {rn    console.log(<code data-enlighter-language="generic" class="EnlighterJSRAW">Mail to ${email} dropped. ${event.description}</code>);rn  }rn  else if (event == 'bounced') {rn    console.log(<code data-enlighter-language="generic" class="EnlighterJSRAW">Error ${event.code}: Mail to ${email} bounced. ${event.error}</code>);rn  }rnrn  res.end();rn});
                                
                            

Podemos até melhorar isso, e:

  • A cada solicitação, verificar em um cache de token para evitar o uso do mesmo token. Todos os tokens serão armazenados lá. Isso evitará ataques de repetição.
  • Verificar se o timestamp não está muito distante do horário atual.

Tornando-o escalável

Se você enviar muitos e-mails e esperar muitos eventos, colocar seu script de webhook em um servidor que não pode ser dimensionado automaticamente é uma má ideia. Mesmo que você não esteja esperando muitos eventos, coisas inesperadas podem causar um aumento neles. Ter um servidor com capacidade de escalonamento automático é muito útil para casos como este.

É aí que entra a computação sem servidor. Em termos simples, a ideia é que você pode delegar a execução do seu código e tudo relacionado a isso para um provedor. Várias instâncias do seu código podem ser executadas em paralelo, e você pode ajustar os recursos de computação, como RAM e tempo de execução, em tempo real. Isso o torna altamente escalável. Você também é cobrado com base nos recursos consumidos e no tempo de execução, portanto, pode ser muito barato se você não enviar muitos e-mails regularmente.

Existem alguns provedores de computação sem servidor. Um que uso e recomendo é o Google Cloud Functions por causa da facilidade de configurar funções HTTP. Uma função HTTP é um bloco de código empacotado como uma função que pode ser acionada ao visitar um URL. Isso é exatamente o que precisamos para o nosso webhook.

Para criar essa função, precisamos escrever uma função JavaScript que será exportada como um módulo do Node.js. A função recebe argumentos específicos do HTTP: request e response.

                                

                                    exports.webhook = function(request, response) {  rn  // Handle event data herern  response.send({status:"ok"});rn}
                                
                            

Com base no content-type da solicitação, o corpo da solicitação é passado automaticamente e disponibilizado no parâmetro body do objeto request.

                                

                                    exports.webhook = function(request, response) {  rn  let event = request.body.event; // deliveredrn  // Handle event data herern  // ...rn  response.send({status:"ok"});rn}
                                
                            

No entanto, isso não funciona para o tipo de conteúdo multipart/form-data. E, como já sabemos, a Mailgun envia alguns dados como multipart/form-data. Podemos incluir uma biblioteca como o Multer usando o require(). No entanto, precisamos garantir que a dependência esteja listada no arquivo package.json.

                                

                                    const multer = require('multer');rnrnexports.webhook = function(request, response) {  rn  parser(request, response, function(){rn    console.log(request.body); // Our event datarn    // Handle event data herern    // ...rn    response.send({status:"ok"});rn  });rn}
                                
                            
                                

                                    {rn  "dependencies": {rn    "multer": "^1.3.0"rn  }rn}
                                
                            

Em seguida, podemos publicar a função no Cloud Functions. Uma maneira fácil de fazer isso é pelo painel do Cloud Functions.

  • Acesse o seu Google Cloud Console (se você ainda não tiver uma conta, crie uma).
  • Ative o Cloud Functions no painel.
  • Clique em “Create Function”.
  • Insira um nome para a sua função (por exemplo, “mailgun-webhook”).
  • Na seção de gatilho, selecione “HTTP trigger”. Anote o URL; esse será o URL do seu webhook.
  • Copie o código de manipulação de dados de evento para a seção index.js da função Cloud.
  • Copie o conteúdo do seu package.json e cole na seção package.json.
  • Selecione ou crie um Stage bucket. O stage bucket é simplesmente onde o código é preparado. Você pode usar qualquer coisa aqui.
  • Em Function to execute, insira o nome da sua função (por exemplo, “webhook”).
  • Salvar.

Agora você pode usar o URL da função na Mailgun como o URL do seu webhook.

Conclusão

Trabalhar com os webhooks da Mailgun é fácil. Existem muitas maneiras de usar os dados do evento para enriquecer seus aplicativos fora da Mailgun. Se, por exemplo, você permitir que seus usuários enviem e-mails do seu site por qualquer motivo e você usar a Mailgun, poderá usar webhooks para fornecer análises para eles. Ou talvez você queira enviar suas análises de e-mail para outra plataforma. Ou talvez você queira receber notificações sobre falhas na sua Slack conta. Ou talvez nem isso; talvez você queira apenas análises mais detalhadas. Seja qual for o caso de uso, os dados do evento estão disponíveis para você.

Para obter um exemplo do mundo real, confira a fonte do arquivo de webhook do Suet.

Quer experimentar os webhooks da Mailgun? Cadastre-se!

E se você precisar de ajuda para começar a usar a Mailgun, confira este treinamento gratuito da Mailgun! Chris Hammer, líder de nossa equipe de suporte ao cliente, orienta você pelo processo de configuração e ajuda a começar a enviar, receber e rastrear seus e-mails com a Mailgun.