innovedus_cms/docs/newsletter_integration_memo.md

424 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 電子報介接備忘錄(租戶端 / Wagtail
最後更新2026-02-18
## 1. 目標與前提
本備忘錄用於整理租戶端Wagtail接下來的實作方向重點是先完成可運行流程再逐步補完細節。
## 2. 已確認決策(本次會議結論)
1. Member Center 雖為共用平台,但目前只有一個租戶。
2. 現階段「發確認信」與「訂閱/退訂頁面」先做在 Wagtail。
3. Wagtail 與 Member Center 的溝通以 API 為主。
4. SMTP relay 僅負責寄出一次重送、token 安全、簽章等由其他系統或工具負責,不屬 Wagtail 責任。
5. 工作大項與順序固定為:
1. 準備工作(電子報系統設定)
2. 訂閱流程
3. 退訂流程
4. 電子報 app
5. 發信流程(排程)
## 3. 工作大項與執行順序(含範圍)
## 3.1 準備工作(電子報系統設定,第一優先)
範圍:
- 建立 Member Center 連線設定base URL、tenant_id、list_id、API timeout 等)。
- 建立 Send Engine 連線設定base URL、OAuth scope、API timeout 等)。
- 建立一般發信設定SMTP relay、寄件者名稱/信箱、reply-to、預設編碼等
- 建立模板設定:
- 訂閱確認信 template
- 訂閱確認(成功)頁面 template
- 取消訂閱頁面 template
重點:
- 設定集中管理,避免散落在程式碼或多處後台欄位。
- 區分「系統連線設定」與「內容模板設定」,便於權限與維運。
完成標準DoD
- 可在單一設定入口完成上述設定並通過基本驗證。
- 模板可被後續訂閱/退訂流程直接引用。
## 3.2 訂閱流程(第二優先)
範圍:
- 在站台頁面區塊(暫定 Footer提供 email 訂閱入口。
- 後端接收 email呼叫 Member Center 訂閱 API。
- 由 Wagtail 送出訂閱確認信(透過 SMTP relay
- 使用者點信內連結回到 Wagtail 確認頁。
- 確認頁自動呼叫 Member Center 確認 API。
重點:
- 流程中 token 需一路帶入與驗證。
- 確認信內容採 HTML可附純文字 fallback
完成標準DoD
- 可從站台成功發起訂閱。
- 可收到確認信並完成確認。
- 確認成功/失敗頁有明確訊息與追蹤記錄。
## 3.3 退訂流程(第三優先)
範圍:
- 電子報底部提供退訂連結,導向 Wagtail 退訂頁。
- 進入退訂頁時,先向 Member Center 申請退訂 token。
- 使用者按確認退訂後,呼叫 Member Center 退訂 API 完成流程。
重點:
- token 不做長期保存。
- 頁面需可處理 token 失效/不存在等錯誤狀態。
完成標準DoD
- 使用者能從信件連結完成退訂。
- 退訂結果頁可正確呈現成功/失敗狀態。
## 3.4 電子報 app第四優先
範圍:
- 在 Wagtail 後台提供電子報內容管理能力。
- 提供 HTML 編輯器建立/編修內容。
- 支援可替換參數(例如 token、email
重點:
- 編輯器中的連結參數以佔位符表示,發送前由 backend 替換。
- 若有 Member Center API 相關連結需在內容中可配置,也採相同參數機制。
完成標準DoD
- 編輯器可存草稿與更新內容。
- 內容中參數可在送出前正確替換。
## 3.5 發信流程(排程,第五優先)
範圍:
- 可建立發送任務與排程時間。
- 排程前可持續編輯內容與調整時間。
- 到時觸發時:先取 Member Center auth再將內容包固定 header/footer送到 Send Engine。
重點:
- 「內容定稿」與「實際送出」拆開。
- 發送前的模板組裝由 Wagtail backend 負責。
完成標準DoD
- 排程到點可成功建立 Send Engine send job。
- 失敗可記錄原因並可人工重試。
## 4. 參數替換規則(先行約定)
建議佔位符格式(待實作時可再定版):
- `{{token}}`
- `{{email}}`
- `{{list_id}}`
- `{{tenant_id}}`
- `{{confirm_url}}`
- `{{unsubscribe_url}}`
替換時機:
- 發信前 backend 最後一步統一替換。
注意事項:
- URL 參數需做 URL encode。
- 缺少必要參數時,中止發送並記錄可追蹤錯誤。
## 5. URL 與系統歸屬標註規範(文件撰寫規則)
後續文件凡提到網址,必須明確標註「是哪個系統」:
1. Wagtail租戶站台網址
- 例:`https://{tenant-site}/newsletter/confirm?token=...`
- 用途:使用者互動頁、站台前台/後台頁面。
2. Member Center API 網址
- 例:`https://{member-center}/newsletter/subscribe`
- 用途:訂閱、確認、退訂 token、退訂等 API。
3. Send Engine API 網址
- 例:`https://{send-engine}/api/send-jobs`
- 用途:建立/查詢/取消發信任務。
4. SMTP Relay 連線端點
- 用途Wagtail 寄送確認信。
- 備註:僅作為寄送通道,不承擔重送與安全機制。
## 6. 非 Wagtail 責任邊界(本階段)
以下不納入目前 Wagtail 工作範圍:
- SMTP 後續重送策略。
- token 簽章與高階安全策略。
- 跨系統風控與防濫用機制。
## 7. 下一步(實作啟動清單)
1. 先完成電子報系統設定Member Center / Send Engine / SMTP / 模板)。
2. 再完成訂閱流程 API 串接與確認頁。
3. 再完成退訂頁與退訂 API 串接。
4. 建立電子報 app 與 HTML 編輯器資料模型。
5. 最後接排程任務與 Send Engine 發信。
## 8. Member Center API 實際規格(以 member_center 程式碼 / OpenAPI 為準)
資料來源2026-02-17 比對):
- `../member_center/src/MemberCenter.Api/Controllers/NewsletterController.cs`
- `../member_center/docs/openapi.yaml`
### 8.1 Base URL 與路徑
- OpenAPI `servers.url``/api`,部署時實際呼叫通常為:
- `https://{member-center}/api/newsletter/...`
- 若部署已在 gateway 做 path rewrite也可為
- `https://{member-center}/newsletter/...`
- 租戶端必須以實際部署路徑設定 `member_center_base_url`
### 8.2 Endpoint / Method / Request
1. 訂閱
- `POST /newsletter/subscribe`
- JSON body必要
- `list_id`Guid
- `email`string
- JSON body可選
- `preferences`object
- `source`string
- 回傳包含 `confirm_token`
2. 訂閱確認(雙重驗證)
- `GET /newsletter/confirm?token=...`
- 注意:**confirm 是 GET不是 POST**
3. 單一名單退訂
- `POST /newsletter/unsubscribe`
- JSON body必要
- `token`string
4. 申請退訂 token
- `POST /newsletter/unsubscribe-token`
- JSON body必要
- `list_id`Guid
- `email`string
- 回傳:
- `unsubscribe_token`string
### 8.3 與租戶端目前實作對齊結果
- `subscribe`:已使用 `POST`
- `confirm`:已改為 `GET + query token`(修正 HTTP 405 問題)。
- `unsubscribe`:已使用 `POST`,且 body 僅送 `token`
- `unsubscribe-token`:已使用 `POST`,且 body 送 `list_id + email`
## 9. List-Unsubscribe One-Click 規範(發信流程階段導入)
說明:
- 本節為「電子報 app + 排程發信」階段的目標規範。
- 已完成的訂閱/退訂流程維持現況,不回頭重做;待發信流程上線時再整合 one-click。
### 9.1 目標
- 提供電子報編輯與排程。
- 產生 `List-Unsubscribe` one-click header。
- 呼叫發信代理 API。
- 提供 unsubscribe endpointone-click + 人類點擊頁)。
### 9.2 Unsubscribe Token 設計
建議使用 JWT 或 HMAC token。
Token 內容建議:
- `subscriber_id`
- `list_id`
- `site_id`
- `exp`
- `nonce`optional
安全要求:
- 不需登入即可退訂。
- 必須可 idempotent。
- Token 需有過期時間。
- Token 必須簽章驗證。
### 9.3 Unsubscribe Endpoint
1. One-click endpoint
- `POST /u/unsubscribe`
- Request`token`
- 流程:
- 驗證 token
- 呼叫會員平台 API
- 回傳 `200`
- 不可:
- 要求登入
- 要求二次確認
2. 人類點擊頁面
- `GET /u/unsubscribe?token=xxx`
- 流程:
- 驗證 token
- 顯示退訂成功頁面
### 9.4 發信 Header 規則
Newsletter 必須包含:
- `List-Unsubscribe: <https://domain/u/unsubscribe?token=xxx>`
- `List-Unsubscribe-Post: List-Unsubscribe=One-Click`
註記:
- Transactional email 不應包含 List-Unsubscribe。
### 9.5 與會員平台整合one-click 目標介面)
呼叫 API
- `POST /api/subscriptions/unsubscribe`
Request
- `subscriber_id`
- `list_id`
- `source: "one_click"`
- `campaign_id`
回傳:
- `success`
- `already_unsubscribed`
### 9.6 測試案例
1. Token 驗證
- 正常 token -> 成功退訂
- 過期 token -> `400``410`
- 已退訂 -> `200`
- 簽章錯誤 -> `400`
2. Header 驗證
- Newsletter 發送時必定包含 `List-Unsubscribe`
- Transactional email 不包含 `List-Unsubscribe`
- Header 格式正確
### 9.7 安全與 UX
安全:
- Unsubscribe endpoint 不受 CSRF 限制
- Token 驗證必須 server-side
- 記錄 audit log
- 不顯示 `subscriber_id` 在 URL 明文
UX
- 退訂成功頁面簡潔
- 提供重新訂閱入口
- 不強迫填問卷
### 9.8 目前實作狀態Wagtail
- 已提供 one-click endpoint
- `POST /u/unsubscribe`token 可由 query/body 提供)
- `GET /u/unsubscribe?token=...`(人類點擊頁)
- 已實作 token 驗證HMAC + `exp`)與 idempotent 行為(重複退訂維持 200
- 已實作 server-side audit log`OneClickUnsubscribeAudit`)。
- 已提供 one-click URL + Header 產生工具:
- `List-Unsubscribe`
- `List-Unsubscribe-Post`
### 9.9 Send Engine 發送介接(依 `../mass_mail_engine/docs/openapi.yaml` 對齊)
已對齊的 API
1. 建立 send job
- `POST /api/send-jobs`
- request 以 `CreateSendJobRequest` 為主(`list_id``subject``body_html/body_text/template`
- response 讀取:
- `send_job_id`
- `status`
2. 查詢 send job
- `GET /api/send-jobs/{id}`
- 若建立後狀態非最終態,持續輪詢直到最終態或超過輪詢上限。
狀態對應Wagtail campaign
- Send Engine `completed` -> campaign `sent`
- Send Engine `failed` / `cancelled` -> campaign `failed`
- 建立失敗 / 輪詢逾時 / 輪詢錯誤 -> campaign `failed`
Send Engine 最終態terminal
- `completed`
- `failed`
- `cancelled`
備註:
- 目前 dispatch 紀錄改為「每次送 job / 重試一次一筆」,不再是逐收件者一筆。
- 若要做投遞到單一收件者的最終監控delivery/bounce/complaint仍建議接 Send Engine webhook 或事件回寫機制處理。
### 9.10 One-Click Token 模式(目前採用)
目前採用:`MemberCenter token relay`
流程:
1. CMS 發送 payload 時同時提供:
- `template.list_unsubscribe_url_template`(給 Send Engine 產生 `List-Unsubscribe` header
信內可點連結由 `body_html/body_text` 直接使用 `{{unsubscribe_token}}` 組 URL。
例如:`https://{cms}/u/unsubscribe?token={{unsubscribe_token}}`
2. Send Engine 於發送時向 Member Center 取得每位收件者的退訂 token並替換 `{{unsubscribe_token}}`
3. 使用者點擊信內退訂(或 mailbox one-click進入 CMS `/u/unsubscribe`
4. CMS 不自行驗簽 token直接以 S2S 呼叫 Member Center `unsubscribe(token)` 完成退訂。
備註:
- 舊的 CMS 自簽 HMAC one-click token 保留工具函式作為備援,但主流程已切到 relay 模式。
## 10. 下一階段演進備忘(先記錄,待試用回饋後再排)
說明:
- 本節為「規劃中」項目,先記錄方向,不立即實作。
- 優先等待:電子報排版產出 + 實際 user 試用回饋。
### 10.1 編輯器能力演進
現況:
- 目前使用 Wagtail Draftail 作為 HTML 編輯器,已可插入圖片(圖庫選擇 / 上傳)。
待回饋後評估:
- 若編輯需求提升table、欄位版型、拖拉區塊、進階 email 元件),再評估導入更完整的 newsletter editor。
- 原則:先確認真實編輯痛點,再導入新編輯器,避免過早複雜化。
### 10.2 電子報樣式CSS策略
建議方向:
- 採「固定樣式基底 + 內容編輯」模式。
- CSS 不建議只放 `<head><style>`,實務上需考慮 email client 相容性,通常會在發送前做 CSS inlining將重要樣式轉為 inline style
- 可保留少量 head style例如 media query但核心排版建議以 inline 為主。
### 10.3 外框模板Header / Footer / Shell管理
目標:
- 提供可編輯的 newsletter shell例如 `layout.html`),內含 head、header、footer 與內容插槽。
建議實作概念:
- 內容編輯區僅負責 body 內文。
- 發送時由 backend 組裝:`shell + content + one-click headers/links`
- 預覽也走同一組裝流程,避免「預覽長得跟實際寄出不一致」。
### 10.4 退訂連結與個人化參數責任分工
共識方向:
- 退訂連結需與收件者身分綁定(每位收件者唯一 token / URL
- 內容模板需預留替換位(例如 `{{unsubscribe_url}}`)。
責任分工建議:
- CMS產內容、產 placeholder、組裝信件與 one-click header。
- Send Engine執行發送、接收投遞回報bounce/complaint 等)。
- SES或下游 provider最終投遞與回執來源。
### 10.5 文章區塊直接引入
需求方向:
- 支援從既有文章中挑選區塊或全文片段匯入電子報。
建議:
- 第一階段可先做「選文章 -> 帶入標題/摘要/首圖/連結」。
- 第二階段再考慮「可編輯快照」或「保持動態同步」策略。
### 10.6 預覽能力
需求方向:
- 內文編輯預覽。
- 樣式shell編輯預覽。
- 能注入假資料(假收件者、假 token、假文章看最終結果。
建議:
- 做「最終寄出 HTML 預覽」功能,預覽管線與正式發送使用同一套 renderer。
- 後續可加多裝置寬度與常見 email client 快速檢視模式(若使用者回饋有需要)。