Legal Cookies
API

Ejemplos de Código

Ejemplos completos de integración en Node.js, Python, PHP, C#, Rust y cURL

Ejemplos de Código

Esta sección contiene ejemplos completos y funcionales para integrar la API de Legal Cookies en diferentes lenguajes de programación. Cada ejemplo incluye una clase cliente reutilizable que encapsula la lógica de autenticación HMAC-SHA256 y el envío de peticiones.

Todos los ejemplos siguen el mismo patrón: crear un cliente con tus credenciales, llamar al método analyze con la URL a analizar, y procesar la respuesta. Las credenciales deben almacenarse en variables de entorno por seguridad.

Node.js

Node.js es una de las opciones más populares para integrar APIs debido a su ecosistema de paquetes y facilidad de uso. El ejemplo utiliza el módulo crypto nativo para calcular los hashes y la firma HMAC, sin necesidad de dependencias externas.

La clase LegalCookiesClient encapsula toda la lógica de autenticación, permitiéndote centrarte en el uso de la API. El método analyze es asíncrono y devuelve una promesa con el resultado del análisis.

const crypto = require('crypto');

class LegalCookiesClient {
  constructor(apiKey, apiSecret, baseUrl = 'https://legalcookies.es') {
    this.apiKey = apiKey;
    this.apiSecret = apiSecret;
    this.baseUrl = baseUrl;
  }

  createSignature(body, timestamp) {
    const bodyHash = crypto
      .createHash('sha256')
      .update(body)
      .digest('hex');

    const stringToSign = `${timestamp}.${bodyHash}`;

    const secretHash = crypto
      .createHash('sha256')
      .update(this.apiSecret)
      .digest('hex');

    return crypto
      .createHmac('sha256', secretHash)
      .update(stringToSign)
      .digest('hex');
  }

  async analyze(url) {
    const body = JSON.stringify({ url });
    const timestamp = Date.now().toString();
    const signature = this.createSignature(body, timestamp);

    const response = await fetch(`${this.baseUrl}/api/v1/analyze`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-Api-Key': this.apiKey,
        'X-Timestamp': timestamp,
        'X-Signature': signature,
      },
      body,
    });

    const data = await response.json();

    if (!response.ok) {
      throw new Error(`API Error: ${data.error} (${data.code})`);
    }

    return data;
  }
}

// Uso
async function main() {
  const client = new LegalCookiesClient(
    process.env.LEGAL_COOKIES_API_KEY,
    process.env.LEGAL_COOKIES_API_SECRET
  );

  try {
    const result = await client.analyze('https://example.com');

    console.log('Score:', result.data.score);
    console.log('Zona:', result.data.zone);
    console.log('Veredicto:', result.data.verdict);
    console.log('Cookies problematicas:', result.data.stats.problematicCookies);

    if (result.data.recommendations.length > 0) {
      console.log('\nRecomendaciones:');
      result.data.recommendations.forEach((r, i) => {
        console.log(`  ${i + 1}. ${r}`);
      });
    }
  } catch (error) {
    console.error('Error:', error.message);
  }
}

main();

Python

Python es ideal para scripts de automatización y análisis de datos. El ejemplo utiliza las librerías estándar hashlib y hmac para la criptografía, y requests para las peticiones HTTP. Si no tienes requests instalado, puedes instalarlo con pip install requests.

La clase LegalCookiesClient sigue el mismo patrón que el ejemplo de Node.js, facilitando la migración entre lenguajes si es necesario.

import hashlib
import hmac
import json
import time
import requests
import os


class LegalCookiesClient:
    def __init__(self, api_key: str, api_secret: str, base_url: str = 'https://legalcookies.es'):
        self.api_key = api_key
        self.api_secret = api_secret
        self.base_url = base_url

    def _create_signature(self, body: str, timestamp: str) -> str:
        # Hash del body
        body_hash = hashlib.sha256(body.encode()).hexdigest()

        # String a firmar
        string_to_sign = f"{timestamp}.{body_hash}"

        # Hash del secret
        secret_hash = hashlib.sha256(self.api_secret.encode()).hexdigest()

        # Firma HMAC
        signature = hmac.new(
            secret_hash.encode(),
            string_to_sign.encode(),
            hashlib.sha256
        ).hexdigest()

        return signature

    def analyze(self, url: str) -> dict:
        body = json.dumps({'url': url})
        timestamp = str(int(time.time() * 1000))
        signature = self._create_signature(body, timestamp)

        headers = {
            'Content-Type': 'application/json',
            'X-Api-Key': self.api_key,
            'X-Timestamp': timestamp,
            'X-Signature': signature,
        }

        response = requests.post(
            f"{self.base_url}/api/v1/analyze",
            headers=headers,
            data=body,
            timeout=60
        )

        data = response.json()

        if not response.ok:
            raise Exception(f"API Error: {data.get('error')} ({data.get('code')})")

        return data


