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, ×tamp)?;
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", ×tamp)
.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