Pular para o conteúdo principal

Alertas de Portadores

Os alertas gerados pela ferramenta antifraude são notificados por meio de Webhook. Para tanto, é necessário, por meio da equipe do suporte, configurar um endereço do endpoint por onde vamos notificar as notificações e também um secret_token que será utilizado para assinar a requisição.

Nesta notificação enviaremos informações dos alertas gerados, bem como de qual portador se trata, para que o cliente possa tomar alguma ação, por exemplo, enviar um push notification para o portador.

Requisição

Request Body
{
"alert_key": "123456",
"cardholder_id": "ef47bc3f-61ac-4b85-ad67-0cfa3a422201",
"company_name": "Cliente 1",
"irregularity_type" : "fraud",
"risk_level": "critical"
}

A requisição possui o formato acima e notifica a abertura de um novo alerta para um Portador - descrito pelo cardholder_id

Assinatura do Webhook

Para garantir que a requisição recebida no seu endpoint partiu dos nossos servidores, enviamos uma assinatura HMAC no header Signature. Você recalcula essa assinatura do seu lado e compara com a recebida — se forem iguais, a requisição é confiável.

Como a assinatura é calculada

Signature = HMAC-SHA1(signature_key, endpoint + method + payload) → hexadecimal

Os três componentes são concatenados nesta ordem, sem separador:

ComponenteO que é
endpointA URL completa do seu webhook, exatamente como foi configurada com o suporte (incluindo https:// e eventual query string).
methodO verbo HTTP em letras maiúsculas — sempre POST nas notificações de alerta.
payloadO corpo da requisição exatamente como recebido, byte a byte.
signature_keyO secret_token que você combinou com o suporte. É a chave do HMAC, não parte da mensagem.
Use o corpo bruto, nunca o JSON reserializado

A assinatura é calculada sobre os bytes exatos do corpo. Se você desserializar o JSON e serializar de novo antes de validar, a ordem das chaves e o espaçamento mudam, e a assinatura nunca vai bater.

Leia o corpo como string/bytes brutos primeiro, valide a assinatura, e só depois faça o parse. Nos exemplos abaixo isso aparece como request.data, file_get_contents('php://input'), req.rawBody etc.

Acentuação no payload

Nós serializamos o corpo com ensure_ascii=False, ou seja, caracteres acentuados vão como UTF-8 literal ("João"), e não escapados ("João"). Trate o corpo como UTF-8 ao calcular o HMAC — é o comportamento padrão em todas as linguagens abaixo, mas é a causa mais comum de assinatura divergente quando o company_name tem acento.

Exemplos de validação

import hashlib
import hmac

SIGNATURE_KEY = "YOUR_SECRET_TOKEN"
WEBHOOK_URL = "https://seu-dominio.com/webhooks/qitech/alertas"


def calculate_signature(endpoint: str, method: str, payload: str) -> str:
hmac_obj = hmac.new(
SIGNATURE_KEY.encode("utf-8"),
(endpoint + method + payload).encode("utf-8"),
hashlib.sha1,
)
return hmac_obj.hexdigest()


def is_valid(received_signature: str, raw_body: str) -> bool:
expected = calculate_signature(WEBHOOK_URL, "POST", raw_body)
# compare_digest evita ataques de temporização
return hmac.compare_digest(expected, received_signature)


# Exemplo com Flask
from flask import Flask, request

app = Flask(__name__)


@app.route("/webhooks/qitech/alertas", methods=["POST"])
def receive_alert():
raw_body = request.get_data(as_text=True) # corpo bruto, sem parse
received = request.headers.get("Signature", "")

if not is_valid(received, raw_body):
return "", 401

alert = request.get_json() # parse só depois de validar
print(alert["cardholder_id"], alert["risk_level"])
return "", 200
Assinatura não bate? Verifique nesta ordem
  1. O corpo foi reserializado? É a causa mais frequente. Use o corpo bruto.
  2. A URL está idêntica? Uma barra final a mais ou a menos (/alertas vs /alertas/) muda a assinatura. Use exatamente a URL configurada com o suporte.
  3. O método está em maiúsculas? Deve ser POST, não post.
  4. A ordem da concatenação está certa? É endpoint + method + payload, nessa ordem.
  5. O digest está em hexadecimal minúsculo? Não é Base64.

Retentativas

A notificação é considerada realizada quando recebe como resposta um HTTP Status 200. Caso as notificações falhem, serão feitas 5 retentativas, com os seguintes intervalos, até que um 200 seja retornado ou as tentativas terminem:

  • 30 segundos
  • 60 segundos
  • 120 segundos
  • 240 segundos
  • 360 segundos