# Uso
def main():
    client = LegalCookiesClient(
        api_key=os.environ['LEGAL_COOKIES_API_KEY'],
        api_secret=os.environ['LEGAL_COOKIES_API_SECRET']
    )

    try:
        result = client.analyze('https://example.com')

        print(f"Score: {result['data']['score']}")
        print(f"Zona: {result['data']['zone']}")
        print(f"Veredicto: {result['data']['verdict']}")
        print(f"Cookies problematicas: {result['data']['stats']['problematicCookies']}")

        recommendations = result['data'].get('recommendations', [])
        if recommendations:
            print("\nRecomendaciones:")
            for i, rec in enumerate(recommendations, 1):
                print(f"  {i}. {rec}")

    except Exception as e:
        print(f"Error: {e}")


if __name__ == '__main__':
    main()

PHP

PHP sigue siendo uno de los lenguajes más utilizados en desarrollo web, especialmente en entornos de hosting compartido. El ejemplo utiliza las funciones nativas hash() y hash_hmac() para la criptografía, y cURL para las peticiones HTTP.

La clase LegalCookiesClient está diseñada para ser compatible con PHP 7.4 o superior, utilizando tipos estrictos para mayor seguridad.

<?php

class LegalCookiesClient {
    private string $apiKey;
    private string $apiSecret;
    private string $baseUrl;

    public function __construct(
        string $apiKey,
        string $apiSecret,
        string $baseUrl = 'https://legalcookies.es'
    ) {
        $this->apiKey = $apiKey;
        $this->apiSecret = $apiSecret;
        $this->baseUrl = $baseUrl;
    }

    private function createSignature(string $body, string $timestamp): string {
        // Hash del body
        $bodyHash = hash('sha256', $body);

        // String a firmar
        $stringToSign = "{$timestamp}.{$bodyHash}";

        // Hash del secret
        $secretHash = hash('sha256', $this->apiSecret);

        // Firma HMAC
        return hash_hmac('sha256', $stringToSign, $secretHash);
    }

    public function analyze(string $url): array {
        $body = json_encode(['url' => $url]);
        $timestamp = (string)(round(microtime(true) * 1000));
        $signature = $this->createSignature($body, $timestamp);

        $ch = curl_init();

        curl_setopt_array($ch, [
            CURLOPT_URL => "{$this->baseUrl}/api/v1/analyze",
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => $body,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 60,
            CURLOPT_HTTPHEADER => [
                'Content-Type: application/json',
                "X-Api-Key: {$this->apiKey}",
                "X-Timestamp: {$timestamp}",
                "X-Signature: {$signature}",
            ],
        ]);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        $data = json_decode($response, true);

        if ($httpCode !== 200) {
            throw new Exception("API Error: {$data['error']} ({$data['code']})");
        }

        return $data;
    }
}

// Uso
$client = new LegalCookiesClient(
    getenv('LEGAL_COOKIES_API_KEY'),
    getenv('LEGAL_COOKIES_API_SECRET')
);

try {
    $result = $client->analyze('https://example.com');

    echo "Score: {$result['data']['score']}\n";
    echo "Zona: {$result['data']['zone']}\n";
    echo "Veredicto: {$result['data']['verdict']}\n";
    echo "Cookies problematicas: {$result['data']['stats']['problematicCookies']}\n";

    if (!empty($result['data']['recommendations'])) {
        echo "\nRecomendaciones:\n";
        foreach ($result['data']['recommendations'] as $i => $rec) {
            echo "  " . ($i + 1) . ". {$rec}\n";
        }
    }
} catch (Exception $e) {
    echo "Error: " . $e->getMessage() . "\n";
}

C# (.NET)

C# es la opción natural para aplicaciones .NET, tanto en entornos Windows como multiplataforma con .NET Core/5+. El ejemplo utiliza las clases del namespace System.Security.Cryptography para los cálculos criptográficos y HttpClient para las peticiones HTTP.

