Os webhooks são uma forma eficiente de receber notificações em tempo real sobre eventos específicos que ocorrem no sistema Mercos.
Geralmente, em até 3 minutos, o evento configurado será enviado para o destino desejado, fornecendo uma atualização automática dos dados desejados.
Para cada evento, um payload em formato JSON é enviado para a URL configurada. A seguir, detalhamos a estrutura de dados retornada para os quatro eventos disponíveis atualmente.
Para mais informações sobre como configurar os Webhooks através do sistema Mercos, confira a Central de Ajuda🔗
Validação de autenticidade [/webhooks/autenticacao]
Ao configurar um webhook com uma chave de validação, cada requisição enviada pela Mercos incluirá o header X-Hub-Signature-256 contendo uma assinatura HMAC-SHA256 do payload. Com isso, é possível verificar que a requisição foi realmente enviada pelo Mercos e que o conteúdo não foi alterado em trânsito.
Obs: Se nenhuma chave de validação estiver configurada, o header X-Hub-Signature-256 não será enviado.
Chave de validação
A chave de validação é uma string hexadecimal de 64 caracteres, configurada no momento da criação ou atualização do webhook. Ela é utilizada como segredo compartilhado entre o Mercos e o seu servidor.
Boas práticas de segurança:
- Armazene a chave de forma segura no seu servidor (ex: variável de ambiente, cofre de segredos)
- Nunca exponha a chave no frontend, em logs ou em repositórios de código
- Em caso de comprometimento, atualize a chave imediatamente via API
Como validar a assinatura
Ao receber uma requisição de webhook, siga estes passos antes de processar o payload:
- Extraia o valor do header
X-Hub-Signature-256da requisição - Remova o prefixo
sha256=do valor obtido — o restante é o digest hexadecimal enviado pelo Mercos - Recalcule o HMAC-SHA256 utilizando:
- Chave: sua chave de validação, convertida de hexadecimal para bytes
- Mensagem: o body raw da requisição (os bytes exatos recebidos, sem deserializar o JSON)
- Algoritmo: SHA-256
- Compare o digest recebido com o digest calculado utilizando uma função de comparação em tempo constante (resistente a timing attacks)
- Se os digests forem iguais, a requisição é autêntica — prossiga com o processamento. Se forem diferentes, rejeite a requisição (ex: retorne HTTP 401)
Obs: Validar a assinatura antes de deserializar o JSON permite rejeitar requisições inválidas sem consumir recursos com o processamento do payload.
Exemplo para verificação
Utilize os valores abaixo para validar sua implementação:
- Chave de validação:
a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2 - Body raw:
{"evento":"pedido.gerado","dados":{"pedido_id":12345}} - Header esperado:
X-Hub-Signature-256: sha256=9295ddcc76d83b5905ba63c27093c2b482ef0b1c7382d80a08bebe5a938f3175
Exemplos de implementação
Python
import hmac
import hashlib
def validar_assinatura(chave_validacao: str, header_assinatura: str, body: bytes) -> bool:
digest_recebido = header_assinatura.removeprefix("sha256=")
digest_calculado = hmac.new(
bytes.fromhex(chave_validacao), body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(digest_recebido, digest_calculado)JavaScript (Node.js)
const crypto = require("crypto");
function validarAssinatura(chaveValidacao, headerAssinatura, body) {
const digestRecebido = headerAssinatura.replace("sha256=", "");
const digestCalculado = crypto
.createHmac("sha256", Buffer.from(chaveValidacao, "hex"))
.update(body)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(digestRecebido),
Buffer.from(digestCalculado)
);
}Go
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"strings"
)
func validarAssinatura(chaveValidacao string, headerAssinatura string, body []byte) bool {
digestRecebido := strings.TrimPrefix(headerAssinatura, "sha256=")
chaveBytes, _ := hex.DecodeString(chaveValidacao)
hash := hmac.New(sha256.New, chaveBytes)
hash.Write(body)
digestCalculado := hex.EncodeToString(hash.Sum(nil))
return hmac.Equal([]byte(digestRecebido), []byte(digestCalculado))
}Java
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.util.HexFormat;
public class WebhookValidator {
public static boolean validarAssinatura(
String chaveValidacao, String headerAssinatura, byte[] body
) throws Exception {
String digestRecebido = headerAssinatura.replace("sha256=", "");
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(
HexFormat.of().parseHex(chaveValidacao), "HmacSHA256"
));
String digestCalculado = HexFormat.of().formatHex(mac.doFinal(body));
return MessageDigest.isEqual(
digestRecebido.getBytes(), digestCalculado.getBytes()
);
}
}