Skip to content

Commit 59c04fe

Browse files
committed
chore: 新增 README.md 文件,包含 ZSend Webhooks 的功能與使用說明
1 parent 212aa42 commit 59c04fe

1 file changed

Lines changed: 132 additions & 0 deletions

File tree

README.md

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
# ZSend Webhooks
2+
3+
ZSend Webhooks 是一個部署在 Cloudflare Workers 上的 webhook 接收端。它會接收 ZSend 傳來的 POST 請求,驗證 `x-zsend-signature``x-zsend-timestamp`,確認 payload 沒有被竄改後才回應成功。
4+
5+
## 功能
6+
7+
- 只接受 `POST` webhook 請求
8+
- 使用 `SECRET_KEY` 驗證 HMAC SHA-256 簽章
9+
- 支援 ZSend 測試事件:`X-ZSend-Event: test`
10+
- 使用 Cloudflare Workers 執行
11+
- 使用 Vitest 與 `@cloudflare/vitest-pool-workers` 測試 Worker 行為
12+
13+
## Zeabur 測試事件例外
14+
15+
目前程式中對 `X-ZSend-Event: test` 有一個暫時性的例外處理:只要收到這個事件,就會直接回應 `200 OK`,不會進入簽章驗證流程。
16+
17+
這是為了相容 Zeabur 目前的設計缺陷。Zeabur 會在提供 webhook secret 的瞬間,甚至在 endpoint 端有機會完成 `SECRET_KEY` 設定之前,就先送出 `test` 事件。此時接收端還來不及填入與 Zeabur 相同的 secret,因此測試事件無法通過一般的 `x-zsend-signature` 驗證流程。為了讓 Zeabur 的測試功能可以正常判定 endpoint 存活,才暫時讓 `test` 事件無論簽章狀態如何都回應成功。
18+
19+
等 Zeabur 官方修正測試 webhook 的簽章行為後,預計會移除這段 `test` 事件的例外處理,讓所有 webhook 請求都走一致的簽章驗證流程。
20+
21+
## 需求
22+
23+
- Node.js 22+
24+
- npm
25+
- Cloudflare 帳號與 Wrangler 登入狀態
26+
27+
## 安裝
28+
29+
```sh
30+
npm ci
31+
```
32+
33+
建立本機環境變數檔:
34+
35+
```sh
36+
# macOS / Linux
37+
cp .env.example .env
38+
39+
# Windows PowerShell
40+
Copy-Item .env.example .env
41+
```
42+
43+
接著設定 webhook 驗章用的 secret:
44+
45+
```env
46+
SECRET_KEY=your_secret_key_here
47+
```
48+
49+
## 本機開發
50+
51+
啟動 Cloudflare Workers 開發伺服器:
52+
53+
```sh
54+
npm run dev
55+
```
56+
57+
如果要使用專案內的 `start` script:
58+
59+
```sh
60+
npm start
61+
```
62+
63+
## 部署
64+
65+
先把 `SECRET_KEY` 設成 Cloudflare Workers secret:
66+
67+
```sh
68+
npx wrangler secret put SECRET_KEY
69+
```
70+
71+
部署 Worker:
72+
73+
```sh
74+
npm run deploy
75+
```
76+
77+
## Webhook 請求格式
78+
79+
Worker 會檢查以下 headers:
80+
81+
| Header | 說明 |
82+
| ------------------- | -------------------------------------------- |
83+
| `x-zsend-event` | ZSend 事件名稱;若為 `test` 會直接回應 `OK!` |
84+
| `x-zsend-signature` | HMAC 簽章,格式為 `sha256=<hex_digest>` |
85+
| `x-zsend-timestamp` | 簽章使用的 timestamp |
86+
87+
Body 必須是合法 JSON。
88+
89+
簽章計算方式:
90+
91+
```ts
92+
const message = `${timestamp}.${rawBody}`;
93+
const digest = crypto.createHmac("sha256", secret).update(message).digest("hex");
94+
const signature = `sha256=${digest}`;
95+
```
96+
97+
注意:`rawBody` 必須是實際送出 request body 的原始字串;如果重新格式化 JSON,簽章會不同。
98+
99+
## 回應狀態
100+
101+
| 狀態碼 | 回應 | 情境 |
102+
| ------ | --------------------------------------------------- | -------------------------------------- |
103+
| `200` | `OK!` | 簽章正確,或收到 `X-ZSend-Event: test` |
104+
| `400` | `Missing signature or timestamp` | 缺少簽章或 timestamp |
105+
| `400` | `Invalid JSON` | body 不是合法 JSON |
106+
| `401` | `Invalid signature` | 簽章驗證失敗 |
107+
| `405` | `Method Not Allowed` | 不是 `POST` 請求 |
108+
| `500` | `Server configuration error: SECRET_KEY is not set` | Worker 未設定 `SECRET_KEY` |
109+
110+
## 測試與檢查
111+
112+
```sh
113+
npm run test:run
114+
npm run typecheck
115+
npm run format:check
116+
npm run types:check
117+
```
118+
119+
開發時也可以使用 watch 模式:
120+
121+
```sh
122+
npm test
123+
```
124+
125+
## 專案結構
126+
127+
```txt
128+
src/_index.ts Worker fetch 入口
129+
src/router.ts Webhook request 驗證與回應邏輯
130+
test/index.spec.ts Worker 單元與整合測試
131+
wrangler.jsonc Cloudflare Workers 設定
132+
```

0 commit comments

Comments
 (0)