El código está diseñado para .NET 6 o superior, aprovechando las nuevas APIs simplificadas de criptografía. Para versiones anteriores, puede ser necesario ajustar el uso de SHA256.HashData().

using System.Security.Cryptography;
using System.Text;
using System.Text.Json;

public class LegalCookiesClient
{
    private readonly string _apiKey;
    private readonly string _apiSecret;
    private readonly string _baseUrl;
    private readonly HttpClient _httpClient;

    public LegalCookiesClient(string apiKey, string apiSecret, string baseUrl = "https://legalcookies.es")
    {
        _apiKey = apiKey;
        _apiSecret = apiSecret;
        _baseUrl = baseUrl;
        _httpClient = new HttpClient();
    }

    private string CreateSignature(string body, string timestamp)
    {
        // Hash del body
        var bodyHash = Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(body))).ToLower();

        // String a firmar
        var stringToSign = $"{timestamp}.{bodyHash}";

        // Hash del secret
        var secretHash = Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(_apiSecret))).ToLower();

        // Firma HMAC
        using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secretHash));
        return Convert.ToHexString(hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign))).ToLower();
    }

    public async Task<JsonElement> AnalyzeAsync(string url)
    {
        var body = JsonSerializer.Serialize(new { url });
        var timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds().ToString();
        var signature = CreateSignature(body, timestamp);

        var request = new HttpRequestMessage(HttpMethod.Post, $"{_baseUrl}/api/v1/analyze");
        request.Headers.Add("X-Api-Key", _apiKey);
        request.Headers.Add("X-Timestamp", timestamp);
        request.Headers.Add("X-Signature", signature);
        request.Content = new StringContent(body, Encoding.UTF8, "application/json");

        var response = await _httpClient.SendAsync(request);
        var responseBody = await response.Content.ReadAsStringAsync();
        var data = JsonSerializer.Deserialize<JsonElement>(responseBody);

        if (!response.IsSuccessStatusCode)
        {
            var error = data.GetProperty("error").GetString();
            var code = data.GetProperty("code").GetString();
            throw new Exception($"API Error: {error} ({code})");
        }

        return data;
    }
}

// Uso
public class Program
{
    public static async Task Main()
    {
        var client = new LegalCookiesClient(
            Environment.GetEnvironmentVariable("LEGAL_COOKIES_API_KEY")!,
            Environment.GetEnvironmentVariable("LEGAL_COOKIES_API_SECRET")!
        );

        try
        {
            var result = await client.AnalyzeAsync("https://example.com");
            var data = result.GetProperty("data");

            Console.WriteLine($"Score: {data.GetProperty("score")}");
            Console.WriteLine($"Zona: {data.GetProperty("zone")}");
            Console.WriteLine($"Veredicto: {data.GetProperty("verdict")}");
            Console.WriteLine($"Cookies problematicas: {data.GetProperty("stats").GetProperty("problematicCookies")}");

            var recommendations = data.GetProperty("recommendations");
            if (recommendations.GetArrayLength() > 0)
            {
                Console.WriteLine("\nRecomendaciones:");
                var i = 1;
                foreach (var rec in recommendations.EnumerateArray())
                {
                    Console.WriteLine($"  {i++}. {rec.GetString()}");
                }
            }
        }
        catch (Exception e)
        {
            Console.WriteLine($"Error: {e.Message}");
        }
    }
}

Rust

Rust es ideal para aplicaciones que requieren alto rendimiento y seguridad de memoria. El ejemplo utiliza las crates sha2 y hmac para la criptografía, reqwest para las peticiones HTTP, y tokio como runtime asíncrono.

Añade estas dependencias a tu Cargo.toml:

[dependencies]
tokio = { version = "1", features = ["full"] }
reqwest = { version = "0.11", features = ["json"] }
sha2 = "0.10"
hmac = "0.12"
hex = "0.4"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
use hmac::{Hmac, Mac};
use sha2::{Digest, Sha256};
use std::time::{SystemTime, UNIX_EPOCH};
use std::env;

type HmacSha256 = Hmac<Sha256>;

pub struct LegalCookiesClient {
    api_key: String,
    api_secret: String,
    base_url: String,
    client: reqwest::Client,
}

impl LegalCookiesClient {
    pub fn new(api_key: String, api_secret: String) -> Self {
        Self {
            api_key,
            api_secret,
            base_url: "https://legalcookies.es".to_string(),
            client: reqwest::Client::new(),
        }
    }

