# Flows 流程以「API 自建 UI」與「會員中心統一 UI」兩種模式描述。 ## F-01 註冊會員 - [API] 站點送出 `POST /auth/register` - [API] 會員中心建立 user、寄送驗證信 - [UI] 導向會員中心註冊頁完成註冊 - [UI] 會員中心寄送驗證信 ## F-02 登入(OAuth2 + OIDC) - [API] 站點送出 `POST /auth/login` 取得 access_token + id_token - [API] 站點建立自身 session - [UI] 使用 `usage=web_login` OAuth client,導向 `/oauth/authorize` 完成 Authorization Code + PKCE - [UI] 若未登入,會員中心會導向 Web login,登入後回到原 authorize request - [UI] 站點用 code + code_verifier 換 token - [UI] `web_login` 可使用 public client,不要求 client secret;必須設定 redirect URI - [UI] 若 Web 與 API 不同 origin,需設定 `Auth:WebLoginUrl`,且 Web 端需允許導回 `Auth:Issuer` 或 `Auth:AllowedLoginReturnUrlPrefixes` - [UI] 若 Web 與 API 位於不同子網域,需設定 `Auth:CookieDomain`,讓 authorize endpoint 可讀取 Web login cookie ## F-02b 內容站台呼叫 Send Engine(Client Credentials + JWT 驗簽) - [API] 內容站台以 `client_credentials` 呼叫 `POST /oauth/token` 取得 access_token(`usage=send_api`) - [API] 內容站台帶 Bearer token 呼叫 Send Engine 建立發送任務 - [API] Send Engine 以 Member Center JWKS 驗簽 token - [API] 驗證 `scope/tenant_id/exp` 通過後才受理任務 ## F-02c 檔案上傳(A service -> File Space) - [API] `A service` 以 `client_credentials` 向 Member Center 取得 access token - [API] token 需包含 `files:upload.write` - [API] token 應包含 `tenant_id`,並以 `aud=file_access_api` 為目標資源 - [API] `A service` 帶 token 呼叫 access agent / file space 上傳檔案 - [API] access agent 以 JWKS 驗簽 JWT,並驗 `iss/aud/exp/scope/tenant_id` ## F-02d 檔案下載(A service -> client -> File Space) - [API] client 向 `A service` 請求下載檔案 - [API] `A service` 驗證該 request 是否可讀取指定檔案 - [API] `A service` 取得或簽發短效 download token - [API] download token 需至少綁定 `tenant_id + file_id/object_key + method=GET + exp` - [UI/API] `A service` 將帶短效 token 的下載 URL 回給 client - [UI/API] client 直接向 access agent / file space 請求檔案 - [API] access agent 驗 token 後放行 註記:下載流程不直接暴露一般 S2S token 給 client。 ## F-03 忘記密碼 / 重設密碼 - [API] 站點送出 `POST /auth/password/forgot` - [UI] 會員中心頁提交 email 並發送重設信 - [API/UI] 使用 token 進入重設密碼頁 註記:目前 Web UI 已實作 forgot/reset 流程,但尚未串接 email 發送;開發階段會直接顯示 reset token 與 reset 連結。 ## F-03b 已登入修改密碼 - [UI] 使用者登入後進入 `/account/changepassword` - [UI] 輸入目前密碼與新密碼 - [UI] 會員中心驗證目前密碼後更新密碼 - [UI] 更新成功後刷新登入狀態 ## F-04 訂閱電子報(未登入) - [API] 站點送出 `POST /newsletter/subscribe` - [API] 會員中心建立 pending 訂閱並發送驗證信 - [UI] 使用者點擊驗證信連結 `/newsletter/confirm?token=...` - [UI] 訂閱改為 active,發出 event `subscription.activated` ## F-05 取消訂閱(單一清單) - [API] 站點以 `list_id + email` 呼叫 `POST /newsletter/unsubscribe-token` 取得 token - [UI] 使用者點擊退訂連結 `/newsletter/unsubscribe?token=...` - [UI] 訂閱狀態改為 unsubscribed - [API] 發出 event `subscription.unsubscribed` ## F-05b One-Click 退訂 Token(Send Engine 發信前) - [API] Send Engine 以 `tenant_id + list_id + subscriber_id` 呼叫 `POST /newsletter/one-click-unsubscribe-token` - [API] 或以 `tenant_id + list_id + subscriber_ids[]` 呼叫 `POST /newsletter/one-click-unsubscribe-tokens` 批次取得 - [API] Member Center 回傳 one-click `unsubscribe_token` - [API] Send Engine 將 token 置入信件 `List-Unsubscribe` URL ## F-06 訂閱偏好管理(登入後) - [API] 站點以 `list_id + email` 讀取 `/newsletter/preferences` - [API] 站點以 `list_id + email` 更新 `/newsletter/preferences` - [UI] 會員中心提供偏好頁(可選) ## F-06b 我的電子報訂閱管理(登入後) - [UI] 使用者登入後進入 `/profile/subscriptions` - [API/UI] 會員中心依目前登入 user 查出其已綁定的訂閱清單 - [UI] 顯示各租戶 / 清單 / 狀態 / 訂閱時間 - [UI] 使用者可直接按下取消訂閱 - [API/UI] 系統驗證該訂閱屬於目前登入 user 後直接退訂 - [API/UI] 不需再次 email token 驗證 - [API] 發出 event `subscription.unsubscribed` ## F-10 Send Engine 事件同步(Member Center → Send Engine) - [API] Member Center 以 webhook 推送 `subscription.activated/unsubscribed/preferences.updated`(scope: `newsletter:events.write`) - [API] Header 使用 `X-Signature` / `X-Timestamp` / `X-Nonce` / `X-Client-Id` - [API] `X-Client-Id` 對應 Send Engine `auth_clients.id`(UUID) - [API] Member Center 從 DB 的 tenant 設定讀取對應 webhook client id - [API] Send Engine 驗證簽章 + timestamp + nonce(重放防護)後入庫 - [API] Send Engine 更新名單快照 ## F-11 黑名單回寫(Send Engine → Member Center) - [API] Send Engine 依事件規則處理: - [API] `hard_bounce` / `soft_bounce_threshold` / `suppression`:回寫後由 Member Center 取消該 email 的所有訂閱(跨租戶)並加入黑名單 - [API] `complaint`:回寫後由 Member Center 僅取消該筆訂閱,不加入黑名單 - [API] 呼叫 `POST /subscriptions/disable`: - [API] tenant client 用 `newsletter:events.write` - [API] 平台 client(SES 聚合事件)用 `newsletter:events.write.global` - [API] body 需含 `tenant_id + subscriber_id + list_id + reason + disabled_by + occurred_at` - [API] `hard_bounce` / `soft_bounce_threshold` / `suppression` 才會寫入 `email_blacklist`;`complaint` 不寫黑名單 ## F-12 Webhook Client Mapping 回填(Send Engine → Member Center) - [API] Send Engine 建立/更新 tenant 對應的 webhook client(`auth_clients.id`) - [API] 呼叫 `POST /integrations/send-engine/webhook-clients/upsert`(scope: `newsletter:events.write.global`) - [API] Member Center 更新 tenant 設定(DB) ## F-07 會員資料查看 - [API] 站點讀取 `/user/profile` - [UI] 會員中心提供個人資料頁 ## F-07b 會員資料維護 - [UI] 使用者登入後進入 `/profile` - [UI] 維護電話、公司、統編等基本資料 - [API/UI] 儲存會員基本資料 ## F-07c 地址簿管理 - [UI] 使用者登入後進入 `/profile/addresses` - [UI] 新增、編輯、刪除收貨地址 - [UI] 可設定預設地址 - [API/UI] 地址簿資料綁定目前登入 user - [API/UI] 若只剩最後一筆地址,不允許刪除 ## F-07d 其他服務讀取會員資料 - [API] 其他服務以 access token 呼叫會員中心 profile API - [API] token 需帶對應 profile scopes - [API] 會員中心依 scope 決定可讀取基本資料、地址簿或訂閱資料 - [API] service API 為主要模式,只要 client 具對應 scope 即可存取對應資料 ## F-08 管理者管理租戶/清單/Client - [UI] 會員中心管理後台進行 CRUD ## F-09 訂閱與會員綁定 - [API] 使用者完成註冊後,會員中心將訂閱資料與 user_id 綁定 - [API] 發送事件 `subscription.linked_to_user`