FastComments.com


С FastComments можно вызывать API endpoint всякий раз, когда в нашей системе добавляется, обновляется или удаляется комментарий.

Мы осуществляем это с помощью asynchronous webhooks по протоколам HTTP/HTTPS.


Что такое вебхуки Internal Link

Вебхук — это механизм или интеграция между двумя системами, где "производитель" (FastComments) генерирует событие которое "потребитель" (Вы) обрабатывает посредством вызова API.

Поддерживаемые события и ресурсы Internal Link

FastComments поддерживает вебхуки только для ресурса Comment.

Мы поддерживаем вебхуки при создании комментария, его удалении и обновлении.

Каждое из них считается отдельным событием в нашей системе и, как следствие, имеет разную семантику и структуру событий вебхуков.

Тестирование Internal Link


В Webhooks admin есть кнопки Send Test Payload для каждого типа события (Create, Update, Delete). События Create и Update отправляют тестовый объект WebhookComment, тогда как при тестировании Delete будет отправлено тестовое тело запроса, содержащее только ID.

Проверка полезной нагрузки

При тестировании интеграции вебхуков убедитесь, что входящие запросы содержат следующие заголовки:

  1. token - Your API Secret
  2. X-FastComments-Timestamp - Unix timestamp (seconds)
  3. X-FastComments-Signature - HMAC-SHA256 signature

Используйте проверку HMAC-подписи, чтобы убедиться в подлинности полезных нагрузок.

Инструменты тестирования

Вы можете использовать такие инструменты, как webhook.site или ngrok для просмотра входящих полезных нагрузок вебхуков во время разработки.

Типы событий

  • Create Event: Вызывается при создании нового комментария. Метод по умолчанию: PUT
  • Update Event: Вызывается при редактировании комментария. Метод по умолчанию: PUT
  • Delete Event: Вызывается при удалении комментария. Метод по умолчанию: DELETE

Каждое событие включает полные данные комментария в теле запроса (см. Структуры данных для формата полезной нагрузки).


Структуры данных Internal Link

Единственная структура, отправляемая через вебхуки — это объект WebhookComment, приведённый ниже на TypeScript.

Структура объекта WebhookComment

The "Create" Event Structure

Тело запроса события "create" — объект WebhookComment.

The "Update" Event Structure

Тело запроса события "update" — объект WebhookComment.

The "Delete" Event Structure

Тело запроса события "delete" — объект WebhookComment.

