FastComments.com


Con FastComments es posible invocar un endpoint de la API cada vez que se añade, actualiza o elimina un comentario de nuestro sistema.

Lo logramos con webhooks asíncronos a través de HTTP/HTTPS.


Qué son los webhooks Internal Link


Un Webhook es un mecanismo, o una integración, entre dos sistemas donde el "productor" (FastComments) desencadena un evento que el "consumidor" (Usted) consume mediante una llamada a la API.


Eventos y recursos compatibles Internal Link


FastComments admite webhooks solo para el recurso Comment.

Admitimos webhooks para la creación, eliminación y actualización de comentarios.

Cada uno de ellos se considera un evento separado en nuestro sistema y, por lo tanto, tiene distintas semánticas y estructuras para los eventos de webhook.


Pruebas Internal Link


En la administración de Webhooks hay botones Send Test Payload para cada tipo de evento (Create, Update, Delete). Los eventos Create y Update envían un objeto WebhookComment de prueba, mientras que al probar Delete se enviará un cuerpo de solicitud de prueba con solo un ID.

Verificar cargas útiles

Al probar la integración de tu webhook, verifica que las solicitudes entrantes incluyan los siguientes encabezados:

  1. token - Tu secreto de API
  2. X-FastComments-Timestamp - Marca de tiempo Unix (segundos)
  3. X-FastComments-Signature - Firma HMAC-SHA256

Utiliza la verificación de la firma HMAC para garantizar que las cargas útiles sean auténticas.

Herramientas de prueba

Puedes usar herramientas como webhook.site o ngrok para inspeccionar las cargas útiles entrantes de los webhooks durante el desarrollo.

Tipos de eventos

  • Create Event: Se desencadena cuando se crea un nuevo comentario. Método predeterminado: PUT
  • Update Event: Se desencadena cuando se edita un comentario. Método predeterminado: PUT
  • Delete Event: Se desencadena cuando se elimina un comentario. Método predeterminado: DELETE

Cada evento incluye los datos completos del comentario en el cuerpo de la solicitud (consulta Estructuras de datos para el formato de la carga útil).


Estructuras de datos Internal Link

La única estructura enviada vía webhooks es el objeto WebhookComment, descrito en TypeScript a continuación.

Estructura del objeto WebhookComment

Estructura del evento "Create"

El cuerpo de la solicitud del evento "create" es un objeto WebhookComment.

Estructura del evento "Update"

El cuerpo de la solicitud del evento "update" es un objeto WebhookComment.

Estructura del evento "Delete"

El cuerpo de la solicitud del evento "delete" es un objeto WebhookComment.

Cambio a partir del 14 de noviembre de 2023
Anteriormente, el cuerpo de la solicitud del evento "delete" solo contenía el id del comentario. Ahora contiene el comentario completo en el momento de la eliminación.
El objeto WebhookComment
Copy CopyRun External Link
1
2interface WebhookComment {
3 /** El id del comentario. **/
4 id: string
5 /** El id o URL que identifica el hilo de comentarios. Normalizado. **/
6 urlId: string
7 /** La URL que apunta a donde se dejó el comentario. **/
8 url?: string
9 /** El id de usuario que dejó el comentario. Si es SSO, con el id del tenant prefijado. **/
10 userId?: string
11 /** El correo electrónico del usuario que dejó el comentario. **/
12 commenterEmail?: string
13 /** El nombre del usuario que se muestra en el widget de comentarios. Con SSO, puede ser displayName. **/
14 commenterName: string
15 /** Texto bruto del comentario. **/
16 comment: string
17 /** Texto del comentario después del análisis. **/
18 commentHTML: string
19 /** Id externo del comentario. **/
20 externalId?: string
21 /** El id del comentario padre. **/
22 parentId?: string | null
23 /** La fecha UTC cuando se dejó el comentario. **/
24 date: UTC_ISO_DateString
25 /** Karma combinado (up - down) de los votos. **/
26 votes: number
27 votesUp: number
28 votesDown: number
29 /** Verdadero si el usuario estaba autenticado cuando comentó, o si verificó el comentario, o si verificó su sesión cuando se dejó el comentario. **/
30 verified: boolean
31 /** Fecha en la que el comentario fue verificado. **/
32 verifiedDate?: number
33 /** Si un moderador marcó el comentario como revisado. **/
34 reviewed: boolean
35 /** La ubicación, o la codificación base64, del avatar. Solo será base64 si ese fue el valor pasado con SSO. **/
36 avatarSrc?: string
37 /** ¿El comentario fue marcado como spam manualmente o automáticamente? **/
38 isSpam: boolean
39 /** ¿El comentario fue marcado automáticamente como spam? **/
40 aiDeterminedSpam: boolean
41 /** ¿Hay imágenes en el comentario? **/
42 hasImages: boolean
43 /** El número de página en el que está el comentario para la dirección de ordenamiento "Most Relevant". **/
44 pageNumber: number
45 /** El número de página en el que está el comentario para la dirección de ordenamiento "Oldest First". **/
46 pageNumberOF: number
47 /** El número de página en el que está el comentario para la dirección de ordenamiento "Newest First". **/
48 pageNumberNF: number
49 /** ¿El comentario fue aprobado automáticamente o manualmente? **/
50 approved: boolean
51 /** El código de localización (formato: en_us) del usuario cuando se escribió el comentario. **/
52 locale: string
53 /** Las @menciones escritas en el comentario que fueron analizadas con éxito. **/
54 mentions?: CommentUserMention[]
55 /** El dominio del que proviene el comentario. **/
56 domain?: string
57 /** La lista opcional de ids de grupos de moderación asociada a este comentario. **/
58 moderationGroupIds?: string[]|null
59}
60

