FastComments.com


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

Мы реализуем это с помощью асинхронных вебхуков по протоколам HTTP/HTTPS.


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


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


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

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

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

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

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

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

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

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

  1. token - Ваш секрет API
  2. X-FastComments-Timestamp - Unix-метка времени (в секундах)
  3. X-FastComments-Signature - подпись HMAC-SHA256

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

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

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

Типы событий

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

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


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

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

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

Структура события "create"

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

Структура события "update"

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

Структура события "delete"

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

Изменение от 14 ноября 2023 г.
Ранее тело запроса события "delete" содержало только идентификатор комментария. Теперь оно содержит полный комментарий на момент удаления.
Объект WebhookComment
Copy CopyRun External Link
1
2interface WebhookComment {
3 /** Идентификатор комментария. **/
4 id: string
5 /** Идентификатор или URL, который определяет поток комментариев. Нормализован. **/
6 urlId: string
7 /** URL, указывающий на страницу, где был оставлен комментарий. **/
8 url?: string
9 /** Идентификатор пользователя, оставившего комментарий. Если SSO, префиксуется идентификатором арендатора. **/
10 userId?: string
11 /** Электронная почта пользователя, оставившего комментарий. **/
12 commenterEmail?: string
13 /** Имя пользователя, отображаемое в виджете комментариев. При SSO может быть displayName. **/
14 commenterName: string
15 /** Необработанный текст комментария. **/
16 comment: string
17 /** Текст комментария после парсинга. **/
18 commentHTML: string
19 /** Внешний идентификатор комментария. **/
20 externalId?: string
21 /** Идентификатор родительского комментария. **/
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 /** Номер страницы, на которой находится комментарий при сортировке «Наиболее релевантные». **/
44 pageNumber: number
45 /** Номер страницы при сортировке «Сначала старые». **/
46 pageNumberOF: number
47 /** Номер страницы при сортировке «Сначала новые». **/
48 pageNumberNF: number
49 /** Комментарий был одобрен автоматически или вручную? **/
50 approved: boolean
51 /** Код локали (формат: en_us) пользователя в момент написания комментария. **/
52 locale: string
53 /** @упоминания, написанные в комментарии и успешно распарсенные. **/
54 mentions?: CommentUserMention[]
55 /** Домен, с которого пришёл комментарий. **/
56 domain?: string
57 /** Необязательный список идентификаторов групп модерации, связанных с этим комментарием. **/
58 moderationGroupIds?: string[]|null
59}
60

Когда пользователи отмечаются в комментарии, информация хранится в списке mentions. Каждый объект в этом списке имеет следующую структуру.

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

HTTP-методы

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

  • Событие «create»: POST или PUT (по умолчанию: PUT)
  • Событие «update»: POST или PUT (по умолчанию: PUT)
  • Событие «delete»: DELETE, POST или PUT (по умолчанию: DELETE)

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

Заголовки запроса

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

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

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

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

Запросы вебхуков FastComments включают несколько механизмов аутентификации для безопасности.

Headers Sent

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

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

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

How the signature is computed:

  1. Concatenate: timestamp + "." + JSON_payload_body
  2. Compute HMAC-SHA256 using your API Secret as the key
  3. Hex-encode the result

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;  // Защита от повторного воспроизведения (replay-атаки)
    }

    // Проверка подписи
    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

The token header containing your API Secret is still sent for backwards compatibility. However, we recommend migrating to HMAC verification for improved security as it protects against replay attacks.



В заключение

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

Мы надеемся, что интеграция FastComments Webhook окажется понятной и быстрой в настройке.

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