FastComments.com

Con FastComments è possibile invocare un endpoint API ogni volta che un commento viene aggiunto, aggiornato o rimosso dal nostro sistema.

Realizziamo questo tramite webhook asincroni su HTTP/HTTPS.


Cosa sono i Webhook Internal Link


Un Webhook è un meccanismo, o un'integrazione, tra due sistemi in cui il "produttore" (FastComments) scatena un evento che il "consumatore" (tu) consuma tramite una chiamata API.


Eventi e risorse supportati Internal Link

FastComments supporta i webhook solo per la risorsa Comment.

Supportiamo webhook per la creazione, la rimozione e l'aggiornamento dei commenti.

Ognuno di questi è considerato un evento separato nel nostro sistema e, come tale, ha semantiche diverse e strutture diverse per gli eventi webhook.

Test Internal Link

Nella Webhooks admin ci sono Send Test Payload buttons per ogni tipo di evento (Create, Update, Delete). Gli eventi Create e Update inviano un oggetto di prova WebhookComment, mentre il test di Delete invierà un corpo della richiesta fittizio contenente solo un ID.

Verifica dei payload

Quando testi la tua integrazione webhook, verifica che le richieste in arrivo includano le seguenti intestazioni:

  1. token - Il tuo API Secret
  2. X-FastComments-Timestamp - Unix timestamp (secondi)
  3. X-FastComments-Signature - HMAC-SHA256 signature

Usa la verifica della firma HMAC per garantire che i payload siano autentici.

Strumenti di test

Puoi usare strumenti come webhook.site o ngrok per ispezionare i payload webhook in arrivo durante lo sviluppo.

Tipi di evento

  • Create Event: Scatenato quando viene creato un nuovo commento. Metodo predefinito: PUT
  • Update Event: Scatenato quando un commento viene modificato. Metodo predefinito: PUT
  • Delete Event: Scatenato quando un commento viene eliminato. Metodo predefinito: DELETE

Ogni evento include tutti i dati del commento nel corpo della richiesta (vedi Strutture dei dati per il formato del payload).

Strutture dati Internal Link

L'unica struttura inviata tramite webhook è l'oggetto WebhookComment, descritto in TypeScript qui sotto.

La struttura dell'oggetto WebhookComment

La "Create" Event Structure

Il body della richiesta dell'evento "create" è un oggetto WebhookComment.

La "Update" Event Structure

Il body della richiesta dell'evento "update" è un oggetto WebhookComment.

La "Delete" Event Structure

Il body della richiesta dell'evento "delete" è un oggetto WebhookComment.

Modifica a partire dal 14 Nov 2023
In precedenza il body della richiesta dell'evento "delete" conteneva solo l'id del commento. Ora contiene il commento completo al momento dell'eliminazione.
Oggetto WebhookComment
Copy CopyRun External Link
1
2interface WebhookComment {
3 /** L'id del commento. **/
4 id: string
5 /** L'id o l'URL che identifica il thread dei commenti. Normalizzato. **/
6 urlId: string
7 /** L'URL che punta al luogo dove è stato lasciato il commento. **/
8 url?: string
9 /** L'id utente che ha lasciato il commento. Se SSO, prefissato con l'id del tenant. **/
10 userId?: string
11 /** L'email dell'utente che ha lasciato il commento. **/
12 commenterEmail?: string
13 /** Il nome dell'utente mostrato nel widget dei commenti. Con SSO, può essere displayName. **/
14 commenterName: string
15 /** Testo grezzo del commento. **/
16 comment: string
17 /** Testo del commento dopo il parsing. **/
18 commentHTML: string
19 /** Id esterno del commento. **/
20 externalId?: string
21 /** L'id del commento genitore. **/
22 parentId?: string | null
23 /** La data UTC in cui il commento è stato lasciato. **/
24 date: UTC_ISO_DateString
25 /** Karma combinato (up - down) dei voti. **/
26 votes: number
27 votesUp: number
28 votesDown: number
29 /** True se l'utente era autenticato quando ha commentato, o se ha verificato il commento, o se ha verificato la sessione quando il commento è stato lasciato. **/
30 verified: boolean
31 /** Data in cui il commento è stato verificato. **/
32 verifiedDate?: number
33 /** Se un moderatore ha contrassegnato il commento come revisionato. **/
34 reviewed: boolean
35 /** La posizione, o codifica base64, dell'avatar. Sarà in base64 solo se questo è stato il valore passato con SSO. **/
36 avatarSrc?: string
37 /** Il commento è stato contrassegnato manualmente o automaticamente come spam? **/
38 isSpam: boolean
39 /** Il commento è stato automaticamente contrassegnato come spam? **/
40 aiDeterminedSpam: boolean
41 /** Ci sono immagini nel commento? **/
42 hasImages: boolean
43 /** Il numero di pagina su cui si trova il commento per l'ordinamento "Most Relevant". **/
44 pageNumber: number
45 /** Il numero di pagina su cui si trova il commento per l'ordinamento "Oldest First". **/
46 pageNumberOF: number
47 /** Il numero di pagina su cui si trova il commento per l'ordinamento "Newest First". **/
48 pageNumberNF: number
49 /** Il commento è stato approvato automaticamente o manualmente? **/
50 approved: boolean
51 /** Il codice locale (formato: en_us) dell'utente quando il commento è stato scritto. **/
52 locale: string
53 /** Le @mention scritte nel commento che sono state analizzate con successo. **/
54 mentions?: CommentUserMention[]
55 /** Il dominio da cui proviene il commento. **/
56 domain?: string
57 /** L'elenco opzionale di id dei gruppi di moderazione associati a questo commento. **/
58 moderationGroupIds?: string[]|null
59}
60