Cuando los usuarios son etiquetados en un comentario, la información se almacena en una lista llamada mentions. Cada objeto en esa lista tiene la siguiente estructura.

El objeto de menciones del webhook
Copy CopyRun External Link
1
2interface CommentUserMention {
3 /** El id del usuario. Para usuarios SSO, esto tendrá el id de tenant prefijado. **/
4 id: string
5 /** El texto final de la etiqueta @mention, incluyendo el símbolo @. **/
6 tag: string
7 /** El texto original de la etiqueta @mention, incluyendo el símbolo @. **/
8 rawTag: string
9 /** Qué tipo de usuario fue etiquetado. user = cuenta de FastComments.com. sso = SSOUser. **/
10 type: 'user'|'sso'
11 /** Si el usuario se da de baja de las notificaciones, esto seguirá establecido en true. **/
12 sent: boolean
13}
14

Métodos HTTP

Puede configurar el método HTTP para cada tipo de evento de webhook en el panel de administración:

  • Create Event: POST o PUT (predeterminado: PUT)
  • Update Event: POST o PUT (predeterminado: PUT)
  • Delete Event: DELETE, POST o PUT (predeterminado: DELETE)

Dado que todas las solicitudes contienen un ID, las operaciones Create y Update son idempotentes por defecto (PUT). Repetir la misma solicitud Create o Update no debería crear objetos duplicados en su lado.

Encabezados de la solicitud

Cada solicitud de webhook incluye los siguientes encabezados:

Header Description
Content-Type application/json
token Su API Secret
X-FastComments-Timestamp Marca de tiempo Unix (segundos) cuando se firmó la solicitud
X-FastComments-Signature Firma HMAC-SHA256 (sha256=<hex>)

Consulte Seguridad y tokens de API para obtener información sobre la verificación de la firma HMAC.


Seguridad y tokens de API Internal Link


Las solicitudes webhook de FastComments incluyen múltiples mecanismos de autenticación por motivos de seguridad.

Encabezados enviados

Header Description
token Tu API Secret (para compatibilidad con versiones anteriores)
X-FastComments-Timestamp Marca de tiempo Unix (segundos) cuando se firmó la solicitud
X-FastComments-Signature Firma HMAC-SHA256 de la carga útil

Verificación de firma HMAC (Recomendado)

Recomendamos encarecidamente verificar la firma HMAC para garantizar que las cargas útiles de los webhooks sean auténticas y no hayan sido manipuladas.

Formato de la firma: sha256=<hex-encoded-signature>

Cómo se calcula la firma:

  1. Concatenar: timestamp + "." + JSON_payload_body
  2. Calcular HMAC-SHA256 usando tu API Secret como clave
  3. Codificar el resultado en hexadecimal

Ejemplo de verificación (Node.js)

const crypto = require('crypto');

function verifyWebhookSignature(req, apiSecret) {
    const timestamp = req.headers['x-fastcomments-timestamp'];
    const signature = req.headers['x-fastcomments-signature'];

    if (!timestamp || !signature) {
        return false;
    }

    // Verificar que la marca de tiempo sea reciente (dentro de 5 minutos)
    const now = Math.floor(Date.now() / 1000);
    if (Math.abs(now - parseInt(timestamp, 10)) > 300) {
        return false;  // Prevención de ataques de repetición
    }

    // Verificar firma
    const payload = JSON.stringify(req.body);
    const expectedSignature = crypto
        .createHmac('sha256', apiSecret)
        .update(`${timestamp}.${payload}`)
        .digest('hex');

    return signature === `sha256=${expectedSignature}`;
}

Ejemplo de verificación (Python)

import hmac
import hashlib
import time
import json

def verify_webhook_signature(headers, body, api_secret):
    timestamp = headers.get('X-FastComments-Timestamp')
    signature = headers.get('X-FastComments-Signature')

    if not timestamp or not signature:
        return False

    # Verificar que la marca de tiempo sea reciente
    now = int(time.time())
    if abs(now - int(timestamp)) > 300:
        return False

    # Verificar firma
    payload = json.dumps(body, separators=(',', ':'))
    message = f"{timestamp}.{payload}"
    expected = hmac.new(
        api_secret.encode(),
        message.encode(),
        hashlib.sha256
    ).hexdigest()

    return signature == f"sha256={expected}"

Ejemplo de verificación (PHP)

function verifyWebhookSignature($headers, $body, $apiSecret) {
    $timestamp = $headers['X-FastComments-Timestamp'] ?? null;
    $signature = $headers['X-FastComments-Signature'] ?? null;

    if (!$timestamp || !$signature) {
        return false;
    }

    // Verificar que la marca de tiempo sea reciente (dentro de 5 minutos)
    $now = time();
    if (abs($now - intval($timestamp)) > 300) {
        return false;
    }

    // Verificar firma
    $payload = json_encode($body, JSON_UNESCAPED_SLASHES);
    $message = $timestamp . '.' . $payload;
    $expectedSignature = 'sha256=' . hash_hmac('sha256', $message, $apiSecret);

    return hash_equals($expectedSignature, $signature);
}

Autenticación heredada

El encabezado token que contiene tu API Secret todavía se envía por compatibilidad con versiones anteriores. Sin embargo, recomendamos migrar a la verificación HMAC para mejorar la seguridad, ya que protege contra ataques de repetición.


En conclusión

Esto concluye nuestra documentación de Webhooks.

Esperamos que la integración de Webhooks de FastComments le resulte fácil de entender y rápida de configurar.

Si considera que ha identificado alguna laguna en nuestra documentación, háganoslo saber a continuación.