> ## Documentation Index
> Fetch the complete documentation index at: https://docs.alforse.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhook

> 每當訂閱的表格中有記錄被插入、更新或刪除時，由 Alforse 向您的端點 POST 一則已簽章的 JSON 通知。

**Webhook** 會讓 Alforse 在訂閱表格中的記錄被插入、更新或刪除時，向您指定的 URL 傳送 HTTP `POST`。開發人員使用 Webhook 來回應 Alforse 中的變更，而不必輪詢 API。

## Webhook 如何運作

* 每個 Webhook 監看**一個表格**以及一種或多種**觸發操作**。
* 當發生符合的變更時，Alforse 會將傳送排入佇列並以非同步方式送出，通常在片刻之內。
* 每次傳送都以屬於該 Webhook 的密鑰簽章，讓您的端點能證明請求來自 Alforse。
* 內文是記錄已變更的通知。在採取動作之前，請先用 [API 金鑰](https://docs.alforse.com/zh-Hant/docs/building/api-keys.md)取得目前的記錄。

## 可訂閱的表格

| 模組 | 表格 |
| - | - |
| 銷售 | `salesOrder`, `repairOrder`, `repairOrderLine`, `repairOrderCharge`, `warrantyTerm`, `customerWarrantyTerm`, `warrantyRegistration` |
| 採購 | `purchaseOrder` |
| 庫存 | `receipt`, `shipment` |
| 生產 | `job` |
| 品項 | `part`, `material`, `tool`, `consumable`, `service` |
| 品質 | `nonConformance` |
| 財務 | `fixedAsset` |
| 使用者 | `employee`（僅更新） |

請以 Webhook 表單中的**表格**下拉選單為準，來確認您的部署接受哪些表格。觸發選項也取決於表格，因為表格只發布部分操作：`receipt` 發布插入與刪除，而 `employee` 只發布更新。

## 設定 Webhook

前往**設定 → Webhook** 建立並管理 Webhook。填寫以下欄位：

| 欄位 | 功能 |
| - | - |
| **名稱** | Webhook 的標籤。在貴公司內必須是唯一的。 |
| **表格** | 要監看的單一表格。 |
| **URL** | Alforse 傳送 `POST` 的位置。必須是解析到公開位址的 `http` 或 `https` URL。 |
| **觸發條件** | **insert**、**update** 與 **delete** 的任意組合。至少選擇一項。 |
| **啟用中** | 預設為開啟。關閉可暫停傳送而不必刪除 Webhook。 |

Alforse 拒絕傳送到回送位址、私有網路、連結本機（link-local）以及雲端中繼資料位址。這可防止 Webhook 被用來存取內部系統。

Webhook 清單會為每個 Webhook 顯示成功次數與錯誤次數，依傳送嘗試計算。

## 傳送內容

### 內文

內文是包含四個欄位的 JSON：

| 欄位 | 意義 |
| - | - |
| `type` | `INSERT`、`UPDATE` 或 `DELETE` |
| `table` | 發生變更的表格 |
| `companyId` | 記錄所屬的公司 |
| `record` | 受影響的記錄 |

```json theme={null}
{
  "type": "UPDATE",
  "table": "salesOrder",
  "companyId": "abc123",
  "record": { "id": "...", "salesOrderId": "SO000042", "status": "To Ship and Invoice" }
}
```

### 標頭

| 標頭 | 值 |
| - | - |
| `Content-Type` | `application/json` |
| `x-omeriva-webhook-signature` | 簽章，格式為 `v1=<hex>`（請見[驗證傳送內容](#verifying-deliveries)） |
| `x-omeriva-webhook-timestamp` | 此次嘗試的時間，以 Unix 秒計 |
| `x-omeriva-webhook-id` | Webhook 的 ID |
| `x-omeriva-webhook-event` | 事件 ID |

請使用事件 ID 作為冪等鍵。一項變更在其所有重試中保持同一個事件 ID，而每個新的變更都會取得新的 ID，包括對您已收過通知的記錄所做的第二次更新。

## 傳送與重試

傳送為**至少一次**。當您的端點出現下列情況時，Alforse 會將該次嘗試視為失敗並重試，採用指數退避，最多 8 次嘗試：

* 在 10 秒內沒有回傳成功狀態，或
* 回傳 `5xx` 或 `429`。

其他結果不會重試：

* 任何其他 `4xx` 回應會結束該變更的傳送。
* `3xx` 回應視為失敗，因為 Alforse 不會跟隨重新導向。請設定最終的 URL。
* 傳送到被拒絕的位址不會重試。

最後一次嘗試失敗後，Alforse 會將該變更標記為失敗並捨棄。

### 設計您的端點

* **預期會有重複。** 在您已處理第一次嘗試之後，重試仍可能抵達。請依 `x-omeriva-webhook-event` 去重，而不是依 `type` 加上 `record.id`，因為對同一筆記錄的不同變更會共用這些值。
* **中斷後要對帳。** 被捨棄的變更不會重新傳送。如果您必須處理每個事件，請在端點停機之後查詢 API 以補上資料。

## 驗證傳送內容

建立 Webhook 時也會產生專屬於它的簽章密鑰，由 Alforse 加密保存。`x-omeriva-webhook-signature` 中的簽章，是 `` `${timestamp}.${body}` `` 的十六進位 HMAC-SHA256，以密鑰字串作為金鑰。`timestamp` 是 `x-omeriva-webhook-timestamp` 的值，`body` 是原始請求內文。

要驗證一次傳送：

1. 在解析之前先擷取原始內文。解析後再重新序列化 JSON 會改變位元組，並破壞簽章。
2. 計算 HMAC，並使用常數時間比較，與 `v1=` 之後的值比對。
3. 拒絕與您的時鐘相差超過五分鐘的任何時間戳記，使擷取到的請求無法被重放。每次重試都會以新的時間戳記重新簽章，所以合法的重試仍會通過。

```ts theme={null}
import { createHmac, timingSafeEqual } from "node:crypto";

export function verifyAlforseSignature(
  rawBody: string,
  signatureHeader: string,
  timestampHeader: string,
  secret: string,
  toleranceSeconds = 300
) {
  const timestamp = Number(timestampHeader);
  if (!Number.isFinite(timestamp)) return false;
  if (Math.abs(Date.now() / 1000 - timestamp) > toleranceSeconds) return false;

  const expected = createHmac("sha256", secret)
    .update(`${timestamp}.${rawBody}`)
    .digest("hex");
  const received = signatureHeader.replace(/^v1=/, "");
  return (
    received.length === expected.length &&
    timingSafeEqual(Buffer.from(received), Buffer.from(expected))
  );
}
```

NOTE

**取得簽章密鑰**

應用程式目前不會顯示或輪替簽章密鑰。設定驗證時，請向 Alforse 支援團隊索取。

## 疑難排解

| 症狀 | 原因 | 處理方式 |
| - | - | - |
| 您的伺服器完全沒有收到任何東西 | URL 解析到回送、私有、連結本機或中繼資料位址，因此 Alforse 拒絕它且不重試 | 使用可公開連線的 URL。 |
| 儘管伺服器正常運作，每次嘗試仍失敗 | URL 回傳 `3xx` 重新導向 | 輸入重新導向所指向的最終 URL。 |
| 同一項變更被處理了兩次 | 回應緩慢或失敗後，重試抵達 | 依 `x-omeriva-webhook-event` 去重。 |
| 簽章檢查對有效的請求失敗 | 在雜湊之前已先解析內文 | 對原始內文位元組做雜湊。 |
| **表格**中缺少某個表格 | 該表格不發布 Webhook 事件 | 訂閱相關的表格，或輪詢 API。 |

## 相關內容

**API 金鑰**

在 Webhook 通知記錄已變更之後，從 Alforse API 取得完整記錄。

[Open](https://docs.alforse.com/zh-Hant/docs/building/api-keys.md)

**整合**

偏好封裝好的連接器？請見第一方整合。

[Open](https://docs.alforse.com/zh-Hant/docs/integrations.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.