When users are tagged in a comment, the information is stored in a list called mentions. Each object in that list has the following structure.

Oggetto Webhook Mentions
Copy CopyRun External Link
1
2interface CommentUserMention {
3 /** L'id utente. Per gli utenti SSO, avrà prefissato l'id del tenant. **/
4 id: string
5 /** Il testo finale del tag @mention, inclusivo del simbolo @. **/
6 tag: string
7 /** Il testo originale del tag @mention, inclusivo del simbolo @. **/
8 rawTag: string
9 /** Che tipo di utente è stato taggato. user = account FastComments.com. sso = SSOUser. **/
10 type: 'user'|'sso'
11 /** Se l'utente rinuncia alle notifiche, questo sarà comunque impostato su true. **/
12 sent: boolean
13}
14

HTTP Methods

Puoi configurare il metodo HTTP per ciascun tipo di evento webhook nel pannello di amministrazione:

  • Evento "Create": POST o PUT (predefinito: PUT)
  • Evento "Update": POST o PUT (predefinito: PUT)
  • Evento "Delete": DELETE, POST o PUT (predefinito: DELETE)

Poiché tutte le richieste contengono un ID, le operazioni Create e Update sono idempotenti per impostazione predefinita (PUT). Ripetere la stessa richiesta di Create o Update non dovrebbe creare oggetti duplicati dalla tua parte.

Request Headers

Ogni richiesta webhook include le seguenti intestazioni:

Header Descrizione
Content-Type application/json
token Il tuo API Secret
X-FastComments-Timestamp Timestamp Unix (secondi) quando la richiesta è stata firmata
X-FastComments-Signature Firma HMAC-SHA256 (sha256=<hex>)

Vedi Sicurezza e token API per informazioni sulla verifica della firma HMAC.

Sicurezza e token API Internal Link

FastComments webhook requests include multiple authentication mechanisms for security.

Intestazioni inviate

Intestazione Descrizione
token Il tuo API Secret (per compatibilità con le versioni precedenti)
X-FastComments-Timestamp Timestamp Unix (secondi) quando la richiesta è stata firmata
X-FastComments-Signature Firma HMAC-SHA256 del payload

Verifica della firma HMAC (Consigliata)

Raccomandiamo vivamente di verificare la firma HMAC per assicurarsi che i payload dei webhook siano autentici e non siano stati manomessi.

Formato della firma: sha256=<hex-encoded-signature>

Come viene calcolata la firma:

  1. Concatena: timestamp + "." + JSON_payload_body
  2. Calcola HMAC-SHA256 usando il tuo API Secret come chiave
  3. Codifica il risultato in esadecimale

Esempio di verifica (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;
    }

    // Verifica che il timestamp sia recente (entro 5 minuti)
    const now = Math.floor(Date.now() / 1000);
    if (Math.abs(now - parseInt(timestamp, 10)) > 300) {
        return false;  // Prevenzione di attacchi di replay
    }

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

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

Esempio di verifica (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

    # Verifica che il timestamp sia recente
    now = int(time.time())
    if abs(now - int(timestamp)) > 300:
        return False

    # Verifica la 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}"

Esempio di verifica (PHP)

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

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

    // Verifica che il timestamp sia recente (entro 5 minuti)
    $now = time();
    if (abs($now - intval($timestamp)) > 300) {
        return false;
    }

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

    return hash_equals($expectedSignature, $signature);
}

Autenticazione legacy

L'intestazione token contenente il tuo API Secret viene ancora inviata per compatibilità con le versioni precedenti. Tuttavia, consigliamo di migrare alla verifica HMAC per una sicurezza migliorata poiché protegge dagli attacchi di replay.

In conclusione

Questa conclude la nostra documentazione sui Webhooks.

Speriamo che troviate l'integrazione FastComments Webhook facile da comprendere e veloce da configurare.

Se ritenete di aver individuato delle lacune nella nostra documentazione, fatecelo sapere qui sotto.