    fn create_signature(&self, body: &str, timestamp: &str) -> Result<String, Box<dyn std::error::Error>> {
        // Hash del body
        let body_hash = hex::encode(Sha256::digest(body.as_bytes()));

        // String a firmar
        let string_to_sign = format!("{}.{}", timestamp, body_hash);

        // Hash del secret
        let secret_hash = hex::encode(Sha256::digest(self.api_secret.as_bytes()));

        // Firma HMAC
        let mut mac = HmacSha256::new_from_slice(secret_hash.as_bytes())?;
        mac.update(string_to_sign.as_bytes());
        Ok(hex::encode(mac.finalize().into_bytes()))
    }

    pub async fn analyze(&self, url: &str) -> Result<serde_json::Value, Box<dyn std::error::Error>> {
        let body = serde_json::json!({ "url": url }).to_string();
        let timestamp = SystemTime::now()
            .duration_since(UNIX_EPOCH)?
            .as_millis()
            .to_string();
        let signature = self.create_signature(&body, &timestamp)?;

        let response = self.client
            .post(format!("{}/api/v1/analyze", self.base_url))
            .header("Content-Type", "application/json")
            .header("X-Api-Key", &self.api_key)
            .header("X-Timestamp", &timestamp)
            .header("X-Signature", &signature)
            .body(body)
            .send()
            .await?;

        let data: serde_json::Value = response.json().await?;

        if data["success"].as_bool() != Some(true) {
            return Err(format!(
                "API Error: {} ({})",
                data["error"], data["code"]
            ).into());
        }

        Ok(data)
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = LegalCookiesClient::new(
        env::var("LEGAL_COOKIES_API_KEY")?,
        env::var("LEGAL_COOKIES_API_SECRET")?,
    );

    let result = client.analyze("https://example.com").await?;
    let data = &result["data"];

    println!("Score: {}", data["score"]);
    println!("Zona: {}", data["zone"]);
    println!("Veredicto: {}", data["verdict"]);
    println!("Cookies problematicas: {}", data["stats"]["problematicCookies"]);

    if let Some(recommendations) = data["recommendations"].as_array() {
        if !recommendations.is_empty() {
            println!("\nRecomendaciones:");
            for (i, rec) in recommendations.iter().enumerate() {
                println!("  {}. {}", i + 1, rec.as_str().unwrap_or(""));
            }
        }
    }

    Ok(())
}

cURL (Bash)

Para scripts de shell o pruebas rápidas, cURL combinado con OpenSSL permite realizar peticiones autenticadas sin necesidad de instalar dependencias adicionales. Este enfoque es especialmente útil para pipelines de CI/CD o automatización en servidores.

El script calcula la firma HMAC usando las herramientas de línea de comandos de OpenSSL, que están disponibles en la mayoría de sistemas Unix/Linux.

#!/bin/bash

# Configuracion
API_KEY="${LEGAL_COOKIES_API_KEY}"
API_SECRET="${LEGAL_COOKIES_API_SECRET}"
BASE_URL="https://legalcookies.es"
URL_TO_ANALYZE="$1"

if [ -z "$URL_TO_ANALYZE" ]; then
    echo "Uso: $0 <url>"
    exit 1
fi

# Preparar body y timestamp
BODY="{\"url\":\"${URL_TO_ANALYZE}\"}"
TIMESTAMP=$(date +%s%3N)

# Calcular firma
BODY_HASH=$(echo -n "$BODY" | openssl dgst -sha256 | awk '{print $2}')
STRING_TO_SIGN="${TIMESTAMP}.${BODY_HASH}"
SECRET_HASH=$(echo -n "$API_SECRET" | openssl dgst -sha256 | awk '{print $2}')
SIGNATURE=$(echo -n "$STRING_TO_SIGN" | openssl dgst -sha256 -hmac "$SECRET_HASH" | awk '{print $2}')

# Ejecutar request
RESPONSE=$(curl -s -X POST "${BASE_URL}/api/v1/analyze" \
    -H "Content-Type: application/json" \
    -H "X-Api-Key: ${API_KEY}" \
    -H "X-Timestamp: ${TIMESTAMP}" \
    -H "X-Signature: ${SIGNATURE}" \
    -d "$BODY")

# Mostrar resultado formateado con jq
echo "$RESPONSE" | jq '.'

Uso manual con cURL

Si solo necesitas hacer una petición puntual para probar la API, puedes calcular la firma manualmente en la terminal. Este enfoque es útil para depuración o verificación rápida de credenciales.

# Variables
API_KEY="lc_pk_xxxxxxxxxxxxxxxxxxxx"
API_SECRET="your_api_secret"
URL="https://example.com"

# Calcular componentes
BODY='{"url":"'"$URL"'"}'
TIMESTAMP=$(date +%s%3N)
BODY_HASH=$(echo -n "$BODY" | openssl dgst -sha256 | awk '{print $2}')
SECRET_HASH=$(echo -n "$API_SECRET" | openssl dgst -sha256 | awk '{print $2}')
SIGNATURE=$(echo -n "${TIMESTAMP}.${BODY_HASH}" | openssl dgst -sha256 -hmac "$SECRET_HASH" | awk '{print $2}')

# Ejecutar
curl -X POST "https://legalcookies.es/api/v1/analyze" \
    -H "Content-Type: application/json" \
    -H "X-Api-Key: $API_KEY" \
    -H "X-Timestamp: $TIMESTAMP" \
    -H "X-Signature: $SIGNATURE" \
    -d "$BODY"

Manejo de errores

Un buen manejo de errores es esencial para aplicaciones de producción. El siguiente ejemplo en Node.js muestra cómo implementar reintentos automáticos con backoff exponencial, distinguiendo entre errores que pueden reintentarse (como rate limits o errores de red) y errores que no (como credenciales inválidas).

Este patrón puede adaptarse fácilmente a cualquier otro lenguaje de programación.

async function analyzeWithRetry(client, url, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await client.analyze(url);
    } catch (error) {
      // Errores que no vale la pena reintentar
      const noRetry = ['INVALID_API_KEY', 'INVALID_SIGNATURE', 'ACCOUNT_'];
      if (noRetry.some(code => error.message.includes(code))) {
        throw error;
      }

      // Rate limit: esperar y reintentar
      if (error.message.includes('LIMIT_EXCEEDED')) {
        console.log('Rate limit alcanzado. Esperando 60s...');
        await new Promise(r => setTimeout(r, 60000));
        continue;
      }

      // Otros errores: reintentar con backoff exponencial
      if (attempt < maxRetries) {
        const waitTime = Math.pow(2, attempt) * 1000;
        console.log(`Reintentando en ${waitTime / 1000}s...`);
        await new Promise(r => setTimeout(r, waitTime));
        continue;
      }

      throw error;
    }
  }
}

