New iFood Developer Portal
Access the new version of the iFood developer portal
logo
logo

Visão geral

URLs de webhooks ficam expostas na internet. Para garantir que uma request realmente veio do iFood, implementamos o header X-IFood-Signature em todas as requests de webhook. Esse header permite validar a autenticidade das mensagens recebidas.Rejeite requests com assinaturas inválidas. Essa validação é obrigatória para homologação do webhook. O iFood pode enviar eventos com assinaturas inválidas para testar sua integração. Essa checagem também protege contra fraudes. Auditamos todos os eventos entregues, as tentativas de entrega e os eventos descartados.

Validação

O webhook usa (HMAC) com SHA256 para assinar cada mensagem. O valor final é codificado em hexadecimal. A assinatura usa o clinte secret da aplicação (o mesmo utilizado para gerar tokens), disponível na aba "Credenciais" do Portal do Desenvolvedor. Cada request inclui essa assinatura no header X-IFood-Signature. Para validar, gere o HMAC da mensagem recebida usando seu secret (em local seguro) e compare com a assinatura recebida usando comparação segura (exemplo). A maioria das linguagens tem bibliotecas com suporte nativo:
Campos podem ser adicionados a qualquer evento a qualquer momento, sem causar breaking change. Por isso, valide a assinatura antes de converter o conteúdo da mensagem em objeto. Gere a assinatura usando o byte array do body exatamente como recebido.Esse cuidado é essencial em JSON, já que {"prop1": "value1", "prop2": "value2"} e {"prop2": "value2", "prop1": "value1"} são equivalentes para parsers, mas formam byte arrays diferentes. Caracteres especiais também podem variar conforme a biblioteca ou linguagem.

Exemplos

Todos os exemplos são válidos para o mesmo JSON, mas com diferentes formatações e devem ser suportados pela sua integração:
{
    "code":"PLC",
    "createdAt":"2023-02-20T18:19:03.20162269Z",
    "fullCode":"PLACED",
    "id":"a38ba215-f949-4b2c-982a-0582a9d0c10e",
    "merchantId":"cad65e8f-6fc6-438a-b159-e64a902a6b9a",
    "orderId":"2c97e104-35ed-4c18-85d7-854a40b6b9e3"
}
Os exemplos mostram como a formatação afeta a assinatura:
X-IFood-Signature: 6f9ed23a7b505a3b6907c5f6eb2ad1b056fbf35a643d365a9a072ed7aabca153. Payload:
{"code":"PLC","createdAt":"2023-02-20T18:19:03.20162269Z","fullCode":"PLACED","id":"a38ba215-f949-4b2c-982a-0582a9d0c10e","merchantId":"cad65e8f-6fc6-438a-b159-e64a902a6b9a","orderId":"2c97e104-35ed-4c18-85d7-854a40b6b9e3"}
'X-IFood-Signature: cf7e092c9148a48f5ee5f12b947f46b331eac6bf0745e1e1d0f3df722e219df3'. Payload:
{ "code":"PLC", "createdAt":"2023-02-20T18:19:03.20162269Z", "fullCode":"PLACED", "id":"a38ba215-f949-4b2c-982a-0582a9d0c10e", "merchantId":"cad65e8f-6fc6-438a-b159-e64a902a6b9a", "orderId":"2c97e104-35ed-4c18-85d7-854a40b6b9e3" }
'X-IFood-Signature: adf5446334f754e73588f3ae10b308306307f0c797f7f678912d740c6deddf6a'. Payload:
{
    "code":"PLC",
    "createdAt":"2023-02-20T18:19:03.20162269Z",
    "fullCode":"PLACED",
    "id":"a38ba215-f949-4b2c-982a-0582a9d0c10e",
    "merchantId":"cad65e8f-6fc6-438a-b159-e64a902a6b9a",
    "orderId":"2c97e104-35ed-4c18-85d7-854a40b6b9e3"
}
X-IFood-Signature: e2d26f22f89932ff3d23a699031b22d6f30323501430dc08c3a971dd875e23b5. Payload:
{"merchantId":"cad65e8f-6fc6-438a-b159-e64a902a6b9a","orderId":"2c97e104-35ed-4c18-85d7-854a40b6b9e3","code":"PLC","createdAt":"2023-02-20T18:19:03.20162269Z","fullCode":"PLACED","id":"a38ba215-f949-4b2c-982a-0582a9d0c10e"}
Segue um snippet Java de como fazer a validação da mensagem:
private String bytesToHexString(byte[] bytes) {
    var sb = new StringBuilder();
    for (var b : bytes) {
        var hex = String.format("%02x", b);
        sb.append(hex);
    }
    return sb.toString();
}

private boolean verifyHmacSHA256(String secret, String data, String expectedSignature) {
    try {
        var mac = Mac.getInstance("HmacSHA256");
        var secretKeySpec = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
        mac.init(secretKeySpec);
        var hmacBytes = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
        return bytesToHexString(hmacBytes).equals(expectedSignature);
    } catch (NoSuchAlgorithmException | InvalidKeyException e) {
        return false;
    }
}
Veja um exemplo o snippet de um enpoint em Go:
http.HandleFunc("/webhook", func(w http.ResponseWriter, r *http.Request) {
  body, err := io.ReadAll(r.Body)
  if err != nil {
    fmt.Printf("could not read body")
    w.WriteHeader(400)
    return
  }

  headerSignature := r.Header.Get("X-IFood-Signature")

  signature, err := hex.DecodeString(headerSignature)
  if err != nil {
    panic(err)
  }

  hasher := hmac.New(sha256.New, SECRET)
  hasher.Write(body)
  expected := hasher.Sum(nil)

  if !hmac.Equal(expected, signature) {
    fmt.Printf("invalid signature")
    w.WriteHeader(401)
    return
  }

  // Message validated...
})

Veja também