FastComments.com


使用 FastComments,可以在評論被新增、更新或從我們的系統中移除時呼叫一個 API 端點。

我們透過 HTTP/HTTPS 的非同步 webhooks 來達成這個目的。


什麼是 Webhooks(網路回呼) Internal Link

Webhook 是一種機制,或是兩個系統之間的整合,其中的 "producer" (FastComments) 觸發一個事件 而 "consumer" (您) 則透過 API 呼叫來接收並處理該事件。

支援的事件與資源 Internal Link


FastComments 僅支援 Comment 資源的 webhooks。

我們支援針對留言的建立、移除與更新的 webhooks。

這些在我們系統中皆被視為獨立事件,因此在 webhook 事件的語義與結構上各有不同。


測試 Internal Link

在 Webhooks 管理介面中,每個事件類型 (Create, Update, Delete) 都有 Send Test Payload 按鈕。Create 和 Update 事件會傳送一個範例的 WebhookComment 物件,而測試 Delete 時則會傳送只有 ID 的範例請求主體。

驗證有效負載

當測試您的 webhook 整合時,請確認收到的請求包含下列標頭:

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

使用 HMAC 簽章驗證來確保有效負載的真實性。

測試工具

在開發期間,您可以使用像 webhook.sitengrok 這類工具來檢查收到的 webhook 有效負載。

事件類型

  • Create Event: Triggered when a new comment is created. Default method: PUT
  • Update Event: Triggered when a comment is edited. Default method: PUT
  • Delete Event: Triggered when a comment is deleted. Default method: DELETE

每個事件在請求主體中包含完整的評論資料(有關有效負載格式,請參閱 Data Structures)。

資料結構 Internal Link

透過 webhook 傳送的唯一結構為下方以 TypeScript 描述的 WebhookComment 物件。

WebhookComment 物件結構

"Create" 事件結構

"create" 事件的請求主體為 WebhookComment 物件。

"Update" 事件結構

"update" 事件的請求主體為 WebhookComment 物件。

"Delete" 事件結構

"delete" 事件的請求主體為 WebhookComment 物件。

Change as of Nov 14th 2023
Previously the "delete" event request body only contained the comment id. It now contains the full comment at the time of deletion.
WebhookComment 物件
Copy CopyRun External Link
1
2interface WebhookComment {
3 /** 評論的 id。 **/
4 id: string
5 /** 用來識別評論串的 id 或 URL。已標準化。 **/
6 urlId: string
7 /** 指向留言位置的 URL。 **/
8 url?: string
9 /** 留下評論的使用者 id。若為 SSO,會加上租戶 id 的前綴。 **/
10 userId?: string
11 /** 留下評論的使用者電子郵件。 **/
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 /** 投票的綜合 karma(上 - 下)。 **/
26 votes: number
27 votesUp: number
28 votesDown: number
29 /** 若使用者在評論時已登入,或他們已驗證該評論,或在留下評論時已驗證其工作階段,則為 true。 **/
30 verified: boolean
31 /** 評論被驗證的日期。 **/
32 verifiedDate?: number
33 /** 是否有管理員將此評論標記為已審查。 **/
34 reviewed: boolean
35 /** 大頭貼的位置,或其 base64 編碼。只有在透過 SSO 傳入該值時,才會是 base64。 **/
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 /** 與此評論相關的(選用)審核群組 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 /** 使用者的 id。對於 SSO 使用者,會加上您的租戶 id 前綴。 **/
4 id: string
5 /** 最終的 @提及 標籤文字,包含 @ 符號。 **/
6 tag: string
7 /** 原始的 @提及 標籤文字,包含 @ 符號。 **/
8 rawTag: string
9 /** 被標註的是哪種類型的使用者。user = FastComments.com 帳戶。sso = SSO 使用者。 **/
10 type: 'user'|'sso'
11 /** 若使用者選擇不接收通知,此欄位仍會被設為 true。 **/
12 sent: boolean
13}
14

HTTP 方法

您可以在管理面板中為每種 webhook 事件類型設定 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 請求不應在您端產生重複的物件。

請求標頭

每個 webhook 請求都包含下列標頭:

標頭 說明
Content-Type application/json
token Your API Secret
X-FastComments-Timestamp Unix timestamp (seconds) when the request was signed
X-FastComments-Signature HMAC-SHA256 signature (sha256=<hex>)

See 安全性與 API 令牌 for information on verifying the HMAC signature.


安全性與 API 令牌 Internal Link

FastComments 的 webhook 請求包含多種驗證機制以確保安全性。

Headers Sent

Header Description
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. 使用您的 API Secret 作為金鑰計算 HMAC-SHA256
  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

包含您 API Secret 的 token 標頭仍然會為了向後相容而發送。然而,我們建議遷移到 HMAC 驗證以提升安全性,因為它可以防止重放攻擊。


結論

以上即為我們的 Webhooks 文件。

我們希望您覺得 FastComments 的 Webhook 整合容易理解且快速設定。

如果您發現我們的文件有任何遺漏,請在下方讓我們知道。