Изменение от 14 ноября 2023 г.
Ранее тело запроса события "delete" содержало только id комментария. Теперь оно содержит полный комментарий на момент удаления.
Объект WebhookComment
Copy CopyRun External Link
1
2interface WebhookComment {
3 /** Идентификатор комментария. **/
4 id: string
5 /** The id or URL that identifies the comment thread. Normalized. **/
6 urlId: string
7 /** URL, указывающий, где был оставлен комментарий. **/
8 url?: string
9 /** id пользователя, который оставил комментарий. Для SSO префикс — tenant id. **/
10 userId?: string
11 /** Email пользователя, который оставил комментарий. **/
12 commenterEmail?: string
13 /** Имя пользователя, отображаемое в виджете комментариев. Для SSO может быть displayName. **/
14 commenterName: string
15 /** Исходный текст комментария. **/
16 comment: string
17 /** Текст комментария после парсинга. **/
18 commentHTML: string
19 /** Внешний id комментария. **/
20 externalId?: string
21 /** id родительского комментария. **/
22 parentId?: string | null
23 /** Дата в UTC, когда комментарий был оставлен. **/
24 date: UTC_ISO_DateString
25 /** Суммарная карма голосов (за - против). **/
26 votes: number
27 votesUp: number
28 votesDown: number
29 /** True — если пользователь был вошедшим в систему при оставлении комментария, либо если комментарий был верифицирован, либо если сессия была верифицирована при оставлении комментария. **/
30 verified: boolean
31 /** Дата, когда комментарий был верифицирован. **/
32 verifiedDate?: number
33 /** Отмечено ли модератором, что комментарий просмотрен. **/
34 reviewed: boolean
35 /** Местоположение или base64-кодированное изображение аватара. Будет в base64 только если такое значение было передано с SSO. **/
36 avatarSrc?: string
37 /** Был ли комментарий помечен как спам вручную или автоматически? **/
38 isSpam: boolean
39 /** Был ли комментарий автоматически помечен как спам? **/
40 aiDeterminedSpam: boolean
41 /** Содержит ли комментарий изображения? **/
42 hasImages: boolean
43 /** Номер страницы, на которой находится комментарий при сортировке "Most Relevant". **/
44 pageNumber: number
45 /** Номер страницы, на которой находится комментарий при сортировке "Oldest First". **/
46 pageNumberOF: number
47 /** Номер страницы, на которой находится комментарий при сортировке "Newest First". **/
48 pageNumberNF: number
49 /** Был ли комментарий одобрен автоматически или вручную? **/
50 approved: boolean
51 /** Код локали (формат: en_us) пользователя в момент написания комментария. **/
52 locale: string
53 /** @упоминания, указанные в комментарии и успешно распарсенные. **/
54 mentions?: CommentUserMention[]
55 /** Домен, с которого пришёл комментарий. **/
56 domain?: string
57 /** Необязательный список id групп модерации, связанных с этим комментарием. **/
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.

Объект упоминаний Webhook
Copy CopyRun External Link
1
2interface CommentUserMention {
3 /** The user id. For SSO users, this will have your tenant id prefixed. **/
4 id: string
5 /** Финальный текст тега @mention, включая символ @. **/
6 tag: string
7 /** Исходный текст тега @mention, включая символ @. **/
8 rawTag: string
9 /** What type of user was tagged. user = FastComments.com account. sso = SSOUser. **/
10 type: 'user'|'sso'
11 /** Даже если пользователь отказался от уведомлений, это поле всё равно будет установлено в true. **/
12 sent: boolean
13}
14

HTTP Methods

Вы можете настроить HTTP-метод для каждого типа события вебхука в панели администратора:

  • Create Event: POST or PUT (default: PUT)
  • Update Event: POST or PUT (default: PUT)
  • Delete Event: DELETE, POST, or PUT (default: DELETE)

Поскольку все запросы содержат ID, операции Create и Update по умолчанию идемпотентны (PUT). Повторение того же запроса Create или Update не должно создавать дубликаты объектов на вашей стороне.

Request Headers

Каждый запрос вебхука содержит следующие заголовки:

Header Description
Content-Type application/json
token Секрет вашего API
X-FastComments-Timestamp Unix-временная метка (в секундах), когда запрос был подписан
X-FastComments-Signature Подпись HMAC-SHA256 (sha256=<hex>)

См. Безопасность и API-токены для информации о проверке HMAC-подписи.


Безопасность и API-токены Internal Link

FastComments webhook requests include multiple authentication mechanisms for security.

Отправляемые заголовки

Заголовок Описание
token Ваш API Secret (для обратной совместимости)
X-FastComments-Timestamp Unix-временная метка (в секундах), когда запрос был подписан
X-FastComments-Signature Подпись HMAC-SHA256 полезной нагрузки

Мы настоятельно рекомендуем проверять подпись HMAC, чтобы убедиться, что полезные данные webhook подлинны и не были изменены.

Signature Format: sha256=<hex-encoded-signature>

How the signature is computed:

  1. Объедините: timestamp + "." + JSON_payload_body
  2. Вычислите HMAC-SHA256, используя ваш API Secret в качестве ключа
  3. Преобразуйте результат в шестнадцатеричную строку

Example Verification (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;
    }

    // Проверить, что метка времени недавняя (в пределах 5 минут)
    const now = Math.floor(Date.now() / 1000);
    if (Math.abs(now - parseInt(timestamp, 10)) > 300) {
        return false;  // Предотвращение атак повторного воспроизведения
    }

    // Проверить подпись
    const payload = JSON.stringify(req.body);
    const expectedSignature = crypto
        .createHmac('sha256', apiSecret)
        .update(`${timestamp}.${payload}`)
        .digest('hex');

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

Example Verification (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

    # Проверить, что метка времени недавняя
    now = int(time.time())
    if abs(now - int(timestamp)) > 300:
        return False

    # Проверить подпись
    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}"

Example Verification (PHP)

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

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

    // Проверить, что метка времени недавняя (в пределах 5 минут)
    $now = time();
    if (abs($now - intval($timestamp)) > 300) {
        return false;
    }

    // Проверить подпись
    $payload = json_encode($body, JSON_UNESCAPED_SLASHES);
    $message = $timestamp . '.' . $payload;
    $expectedSignature = 'sha256=' . hash_hmac('sha256', $message, $apiSecret);

    return hash_equals($expectedSignature, $signature);
}

Legacy Authentication

Заголовок token, содержащий ваш API Secret, по-прежнему отправляется для обратной совместимости. Тем не менее, мы рекомендуем перейти на проверку HMAC для повышения безопасности, так как она защищает от атак повторного воспроизведения.

В заключение

На этом завершается наша документация по вебхукам.

Надеемся, что интеграция вебхуков FastComments понятна и проста в настройке.

Если вы считаете, что нашли пробелы в нашей документации, сообщите нам ниже.