{"templateId":"markdown","sharedDataIds":{},"props":{"metadata":{"markdoc":{"tagList":["admonition"]},"type":"markdown"},"seo":{"title":"Validação HMAC-SHA256 — Webhooks","description":"APIs públicas da Movvia para parceiros, estabelecimentos comerciais e clientes de dados veiculares.","meta":[{"name":"theme-color","content":"#7E3DEE"},{"name":"apple-mobile-web-app-title","content":"Movvia Docs"},{"name":"application-name","content":"Movvia Docs"}],"llmstxt":{"hide":false,"sections":[{"title":"Table of contents","includeFiles":["**/*"],"excludeFiles":[]}],"excludeFiles":[]}},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"validação-hmac-sha256--webhooks","__idx":0},"children":["Validação HMAC-SHA256 — Webhooks"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Os webhooks entregues a Parceiros são assinados com ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["HMAC-SHA256"]},". Valide a assinatura antes de processar qualquer payload para garantir que a requisição veio da Movvia."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"como-funciona","__idx":1},"children":["Como funciona"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A cada entrega de webhook, a Movvia inclui o header ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["X-Movvia-Signature"]}," com o HMAC calculado sobre o corpo bruto da requisição usando o segredo compartilhado negociado no onboarding."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"X-Movvia-Signature: sha256={hex_do_hmac}\n"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"algoritmo-de-validação","__idx":2},"children":["Algoritmo de validação"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Leia o corpo da requisição como bytes brutos (não faça parse de JSON antes)."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Calcule ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["HMAC-SHA256(corpo_bytes, segredo)"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Compare o resultado (em hex) com o valor de ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["X-Movvia-Signature"]}," após o prefixo ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sha256="]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Use comparação em tempo constante (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["hmac.compare_digest"]}," ou equivalente) para evitar timing attacks."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Se os valores coincidirem, processe o evento. Caso contrário, retorne ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["401"]}," e descarte."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"exemplos-de-validação","__idx":3},"children":["Exemplos de validação"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"typescript","header":{"controls":{"copy":{}}},"source":"import crypto from 'crypto';\n\nfunction validarAssinatura(\n  corpo: Buffer,\n  headerAssinatura: string,\n  segredo: string,\n): boolean {\n  const esperado = 'sha256=' + crypto\n    .createHmac('sha256', segredo)\n    .update(corpo)\n    .digest('hex');\n  return crypto.timingSafeEqual(\n    Buffer.from(headerAssinatura),\n    Buffer.from(esperado),\n  );\n}\n","lang":"typescript"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"import hmac\nimport hashlib\n\ndef validar_assinatura(corpo: bytes, header_assinatura: str, segredo: str) -> bool:\n    esperado = 'sha256=' + hmac.new(\n        segredo.encode(),\n        corpo,\n        hashlib.sha256,\n    ).hexdigest()\n    return hmac.compare_digest(header_assinatura, esperado)\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"java","header":{"controls":{"copy":{}}},"source":"import javax.crypto.Mac;\nimport javax.crypto.spec.SecretKeySpec;\nimport java.security.MessageDigest;\n\npublic boolean validarAssinatura(byte[] corpo, String headerAssinatura, String segredo) throws Exception {\n    Mac mac = Mac.getInstance(\"HmacSHA256\");\n    mac.init(new SecretKeySpec(segredo.getBytes(), \"HmacSHA256\"));\n    String esperado = \"sha256=\" + bytesToHex(mac.doFinal(corpo));\n    return MessageDigest.isEqual(headerAssinatura.getBytes(), esperado.getBytes());\n}\n","lang":"java"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"catálogo-de-eventos","__idx":4},"children":["Catálogo de eventos"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Os eventos seguem o padrão ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["pe.<modulo>.<evento>"]},". Exemplos:"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Evento"},"children":["Evento"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Descrição"},"children":["Descrição"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["pe.transacao.recebida"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Nova passagem detectada para uma placa cadastrada."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["pe.pedidos.confirmado"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Pedido confirmado pelo parceiro."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["pe.pedidos.cancelado"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Pedido cancelado."]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["O catálogo completo está na ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/apis/parceiros/openapi"},"children":["Referência da API de Parceiros"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"retry-e-entrega-garantida","__idx":5},"children":["Retry e entrega garantida"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A Movvia retentar a entrega com backoff exponencial por até 24 horas em caso de falha (timeouts ou respostas ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["5xx"]},"). Implemente idempotência no seu receptor para lidar com entregas duplicadas em caso de retentativa."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Veja: ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/compartilhado/seguranca/idempotencia"},"children":["Idempotência"]}]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"success","name":"Endpoint de recepção"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Seu endpoint de webhook deve responder com ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["200 OK"]}," dentro de 5 segundos. Processe o evento de forma assíncrona se necessário."]}]}]},"headings":[{"value":"Validação HMAC-SHA256 — Webhooks","id":"validação-hmac-sha256--webhooks","depth":1},{"value":"Como funciona","id":"como-funciona","depth":2},{"value":"Algoritmo de validação","id":"algoritmo-de-validação","depth":2},{"value":"Exemplos de validação","id":"exemplos-de-validação","depth":2},{"value":"Catálogo de eventos","id":"catálogo-de-eventos","depth":2},{"value":"Retry e entrega garantida","id":"retry-e-entrega-garantida","depth":2}],"frontmatter":{"title":"Validação HMAC-SHA256 — Webhooks","description":"Como validar a assinatura HMAC-SHA256 dos webhooks entregues pela API de Parceiros Movvia.","seo":{"title":"Validação HMAC-SHA256 — Webhooks"}},"lastModified":"2026-04-25T15:17:56.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/compartilhado/seguranca/hmac","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}