Integración CI/CD

La integración en pipelines de CI/CD permite automatizar la verificación de compliance como parte del proceso de desarrollo. El siguiente ejemplo muestra cómo configurar un workflow de GitHub Actions que analiza tu sitio web cada lunes y falla si el score está en zona roja.

Puedes adaptar este workflow para ejecutarse en cada pull request, antes de cada deploy, o con cualquier otra frecuencia que se adapte a tus necesidades.

name: Cookie Compliance Check

on:
  schedule:
    - cron: '0 9 * * 1'  # Cada lunes a las 9:00
  workflow_dispatch:

jobs:
  check-compliance:
    runs-on: ubuntu-latest
    steps:
      - name: Check website compliance
        env:
          API_KEY: ${{ secrets.LEGAL_COOKIES_API_KEY }}
          API_SECRET: ${{ secrets.LEGAL_COOKIES_API_SECRET }}
        run: |
          BODY='{"url":"https://your-website.com"}'
          TIMESTAMP=$(date +%s%3N)
          BODY_HASH=$(echo -n "$BODY" | openssl dgst -sha256 | awk '{print $2}')
          SECRET_HASH=$(echo -n "$API_SECRET" | openssl dgst -sha256 | awk '{print $2}')
          SIGNATURE=$(echo -n "${TIMESTAMP}.${BODY_HASH}" | openssl dgst -sha256 -hmac "$SECRET_HASH" | awk '{print $2}')

          RESPONSE=$(curl -s -X POST "https://legalcookies.es/api/v1/analyze" \
            -H "Content-Type: application/json" \
            -H "X-Api-Key: $API_KEY" \
            -H "X-Timestamp: $TIMESTAMP" \
            -H "X-Signature: $SIGNATURE" \
            -d "$BODY")

          SCORE=$(echo "$RESPONSE" | jq -r '.data.score')
          ZONE=$(echo "$RESPONSE" | jq -r '.data.zone')

          echo "Score: $SCORE"
          echo "Zone: $ZONE"

          if [ "$ZONE" = "red" ]; then
            echo "::error::Compliance score is in red zone ($SCORE)"
            exit 1
          fi