Webhook de atualização | Como testar o desfecho que muda depois
Uma verificação que termina em revisão e é decidida depois gera dois eventos. Como deduplicar pelo id, decidir pelo estado e provar a integração no sandbox sem esperar um caso real.
Foto: Pankaj Patel, Unsplash
Quase toda integração com uma API de verificação nasce testada no caminho curto: o cadastro entra, a verificação termina, o webhook chega com aprovado ou reprovado, e a conta é liberada ou barrada. O caminho que quebra em produção é o outro. A verificação termina em revisão, a integração coloca o cadastro em espera, e horas depois a mesma verificação é decidida de novo. Chega um segundo evento sobre o mesmo caso, com um desfecho diferente, e o código que só conhecia o primeiro não sabe o que fazer com ele.
O problema não é raro por acaso. Revisão humana, resultado de consulta que chega depois e decisão de um analista que reabre um caso são parte normal de qualquer operação de verificação. O que é raro é conseguir provocar esse caminho em teste, e é por isso que ele costuma ser o último a ser exercitado.
Dois eventos sobre o mesmo caso não são duplicata
A primeira decisão de desenho é separar duas coisas que parecem iguais: o mesmo evento entregue duas vezes e dois eventos diferentes sobre o mesmo objeto.
A especificação CloudEvents, mantida pela Cloud Native Computing Foundation, a CNCF, define essa separação no atributo id. O produtor deve garantir que a combinação de origem e id seja única para cada evento distinto; se um evento duplicado for reenviado, por exemplo por erro de rede, ele pode manter o mesmo id; e o consumidor pode presumir que eventos com a mesma origem e o mesmo id são duplicatas.
Aplicado ao caso da verificação, isso dá duas regras. Um reenvio do primeiro evento, com o mesmo id, é descartado sem efeito. O segundo evento, que traz o desfecho novo, tem id próprio e precisa ser processado, mesmo que a verificação a que ele se refere seja a mesma. Integração que deduplica pelo identificador da verificação, e não pelo identificador do evento, joga fora exatamente a atualização que importa.
Deduplicar pelo id, decidir pelo estado
Separado o duplicado do novo, o processamento precisa ser idempotente. A RFC 9110, que define a semântica do HTTP, chama de idempotente o método em que o efeito pretendido no servidor de várias requisições idênticas é o mesmo de uma só. O mesmo critério vale para quem recebe webhook: aplicar o mesmo evento duas vezes não pode liberar a conta duas vezes, nem mandar dois e-mails, nem lançar dois créditos.
O jeito mais simples de chegar lá é guardar o estado, e não a sequência de eventos. Para cada verificação, a integração mantém o desfecho atual e o identificador do último evento aplicado. Cada evento que chega é conferido contra o que já foi visto; se for novo, o desfecho que ele traz substitui o anterior, e as consequências de negócio são recalculadas a partir do estado final, não somadas à anterior. O artigo sobre reconciliação de webhook detalha o que fazer quando o evento não chega, e o sobre webhooks seguros em KYC cobre a assinatura, que continua valendo para o segundo evento como valia para o primeiro.
O que o segundo evento muda na sua operação
O desfecho tardio não é só um campo que muda de valor. Ele costuma disparar ação. Uma revisão que vira aprovação libera o cadastro que estava em espera e deveria avisar o usuário. Uma revisão que vira reprovação precisa desfazer o que foi concedido de forma provisória, se algo foi concedido, e registrar o motivo.
Três perguntas ajudam a desenhar esse trecho antes de ele virar incidente. O que o sistema concede enquanto a verificação está em revisão, se é que concede alguma coisa. Quem é avisado quando o desfecho final chega, e por qual canal. E o que acontece se a decisão humana, tomada no painel do fornecedor, chegar antes da decisão automática. Esta última é a que mais surpreende: numa operação saudável, a decisão de uma pessoa prevalece, e a integração tem de aceitar que o desfecho final pode vir de um analista.
Por que testar sem esperar um caso real
Esperar um caso real de revisão para testar essa lógica tem dois problemas. O primeiro é tempo: em volume baixo, pode levar semanas até um caso cair em revisão e ser decidido. O segundo é que o teste vira uma observação única, impossível de repetir quando o código muda.
O que resolve é um ambiente de teste com desfecho determinístico, a propriedade descrita no artigo sobre sandbox antes de produção: o mesmo dado de entrada sempre produz o mesmo resultado, e existe um dado de entrada que produz a atualização tardia de propósito. Com ele, o caminho da revisão que vira aprovação e o da revisão que vira reprovação entram na suíte de testes da integração, rodam a cada mudança e param de depender da sorte.
Na UNIFOKAL, por exemplo, isso está no sandbox pelos sufixos de teste -04 e -05 do documento enviado pela API. A verificação termina em revisão e o primeiro verification.completed chega na hora; cerca de um minuto depois, ela é decidida de novo e chega um segundo verification.completed, com id de evento novo, aprovado no -04 e reprovado no -05. Se alguém decidir a verificação pelo painel antes disso, vale a decisão da pessoa e o segundo evento não sai, que é justamente o terceiro cenário a testar. O passo a passo está na documentação de ambientes.
Uma lista curta para a sua integração
Antes de ir para produção, vale conferir cinco pontos com testes automatizados, e não só com leitura de código:
- o reenvio do mesmo evento, com o mesmo
id, não produz efeito nenhum; - um evento novo sobre a mesma verificação substitui o desfecho anterior;
- a consequência de negócio é recalculada a partir do estado final;
- a decisão humana tomada no painel é aceita como desfecho final;
- a assinatura é verificada em todos os eventos, inclusive no segundo.
Nenhum desses pontos depende do fornecedor que você usa. Todos dependem de o caminho da atualização ter sido exercitado antes do primeiro caso real.
Fontes citadas
- CloudEvents, especificação versão 1.0.2, Cloud Native Computing Foundation, atributo
id: github.com/cloudevents/spec - RFC 9110, HTTP Semantics, IETF, seção 9.2.2, métodos idempotentes: rfc-editor.org
- Documentação de ambientes da UNIFOKAL: os sufixos do sandbox e o desfecho tardio.
