Com a Wompi você assina o pagamento no seu servidor com um SHA-256 de referência, valor em centavos, moeda e o seu segredo de integridade, e verifica cada evento recalculando um SHA-256 com as propriedades que o evento declara, o seu timestamp e o seu segredo de eventos.
1. Duas assinaturas diferentes, dois segredos diferentes#
Integrar a Wompi em um site implica duas verificações que se confundem com facilidade. A assinatura de integridade protege o pagamento que o seu servidor entrega ao checkout: evita que alguém altere o valor no navegador. A verificação do evento protege o seu servidor: comprova que a notificação de uma mudança de estado vem da Wompi e não de um terceiro.
Cada uma usa um segredo diferente, que não é nem a chave pública nem a privada. Segundo a documentação da Wompi, ambos estão no painel de comerciantes, na seção de segredos para integração técnica, e não devem sair do seu servidor.
Documentação: Wompi: Widget e Checkout Web (assinatura de integridade, espanhol) ↗ · Wompi: Eventos (verificação do checksum, espanhol) ↗
2. A assinatura de integridade do checkout#
A documentação indica que a assinatura é o SHA-256, em hexadecimal, da concatenação da referência, do valor em centavos, da moeda e do segredo de integridade. Se o pagamento tem data de expiração, essa data vai entre a moeda e o segredo. O valor vai em centavos: 95.000 pesos são enviados como 9500000, e a única moeda disponível é COP.
| Dado | Valor | Nota |
|---|---|---|
| Referência | sk8-438k4-xmxm392-sn2m | O seu identificador único do pagamento |
| Valor | 2490000 | $24.900 COP em centavos |
| Moeda | COP | A única disponível hoje |
| Segredo | prod_integrity_… | Nunca no navegador |
| Texto | sk8-438k4-xmxm392-sn2m2490000COPprod_integrity_… | Aplica-se SHA-256 em hexadecimal |
A tabela reconstrói o exemplo da documentação: uma referência, um valor de 24.900 pesos (2490000 centavos) e o segredo de exemplo. O código a seguir produz exatamente o hash que a documentação publica para esse texto.
3. Verificar um evento (webhook)#
Para verificar um evento, a documentação indica recalcular o checksum assim: ler a lista signature.properties do próprio evento; buscar cada caminho dentro do campo data e concatenar os valores nessa ordem; acrescentar o timestamp do evento e, por último, o segredo de eventos; aplicar SHA-256 e comparar com o checksum recebido, sem distinguir maiúsculas de minúsculas. Se não coincidirem, ignore o evento.
A lista de propriedades pode variar, então não a escreva fixa no seu código: leia-a do evento, como fazem as funções abaixo. As funções comparam em tempo constante.
import hashlib
import hmac
def assinatura_integridade(referencia, valor_em_centavos, moeda, segredo, expiracao=""):
"""Assinatura do checkout: SHA-256 de referência + valor + moeda [+ expiração] + segredo."""
texto = f"{referencia}{valor_em_centavos}{moeda}{expiracao}{segredo}"
return hashlib.sha256(texto.encode()).hexdigest()
def valor_em(data, caminho):
"""Lê um caminho como 'transaction.status' dentro do campo data do evento."""
atual = data
for parte in caminho.split("."):
atual = atual[parte]
return str(atual)
def evento_autentico(evento, segredo_eventos):
"""Recalcula o checksum com as propriedades que o próprio evento declara."""
firma = evento["signature"]
valores = "".join(valor_em(evento["data"], p) for p in firma["properties"])
texto = f"{valores}{evento['timestamp']}{segredo_eventos}"
esperado = hashlib.sha256(texto.encode()).hexdigest()
return hmac.compare_digest(esperado.lower(), firma["checksum"].lower())
# Exemplo da documentação da Wompi: $24.900 COP são 2490000 centavos
print(assinatura_integridade("sk8-438k4-xmxm392-sn2m", 2490000, "COP", "prod_integrity_Z5mMke9x0k8gpErbDqwrJXMqsI6SFli6"))
# Evento de teste com valores e segredo inventados
segredo = "test_events_segredo_de_exemplo"
evento = {
"data": {"transaction": {"id": "T-0001", "status": "APPROVED", "amount_in_cents": 9500000}},
"timestamp": 1760000000,
"signature": {"properties": ["transaction.id", "transaction.status", "transaction.amount_in_cents"]},
}
texto = "T-0001" + "APPROVED" + "9500000" + "1760000000" + segredo
evento["signature"]["checksum"] = hashlib.sha256(texto.encode()).hexdigest().upper()
print(evento_autentico(evento, segredo), evento_autentico(evento, "outro_segredo"))import { createHash, timingSafeEqual } from "node:crypto";
const sha256 = texto => createHash("sha256").update(texto).digest("hex");
function assinaturaIntegridade(referencia, valorEmCentavos, moeda, segredo, expiracao = "") {
return sha256(`${referencia}${valorEmCentavos}${moeda}${expiracao}${segredo}`);
}
function valorEm(data, caminho) {
return String(caminho.split(".").reduce((atual, parte) => atual[parte], data));
}
function eventoAutentico(evento, segredoEventos) {
const { properties, checksum } = evento.signature;
const valores = properties.map(p => valorEm(evento.data, p)).join("");
const esperado = Buffer.from(sha256(`${valores}${evento.timestamp}${segredoEventos}`));
const recebido = Buffer.from(checksum.toLowerCase());
return esperado.length === recebido.length && timingSafeEqual(esperado, recebido);
}
console.log(assinaturaIntegridade("sk8-438k4-xmxm392-sn2m", 2490000, "COP", "prod_integrity_Z5mMke9x0k8gpErbDqwrJXMqsI6SFli6"));
const segredo = "test_events_segredo_de_exemplo";
const evento = {
data: { transaction: { id: "T-0001", status: "APPROVED", amount_in_cents: 9500000 } },
timestamp: 1760000000,
signature: { properties: ["transaction.id", "transaction.status", "transaction.amount_in_cents"] },
};
evento.signature.checksum = sha256("T-0001" + "APPROVED" + "9500000" + "1760000000" + segredo).toUpperCase();
console.log(eventoAutentico(evento, segredo), eventoAutentico(evento, "outro_segredo"));Um detalhe importante: ao recalcular o exemplo de eventos da documentação com os valores que ela lista, não obtivemos o checksum que publica. Por isso o evento de teste deste guia usa valores e segredo inventados, e recomendamos validar a sua implementação com eventos reais do ambiente de testes (sandbox) antes de ir para produção.
Documentação: Python: hashlib ↗ · Node.js: crypto ↗
4. O que fazer depois de verificar#
- Responda rápido ao webhook e faça o trabalho pesado depois, para não esgotar o tempo de espera de quem envia o evento.
- Trate os eventos como idempotentes: guarde o id da transação e não aplique duas vezes o mesmo estado.
- Compare o valor e a referência do evento com o seu pedido, não confie só no estado.
- Considere rejeitar eventos com um timestamp muito antigo; é uma medida adicional sua, não um requisito da documentação.
- Registre os eventos descartados sem guardar os segredos.
Para se aprofundarFilas, retries e idempotência: evite processar duas vezes
5. Erros frequentes#
- Calcular a assinatura de integridade no navegador e expor o segredo.
- Enviar o valor em pesos em vez de centavos.
- Misturar o segredo de integridade com o de eventos.
- Escrever a lista de propriedades do evento no código em vez de lê-la do evento.
- Comparar o checksum com == em vez de uma comparação de tempo constante.
Para se aprofundarVariáveis de ambiente e DATABASE_URL em produção: guia completo
6. Mantenha seus números em dia#
A integração é descrita como estava a documentação da Wompi em 18 de outubro de 2026. Os campos, as moedas disponíveis ou o algoritmo podem mudar; revise a documentação oficial e teste no sandbox antes de implantar.
- Revise a documentação da Wompi antes de cada mudança de integração.
- Teste com transações de sandbox aprovadas, recusadas e anuladas.
- Troque os segredos se suspeitar que vazaram.
Fontes e escopo
Documentação consultada em 18 de outubro de 2026. Os exemplos e critérios de decisão são propostas editoriais; adapte-os ao contrato da sua aplicação e valide-os no seu ambiente de testes autorizado.