Bản dịch: UC_9.2 → UC_9.17 (Background Mechanisms & Cron Jobs)
Bản dịch: UC_9.2 → UC_9.17 (BACKGROUND MECHANISMS & CRON JOBS)
Tổng hợp bản dịch tiếng Việt cho 11 use case thuộc nhóm BACKGROUND MECHANISMS & CRON JOBS đã có tài liệu SRS. File gốc tiếng Anh không bị thay đổi — khi có mâu thuẫn, file gốc là bản chuẩn. Mọi mã UC, field name, API endpoint, business rule ID, status value, code block và nội dung tin nhắn/email gửi cho user giữ nguyên tiếng Anh. UC_9.1, UC_9.4–UC_9.7, UC_9.10 chưa có tài liệu SRS nên chưa có trong bản dịch này. Ngày dịch: 2026-10-27. Cập nhật 2026-10-27: UC_9.2 (v2.0) và UC_9.12 (v1.1) theo CR-20261027-001 (Flow 13 hybrid); UC_9.2 (v2.1) và UC_9.17 (v1.3) đồng bộ
account_status = 'Active_DMA'. Cập nhật 2026-10-28: UC_9.3 (v1.1) theo spec Flow 18. Cập nhật 2026-11-05: đồng bộ UC_9.2 (v2.5), UC_9.3 (v1.3), UC_9.11 (v1.1), UC_9.12 (v1.2), UC_9.14 (v1.1), UC_9.15 (v1.1), UC_9.16 (v1.1) theo câu trả lời của khách 2026-10-30 / 2026-11-04; mọi link .md đổi sang đường dẫn tuyệt đối /docs/. Cập nhật 2026-11-05 (sau): UC_9.8 (v1.2) theo câu trả lời của khách 2026-11-03 (Flow 30 — refund và cờwarning_80_fired).
Mục lục
| UC_ID | Tên (tiếng Việt) | File gốc |
|---|---|---|
| UC_9.2 | Luồng 13: Quản lý Nghỉ phép (DTO W-2 & Tạm ngưng Quốc tế) | UC_9.2.md |
| UC_9.3 | Luồng 18: Sao lưu Cơ sở dữ liệu Cấu hình & Đồng bộ Khôi phục Thảm họa | UC_9.3.md |
| UC_9.8 | Luồng 30: Cảnh báo Công suất MID | UC_9.8.md |
| UC_9.9 | Luồng 31: Giám sát Cạn kiệt Tổng hợp (Sev-1) | UC_9.9.md |
| UC_9.11 | Luồng 34: Đồng bộ Sổ cái Lương Rippling | UC_9.11.md |
| UC_9.12 | Luồng 35: Đồng bộ Sổ cái PTO Rippling | UC_9.12.md |
| UC_9.13 | Luồng 36: Đồng bộ Sổ cái Retainer Dots | UC_9.13.md |
| UC_9.14 | Luồng 32: Velocity Reward Engine | UC_9.14.md |
| UC_9.15 | Luồng 33: Xác minh Amplify Bounty | UC_9.15.md |
| UC_9.16 | Luồng 38: Chặn Hỗ trợ & Tạo Ticket Freshdesk | UC_9.16.md |
| UC_9.17 | Luồng 39: Gửi lại Hướng dẫn Dữ liệu Thị trường BPS | UC_9.17.md |
UC_9.2 — Luồng 13: Quản lý Nghỉ phép (DTO W-2 & Tạm ngưng Quốc tế)
| Trường | Giá trị |
|---|---|
| BA phụ trách | linh.nguyen34 |
| Ngày tạo | 2026-08-20 |
| Phiên bản | v2.7 |
| Cập nhật lần cuối | 2026-11-05 — §2.2 modal Request Time Off: thêm ảnh màn hình; ghi chú thêm nút Cancel. Trước đó cùng ngày: Path A: gia hạn stagnation_deadline, đóng vị thế và khóa tài khoản bắt đầu vào leave_start_date đã đăng ký trên Rippling; trước ngày đó trader giao dịch bình thường. Trước đó cùng ngày: Bước A-3.5/B-3.5: đóng toàn bộ vị thế đang mở trước khi khóa — không còn đặc tả thuật toán TRIM_TO_COMPLIANCE. Lần trước: 2026-11-04 — Bảng tính của khách hàng (Adam/Adrian): Safety Check được viết lại dựa trên dữ liệu sẵn có (bảng blacklist, Users.status, Users.payout_status — không có field Compliance_Flagged); xử lý webhook đến muộn (2 trường hợp); hủy/rút ngắn PTO → Ops xử lý thủ công; carry của Pod Leader vẫn tiếp tục vest trong thời gian nghỉ; Flow 13 = Release 2. |
| Tài liệu tham chiếu | CR-20261027-001 (Flow 13 hybrid — ghi đè trigger §Flow 13 của V7 + Path A Action 1) · QnA_STAGE2_SETTINGS_CAREER_VIRAL.md [HR-DEF-01] · References/Wireframe/Dashboard/Flow 13_Request Time Off modal (International).jpg · Zapier Integration V7.pdf (§Flow 13: Time-Off Management lines 1949–1982; §Flow 6: International L6 retainer trigger line 1635; §Flow 35: Rippling PTO Approval lines 2535–2548; §Flow 36: Retainer Execution lines 2549–2561; §Table A DTO_Days column lines 2952–3004; §Table B DTO_Days column lines 3005–3031; §POST /system/set-account-state lines 136–140) · RFQ_ Stack Trading Prop Tech V7.pdf (§BPS Endpoints lines 2721–2867; §Support Action Audit Trail line 3349; §Break-Glass procedure line 3379) · QnA_STAGE6_POD_SYSTEM_FLOW10_11.md [POD-XF-15] [POD-XF-16] [POD-XF-17] [POD-JOIN-19] [POD-SLA-08] [POD-AUDIT-10] [POD-DISP-08] |
Danh mục UC
| UC_ID | Tên Use Case | Mô tả nghiệp vụ |
|---|---|---|
| UC_9.2 | Quản lý Nghỉ phép | Xử lý yêu cầu nghỉ phép và tạm ngưng giao dịch của trader Level 6+. Hai kênh vào (CR-20261027-001): path USA W-2 Paid DTO (trader yêu cầu nghỉ trong Rippling → webhook duyệt của Rippling → gia hạn deadline stagnation → khóa tài khoản giao dịch) hoặc path International Contractor Unpaid Hiatus (form "Request Time Off" trên Dashboard → tạm ngưng retainer Dots → gia hạn deadline stagnation → khóa tài khoản giao dịch). Khi kết thúc thời gian tạm ngưng, cổng Safety Check ngăn việc kích hoạt lại nếu trader khớp với bảng blacklist, có Users.status ∈ {Banned_Chargeback, Failed, Terminated}, hoặc payout_status ≠ Approved; nếu không thì khôi phục quyền giao dịch và tiếp tục thanh toán. |
<a id="UC_9.2"></a>
UC_9.2 — Luồng 13: Quản lý Nghỉ phép (DTO W-2 & Tạm ngưng Quốc tế)
1. Tổng quan
| Trường | Nội dung |
|---|---|
| ID | UC_9.2 |
| Use Case | Quản lý Nghỉ phép |
| Mô tả | Xử lý yêu cầu nghỉ phép từ trader Level 6+ qua hai kênh vào (CR-20261027-001): trader USA W-2 yêu cầu và được duyệt ngay trong Rippling — webhook duyệt của Rippling kích hoạt Path A (Paid DTO); International Contractor gửi form native trên Dashboard — kích hoạt Path B (Unpaid Hiatus). Quốc gia đã xác minh KYC / Employment_Type được giữ lại làm guard. Khóa tài khoản giao dịch, gia hạn stagnation_deadline, và quản lý việc tạm ngưng/tiếp tục thanh toán. Khi trader quay lại, thực thi Safety Check trước khi khôi phục quyền giao dịch. |
| Zapier Flow | Flow 13 (Time-Off Management — bao gồm delay_until cho Return Protocol) · Flow 35 (Rippling PTO Approval — Ledger Sync) · Flow 36 (Retainer Execution — Ledger Sync, cho retainer được tiếp tục) |
| Zapier Table | Table A / Table B (cột DTO_Days — số ngày nghỉ có lương tối đa theo level) |
| 3rd Party | Rippling (USA W-2: sở hữu yêu cầu nghỉ phép, số dư DTO và việc duyệt; webhook Time Off Request Approved kích hoạt Path A và đồng bộ PTO ledger Flow 35) · Dots (International: tạm ngưng và tiếp tục retainer) · AWS SES (email DTO_Approval_Notice, DTO_Welcome_Back) |
| Release | Release 2 (bảng tính khách hàng F13-01, 2026-11-04) |
2. Trigger
Hai kênh vào — kênh quyết định path (CR-20261027-001):
| Loại trader | Nơi trader yêu cầu nghỉ phép | Trigger vào Flow 13 | Path |
|---|---|---|---|
| USA W-2 (Level 6+) | Ngay trong Rippling (app/UI Rippling). Dashboard không cung cấp UI yêu cầu nghỉ phép cho trader W-2 — Career & HR Widget chỉ hiển thị CTA "View in Rippling" ([HR-DEF-01]). | Webhook Rippling Time Off Request Approved → Zapier Flow 13 | Path A |
| International Contractor (Level 6+) | Form "Request Time Off" native trên Dashboard | Submit form → JSON webhook → Zapier Flow 13 | Path B |
(Nguồn: CR-20261027-001 — khách hàng: "W2 traders can use Rippling" · "Create a very simple request form on the unified Dashboard for international contractors"; QnA [HR-DEF-01] 2026-08-15)
2.1 Trigger Path A — webhook Rippling Time Off Request Approved
- Rippling sở hữu yêu cầu của W-2, số dư DTO và việc quản lý duyệt. Flow 13 chỉ bắt đầu sau khi Rippling đã duyệt yêu cầu.
- Khi nhận webhook, Zap chỉ kiểm tra và lưu yêu cầu. Các bước xử lý nghỉ phép (Bước A-3 → A-4) chạy vào
leave_start_dateđã đăng ký trên Rippling, qua Zapierdelay_until— cùng cơ chế Return Protocol dùng cho ngày kết thúc. - Cùng webhook này cũng kích hoạt Flow 35 — PTO Ledger Sync (UC_9.12). Flow 13 Path A và Flow 35 chạy độc lập trên cùng một event.
- Các trường được dùng từ payload: Rippling worker →
user_id; Rippling Request ID; ngày bắt đầu nghỉ; ngày kết thúc nghỉ (→DTO_Hiatus_End_Date); số ngày được duyệt (→Requested_Days).
2.2 Trigger Path B — form "Request Time Off" trên Dashboard (chỉ International Contractor)
Điểm vào UI (UC_4.16.2 — Career & HR Widget):
- Nút: "Request Time Off" — góc trên bên phải của phần Employment Status trong Career & HR Widget.
- Điều kiện hiển thị: chỉ International Contractor có
Current_Level >= 6. Ẩn với trader W-2 (Employment_Type = 'W-2') và với trader dưới Level 6. - Thiết kế:
References/Wireframe/Dashboard/Flow 13_Request Time Off modal (International).jpg· Figma — modal Request Time Off.
Modal "Request Time Off" (thiết kế đã duyệt):

Figma: modal Request Time Off
| Thành phần | Loại | Quy tắc |
|---|---|---|
| Title | Text | "Request Time Off" |
| Description | Text (tĩnh) | "As an international contractor, you may request an unpaid hiatus of any duration. Your account and monthly retainer will be paused during this time without penalty." |
| Start Date | Date picker, MM/DD/YYYY | Bắt buộc. Ngày đầu tiên của kỳ tạm ngưng. |
| End Date | Date picker, MM/DD/YYYY | Bắt buộc. Phải ≥ Start Date. |
| Requested Days | Text chỉ đọc | Tự động tính từ Start Date và End Date; hiển thị _ cho đến khi cả hai ngày được chọn. |
| Reason | Textarea | Xem Q-4 (chưa xác nhận bắt buộc / độ dài tối đa). |
| Send | Nút chính | Gửi form. |
| Cancel | Nút phụ | ⚠️ Cần bổ sung — thiết kế hiện tại chưa có. Đóng modal mà không gửi: không gửi webhook, không thay đổi trạng thái DB. |
⚠️ Ghi chú: cần thêm nút Cancel vào thiết kế (hiện chỉ có Send). Các cách đóng khác (X / click ra ngoài / Esc) — xem Q-4.
Công cụ form: Form native của Dashboard — KHÔNG phải công cụ bên thứ ba (ví dụ Typeform hoặc Google Forms).
Webhook payload được gửi tới Zapier Flow 13 khi Send:
| Trường | Mô tả |
|---|---|
user_id (hoặc email đã đăng ký) | Định danh trader — dùng để tra cứu Current_Level, Employment_Type, quốc gia đã xác minh KYC, và bản ghi DB |
Start_Date | Ngày đầu tiên của kỳ tạm ngưng (từ form) |
DTO_Hiatus_End_Date | End Date từ form — Zapier map trực tiếp vào action delay_until cho Return Protocol |
Requested_Days | Số ngày tự động tính — dùng để gia hạn stagnation_deadline (Bước B-3) |
Reason | Văn bản tự do từ form |
Loại trigger: Submit form → JSON webhook → endpoint catch-hook của Zapier → Flow 13.
2.3 Cổng vào (cả hai kênh)
Current_Level >= 6 — được kiểm tra khi nhận webhook Rippling (Path A) hoặc khi submit form (Path B).
- Nếu
Current_Level < 6→ từ chối / bỏ qua. Không xử lý thêm. Path A: ghi log vàoBPS_Audit_Log(xem Exc-1).
Lý do: Level 6 là ngưỡng tối thiểu cho hợp đồng lao động W-2 (USA) hoặc hợp đồng independent contractor dựa trên retainer (International). Trader dưới Level 6 hoạt động theo mô hình bonus theo yêu cầu và không đủ điều kiện hưởng chính sách này. (Nguồn: Zapier Integration V7.pdf §Flow 13 lines 1949–1951; khách hàng xác nhận 2026-08-22)
3. Path Router
Path được xác định bởi kênh trigger:
| Kênh trigger | Path |
|---|---|
| Webhook Rippling Time Off Request Approved | Path A — USA W-2 Paid DTO |
| Form "Request Time Off" trên Dashboard | Path B — International Contractor Unpaid Hiatus |
Guard (kiểm tra nhất quán): quốc gia đã xác minh KYC (được ưu tiên hơn billing_country theo CR-20260812-005) và Employment_Type phải khớp với kênh:
- Webhook Rippling cho trader không phải
Employment_Type = 'W-2'→ không xử lý; ghi log vàoBPS_Audit_Log(Exc-1). - Form Dashboard được gửi bởi trader W-2 (nút lẽ ra phải bị ẩn — defence in depth) → từ chối; không thay đổi trạng thái.
(Nguồn: CR-20261027-001; Zapier Integration V7.pdf §Flow 13 — "Path Router" line 1951)
4. Luồng cơ bản
Path A — USA W-2 Paid DTO
Bước A-1. Nhận webhook duyệt từ Rippling
- Zapier nhận webhook Rippling Time Off Request Approved (§2.1).
- Ánh xạ Rippling worker sang
user_id; áp dụng cổng vào (Current_Level >= 6) và guard W-2 (§3). Thất bại → Exc-1. - Dedup trên Rippling Request ID — webhook được gửi lại không được gia hạn
stagnation_deadlinehai lần. - Không có DTO Balance Check và không có lời gọi Rippling API nào từ Stack Trading. Rippling sở hữu số dư DTO và việc duyệt; Flow 13 chỉ xử lý các yêu cầu đã được duyệt.
Requested_Days= số ngày được duyệt từ payload.
(Nguồn: QnA_STAGE2_SETTINGS_CAREER_VIRAL.md [HR-DEF-01] — yêu cầu PTO, số dư và việc duyệt do Rippling sở hữu)
Bước A-1.5. Chờ đến ngày bắt đầu nghỉ
- Hôm nay <
leave_start_date: Zap tạm dừng vớidelay_until=leave_start_date(lấy từ payload Rippling) và tiếp tục Bước A-3 vào ngày đó. Trước ngày đó trader giao dịch bình thường;trader_statuskhông đổi. - Hôm nay ≥
leave_start_date: tiếp tục ngay — webhook đến trễ được xử lý theo Exc-2 (Trường hợp 1 / Trường hợp 2). - Bước A-2 (ledger Flow 35) và Bước A-5 (
DTO_Approval_Notice) không bị trì hoãn — chạy ngay khi nhận webhook.
Bước A-2. PTO Ledger Sync (Flow 35 — song song, không thuộc Zap này)
- Cùng webhook Rippling kích hoạt Flow 35 (UC_9.12), gọi
POST /api/ledger/recordvớimetric_category = "Corporate Benefits",metric_value = "[Days_Approved] Days PTO Approved (Paid Time Off)",receipt_hash = [Rippling Request ID].
(Nguồn: Zapier Integration V7.pdf §Flow 35 lines 2535–2548)
Bước A-3. Gia hạn Stagnation Deadline
- Thực thi SQL update:
UPDATE Users SET stagnation_deadline = stagnation_deadline + INTERVAL '[Requested_Days] days' WHERE user_id = [trader_id]; - Việc này đảm bảo Flow 12 (Zombie Protocol) và Flow 19 (Stagnation Monitor) không phạt trader vì không hoạt động trong thời gian nghỉ phép đã được duyệt.
(Nguồn: Zapier Integration V7.pdf §Flow 13 line 1961)
Bước A-3.5. Đóng toàn bộ vị thế đang mở
Trước khi khóa tài khoản, Middleware đóng toàn bộ lệnh / vị thế đang mở của trader. Việc khóa tài khoản ở Bước A-4 chỉ được thực thi sau khi tài khoản không còn vị thế đang mở.
(Nguồn: Zapier Integration V7.pdf §Flow 13 — "Disable Trading Account via Middleware (Lock)")
Bước A-4. Khóa tài khoản giao dịch
- Gọi
POST /system/set-account-state:{ "user_id": "[trader_id]", "state": "Locked", "context": "DTO" } - Middleware gửi các lệnh khóa theo từng nền tảng sau khi xác nhận tài khoản đã flat (Bước A-3.5):
- Rithmic: R|API+
Suspend Trading. - MT5: YourBourse Contest Stop Out Plugin API (POST) — đặt tài khoản ở chế độ Read-Only; đóng băng giá trị Target Profit / Stop Loss trong RAM.
- TraderEvolution: Admin API — đặt tài khoản ở
Lock/Read-Only.
- Rithmic: R|API+
- Ghi DB sau gateway (chỉ thực thi sau khi nhận HTTP 200 từ tất cả gateway đang hoạt động):
trader_status = 'On_Leave'is_professional = TRUE(nếu trader dùng DMA)stagnation_deadlineđã được cập nhật ở Bước A-3
(Nguồn: Zapier Integration V7.pdf §Flow 13 line 1961; §POST /system/set-account-state lines 136–140; Supplement §3.2)
Bước A-5. Thông báo cho trader
- Gửi ngay khi nhận webhook (không bị trì hoãn bởi Bước A-1.5). Gửi email
DTO_Approval_Noticequa AWS SES xác nhận duyệt nghỉ phép, ngày bắt đầu và ngày dự kiến quay lại. - Thông báo Discord: Đẩy thông báo trạng thái nghỉ phép vào Redis memory queue. Zapier/Bot gom batch các message từ queue (tối đa 5 message / 5 giây) trước khi gửi tới Discord để tránh vi phạm rate-limit.
(Nguồn: Zapier Integration V7.pdf §Flow 13 line 1963; Supplement §3.4)
Path B — International Contractor Unpaid Hiatus
Bước B-1. Validation
- Không áp dụng giới hạn số ngày DTO. Tiếp tục ngay mà không kiểm tra số dư.
(Nguồn: Zapier Integration V7.pdf §Flow 13 line 1965)
Bước B-2. Tạm ngưng Retainer Dots
- Retainer hằng tháng của International Contractor được quản lý bởi một cron job của Middleware (Node.js, chạy hằng tháng) gọi Dots payout API. Đây KHÔNG phải một bước Zapier và không thể tạm dừng qua Zapier.
- Bản thân Dots API không có endpoint "suspend retainer" native — Dots chỉ xử lý các yêu cầu payout riêng lẻ theo yêu cầu.
- Cơ chế: Middleware đặt một DB flag (ví dụ
is_retainer_paused = TRUE) trên bản ghi Users của trader. Cron retainer hằng tháng kiểm tra flag này trước khi gọi DotsPOST /payeesvà bỏ qua mọi bản ghi có flag được đặt. - Tên field chính xác là quyết định triển khai của dev; yêu cầu nghiệp vụ là: không có khoản thanh toán retainer nào được gửi tới Dots trong khi trader đang tạm ngưng.
- Khi quay lại (Bước R-3), Middleware xóa flag (
is_retainer_paused = FALSE) để tiếp tục thanh toán hằng tháng bình thường.
(Nguồn: Zapier Integration V7.pdf §Flow 13 lines 1966–1968; §Flow 6 line 1635 — "Update the recurring backend trigger for the new Dots monthly retainer"; khách hàng xác nhận 2026-08-21)
Bước B-3. Gia hạn Stagnation Deadline
- Cùng SQL update như Path A Bước A-3: gia hạn
stagnation_deadlinethêmRequested_Days.
(Nguồn: Zapier Integration V7.pdf §Flow 13 line 1969)
Bước B-3.5. Đóng toàn bộ vị thế đang mở
- Giống Path A Bước A-3.5: đóng toàn bộ lệnh / vị thế đang mở.
- Việc khóa tài khoản ở Bước B-4 chỉ được thực thi sau khi tài khoản không còn vị thế đang mở.
(Nguồn: Zapier Integration V7.pdf §Flow 13 — "Disable Trading Account via Middleware (Lock)")
Bước B-4. Khóa tài khoản giao dịch
- Gọi
POST /system/set-account-state:{ "user_id": "[trader_id]", "state": "Locked", "context": "Hiatus" } - Middleware gửi các lệnh khóa theo từng nền tảng sau khi xác nhận tài khoản đã flat (Bước B-3.5):
- Rithmic: R|API+
Suspend Trading. - MT5: YourBourse Contest Stop Out Plugin API (POST) — đặt tài khoản ở chế độ Read-Only; đóng băng Target Profit / Stop Loss trong RAM.
- TraderEvolution: Admin API — đặt tài khoản ở
Lock/Read-Only.
- Rithmic: R|API+
- Ghi DB sau gateway (sau khi nhận HTTP 200 từ tất cả gateway đang hoạt động):
trader_status = 'On_Leave'is_professional = TRUE(nếu trader dùng DMA)stagnation_deadlineđã được cập nhật ở Bước B-3
(Nguồn: Zapier Integration V7.pdf §Flow 13 line 1970; Supplement §3.2)
Bước B-5. Thông báo cho trader
- Gửi email
DTO_Approval_Noticequa AWS SES. - Thông báo Discord: Đẩy thông báo trạng thái nghỉ phép vào Redis memory queue. Zapier/Bot gom batch (tối đa 5 message / 5 giây) trước khi gửi tới Discord.
(Nguồn: Zapier Integration V7.pdf §Flow 13 line 1972; Supplement §3.4)
Return Protocol (áp dụng cho cả hai Path)
Trigger: Zap Flow 13 gốc (chính Zap đã xử lý việc duyệt nghỉ phép) tiếp tục thực thi thông qua action delay_until của Zapier được đặt theo DTO/Hiatus End Date — Path A: ngày kết thúc nghỉ từ payload webhook Rippling; Path B: End Date từ form Dashboard. Đây KHÔNG phải một backend cron job riêng — Zap tạm dừng giữa chừng tại thời điểm duyệt nghỉ phép và tự động tiếp tục vào ngày kết thúc đã chỉ định.
(Nguồn: Zapier Integration V7.pdf §Flow 13; khách hàng xác nhận 2026-08-22)
Mô hình sở hữu:
- Zapier chịu trách nhiệm: timer
delay_until, điều khiển luồng và trình tự các bước sau khi tiếp tục, logic kiểm tra flag/database, và điều hướng thông báoDTO_Welcome_Back. - Middleware chịu trách nhiệm: thực thi Safety Check (truy vấn bảng
Usersvàblacklisttrong PostgreSQL), gọiPOST /system/set-account-statekhi vượt qua check sạch, đẩy lệnh Restore Trading trực tiếp tới broker server (Rithmic / MT5 / TraderEvolution), và tiếp tục trigger retainer Dots (Path B).
Bước R-1. Safety Check
- Middleware truy vấn PostgreSQL theo
user_id.Compliance_Flaggedkhông phải là một field được lưu trữ — nó được đánh giá từ dữ liệu sẵn có:- NẾU bất kỳ điều kiện nào sau đây đúng:
- Email của user có một dòng khớp trong bảng
blacklist(blacklist.email = Users.email AND blacklist.is_active = TRUE— cùng cách kiểm tra như UC_2.6.2 Blacklist Check); Users.status∈ {Banned_Chargeback,Failed,Terminated};Users.payout_status≠'Approved'(ví dụHold/Flaggedđược đặt bởi BPS Apply Payout Hold, webhook payout-profile của Dots, hoặc dispute hold của trader Live ở Flow 14).
- DỪNG toàn bộ việc kích hoạt lại. KHÔNG mở khóa tài khoản giao dịch. KHÔNG tiếp tục thanh toán.
- Gửi cảnh báo Slack ngay lập tức cho Head of Ops để review thủ công.
- Kết thúc luồng.
- Email của user có một dòng khớp trong bảng
- NGƯỢC LẠI: Tiếp tục sang Bước R-2.
- NẾU bất kỳ điều kiện nào sau đây đúng:
(Nguồn: Zapier Integration V7.pdf §Flow 13 lines 1974–1977; khách hàng xác nhận 2026-08-22; QnA_BACKGROUND_MECHANISMS_CRON.md [BG-005] [BG-006] [BG-007] — bảng tính khách hàng 2026-11-04; schema blacklist theo UC_2.6.2 / CHR-17)
Bước R-2. Kích hoạt lại tài khoản giao dịch
- Middleware gọi
POST /system/set-account-state:{ "user_id": "[trader_id]", "state": "Active", "context": "DTO_Return" } - Middleware đọc
Current_Levelhiện tại của trader từ PostgreSQL tại thời điểm này (không phải level tại thời điểm duyệt nghỉ phép — việc bị hạ cấp trong thời gian nghỉ được phản ánh tại đây). - Sau đó Middleware đẩy lệnh Restore Trading tới nền tảng thực thi của broker với các tham số rủi ro tương ứng với level hiện tại của trader: Rithmic 'Enable Trading' / MT5 'Enable' / TraderEvolution tương đương — bao gồm Notional, Daily Loss Limit và Level Stop chính xác cho
Current_Leveltại thời điểm quay lại.
(Nguồn: Zapier Integration V7.pdf §Flow 13 line 1978; khách hàng xác nhận 2026-08-22, 2026-09-09)
Bước R-3. Tiếp tục Retainer (chỉ Path B)
- Middleware tiếp tục trigger payout retainer Dots định kỳ đã lên lịch (xóa
is_retainer_paused = FALSE). - Flow 36 (Retainer Execution — Ledger Sync) kích hoạt ở chu kỳ thanh toán retainer kế tiếp và ghi bút toán Sổ cái Bất biến (Immutable Ledger).
- Nếu trader bị hạ cấp trong thời gian nghỉ: retainer tiếp tục theo mức retainer tương ứng với
Current_Levelhiện tại (thấp hơn) của họ, không phải level tại thời điểm duyệt nghỉ phép.
(Nguồn: Zapier Integration V7.pdf §Flow 13 line 1980; khách hàng xác nhận 2026-08-22, 2026-09-09)
Bước R-4. Thông báo cho trader
- Gửi mail
DTO_Welcome_Backqua AWS SES.
(Nguồn: Zapier Integration V7.pdf §Flow 13 line 1982)
4.1 Trường hợp đặc biệt: Xử lý Pod Leader & Founding Mentor (Level 7+)
Mục này điều chỉnh mọi hành vi trường hợp đặc biệt chỉ áp dụng khi Trader có is_pod_leader = TRUE hoặc role_flag = 'Founding_Mentor'.
4.1.1 Failsafe Anti-Termination Gate
Trước khi thực thi bất kỳ hành động nghỉ phép nào (Bước A-1/B-1), Middleware PHẢI kiểm tra role flag:
IF users.is_pod_leader = TRUE OR users.role_flag = 'Founding_Mentor':
→ MUST block: do NOT fire POD_LEADER_TERMINATED webhook
→ MUST NOT invoke Flow 15C (offboarding cascade)
→ Proceed with leave processing (soft-lock, stagnation extension, etc.)
ELSE:
→ Standard Trader flowCác hành động bị chặn trong thời gian nghỉ (áp dụng cho cả hai trigger):
- Webhook
POD_LEADER_TERMINATED: không bao giờ được kích hoạt - Flow 15C (giải thể Pod, tịch thu carry, offboarding Discord/Workspace): không bao giờ được gọi
- Việc zero hóa
Pending_Carry_Balance: không bao giờ được thực thi - Xóa/tạm ngưng Google Workspace: không bao giờ được kích hoạt
- Lưu trữ kênh Discord / thu hồi Role: không bao giờ được kích hoạt
(Nguồn: QnA_STAGE6_POD_SYSTEM_FLOW10_11.md [POD-XF-15] — khách hàng xác nhận 2026-10-30: nghỉ phép đã duyệt là nghỉ tạm thời, KHÔNG phải Drawdown Defense (Flow 4B) hay Termination (Flow 15C))
4.1.2 Tài nguyên được giữ nguyên trong On_Leave
| Tài nguyên | Active | On_Leave | Terminated (Flow 15C) |
|---|---|---|---|
| Tài khoản giao dịch | Active | Locked (giống mọi Trader) | Disabled |
Google Workspace @stacktrading.com | Active | Active — được giữ nguyên (vẫn tiếp tục nhận Wednesday AI Brief) | Bị xóa / tạm ngưng |
Pod_Leader_Registry.Status | Active | On_Leave — xem §4.1.4 | Terminated |
Kênh Discord pod-[leader_name] | Active | Active — không bị lưu trữ | Đã lưu trữ |
Discord Role Pod Leader | Đã gán | Đã gán — được giữ nguyên | Bị thu hồi |
Pod_Leader_Registry.Pending_Carry_Balance | Đang tích lũy / vesting | Đang tích lũy / vesting — không thay đổi | Bị tịch thu ($0.00) |
| Danh sách mentee của Pod | Liên kết với pod_id | Được giữ nguyên — không phân công lại | Bị phân tán (Flow 15C) |
Lưu ý về Founding Mentor: theo BR_9.2.19 và quy tắc Defense Bypass, tài khoản giao dịch của họ VẪN bị khóa trong On_Leave — ngoại lệ Founding Mentor chỉ bao phủ Administrative Roles (Pod/Discord), không bao phủ việc khóa tài khoản giao dịch.
(Nguồn: QnA_STAGE6_POD_SYSTEM_FLOW10_11.md [POD-XF-15], [POD-XF-16] — khách hàng xác nhận 2026-10-30; RFQ_ Stack Trading Prop Tech V7.pdf §Founding Mentor Defense Bypass)
4.1.3 Hành vi của Carry Balance
Pending_Carry_Balance tiếp tục tích lũy và vest bình thường trong thời gian nghỉ phép đã được duyệt. Lần chạy vest hằng tháng (Flow 17, ngày 1 hằng tháng) PHẢI bao gồm các Pod Leader có Pod_Leader_Registry.Status = 'On_Leave' — không đóng băng, không bỏ qua. Áp dụng như nhau cho Pod Leader thông thường và Founding Mentor. Carry chỉ bị đóng băng hoặc tịch thu khi bị tạm ngưng do Drawdown Defense hoặc Termination (Flow 15C).
Các khoản thanh toán khác trong thời gian nghỉ: lương cơ bản W-2 (Path A) tiếp tục qua payroll Rippling; retainer International (Path B) bị tạm ngưng (Bước B-2) và được tiếp tục ở R-3; các khoản payout hiệu suất giao dịch (Flow 5) đạt được trước kỳ nghỉ được xử lý bình thường — không thể đạt target mới trong khi tài khoản đang bị khóa.
(Nguồn: QnA_BACKGROUND_MECHANISMS_CRON.md [BG-008] [BG-009]; QnA_STAGE6_POD_SYSTEM_FLOW10_11.md [POD-XF-18] — bảng tính khách hàng 2026-11-04)
4.1.4 Pod_Leader_Registry Status trong thời gian nghỉ
- Tại Bước A-4 / B-4 (sau khi khóa gateway thành công), Middleware đặt
Pod_Leader_Registry.Status = 'On_Leave'cho leader. - Việc ghép cặp ở Flow 10 chỉ chọn
Status == 'Active'[POD-JOIN-19] → leader ởOn_Leavetự động bị loại khỏi việc ghép mentee mới. Việc này KHÔNG kích hoạt logic giải thể podSuspendedcủa Flow 15A. - Tại Bước R-2 của Return Protocol (khi kích hoạt lại thành công), Middleware đặt
Pod_Leader_Registry.Status = 'Active'.
(Nguồn: QnA_STAGE6_POD_SYSTEM_FLOW10_11.md [POD-XF-16] — khách hàng xác nhận 2026-10-30)
4.1.5 SLA của Pod trong thời gian nghỉ
Các timer SLA của Pod được tạm dừng trong khi leader đang nghỉ — không có Acting Cover, không ghép lại mentee:
- Cron SLA Enforcement của Flow 10.2 loại trừ các user ở
Inactive_Hold(Flow 12) hoặc bị Locked do DTO/Hiatus (Flow 13) [POD-SLA-08]. - Daily Engagement Audit của Flow 10.5 loại trừ các leader bị Locked do DTO/Hiatus [POD-AUDIT-10].
- Calendar Compliance Auditor của Flow 10.1B: leader đang DTO/Hiatus được miễn [POD-DISP-08].
(Nguồn: QnA_STAGE6_POD_SYSTEM_FLOW10_11.md [POD-XF-17] — khách hàng xác nhận 2026-10-30)
5. Điều kiện tiên quyết
| ID | Điều kiện tiên quyết |
|---|---|
| P1 | Path A: Rippling đã duyệt yêu cầu nghỉ phép của trader và phát webhook Time Off Request Approved. Path B: International Contractor gửi form "Request Time Off" trên Dashboard. |
| P2 | Current_Level >= 6 trong bảng Users. |
| P3 | account_status của trader là 'Active_DMA' (chưa bị Locked, Terminated hoặc Banned). Level lưu ở current_level. |
| P4 | Với Path A: Bản ghi Rippling worker tồn tại và được liên kết với user_id của trader; Employment_Type = 'W-2'. |
| P5 | Với Path B: Lịch retainer Dots đang hoạt động cho trader này. |
6. Hậu điều kiện
| Kết quả | Hậu điều kiện |
|---|---|
| Path A được xử lý | stagnation_deadline được gia hạn; tài khoản giao dịch Locked; DTO_Approval_Notice đã gửi. (Kỳ nghỉ đã được ghi nhận/duyệt trong Rippling; Flow 35 ghi bút toán sổ cái.) |
| Path B được xử lý | stagnation_deadline được gia hạn; tài khoản giao dịch Locked; retainer Dots bị tạm ngưng; DTO_Approval_Notice đã gửi. |
| Bị từ chối (cổng Level) | Không thay đổi trạng thái. |
| Bị từ chối (hết DTO) | Path A: được xử lý trong Rippling — không duyệt, không webhook, Flow 13 không được kích hoạt. Path B: N/A (không giới hạn ngày). |
| Quay lại — sạch | Tài khoản giao dịch được kích hoạt lại; retainer Dots được tiếp tục (Path B); DTO_Welcome_Back đã gửi. |
| Quay lại — Safety Check thất bại | Tài khoản vẫn Locked; Head of Ops được cảnh báo; không kích hoạt lại tự động. |
7. Luồng thay thế
Alt-1 — Số dư DTO đã hết (Path A):
- Được xử lý hoàn toàn bên trong Rippling (policy Rippling do Ops cấu hình theo
DTO_Days, Table A/B). Yêu cầu bị từ chối không bao giờ tạo webhook Time Off Request Approved — Flow 13 không được kích hoạt. Xem Q-1.
Alt-3 — Lần nghỉ phép thứ hai được duyệt trong Rippling khi trader đang nghỉ (Path A):
- Mỗi webhook duyệt bổ sung của Rippling (Rippling Request ID mới) gia hạn
stagnation_deadlinethêm số ngày vừa được duyệt và cập nhậtDTO_Hiatus_End_Date. - Việc gọi lại
POST /system/set-account-statevớistate = "Locked"là idempotent (chống trùng lặp) — tài khoản đã bị khóa, không tạo flag lồng nhau hoặc khóa trùng lặp. - Việc thực thi hạn mức là trách nhiệm của Rippling (Stack Trading không kiểm tra lại số dư).
Alt-4 — Yêu cầu tạm ngưng thứ hai được gửi khi trader đang nghỉ (Path B):
- Không có giới hạn ngày cho International Contractor. Luôn duyệt — gia hạn
stagnation_deadlinethêmRequested_Daysbổ sung. Việc tạm ngưng lại retainer Dots (is_retainer_paused = TRUE) là idempotent — flag đã được đặt, không thay đổi. - Return Protocol (Zapier
delay_until) theo dõiDTO_Hiatus_End_Dateđã cập nhật. Chỉ có một lần thực thi Return Protocol vào ngày kết thúc đã gia hạn cuối cùng.
(Nguồn: Zapier Integration V7.pdf pp. 21, 228-230, 392-394; khách hàng xác nhận 2026-09-09)
Alt-2 — Ngày quay lại của trader đến trong khi các flag Safety Check đang được đặt:
- Flow tự động kích hoạt Return Protocol vào ngày kết thúc đã lên lịch.
- Safety Check phát hiện flag → HALT. Tài khoản vẫn Locked vô thời hạn cho đến khi Ops giải quyết thủ công và kích hoạt lại.
- Đường kích hoạt lại thủ công: Ops gọi
POST /system/set-account-statevớistate = "Active"sau khi giải quyết vấn đề compliance/chargeback — phải được thực thi qua BPS Admin Console (áp dụng cổng RBACLevel_2+/Admin, bắt buộc córeason+transcript_url, ghi log vàoBPS_Audit_Log). Bản thân endpoint không có gate; Console là điểm thực thi RBAC (xem BR_9.2.12). (Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §BPS Endpoints lines 2721–2867; khách hàng xác nhận 2026-08-27)
Alt-5 — Trader W-2 hủy hoặc rút ngắn PTO đã duyệt trong Rippling (Path A):
- Không tự động tính lại. Gửi cảnh báo Slack tới
#ops-general(hoặc tạo ticket Freshdesk) để Ops kiểm tra thủ công, kích hoạt lại tài khoản giao dịch và điều chỉnhstagnation_deadlinenếu cần. - Không có bút toán ledger điều chỉnh (Flow 35).
- Nếu PTO bị hủy trước
leave_start_date, Zap đang chờ (Bước A-1.5) vẫn sẽ khóa tài khoản vào ngày đó — Ops phải hủy lượt Zap đang chờ hoặc mở khóa tài khoản thủ công.
(Nguồn: QnA_BACKGROUND_MECHANISMS_CRON.md [BG-002], [BG-017] — bảng tính khách hàng 2026-11-04)
8. Luồng ngoại lệ
Exc-1 — Webhook Rippling không qua cổng vào / guard (Path A, Bước A-1):
- Worker không thể ánh xạ sang
user_id,Current_Level < 6, hoặcEmployment_Type != 'W-2'. - Không gia hạn
stagnation_deadline; không khóa. Ghi log vàoBPS_Audit_Logđể Ops review. Không thông báo cho trader.
Exc-2 — Webhook Rippling đến sau ngày bắt đầu nghỉ (Path A):
- Trường hợp 1 — Hôm nay nằm giữa
leave_start_datevàleave_end_date: khóa ngay cho phần còn lại của kỳ nghỉ; gia hạnstagnation_deadlinethêm đủRequested_Daysđã duyệt; gửi thông báo Slack thường lệ tới#ops-generalghi nhận việc duyệt muộn. - Trường hợp 2 — Hôm nay đã qua
leave_end_date: KHÔNG khóa; Flow 35 vẫn ghi PTO vào ledger; gia hạnstagnation_deadlinethêm số ngày đã duyệt; gửi thông báo Slack thường lệ tới#ops-general. Không lên lịch Return Protocol.
(Nguồn: QnA_BACKGROUND_MECHANISMS_CRON.md [BG-003] — bảng tính khách hàng 2026-11-04)
Exc-3 — Lời gọi POST /system/set-account-state thất bại HOẶC execution gateway API trả về lỗi/timeout:
- Tài khoản giao dịch KHÔNG bị khóa như mong đợi. Rủi ro cao: trader có thể tiếp tục giao dịch trong thời gian nghỉ.
- Xử lý lỗi gateway: Middleware retry tối đa 3 lần với exponential backoff. Nếu hết retry → ghi log vào
BPS_Audit_Log+ gửi cảnh báo Slack SEV-1 tới Ops (#ops-it) để khóa thủ công. (Nguồn: Zapier Integration V7.pdf §Flow 13)
Exc-4 — Việc gửi cảnh báo Safety Check thất bại (thông báo Head of Ops):
- Bản thân quyết định HALT vẫn có hiệu lực — không kích hoạt lại tài khoản trong mọi trường hợp.
- Ghi log lỗi gửi cảnh báo. Điều hướng sang kênh cảnh báo phụ (ví dụ: ticket Freshdesk, email dự phòng).
Exc-5 — Trader có Luồng 14 (Payment Dispute / Chargeback) đang hoạt động trong khi nghỉ phép:
- Nếu một dispute được tạo khi
trader_status = 'On_Leave', trạng thái dispute có mức ưu tiên cao hơnOn_Leave: trader SIM →Users.status = 'Banned_Chargeback'; trader Live → dispute hold đặtUsers.payout_status≠'Approved'. - Tài khoản KHÔNG ĐƯỢC tự động kích hoạt lại vào
Return_Dateđã lên lịch cho đến khi Admin giải quyết dispute thủ công. - Safety Check (Bước R-1) bắt cả hai trường hợp (điều kiện 2 và 3) và dừng việc quay lại — ngoại lệ này ghi lại kịch bản dispute phát sinh giữa kỳ nghỉ để việc thiết lập dữ liệu test được rõ ràng.
(Nguồn: QnA_BACKGROUND_MECHANISMS_CRON.md [BG-005] [BG-006] — bảng tính khách hàng 2026-11-04)
9. Quy tắc nghiệp vụ
| Rule ID | Quy tắc | Nguồn |
|---|---|---|
| BR_9.2.1 | Chỉ trader có Current_Level >= 6 mới được gửi yêu cầu nghỉ phép hoặc tạm ngưng. Yêu cầu từ level thấp hơn bị từ chối mà không tạo bất kỳ thay đổi trạng thái nào. | Zapier Integration V7.pdf §Flow 13 |
| BR_9.2.2 | Việc điều hướng path được xác định bởi kênh trigger: webhook Rippling Time Off Request Approved → Path A (USA W-2 Paid DTO); form "Request Time Off" trên Dashboard → Path B (International Contractor Unpaid Hiatus qua Dots). Quốc gia đã xác minh KYC (được ưu tiên hơn billing_country theo CR-20260812-005) và Employment_Type chỉ được dùng làm guard kiểm tra nhất quán. | CR-20261027-001; CR-20260812-005 |
| BR_9.2.3 | Hạn mức DTO_Days được lấy từ Table A (Futures) hoặc Table B (Forex) theo Current_Level của trader. Hạn mức được thực thi bên trong Rippling (time-off policy do Ops cấu hình); Stack Trading không kiểm tra Used_DTO và chỉ xử lý các yêu cầu Rippling đã duyệt. | Zapier Integration V7.pdf §Table A / Table B; CR-20261027-001 |
| BR_9.2.4 | stagnation_deadline PHẢI được gia hạn thêm Requested_Days cho cả hai path. Điều này ngăn Flow 12 (Zombie Protocol) và Flow 19 (Stagnation Monitor) phạt trader trong thời gian nghỉ phép đã được duyệt. | Zapier Integration V7.pdf §Flow 13; ngày lịch, không tính theo ngày làm việc — QnA [BG-004] bảng tính khách hàng 2026-11-04 |
| BR_9.2.5 | Tài khoản giao dịch PHẢI được khóa qua POST /system/set-account-state (state = "Locked"). Path A: khóa vào leave_start_date đã đăng ký trên Rippling (Bước A-1.5). Path B: khóa khi gửi form. Việc khóa phải được thực thi ở cấp nền tảng thực thi (Rithmic Suspend Trading hoặc MT5 Disable/Read-Only). | Zapier Integration V7.pdf §Flow 13; §POST /system/set-account-state |
| BR_9.2.6 | Với Path B, khoản thanh toán định kỳ retainer Dots PHẢI được tạm ngưng khi duyệt nghỉ phép và tiếp tục khi quay lại thành công. Cơ chế: Middleware đặt một DB flag (ví dụ is_retainer_paused = TRUE) trên bản ghi Users của trader; cron retainer hằng tháng bỏ qua các bản ghi có flag này được đặt trước khi gọi Dots POST /payees. Bản thân Dots API không có endpoint suspend native — cron job hoàn toàn do Middleware sở hữu. Khi quay lại, flag được xóa để tiếp tục thanh toán bình thường. Tên field là quyết định triển khai. | Zapier Integration V7.pdf §Flow 13 lines 1966–1968; §Flow 6 line 1635; khách hàng xác nhận 2026-08-21 |
| BR_9.2.7 | Safety Check của Return Protocol PHẢI chạy trước mọi hành động kích hoạt lại. Nếu bất kỳ điều kiện nào của Bước R-1 đúng (khớp blacklist theo email với is_active = TRUE; Users.status ∈ {Banned_Chargeback, Failed, Terminated}; Users.payout_status ≠ 'Approved'), tài khoản PHẢI giữ nguyên trạng thái khóa và Head of Ops PHẢI được thông báo. Không có kích hoạt lại tự động trong trạng thái này. Việc kích hoạt lại thủ công bởi Ops qua POST /system/set-account-state yêu cầu Admin JWT (vai trò Level_2 hoặc Admin) — được thực thi ở tầng trước, tại tầng BPS Admin Console, không phải bởi bản thân endpoint (xem BR_9.2.12). Mọi hành động đều ghi log vào BPS_Audit_Log. | Zapier Integration V7.pdf §Flow 13 lines 1974–1977; RFQ_ Stack Trading Prop Tech V7.pdf §BPS Endpoints lines 2721–2867; khách hàng xác nhận 2026-08-27; QnA [BG-006] [BG-007] bảng tính khách hàng 2026-11-04 |
| BR_9.2.8 | Quét hằng ngày của Flow 12 (Zombie Protocol) dùng last_trade_date < (Today - 30). Trader đang nghỉ phép đã được duyệt sẽ có stagnation_deadline được gia hạn (BR_9.2.4), bảo vệ họ khỏi Flow 19 (Stagnation Monitor). Flow 12 KHÔNG bypass đối với trader ở trạng thái nghỉ phép — đây là hành vi đã được xác nhận. Nếu last_trade_date của trader vượt quá 30 ngày (bao gồm cả những ngày nghỉ phép đã duyệt), Flow 12 sẽ kích hoạt và vô hiệu hóa market data như bình thường. Việc gia hạn stagnation_deadline (BR_9.2.4) chỉ bảo vệ khỏi việc chấm dứt của Flow 19 — không ngăn Flow 12. Những ngày nghỉ KHÔNG bị trừ khỏi bộ đếm không hoạt động 30 ngày của Zombie Protocol. Trader nghỉ phép dài ngày cần biết rằng market data có thể bị Flow 12 vô hiệu hóa trong thời gian vắng mặt. | Zapier Integration V7.pdf §Flow 12; §Flow 13; khách hàng xác nhận 2026-08-22 |
| BR_9.2.9 | Đồng bộ Sổ cái Bất biến (Immutable Ledger) cho PTO (Flow 35) tự động kích hoạt từ webhook duyệt của Rippling — cùng một webhook kích hoạt Flow 13 Path A. Flow 35 là một Zap tách biệt và ghi log: metric_category = "Corporate Benefits", metric_value = "[Days_Approved] Days PTO Approved (Paid Time Off)", receipt_hash = [Rippling Request ID]. | Zapier Integration V7.pdf §Flow 35 |
| BR_9.2.10 | Return Protocol được kích hoạt bởi action delay_until của Zapier trong Zap Flow 13 gốc — KHÔNG phải một backend cron job hoặc webhook theo lịch riêng. Zap tạm dừng giữa chừng tại thời điểm duyệt nghỉ phép và tự động tiếp tục vào DTO/Hiatus End Date. Việc thực thi Return Protocol được đồng sở hữu: Zapier xử lý orchestration, điều khiển luồng và thông báo DTO_Welcome_Back; Middleware thực thi Safety Check, gọi POST /system/set-account-state, đẩy lệnh Restore Trading tới broker server (Rithmic / MT5 / TraderEvolution), và tiếp tục trigger retainer Dots (Path B). | Zapier Integration V7.pdf §Flow 13; khách hàng xác nhận 2026-08-22 |
| BR_9.2.11 | Kênh yêu cầu theo loại trader. Trader USA W-2 yêu cầu nghỉ phép ngay trong Rippling — Dashboard không được cung cấp UI yêu cầu nghỉ phép cho W-2 (Career & HR Widget chỉ hiển thị "View in Rippling"). International Contractor L6+ yêu cầu qua form "Request Time Off" native trên Dashboard (không phải công cụ bên thứ ba); khi Send, Dashboard gửi một JSON webhook tới catch-hook của Zapier với user_id (hoặc email đã đăng ký), Start_Date, DTO_Hiatus_End_Date, Requested_Days, Reason. | CR-20261027-001; QnA [HR-DEF-01]; khách hàng xác nhận 2026-08-27 |
| BR_9.2.12 | POST /system/set-account-state là một endpoint hệ thống nội bộ — nó KHÔNG tự thực thi cổng Human RBAC. Endpoint được thiết kế để Zapier automation bus và background worker gọi mà không cần ủy quyền JWT cho từng lời gọi. Human RBAC được thực thi ở tầng trước tại tầng BPS Admin Console: yêu cầu vai trò Level_2+ hoặc Admin để gọi bất kỳ hành động thủ công nào của Ops; caller Level_1 nhận HTTP 403 Forbidden tại tầng Console trước khi lời gọi tới endpoint. Bảo mật cho machine caller (Zapier, background worker) được thực thi qua Zapier JWT Signature và cô lập mạng VPC/VPN. Ops thủ công luôn phải đi qua BPS Admin Console, nơi áp dụng cổng RBAC, bắt buộc các trường reason + transcript_url, và ghi vào BPS_Audit_Log. Sau đó BPS Admin Console mới gọi xuống POST /system/set-account-state chỉ khi kiểm tra RBAC đã qua. | RFQ_ Stack Trading Prop Tech V7.pdf §BPS Endpoints lines 2721–2867; khách hàng xác nhận 2026-08-27 |
| BR_9.2.13 | Các yêu cầu nghỉ phép/tạm ngưng liên tiếp khi đã đang nghỉ được xử lý như gia hạn idempotent, không phải khóa chồng. Path A: mỗi lần duyệt bổ sung của Rippling (Rippling Request ID mới) gia hạn stagnation_deadline thêm số ngày vừa được duyệt; việc thực thi hạn mức là của Rippling (không kiểm tra lại). Webhook được gửi lại với cùng Rippling Request ID bị bỏ qua. Path B: luôn duyệt (không giới hạn ngày) → gia hạn stagnation_deadline. Ở cả hai path: việc gọi lại POST /system/set-account-state với state = "Locked" là idempotent (không có flag lồng nhau); flag retainer Dots (is_retainer_paused = TRUE) đã được đặt (idempotent). Return Protocol kích hoạt đúng một lần vào DTO_Hiatus_End_Date đã gia hạn cuối cùng. | Zapier Integration V7.pdf pp. 21, 228-230, 392-394; khách hàng xác nhận 2026-09-09 |
| BR_9.2.14 | Current_Level >= 6 chỉ là cổng vào — được kiểm tra khi nhận webhook Rippling (Path A) hoặc khi submit form (Path B), không kiểm tra khi quay lại. Cổng kích hoạt lại duy nhất của Return Protocol là Safety Check (Bước R-1: blacklist / Users.status / Users.payout_status). Nếu trader bị hạ xuống dưới Level 6 trong thời gian nghỉ, Return Protocol vẫn chạy bình thường vào ngày kết thúc đã lên lịch. Khi kích hoạt lại, Middleware đọc Current_Level hiện tại của trader từ PostgreSQL tại thời điểm đó và đẩy các tham số rủi ro (Notional, Daily Loss Limit, Level Stop) tương ứng với level bị hạ xuống broker gateway. Mọi tài khoản Live DMA từ Level 1-24 đều có account_status = 'Active_DMA' (enum cố định; level lưu ở current_level) — việc bị hạ cấp chỉ thay đổi current_level và không làm mất hiệu lực trạng thái tài khoản Live. Với Path B, retainer hằng tháng tiếp tục theo mức retainer của level bị hạ. | Zapier Integration V7.pdf; khách hàng xác nhận 2026-09-09; khách hàng xác nhận 2026-09-14 (UC_5.8 BR_5.8.9) |
| BR_9.2.15 | Trước khi khóa tài khoản giao dịch (Bước A-4/B-4), Middleware PHẢI đóng toàn bộ lệnh / vị thế đang mở. Việc khóa Execution Gateway chỉ kích hoạt sau khi tài khoản không còn vị thế đang mở. | Zapier Integration V7.pdf §Flow 13 |
| BR_9.2.16 | Trình tự khóa Execution Gateway là: Rithmic — R|API+ Suspend Trading; MT5 — YourBourse Contest Stop Out Plugin API (POST) đặt tài khoản ở Read-Only và đóng băng Target Profit / Stop Loss trong RAM; TraderEvolution — Admin API đặt tài khoản ở Lock/Read-Only. Cả ba lời gọi gateway PHẢI thành công (HTTP 200) trước khi thực thi các lệnh ghi DB sau gateway. | Supplement §3.2 |
| BR_9.2.17 | Các lệnh ghi DB được sắp xếp tuần tự sau khi gateway xác nhận. Các field sau chỉ được ghi vào bảng Users sau khi nhận HTTP 200 từ tất cả broker gateway đang hoạt động: trader_status = 'On_Leave'; is_professional = TRUE (nếu trader dùng DMA). stagnation_deadline được gia hạn ở bước trước (A-3/B-3) — không phụ thuộc vào phản hồi gateway. | Supplement §3.3 |
| BR_9.2.18 | Thông báo trạng thái nghỉ phép trên Discord PHẢI được đẩy vào Redis memory queue thay vì gửi trực tiếp tới Discord. Zapier/Bot xả queue với mức giới hạn tối đa 5 message mỗi 5 giây trước khi gửi tới Discord, để ngăn vi phạm rate-limit trên nền tảng Discord. | Supplement §3.4 |
| BR_9.2.19 | Phạm vi ngoại lệ Founding Mentor: Flag Founding_Mentor chỉ bảo vệ Administrative Roles — cụ thể là miễn khỏi Digital Eviction (vai trò Pod Leader và kênh Discord Pod không bị gỡ khi tài khoản giao dịch bị khóa hoặc vi phạm). Flag này KHÔNG miễn tài khoản giao dịch khỏi việc bị khóa trong thời gian nghỉ phép hoặc Hard Breach. Trong các trạng thái On_Leave hoặc Banned_Chargeback, tài khoản giao dịch của Founding Mentor bị khóa và toàn bộ vị thế đang mở bị đóng, giống hệt như với mọi trader khác. | Supplement §6 — BR 4 |
| BR_9.2.20 | Form "Request Time Off" (Path B). Chỉ hiển thị cho International Contractor có Current_Level >= 6. Các trường theo thiết kế đã duyệt: Start Date (bắt buộc), End Date (bắt buộc, ≥ Start Date), Requested Days (chỉ đọc, tự động tính từ hai ngày), Reason (textarea); nút Send và nút Cancel (Cancel cần bổ sung vào thiết kế). Văn bản mô tả tĩnh được cố định như trong thiết kế đã duyệt. | CR-20261027-001; References/Wireframe/Dashboard/Flow 13_Request Time Off modal (International).jpg |
| BR_9.2.21 | Thời điểm của Path A. Gia hạn stagnation_deadline (A-3), đóng vị thế đang mở (A-3.5), khóa tài khoản và trader_status = 'On_Leave' (A-4) cùng diễn ra vào leave_start_date trên Rippling. Từ lúc duyệt đến leave_start_date, trader giao dịch bình thường và chưa ở trạng thái On_Leave. | §2.1; Bước A-1.5 |
10. Tiêu chí chấp nhận
| ID | Kịch bản | Kết quả mong đợi |
|---|---|---|
| AC-9.2-01 | Trader có Current_Level = 5 — International: không thấy nút "Request Time Off"; W-2: nhận webhook Rippling cho worker Level 5 | Không thay đổi DB. Không gửi email. Path A: chỉ có bản ghi audit log. |
| AC-9.2-02 | Trader USA W-2 (Current_Level = 6) yêu cầu 5 ngày trong Rippling; Rippling duyệt và gửi webhook Time Off Request Approved | Path A: stagnation_deadline được gia hạn thêm 5 ngày; account_status = "Locked"; DTO_Approval_Notice đã gửi; Stack Trading không thực hiện lời gọi Rippling API nào. |
| AC-9.2-03 | Yêu cầu của trader USA W-2 bị từ chối bên trong Rippling (ví dụ hết DTO) | Không có webhook; Flow 13 không được kích hoạt; không thay đổi trạng thái. |
| AC-9.2-04 | Trader International Contractor (Current_Level = 7) gửi form "Request Time Off" trên Dashboard | Path B: retainer Dots bị tạm ngưng; stagnation_deadline được gia hạn; tài khoản bị khóa; DTO_Approval_Notice đã gửi. |
| AC-9.2-05 | Đến ngày quay lại qua Zapier delay_until — trader sạch (không có flag) | POST /system/set-account-state được gọi với state = "Active"; lệnh Restore Trading của broker được thực thi; DTO_Welcome_Back đã gửi; retainer Dots được tiếp tục (chỉ Path B). |
| AC-9.2-06 | Đến ngày quay lại qua Zapier delay_until — trader có Users.status = 'Banned_Chargeback' | Tài khoản vẫn Locked. Không gửi DTO_Welcome_Back. Head of Ops được cảnh báo. |
| AC-9.2-07 | Đến ngày quay lại qua Zapier delay_until — trader có Users.payout_status = 'Hold' (BPS Apply Payout Hold) | Giống AC-9.2-06. |
| AC-9.2-08 | Nhận webhook Rippling cho worker có Employment_Type != 'W-2' hoặc không ánh xạ được tới user_id nào | Không được xử lý; không khóa; không thay đổi stagnation_deadline; ghi một bản ghi BPS_Audit_Log. |
| AC-9.2-09 | POST /system/set-account-state thất bại trong lúc duyệt nghỉ phép | Tài khoản KHÔNG bị khóa. Cảnh báo Slack SEV-1 tới Ops để khóa thủ công. |
| AC-9.2-10 | Flow 35 kích hoạt sau khi Rippling duyệt PTO cho trader Path A | POST /api/ledger/record được gọi với metric_category = "Corporate Benefits", metric_value = "[Days_Approved] Days PTO Approved (Paid Time Off)". |
| AC-9.2-15 | Trader USA W-2 đang nghỉ được duyệt lần nghỉ thứ hai trong Rippling (Rippling Request ID mới) | stagnation_deadline được gia hạn thêm số ngày vừa duyệt; DTO_Hiatus_End_Date được cập nhật; khóa tài khoản không đổi (idempotent). |
| AC-9.2-16 | Rippling gửi lại cùng webhook Time Off Request Approved (cùng Rippling Request ID) | Bị bỏ qua — stagnation_deadline không bị gia hạn hai lần; khóa không đổi. |
| AC-9.2-17 | International Contractor đang tạm ngưng gửi yêu cầu tạm ngưng thứ hai | Yêu cầu được duyệt — stagnation_deadline được gia hạn thêm Requested_Days bổ sung; is_retainer_paused vẫn là TRUE (idempotent); khóa tài khoản không đổi; Return Protocol kích hoạt một lần vào DTO_Hiatus_End_Date đã gia hạn cuối cùng. |
| AC-9.2-18 | Trader là Level 7 lúc duyệt nghỉ phép; bị hạ xuống Level 4 trong thời gian nghỉ; đến ngày quay lại với Safety Check sạch | Tài khoản được kích hoạt lại: account_status = 'Active_DMA', current_level = 4; broker gateway được nạp tham số rủi ro Level 4 (Notional, Daily Loss Limit, Level Stop); retainer Path B tiếp tục theo mức retainer Level 4. |
| AC-9.2-19 | Trader là Level 7 lúc duyệt nghỉ phép; bị hạ xuống Level 4 trong thời gian nghỉ; đến ngày quay lại với Users.payout_status = 'Flagged' | Return Protocol dừng tại Safety Check — tài khoản vẫn Locked; Head of Ops được cảnh báo; mức hạ cấp không liên quan (cổng là trạng thái flag, không phải Level). |
| AC-9.2-20 | International Contractor (Level 8) click "Request Time Off" trong Career & HR Widget, chọn Start Date + End Date (Requested Days tự động điền), nhập Reason, click Send | Modal đóng; payload (user_id, Start_Date, DTO_Hiatus_End_Date, Requested_Days, Reason) được gửi tới Zapier; flow tiến tới Path B. |
| AC-9.2-21 | International Contractor mở "Request Time Off" rồi click Cancel (hoặc đóng modal) mà không click Send | Không có webhook nào được gửi; không thay đổi trạng thái DB; trader giữ nguyên trạng thái hiện tại. |
| AC-9.2-22 | Path A/B được duyệt — trader có vị thế đang mở | Toàn bộ lệnh / vị thế đang mở được đóng; việc khóa execution gateway chỉ tiến hành sau khi tài khoản không còn vị thế đang mở. |
| AC-9.2-23 | Path A được duyệt — khóa execution gateway thành công (HTTP 200 tất cả gateway); các lệnh ghi DB thực thi | trader_status = 'On_Leave'; is_professional = TRUE (nếu DMA) được ghi vào Users. |
| AC-9.2-24 | International Contractor (Path B, Live) — một dispute hold của Flow 14 được áp dụng giữa kỳ nghỉ; đến ngày quay lại | Safety Check bắt Users.payout_status ≠ 'Approved'; tài khoản vẫn Locked; Head of Ops được cảnh báo; không kích hoạt lại tự động cho đến khi Admin giải quyết dispute. |
| AC-9.2-25 | Trader W-2 (Level 8) mở Career & HR Widget | Nút "Request Time Off" không được hiển thị; chỉ hiển thị CTA "View in Rippling". |
| AC-9.2-26 | International Contractor chọn End Date sớm hơn Start Date | Lỗi validation; Send không submit; Requested Days không được tính. |
| AC-9.2-27 | International Contractor chọn Start Date và End Date | Requested Days hiển thị giá trị tự động tính (quy tắc tính theo Q-4). |
| AC-9.2-28 | Đến ngày quay lại; email của trader có một dòng đang hoạt động trong bảng blacklist (is_active = TRUE) | Giống AC-9.2-06. |
| AC-9.2-29 | Webhook Rippling đến khi Hôm nay nằm giữa ngày bắt đầu và ngày kết thúc nghỉ | Tài khoản bị khóa; stagnation_deadline + đủ Requested_Days; thông báo duyệt muộn trên Slack #ops-general. |
| AC-9.2-30 | Webhook Rippling đến sau leave_end_date | Không khóa; stagnation_deadline được gia hạn; bút toán ledger Flow 35 được ghi; thông báo Slack #ops-general. |
| AC-9.2-31 | Pod Leader đang nghỉ phép đã duyệt; đến ngày 1 của tháng | Flow 17 vest Pending_Carry_Balance bình thường (không bỏ qua). |
| AC-9.2-32 | Webhook Rippling nhận ngày 01/12; leave_start_date = 10/12 | Trước 10/12: không khóa, stagnation_deadline không đổi, trader_status không đổi; DTO_Approval_Notice gửi ngày 01/12. Ngày 10/12: đóng vị thế, khóa tài khoản, gia hạn stagnation_deadline, trader_status = 'On_Leave'. |
| AC-9.2-33 | Giống AC-9.2-32, trader đặt lệnh ngày 05/12 | Lệnh được chấp nhận — tài khoản chưa bị khóa trước leave_start_date. |
10.1 Câu hỏi mở (CR-20261027-001)
| ID | Câu hỏi | Giả định hiện tại |
|---|---|---|
| Q-1 | Rippling có chặn các yêu cầu vượt DTO_Days (Table A/B) theo policy không, hay Middleware phải kiểm tra lại khi nhận webhook? | Policy Rippling do Ops cấu hình; không kiểm tra lại. |
| Q-2 | Nếu trader W-2 hủy / rút ngắn kỳ nghỉ đã duyệt trong Rippling, có webhook không, và có nên mở khóa tài khoản sớm / giảm stagnation_deadline không? | ✅ Đã trả lời 2026-11-04 — xem Alt-5. |
| Q-3 | Webhook Rippling bị trễ quá ngày bắt đầu nghỉ → khóa muộn. Có cảnh báo Ops không? | ✅ Đã trả lời 2026-11-04 — xem Exc-2. |
| Q-4 | Form: Reason có bắt buộc không, độ dài tối đa? Requested Days = số ngày lịch tính cả hai đầu? Start Date có được ở quá khứ không? Ngoài nút Cancel (sẽ bổ sung), modal có đóng được qua X / click ra ngoài / Esc không? | Reason tùy chọn ≤ 500 ký tự; ngày lịch tính cả hai đầu; Start Date ≥ hôm nay; đóng qua X / click ra ngoài / Esc. |
11. Lịch sử thay đổi
| Ngày | Phiên bản | Mục được cập nhật | Trước | Sau | Lý do |
|---|---|---|---|---|---|
| 2026-11-05 | v2.6 → v2.7 | Header; §2.2 modal (ảnh màn hình, dòng Cancel, ghi chú); §9 BR_9.2.20; §10 AC-9.2-21; §10.1 Q-4 | Chưa có ảnh màn hình; không có nút Cancel (đóng modal chỉ theo Q-4) | Đã nhúng ảnh màn hình; cần bổ sung nút Cancel — đóng modal mà không gửi | Rà soát thiết kế 2026-11-05 |
| 2026-11-05 | v2.5 → v2.6 | Header; §2.1; §4 Bước A-1.5 (mới), Bước A-5; §7 Alt-5; §9 BR_9.2.5, BR_9.2.21 (mới); §10 AC-9.2-32/33 (mới) | Path A khóa và gia hạn stagnation_deadline ngay khi nhận webhook duyệt | Các bước của Path A bắt đầu vào leave_start_date trên Rippling (Zapier delay_until); email duyệt và Flow 35 vẫn chạy khi nhận webhook; Path B không đổi | Kỳ nghỉ bắt đầu vào ngày đã đăng ký trên Rippling |
| 2026-11-05 | v2.4 → v2.5 | Header; §4 Bước A-3.5, B-3.5; §8 Exc-3; §9 BR_9.2.15, BR_9.2.19; §10 AC-9.2-22 | Thuật toán TRIM_TO_COMPLIANCE (sắp xếp theo kích thước, Market Close tuần tự, listener ExecutionReport, failsafe Hard Liquidation 2,000 ms) — nguồn "Supplement §3.1 / §7" (không có trong References) | Đóng toàn bộ lệnh / vị thế đang mở trước khi khóa; chỉ khóa sau khi tài khoản không còn vị thế đang mở; không đặc tả thuật toán | Flow 13 chỉ nêu việc đóng vị thế; Zapier Integration V7.pdf §Flow 13 |
| 2026-11-04 | v2.3 → v2.4 | Header; §1 (dòng Release); mô hình sở hữu Return Protocol + Bước R-1; §4.1.2 dòng carry; §4.1.3 (viết lại); §7 Alt-5 (mới); §8 Exc-2, Exc-5; §9 nguồn BR_9.2.4, BR_9.2.7; §10 AC-9.2-07/19/24, AC-9.2-28–31 (mới); Danh mục UC + cách diễn đạt Safety Check ở BR_9.2.14; §10.1 Q-2/Q-3 | Safety Check dựa trên Trader_Status ∈ {Banned_Chargeback, Compliance_Flagged}; Exc-2 khóa khi nhận, không cảnh báo; không có quy tắc cho việc hủy/rút ngắn PTO; carry bị đóng băng trong thời gian nghỉ, Flow 17 bỏ qua On_Leave; nguồn Exc-5 "Supplement §7" | Safety Check = khớp blacklist HOẶC Users.status ∈ {Banned_Chargeback, Failed, Terminated} HOẶC payout_status ≠ Approved (chỉ PostgreSQL); Exc-2 tách thành 2 trường hợp với Slack #ops-general; Alt-5 Ops xử lý thủ công; carry vẫn tiếp tục vest với mọi Pod Leader kể cả Founding Mentor; Release 2 | Bảng tính khách hàng Adam/Adrian 2026-11-04 (QnA [BG-002]–[BG-009], [BG-017]; [POD-XF-18]) |
| 2026-10-30 | v2.2 → v2.3 | Header; §4.1.1 nguồn; §4.1.2 (dòng Workspace, dòng status Registry mới, nhãn dòng Pending_Carry_Balance, nguồn); §4.1.4, §4.1.5 (mới) | §4.1.1/§4.1.2 trích dẫn "RFQ V7 §Time-Off & Hiatus Management Engine" không tồn tại; không có quy tắc cho Pod_Leader_Registry.Status trong thời gian nghỉ; không có quy tắc cho SLA của pod trong thời gian nghỉ | Nguồn → xác nhận của khách hàng [POD-XF-15/16]; Pod_Leader_Registry.Status = 'On_Leave' được đặt ở A-4/B-4, trở về 'Active' ở R-2, bị loại khỏi ghép cặp Flow 10; timer SLA của pod tạm dừng (SLA cron / Daily Audit / Calendar Auditor); bỏ nhãn "Table E" — field được đặt tên Pod_Leader_Registry.Pending_Carry_Balance | Phản hồi của khách hàng 2026-10-30 (F13-10, F13-11, F13-14) |
| 2026-10-28 | v2.1 → v2.2 | §4 Bước A-4/B-4 ghi DB sau gateway; §4.1.4 (đã xoá); §9 BR_9.2.17; §10 AC-9.2-23 | A-4/B-4, BR_9.2.17, AC-9.2-23 ghi credentials_last_rotated_at = NOW() khi khoá tài khoản nghỉ phép (nguồn "Supplement §3.3"); §4.1.4 cơ chế Acting Cover cho SLA của mentee (nguồn "RFQ V7 §Time-Off & Hiatus Management Engine") | Xoá cả hai — không có nguồn trong References (V7 chỉ set credentials_last_rotated_at khi cấp/đổi credential ở Flow 3H; Acting_Cover trong V7 chỉ là status của Flow 15A) | Rà soát nguồn 2026-10-28 |
| 2026-10-27 | v2.0 → v2.1 | §5 P3; §9 BR_9.2.14; §10 AC-9.2-18 | account_status ghi dạng Active_L[N]_DMA / Active_Lx_DMA / Active_L4_DMA (level ghép vào status) | account_status = 'Active_DMA' (enum cố định) + current_level | Khách xác nhận 2026-09-14 — account_status là enum cố định; level lưu ở current_level (UC_5.8 BR_5.8.9) |
| 2026-10-27 | v1.9 → v2.0 | Header; Danh mục UC; §1; §2 (viết lại: 2 kênh, 2.1–2.3); §3 Path Router; §4 Steps A-1/A-2; trigger Return Protocol; §5 P1/P4; §6; §7 Alt-1/Alt-3; §8 Exc-1/Exc-2; §9 BR_9.2.2/3/9/11/13/14, BR_9.2.20 (mới); §10 AC-9.2-01/02/03/04/08/15/16/20/21, AC-9.2-25/26/27 (mới), §10.1 Câu hỏi mở | Một form Dashboard cho mọi trader L6+; Path A thực hiện Rippling DTO Balance Check (A-1) + "Log Time Off in Rippling API" (A-2); path được điều hướng theo quốc gia KYC; nút hiển thị cho cả W-2 và International; trường form Request Date + Start Date | W-2 yêu cầu trong Rippling → webhook duyệt của Rippling kích hoạt Path A (A-1 = nhận webhook + gate + dedup; A-2 = Flow 35 song song; không gọi Rippling API). International dùng form Dashboard đã duyệt (Start/End Date, Requested Days tự động, Reason, Send) → Path B. Điều hướng theo kênh trigger; KYC/Employment_Type làm guard. Exc-1/Exc-2 cũ bị loại bỏ. | CR-20261027-001 (khách hàng chọn Option B Hybrid, 2026-10-27); [HR-DEF-01]; thiết kế form đã duyệt |
| 2026-10-01 | v1.8 → v1.9 | Thêm §4.1 Trường hợp đặc biệt Pod Leader / Founding Mentor: Failsafe Anti-Termination Gate, bảng bảo toàn tài nguyên, đóng băng carry balance, cơ chế Acting Cover | Không có mục trường hợp đặc biệt Pod Leader / Founding Mentor; BR_9.2.19 chỉ đề cập ngoại lệ Founding Mentor trong phạm vi | Đã thêm §4.1: 4 mục con bao gồm anti-termination gate (chặn POD_LEADER_TERMINATED + Flow 15C), ma trận bảo toàn tài nguyên On_Leave, phân biệt đóng băng carry với tịch thu, Acting Cover bao phủ SLA tạm thời | RFQ_ Stack Trading Prop Tech V7.pdf §Time-Off & Hiatus Management Engine |
| 2026-09-19 | v1.7 → v1.8 | §2 Trigger (UI Entry Point); §4 Steps A-3.5/B-3.5/A-4/B-4/A-5/B-5; §8 Exc-3/Exc-5; §9 BR_9.2.15–19; §10 AC-9.2-20–24 | Không có bước TRIM_TO_COMPLIANCE trước khi khóa; bước khóa chỉ nhắc Rithmic/MT5 một cách chung chung; không có đặc tả ghi DB sau gateway; A-5/B-5 không có Discord batching; B-5 không có link compliance Dots; chưa định nghĩa điểm vào UI; Exc-3 không có đặc tả retry; chưa ghi nhận xung đột Flow 14; ngoại lệ Founding Mentor chưa được ghi nhận | TRIM_TO_COMPLIANCE (loop & close + failsafe Hard Liquidation 2,000 ms) được thêm làm Bước A-3.5/B-3.5. Bước khóa được mở rộng: MT5 YourBourse Contest Stop Out Plugin + TraderEvolution Admin API. Thêm ghi DB sau gateway: trader_status='On_Leave', is_professional=TRUE, credentials_last_rotated_at=NOW(). Ghi nhận nút "Request Time Off" tại UC_4.16.2 Career & HR Widget (trường bắt buộc: Request Date, Start Date). Thông báo Discord qua Redis queue (tối đa 5 msg/5 s). Email B-5 được bổ sung link compliance white-label của Dots. Exc-3 được mở rộng với 3× retry + exponential backoff. Thêm Exc-5: dispute Flow 14 pre-empt việc quay lại On_Leave. Thêm BR 9.2.15–19 và AC 9.2-20–24. | Technical supplement 2026-09-19 |
| 2026-09-09 | v1.6 → v1.7 | Step R-2/R-3 + BR_9.2.14 + AC-9.2-18/19 | Return Protocol kích hoạt lại trader về level ban đầu; trường hợp biên khi Level giảm trong thời gian nghỉ chưa được xử lý | Step R-2: Middleware nay đọc Current_Level hiện tại tại thời điểm quay lại và đẩy tham số rủi ro của level bị hạ. Step R-3: retainer Path B tiếp tục theo mức của level bị hạ. BR_9.2.14: Current_Level >= 6 được xác nhận chỉ là cổng vào — không kiểm tra lại khi quay lại. Safety Check là cổng quay lại duy nhất. Mọi tài khoản Live DMA Level 1-24 đều mang trạng thái Active_Lx_DMA hợp lệ. Thêm AC-9.2-18 (quay lại sạch, bị hạ cấp) và AC-9.2-19 (Safety Check halt, bị hạ cấp). | Khách hàng xác nhận 2026-09-09; đóng Q11 |
| 2026-09-09 | v1.5 → v1.6 | §7 Alternative Flows (thêm Alt-3/Alt-4) + BR_9.2.13 + AC-9.2-15/16/17 | Chưa ghi nhận hành vi cho các yêu cầu nghỉ phép liên tiếp khi trader đã đang nghỉ | Thêm Alt-3 (Path A: kiểm tra lại số dư DTO → gia hạn hoặc từ chối) và Alt-4 (Path B: luôn gia hạn). BR_9.2.13 chính thức hóa hành vi state machine idempotent (POST /system/set-account-state idempotent; flag Dots idempotent; một Return Protocol duy nhất vào ngày kết thúc cuối cùng). Thêm AC-9.2-15/16/17. | Khách hàng xác nhận 2026-09-09; đóng Q9 |
| 2026-08-22 | v1.3 → v1.4 | §2 Trigger được viết lại + thêm BR_9.2.11 | §2: "Trader submits a Time-Off / Hiatus Request Form. Trigger type: Form submission webhook." Không có công cụ form, không có danh sách field, không có định nghĩa payload. | Form được xác nhận là form Dashboard native (không phải bên thứ ba). Định nghĩa webhook payload: user_id/Requested_Days/DTO_Hiatus_End_Date. Path Router truy vấn AC/PostgreSQL lấy billing_country. Thêm BR_9.2.11. | Khách hàng xác nhận 2026-08-22; đóng Q2 từ audited-v1.md |
| 2026-08-22 | v1.2 → v1.3 | Return Protocol — cơ chế Trigger + BR_9.2.10 | Trigger: "DTO/Hiatus end date reached (date-based cron or scheduled webhook)." Các Step R-1/R-2/R-3/R-4 không nêu rõ quyền sở hữu. | Return Protocol được kích hoạt bởi Zapier delay_until trên Zap Flow 13 gốc (không phải cron). Đồng sở hữu: Zapier (orchestration/flow/email) + Middleware (thực thi Safety Check, POST /system/set-account-state, broker Restore Trading, tiếp tục retainer Dots). Thêm BR_9.2.10. Cập nhật mô tả trigger của AC-9.2-05/06/07. | Khách hàng xác nhận 2026-08-22; đóng Q1 từ audited-v1.md |
| 2026-08-22 | v1.1 → v1.2 | BR_9.2.8 — đóng OPEN | [⚠️ OPEN] — Flow 12 bypass cho trader nghỉ phép chưa được định nghĩa | Xác nhận Flow 12 kích hoạt bình thường trong thời gian nghỉ phép đã duyệt. Market data bị vô hiệu hóa khi last_trade_date > 30 days. Những ngày nghỉ KHÔNG bị trừ khỏi bộ đếm không hoạt động 30 ngày. Việc gia hạn stagnation_deadline (BR_9.2.4) chỉ bảo vệ Flow 19. | Khách hàng xác nhận 2026-08-22 |
| 2026-08-21 | v1.0 → v1.1 | Step B-2; Alt-2; BR_9.2.6; BR_9.2.7 | Step B-2: [OPEN] — cơ chế chưa xác định. Alt-2: [OPEN] — không có chi tiết RBAC. BR_9.2.6/7: chung chung. | Step B-2: xác nhận cơ chế DB flag (is_retainer_paused) + Middleware cron — không phải Zapier, không phải Dots API. Alt-2: BPS RBAC được xác nhận (JWT Level_2/Admin, BPS_Audit_Log); cổng RBAC của POST /system/set-account-state vẫn [OPEN]. | Khách hàng xác nhận 2026-08-21 |
| 2026-08-20 | v1.0 | Tạo ban đầu | — | SRS đầy đủ cho UC_9.2 (Flow 13) | Lần soạn đầu tiên. Nguồn: Zapier Integration V7.pdf §Flow 13 + §Flow 35. |
UC_9.3 — Luồng 18: Sao lưu Cơ sở dữ liệu Cấu hình & Đồng bộ Khôi phục Thảm họa
| Trường | Giá trị |
|---|---|
| BA phụ trách | linh.nguyen34 |
| Ngày tạo | 2026-08-21 |
| Phiên bản | v1.3 |
| Cập nhật lần cuối | 2026-11-04 — Bảng tính của khách hàng (Adam/Adrian): lịch chạy cố định 02:00 UTC (không áp dụng DST; mọi backend cron đều chạy theo UTC cố định); lifecycle = S3 Standard 90 ngày → S3 Glacier → hết hạn sau 1,856 ngày (5 năm + 1 tháng), chỉ dùng S3 Lifecycle Rule gốc. Trước đó: 2026-10-28 — Cập nhật đặc tả Flow 18: lịch chạy 02:00 AM CST (08:00 AM UTC); Object Lock bắt buộc (Compliance Mode); phân tầng lifecycle HOT/WARM/COLD + hết hạn sau 61 tháng; retry 3× với exponential backoff trước khi cảnh báo; đóng xung đột phạm vi bảng (Tables A–F + các bảng mới được thêm). Cảnh báo: chỉ Slack #ops-dev (không dùng Freshdesk). |
| Tài liệu tham chiếu | Zapier Integration V7.pdf (§Flow 18: Configuration Database Backup lines 2099–2121; §Flow 40: Zapier DR Recovery Snapshot lines 2625–2660) · RFQ_ Stack Trading Prop Tech V7.pdf (§5.0 Immutable Archival S3 Object Lock lines 3339–3357; §NFA Audit Requirements lines 3357–3362) · slack_channel_usage.md (CR-15.2 — #ops-dev) · Flow 18 spec 2026-10-28 · Câu hỏi mở: UC_9.x_Client_Questions.md §Flow 18 |
Danh mục UC
| UC_ID | Tên use case | Mô tả nghiệp vụ |
|---|---|---|
| UC_9.3 | Config DB Backup & DR Sync | Hằng ngày lúc 02:00 UTC (cố định, không áp dụng DST): export toàn bộ Zapier Tables (A–F + các bảng mới được thêm) ra JSON kèm schema_version + SHA-256 checksum → upload lên S3 bucket dùng chung với Flow 40 (SSE-KMS, versioning, Object Lock Compliance Mode, S3 Standard 90 ngày → S3 Glacier, hết hạn sau 1,856 ngày). Bước xác minh độc lập tải lại file, kiểm tra lại checksum và test-parse JSON. Sao lưu hoặc xác minh thất bại → retry tối đa 3× (exponential backoff) → cảnh báo Slack. Mọi cảnh báo chỉ gửi tới Slack #ops-dev. |
<a id="UC_9.3"></a>
UC_9.3 — Luồng 18: Sao lưu Cơ sở dữ liệu Cấu hình & Đồng bộ Khôi phục Thảm họa
1. Tổng quan
| Trường | Nội dung |
|---|---|
| ID | UC_9.3 |
| Use Case | Config DB Backup & DR Sync |
| Mô tả | Sao lưu toàn bộ dữ liệu cấu hình Zapier Table (Tables A–F cùng mọi bảng mới được thêm) hằng ngày lên một S3 bucket bất biến phục vụ khôi phục thảm họa và kiểm toán theo quy định. Một bước xác minh độc lập kiểm tra tính toàn vẹn của bản sao lưu. Lỗi được tự động retry trước khi cảnh báo Ops. Luồng này bảo vệ lớp business logic (giới hạn rủi ro, ma trận giá, platform registry, email/event mapping) vốn không nằm trong phạm vi sao lưu RDS PostgreSQL Multi-AZ — cơ chế xử lý dữ liệu giao dịch của trader. |
| Zapier Flow | Flow 18 (Configuration Database Backup) · Flow 40 (Zapier DR Recovery Snapshot — dùng chung S3 bucket/prefix) |
| Zapier Table | Tables A–F (toàn bộ bảng cấu hình đang hoạt động) + mọi bảng mới được thêm, phát hiện tại runtime |
| 3rd Party | Zapier (nguồn export table) · AWS S3 (lưu trữ sao lưu — SSE-KMS, versioning, Object Lock, phân tầng lifecycle) · AWS KMS (quản lý khóa mã hóa) · Slack (#ops-dev — xác nhận thành công và cảnh báo thất bại) |
2. Trigger
Lịch chạy: Cron job theo lịch, hằng ngày lúc 02:00 UTC — cố định theo UTC, không dịch chuyển theo Giờ mùa hè (DST). Mọi lịch cron backend trong toàn hệ thống đều chạy theo UTC cố định.
(Nguồn: QnA_BACKGROUND_MECHANISMS_CRON.md [BG-011] — bảng tính của khách hàng 2026-11-04; ghi đè Zapier Integration V7.pdf §Flow 18 line 2101 "Daily at 02:00 AM CST")
3. Luồng cơ bản
Bước 1 — Export Zapier Tables
- Export toàn bộ các bảng cấu hình: Tables A đến F, cùng các bảng mới được thêm phát hiện tại runtime.
- Định dạng export: JSON, với hai trường metadata bắt buộc được nhúng trong mọi file export:
| Trường metadata | Mục đích |
|---|---|
schema_version | Theo dõi phiên bản cấu trúc cấu hình để kiểm tra khả năng tương thích khi restore |
checksum | Mã băm SHA-256 của nội dung file, tính tại thời điểm export — dùng để phát hiện dữ liệu bị hỏng hoặc bị cắt cụt trong quá trình truyền |
(Nguồn: Zapier Integration V7.pdf §Flow 18 lines 2102–2108)
Bước 2 — Upload lên S3
- Upload file export JSON lên S3 bucket riêng.
- Quy ước đặt tên:
config_backup_YYMMDD_HHMMSS.json - Bucket dùng chung: Flow 18 dùng cùng S3 bucket và prefix với Flow 40 (Zapier DR Recovery Snapshot). Cả hai luồng dùng chung cấu hình bảo mật và lifecycle.
S3 bucket PHẢI được cấu hình với các thuộc tính sau:
| Thuộc tính | Yêu cầu | Nguồn |
|---|---|---|
| SSE-KMS encryption | Toàn bộ file được mã hóa at rest bằng AWS KMS với khóa do công ty quản lý | Zapier V7 §Flow 18 |
| Bucket versioning | Bật — giữ lại nhiều phiên bản lịch sử; ngăn việc ghi đè làm mất các bản sao trước đó | Zapier V7 §Flow 18 |
| Object Lock (WORM) | Bắt buộc — Compliance Mode. File không thể bị chỉnh sửa hoặc xóa trong thời hạn lưu giữ bởi bất kỳ tài khoản nào, kể cả Admin/root. | Flow 18 spec 2026-10-28 (V7 ghi Object Lock là tùy chọn — xem F18-02) |
| Phân tầng lifecycle + hết hạn | Xem bảng bên dưới — S3 Lifecycle Rule gốc trên prefix sao lưu | Bảng tính của khách hàng 2026-11-04 [BG-010] |
| Cross-region replication | Khuyến nghị để DR khi toàn bộ region ngừng hoạt động | Zapier V7 §Flow 18 — xem F18-05 |
Quy tắc lifecycle (Hot / Cold):
| Tầng | Tuổi object | Storage class |
|---|---|---|
| Hot | 0–90 ngày (1 quý) | S3 Standard |
| Cold | 91 ngày – 1,856 ngày | S3 Glacier |
| Hết hạn | Sau 1,856 ngày (5 năm + 1 tháng) | Object tự động bị xóa |
- Không dùng script tùy biến nén thư mục theo tuần/tháng — cron hằng ngày chỉ upload file JSON; việc phân tầng và hết hạn do S3 Lifecycle Rule đảm nhiệm.
- Bản sao lưu cấu hình là điểm khôi phục, không phải audit log — quy tắc "2 years immediately accessible" của RFQ V7 §5.0 (áp dụng cho trading archive) không áp dụng ở đây.
- ⚠️ Thời hạn lưu giữ của Object Lock không được vượt quá 1,856 ngày, nếu không quy tắc hết hạn sẽ không thể xóa object.
(Nguồn: Zapier Integration V7.pdf §Flow 18 lines 2109–2116; §Flow 40 line 2652; QnA_BACKGROUND_MECHANISMS_CRON.md [BG-010] — bảng tính của khách hàng 2026-11-04)
Bước 3 — Xác minh sao lưu độc lập
Một bước xác minh tự động độc lập chạy ngay sau khi upload:
- Tải xuống file JSON vừa upload từ S3.
- Tính lại checksum SHA-256 của file đã tải xuống và so sánh với
checksumđã tính lúc export.- Khớp → file toàn vẹn. Tiếp tục.
- Không khớp → xác minh thất bại (→ Bước 4, nhánh thất bại).
- Test-parse nội dung file theo cấu trúc JSON kỳ vọng.
- Parse thành công → xác minh đạt. Luồng kết thúc thành công.
- Parse thất bại → xác minh thất bại (→ Bước 4, nhánh thất bại).
(Nguồn: Zapier Integration V7.pdf §Flow 18 lines 2117–2119)
Bước 4 — Xử lý lỗi: Retry, sau đó Cảnh báo
- Nếu sao lưu (Bước 1–2) hoặc xác minh (Bước 3) thất bại, job tự động retry tối đa 3 lần với exponential backoff.
- Nếu vẫn thất bại sau lần retry thứ 3 → gửi cảnh báo Slack tới
#ops-devchứa bước thất bại và lỗi cụ thể (ví dụ: lỗi xác thực KMS, S3 upload timeout, checksum mismatch, lỗi JSON parse).
| Kết quả | Cảnh báo |
|---|---|
| Thành công | ✅ Xác nhận Slack: Backup complete — config_backup_YYMMDD_HHMMSS |
| Thất bại sau 3 lần retry | 🚨 Cảnh báo Slack kèm bước thất bại và chi tiết lỗi. |
Kênh cảnh báo: chỉ Slack #ops-dev — theo danh mục kênh Slack (CR-15.2, slack_channel_usage.md): "Config DB Backup success/failure notifications". Không tạo ticket Freshdesk.
(Nguồn: Zapier Integration V7.pdf §Flow 18 lines 2120–2121; CR-15.2; Flow 18 spec 2026-10-28)
4. Điều kiện tiên quyết
| ID | Điều kiện tiên quyết |
|---|---|
| P1 | Toàn bộ Zapier Tables đang hoạt động (A–F + các bảng mới được thêm) có thể truy cập và đọc được qua table API của Zapier. |
| P2 | S3 bucket riêng (dùng chung với Flow 40) đã tồn tại và được cấu hình SSE-KMS, versioning, Object Lock (Compliance Mode) và các quy tắc lifecycle ở Bước 2. |
| P3 | Khóa AWS KMS đang hoạt động và IAM role của Zapier/Middleware có quyền kms:GenerateDataKey và s3:PutObject. |
| P4 | Slack webhook cho #ops-dev đang hoạt động. |
5. Hậu điều kiện
| Kết quả | Hậu điều kiện |
|---|---|
| Thành công | config_backup_YYMMDD_HHMMSS.json được upload lên S3 với đầy đủ thuộc tính bảo mật; checksum và parse đã được xác minh; đã gửi xác nhận Slack ✅ tới #ops-dev. |
| Thất bại — phục hồi nhờ retry | Một lần retry thành công; giống Thành công (xác nhận Slack ✅). Không có cảnh báo thất bại. |
| Thất bại — sau 3 lần retry (lỗi upload) | File không được lưu vào S3. Cảnh báo thất bại Slack được gửi kèm chi tiết lỗi. |
| Thất bại — sau 3 lần retry (checksum mismatch / lỗi parse) | File nằm trên S3 nhưng được gắn cờ là chưa xác minh. Cảnh báo thất bại Slack được gửi. File KHÔNG được dùng để restore. |
6. Luồng ngoại lệ
Exc-1 — Upload S3 thất bại (network timeout, permission denied, KMS key không khả dụng):
- Retry tối đa 3 lần với exponential backoff (Bước 4).
- Tất cả các lần retry đều thất bại → bản sao lưu không được lưu; cảnh báo thất bại Slack được gửi kèm mã lỗi.
Exc-2 — Checksum mismatch sau khi upload:
- File nằm trên S3 nhưng không thể tin cậy là điểm restore hợp lệ.
- Retry tối đa 3 lần (Bước 4). Nếu vẫn không khớp → cảnh báo thất bại Slack kèm chi tiết "checksum mismatch".
- File KHÔNG bị xóa (Object Lock ngăn việc này) — S3 versioning giữ sẵn phiên bản sạch trước đó.
⚠️ [MỞ — Exc-2] Nguồn không quy định file chưa được xác minh có được gắn tag trong S3 metadata hay không. BA cần xác nhận có nên áp dụng tag
corrupt=truecho S3 object hay không.
Exc-3 — JSON parse thất bại:
- File có thể bị cắt cụt hoặc gặp vấn đề encoding.
- Hành vi retry và bảo toàn file giống Exc-2; chi tiết cảnh báo "JSON parse error".
Exc-4 — Đọc Zapier Table thất bại (một hoặc nhiều bảng không khả dụng):
- Có thể sinh ra bản export một phần.
- Bản sao lưu một phần có thể âm thầm tạo ra điểm restore không đầy đủ.
⚠️ [MỞ — Exc-4] Nguồn không quy định bản export một phần được upload hay toàn bộ job bị hủy khi một bảng lỗi. BA cần xác nhận: abort-all hay upload-partial-with-error-flag.
Exc-5 — Gửi cảnh báo Slack thất bại:
- Kết quả sao lưu không bị ảnh hưởng, nhưng Ops không có khả năng quan sát.
- Không có kênh dự phòng — cảnh báo chỉ qua Slack. Việc gửi thất bại được ghi log phía server.
7. Quy tắc nghiệp vụ
| Rule ID | Quy tắc | Nguồn |
|---|---|---|
| BR_9.3.1 | Flow 18 chỉ sao lưu dữ liệu cấu hình Zapier Table — KHÔNG sao lưu dữ liệu giao dịch của trader (phần này do Amazon RDS PostgreSQL Multi-AZ automated backups đảm nhiệm). | Zapier Integration V7.pdf §Flow 18 |
| BR_9.3.2 | Phạm vi sao lưu là Tables A–F cùng các bảng mới được thêm, phát hiện tại runtime. | Zapier Integration V7.pdf §Flow 18 line 2102; Flow 18 spec 2026-10-28 |
| BR_9.3.3 | Mọi bản export JSON PHẢI nhúng schema_version và trường checksum SHA-256. Đây là metadata, không phải dữ liệu payload. | Zapier Integration V7.pdf §Flow 18 lines 2107–2108 |
| BR_9.3.4 | S3 bucket PHẢI bật mã hóa SSE-KMS, bucket versioning và S3 Object Lock ở Compliance Mode. Cross-region replication được khuyến nghị. | Zapier Integration V7.pdf §Flow 18 lines 2110–2115; Flow 18 spec 2026-10-28 (F18-02, F18-05) |
| BR_9.3.5 | S3 Lifecycle Rule trên prefix sao lưu: S3 Standard trong 90 ngày đầu → S3 Glacier sau 90 ngày → xóa sau 1,856 ngày (5 năm + 1 tháng). Không dùng code nén/xoay vòng tùy biến. | QnA_BACKGROUND_MECHANISMS_CRON.md [BG-010] — bảng tính của khách hàng 2026-11-04 |
| BR_9.3.6 | Bước xác minh độc lập (tải lại + so sánh checksum + JSON test-parse) PHẢI chạy ngay sau mỗi lần upload. Bản sao lưu chỉ được coi là thành công khi xác minh đạt. | Zapier Integration V7.pdf §Flow 18 lines 2117–2119 |
| BR_9.3.7 | Flow 40 (Zapier DR Recovery Snapshot) dùng chung S3 bucket và prefix với Flow 18. Bucket áp dụng một chính sách bảo mật, versioning và lifecycle thống nhất cho cả hai luồng. | Zapier Integration V7.pdf §Flow 40 line 2652 |
| BR_9.3.8 | Đặt tên file PHẢI theo quy ước: config_backup_YYMMDD_HHMMSS.json. | Zapier Integration V7.pdf §Flow 18 line 2111 |
| BR_9.3.9 | Nếu sao lưu hoặc xác minh thất bại, job PHẢI retry tối đa 3 lần với exponential backoff. Cảnh báo thất bại Slack chỉ được gửi sau khi lần retry thứ 3 thất bại. | Flow 18 spec 2026-10-28 |
| BR_9.3.10 | Mọi thông báo của Flow 18 chỉ gửi tới Slack #ops-dev: xác nhận ✅ sau mỗi lần sao lưu thành công và cảnh báo 🚨 sau lần retry thứ 3 thất bại. Không tạo ticket Freshdesk. | Zapier Integration V7.pdf §Flow 18 line 2121; CR-15.2 (slack_channel_usage.md) |
8. Yêu cầu phi chức năng
| Khía cạnh | Yêu cầu |
|---|---|
| Bảo mật | Mã hóa SSE-KMS at rest. IAM least-privilege cho quyền ghi S3 và truy cập KMS. Object Lock (Compliance Mode) ngăn xóa trong thời hạn lưu giữ, kể cả bởi tài khoản Admin/root. |
| Tuân thủ | Lưu giữ 5 năm + 1 tháng (1,856 ngày); object cũ hơn 90 ngày nằm trong S3 Glacier; xóa ở ngày thứ 1,856. |
| Độ bền | Độ bền S3 standard (99.999999999%). Cross-region replication được khuyến nghị để DR khi toàn bộ region ngừng hoạt động. |
| Toàn vẹn | Checksum SHA-256 được nhúng trong mọi file export. Bước xác minh độc lập là bắt buộc sau mỗi lần upload. |
| Khả dụng | Chạy hằng ngày — một lần thất bại không gây mất dữ liệu (bản sao lưu ngày trước vẫn hợp lệ trên S3 nhờ versioning). Lỗi tạm thời được hấp thụ bởi 3 lần retry tự động. |
| Khả năng quan sát | Chỉ Slack #ops-dev: xác nhận ✅ sau mỗi lần sao lưu thành công; cảnh báo 🚨 sau lần retry thứ 3 thất bại, kèm bước thất bại và chi tiết lỗi cụ thể. |
9. Tiêu chí chấp nhận
| ID | Kịch bản | Kết quả mong đợi |
|---|---|---|
| AC-9.3-01 | Job theo lịch chạy lúc 02:00 UTC (cùng một giờ UTC quanh năm, không dịch chuyển theo DST) — mọi bảng đều đọc được | File JSON được export với schema_version và SHA-256 checksum; upload lên S3 với tên config_backup_YYMMDD_HHMMSS.json; áp dụng SSE-KMS và Object Lock; xác minh đạt. Gửi xác nhận Slack ✅ tới #ops-dev. |
| AC-9.3-02 | Xác minh: checksum của file đã tải xuống khớp với checksum lúc export | Xác minh đạt. Không gửi cảnh báo thất bại. |
| AC-9.3-03 | Xác minh: checksum mismatch ở mọi lần thử | Thực hiện 3 lần retry với exponential backoff; sau khi lần retry thứ 3 thất bại, gửi cảnh báo Slack 🚨 tới #ops-dev kèm chi tiết "checksum mismatch". |
| AC-9.3-04 | Xác minh: JSON test-parse thất bại ở mọi lần thử | Thực hiện 3 lần retry; gửi cảnh báo Slack 🚨 tới #ops-dev kèm chi tiết "JSON parse error". |
| AC-9.3-05 | Upload S3 thất bại (ví dụ KMS key không khả dụng) ở mọi lần thử | Không có file nào được ghi vào S3. Thực hiện 3 lần retry; gửi cảnh báo Slack 🚨 tới #ops-dev kèm mã lỗi cụ thể. |
| AC-9.3-06 | Upload S3 thất bại ở lần thử đầu và thành công ở lần retry thứ 2 | Sao lưu hoàn tất và xác minh đạt. Gửi xác nhận Slack ✅; không gửi cảnh báo thất bại. |
| AC-9.3-07 | Lần sao lưu thứ hai trong cùng ngày tạo ra file mới | S3 versioning giữ lại cả hai file. Không ghi đè bản sao lưu trước. |
| AC-9.3-08 | Thử xóa hoặc ghi đè một file sao lưu trong thời hạn lưu giữ (kể cả bằng tài khoản Admin/root) | Bị S3 Object Lock (Compliance Mode) từ chối. |
| AC-9.3-09 | Một file sao lưu đạt 91 ngày tuổi | Quy tắc lifecycle chuyển file từ S3 Standard sang S3 Glacier. File vẫn truy xuất được. |
| AC-9.3-10 | Một file sao lưu có tuổi 1–90 ngày | File vẫn nằm trong S3 Standard (hot). |
| AC-9.3-11 | Một file sao lưu đạt 1,857 ngày tuổi | Quy tắc lifecycle tự động xóa file. |
| AC-9.3-12 | Flow 40 upload một artifact lên cùng S3 bucket | File cùng tồn tại với các bản sao lưu Flow 18 dưới cùng prefix. Các quy tắc KMS, versioning, Object Lock và lifecycle áp dụng cho cả hai. |
10. Lịch sử cập nhật
| Ngày | Phiên bản | Mục được cập nhật | Trước | Sau | Lý do |
|---|---|---|---|---|---|
| 2026-11-04 | v1.2 → v1.3 | Header; Danh mục UC; §2 Trigger (lịch chạy, lý do, nguồn); §3 Bước 2 (dòng lifecycle, bảng lifecycle + ghi chú, nguồn); §7 BR_9.3.5; §8 Tuân thủ; §9 AC-9.3-01/09/10/11 | Hằng ngày 02:00 AM CST (08:00 AM UTC), DST còn mở (F18-04); phân tầng HOT/WARM/COLD 0–12 / 13–24 / 25–61 tháng, xóa ở 61 tháng | Hằng ngày 02:00 UTC cố định (không DST; mọi backend cron theo UTC cố định); S3 Lifecycle Rule: S3 Standard 90 ngày → S3 Glacier → xóa ở 1,856 ngày; không dùng script nén tùy biến | Bảng tính của khách hàng Adam/Adrian 2026-11-04 (QnA [BG-010] [BG-011]) |
| 2026-10-28 | v1.1 → v1.2 | §1 3rd Party; §3 Bước 4; §4 P4; §5; §6 Exc-5; §7 BR_9.3.10 (mới); §8 Khả năng quan sát; §9 AC-9.3-01/03/04/05/06 | Kênh cảnh báo và dự phòng Freshdesk còn mở (F18-01); chưa rõ có gửi khi thành công | Chỉ Slack #ops-dev — ✅ khi thành công, 🚨 sau lần retry thứ 3 thất bại; không dùng Freshdesk | Quyết định 2026-10-28 (đóng F18-01) |
| 2026-10-28 | v1.0 → v1.1 | Header; Danh mục UC; §1; §2; §3 Bước 1–4; §4–§6; §7 BR_9.3.2/4/5/6/7, BR_9.3.9 (mới); §8; §9 AC-9.3-01–12 | Object Lock tùy chọn; lifecycle "> 25 tháng → Glacier, xóa ở 61 tháng"; không retry (Exc-1 OPEN); cảnh báo Slack tới #dev_ops khi thành công hoặc thất bại; SOURCE CONFLICT về phạm vi bảng (A–F vs A–J) | Object Lock bắt buộc (Compliance Mode); phân tầng HOT/WARM/COLD (Instant 0–12 th → Flexible 13–24 th → Deep Archive 25–61 th) + hết hạn ở 61 th; retry 3× exponential backoff rồi cảnh báo Slack; kênh ghi nhận là #ops-dev (CR-15.2); đóng xung đột phạm vi — Tables A–F + các bảng mới được thêm. Các mục mở chuyển sang F18-01 → F18-05. | Flow 18 spec 2026-10-28 |
| 2026-08-21 | v1.0 | Khởi tạo | — | SRS đầy đủ cho UC_9.3 (Flow 18) | Lần soạn thảo đầu tiên. Nguồn: Zapier Integration V7.pdf §Flow 18; Prop Tech V7 §CFTC 1.31. |
UC_9.8 — Luồng 30: Cảnh báo Công suất MID
| Trường | Giá trị |
|---|---|
| BA phụ trách | linh.nguyen34 |
| Ngày tạo | 2026-09-03 |
| Phiên bản | v1.2 |
| Cập nhật lần cuối | 2026-11-05 — khách trả lời 2026-11-03: refund trừ current_mtd_volume (chặn đáy 0); warning_80_fired được re-arm trong Check MID Capacity khi volume tụt dưới 80% |
| Tài liệu tham chiếu | Zapier Integration V7.pdf (§Flow 30 lines 2461–2469) · RFQ_ Stack Trading Prop Tech V7.pdf (§Check MID Capacity lines 1455–1459; §Process Payment Webhook lines 1339–1373; §Merchant_Routing_Config lines 847–855; §Alerting lines 3273) |
Danh mục UC
| UC_ID | Tên use case | Mô tả nghiệp vụ |
|---|---|---|
| UC_9.8 | MID Capacity Warning | Thời gian thực: sau mỗi giao dịch thanh toán thành công, Middleware kiểm tra MID được dùng đã đạt 80% hạn mức khối lượng hằng tháng hay chưa. Nếu đã đạt và chưa cảnh báo trong tháng này, bắn webhook → cảnh báo Slack tới #ops-treasury. |
<a id="UC_9.8"></a>
UC_9.8 — Luồng 30: Cảnh báo Công suất MID
1. Tổng quan
| Trường | Nội dung |
|---|---|
| ID | UC_9.8 |
| Use Case | MID Capacity Warning |
| Mô tả | Hệ thống cảnh báo sớm về công suất xử lý hằng tháng của MID (Merchant Identification Number). Sau mỗi giao dịch thanh toán thành công, Middleware đánh giá MID được dùng đã tiêu thụ ≥ 80% monthly_limit hay chưa. Nếu vượt ngưỡng và warning_80_fired = FALSE, Middleware đặt cờ và bắn webhook để gửi cảnh báo Slack tới kênh Operations. Ngăn tình trạng đóng băng pipeline doanh thu do MID bị từ chối vì vượt hạn mức. |
| Flow | Flow 30 (MID Capacity Warning) |
| Bảng | Merchant_Routing_Config (bảng PostgreSQL) |
| 3rd Party | NMI (payment gateway — nguồn phát sinh sự kiện thanh toán thành công) · Slack (gửi cảnh báo) |
2. Trigger
Loại: Nội bộ — được Middleware Hub kích hoạt theo chương trình ngay sau khi Process Payment Webhook cập nhật thành công current_mtd_volume trong cơ sở dữ liệu.
Điều kiện trigger: Mỗi giao dịch thanh toán thành công từ bất kỳ gateway nào (NMI, Triple-A, Nomupay, EBANX, Dusupay, Skrill) và mỗi lần refund đều dẫn đến một lệnh gọi POST /merchant/check-capacity cho MID được dùng.
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §Check MID Capacity lines 1455–1459; §Process Payment Webhook lines 1339–1346)
3. Luồng cơ bản
Bước 1 — Cập nhật khối lượng (upstream — Process Payment Webhook)
Sau một giao dịch thanh toán thành công, Process Payment Webhook cập nhật tổng lũy kế của MID trong Merchant_Routing_Config:
current_mtd_volume = current_mtd_volume + transaction_amountKhi refund, tổng chạy của MID bị trừ và không bao giờ xuống dưới 0 (ví dụ refund giao dịch của tháng trước ngay sau khi reset ngày 1):
current_mtd_volume = GREATEST(0, current_mtd_volume - refund_amount)current_mtd_volume chỉ là bộ đếm month-to-date nội bộ dùng để cân tải — không phải số dư tài khoản ngân hàng merchant. Bản thân refund vẫn được NMI xử lý bình thường kể cả khi current_mtd_volume = 0.
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §Process Payment Webhook line 1345; QnA_BACKGROUND_MECHANISMS_CRON.md [BG-024] — khách trả lời 2026-11-03)
Bước 2 — Kiểm tra công suất (POST /merchant/check-capacity)
Ngay sau khi cập nhật DB, Middleware gọi nội bộ POST /merchant/check-capacity.
Input payload:
{
"merchant_name": "<MID identifier>",
"current_mtd_volume": <updated value>
}Logic đánh giá:
ratio = current_mtd_volume / monthly_limit
IF ratio >= 0.80 AND warning_80_fired = FALSE:
→ SET warning_80_fired = TRUE
→ FIRE webhook
ELSE IF ratio < 0.80 AND warning_80_fired = TRUE:
→ SET warning_80_fired = FALSE (no Slack alert)
ELSE:
→ No action. Return HTTP 200.Cờ warning_80_fired giúp cảnh báo 80% chỉ bắn một lần cho mỗi lần vượt ngưỡng: khi cờ đang được đặt, các giao dịch tiếp theo trên cùng MID không kích hoạt lại luồng, kể cả khi ratio tăng vượt 90% hoặc 100%. Nếu volume tụt lại dưới 80% (do refund, hoặc do monthly_limit được nâng), cờ được xóa và lần vượt ngưỡng kế tiếp sẽ bắn cảnh báo mới. Không xây DB trigger hay watcher riêng — toàn bộ xử lý nằm trong bước kiểm tra này.
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §Check MID Capacity lines 1456–1459; QnA_BACKGROUND_MECHANISMS_CRON.md [BG-025] — khách trả lời 2026-11-03)
Response: HTTP 200 OK trong mọi trường hợp (bắn hoặc không bắn).
Bước 3 — Cảnh báo Slack
Gửi tin nhắn Slack tới kênh Operations (#ops-treasury hoặc kênh Ops được cấu hình).
Nội dung tin nhắn Slack:
⚠️ [WARN] MID VOLUME CAPACITY ALERT
MID [merchant_name] has reached [ratio]% of its monthly processing limit.
Current MTD Volume: $[current_mtd_volume]
Monthly Limit: $[monthly_limit]
Action Required: Contact processor to request limit increase, or adjust
Weighted Round-Robin weights in Merchant_Routing_Config to offload traffic.(Nguồn: Zapier Integration V7.pdf §Flow 30 lines 2466–2469)
4. Mô hình dữ liệu
Bảng Merchant_Routing_Config (PostgreSQL)
| Trường | Kiểu | Mô tả |
|---|---|---|
merchant_name | VARCHAR (PK) | Định danh MID duy nhất (ví dụ NMI_US_Credit_01) |
nmi_routing_id | VARCHAR | Mã routing được cấu hình trong NMI gateway |
monthly_limit | DECIMAL | Khối lượng giao dịch tối đa mỗi tháng dương lịch |
current_mtd_volume | DECIMAL | Tổng lũy kế từ đầu tháng của các giao dịch thành công |
is_active | BOOLEAN | MID này hiện có đang trong vòng xoay (rotation) hay không |
warning_80_fired | BOOLEAN (default: FALSE) | Cờ idempotency (chống trùng lặp) — ngăn cảnh báo trùng lặp trong cùng tháng |
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §Merchant_Routing_Config lines 847–855; §Alerting line 3273; client confirmed 2026-09-18)
5. Tham chiếu API
| Endpoint | Method | Auth | Bên gọi | Mục đích |
|---|---|---|---|---|
POST /merchant/check-capacity | POST | Internal — no public auth | Middleware (gọi sau Process Payment Webhook) | Đánh giá ngưỡng 80%; bắn webhook nếu được kích hoạt |
Input:
| Trường | Kiểu | Bắt buộc | Mô tả |
|---|---|---|---|
merchant_name | VARCHAR | ✅ | Xác định MID cần đánh giá |
current_mtd_volume | DECIMAL | ✅ | Khối lượng MTD sau cập nhật của MID này |
Output: HTTP 200 OK (không cần body — chỉ có side effect)
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §Check MID Capacity lines 1455–1459)
6. Luồng ngoại lệ
Exc-1 — warning_80_fired đã là TRUE (chặn trùng lặp):
- Không bắn webhook. Trả về HTTP 200 âm thầm.
- Đây là hành vi mong đợi — cờ được reset bởi cron ngày 1 hằng tháng, hoặc bởi lần kiểm tra công suất kế tiếp khi volume dưới 80% (xem §7).
Exc-2 — monthly_limit của MID là NULL hoặc bằng 0:
Middleware PHẢI áp dụng guard clause: nếu monthly_limit IS NULL OR monthly_limit = 0, bỏ qua phép tính công suất 80% cho MID đó, ghi log cảnh báo phía server với mã NULL_OR_ZERO_MONTHLY_LIMIT_WARNING, và trả về HTTP 200 (không ném exception 500). Ngoài ra, Middleware PHẢI gửi một cảnh báo Ops chủ động tới kênh Slack #ops-treasury với nội dung:
⚠️ Configuration Error: MID [merchant_name] has an active status but a zero or null monthly limit.Lý do: monthly_limit NULL hoặc bằng 0 trên một MID đang active là lỗi cấu hình nghiêm trọng — phải chuyển cho đội Ops để sửa DB ngay. Đây là cảnh báo cấu hình nội bộ và KHÔNG ĐƯỢC chuyển tới Freshdesk.
(Nguồn: client confirmed 2026-09-18)
Exc-3 — Gửi webhook thất bại:
Khi Middleware bắn webhook cảnh báo công suất và nhận response khác 200 hoặc timeout:
Giai đoạn 1 — Retry: Middleware thực hiện 3 lần retry tự động với exponential backoff.
Giai đoạn 2 — Fallback (đã dùng hết 3 lần retry): Middleware đẩy cảnh báo fallback trực tiếp tới #dev_ops qua lệnh gọi native Slack API webhook.
(Nguồn: client confirmed 2026-09-18)
7. Quy tắc nghiệp vụ
| Rule ID | Quy tắc | Nguồn |
|---|---|---|
| BR_9.8.1 | POST /merchant/check-capacity PHẢI được gọi đồng bộ sau mỗi giao dịch thanh toán thành công và mỗi lần refund có cập nhật current_mtd_volume. Đây là trigger nội bộ — không mở ra cho client bên ngoài. | RFQ_ Stack Trading Prop Tech V7.pdf §Check MID Capacity lines 1455–1459 |
| BR_9.8.2 | Ngưỡng công suất là đúng 80% (ratio >= 0.80). Cảnh báo bắn một lần cho mỗi lần vượt ngưỡng — khi warning_80_fired = TRUE, các giao dịch tiếp theo KHÔNG kích hoạt lại kể cả khi ratio đạt 90% hoặc 100%. Nếu ratio tụt dưới 80%, cờ được reset về FALSE và lần vượt ngưỡng kế tiếp sẽ cảnh báo lại. | RFQ_ Stack Trading Prop Tech V7.pdf §Check MID Capacity lines 1456–1458; RFQ_ Stack Trading Prop Tech V7.pdf §Alerting line 3273; QnA [BG-025] khách trả lời 2026-11-03 |
| BR_9.8.3 | warning_80_fired PHẢI được đặt thành TRUE một cách atomic cùng với việc bắn webhook. Nếu cập nhật DB và bắn webhook không atomic, một lần retry có thể gửi cảnh báo Slack trùng lặp. Ghi chú triển khai: đặt cờ trước khi bắn webhook; nếu webhook thất bại, cờ vẫn được giữ nguyên (ưu tiên bỏ lỡ cảnh báo hơn là cảnh báo trùng lặp). | RFQ_ Stack Trading Prop Tech V7.pdf §Check MID Capacity lines 1456–1459; client confirmed 2026-09-03 |
| BR_9.8.4 | Reset hằng tháng: current_mtd_volume được reset về 0.00 VÀ warning_80_fired được reset về FALSE cho toàn bộ MID vào 00:00 UTC ngày 1 hằng tháng bởi cron job hằng tháng. | RFQ_ Stack Trading Prop Tech V7.pdf §Merchant_Routing_Config line 222; client confirmed 2026-09-03 |
| BR_9.8.5 | Chỉ các MID có is_active = TRUE tham gia routing Weighted Round-Robin. MID không active không được đánh giá công suất. | RFQ_ Stack Trading Prop Tech V7.pdf §Merchant_Routing_Config lines 847–855 |
| BR_9.8.6 | Re-arm cờ — không có trigger riêng: Không xây DB trigger hay watcher theo dõi thay đổi monthly_limit. Việc re-arm nằm trong Check MID Capacity: nếu current_mtd_volume < 0.80 × monthly_limit VÀ warning_80_fired = TRUE → đặt warning_80_fired = FALSE. Áp dụng cho cả trường hợp nâng monthly_limit lẫn refund làm volume tụt dưới 80%. | QnA_BACKGROUND_MECHANISMS_CRON.md [BG-025] — khách trả lời 2026-11-03 |
| BR_9.8.7 | Guard cho limit Null/Zero — bắt buộc cảnh báo Slack chủ động: Nếu monthly_limit IS NULL OR monthly_limit = 0 với một MID is_active = TRUE, Middleware PHẢI (a) bỏ qua phép tính công suất 80%, (b) ghi log NULL_OR_ZERO_MONTHLY_LIMIT_WARNING phía server, (c) trả về HTTP 200 (không 500), VÀ (d) gửi cảnh báo Slack tới #ops-treasury: "⚠️ Configuration Error: MID [merchant_name] has an active status but a zero or null monthly limit." KHÔNG ĐƯỢC chuyển tới Freshdesk. | Client confirmed 2026-09-18 |
| BR_9.8.8 | Gửi webhook thất bại — retry + native Slack fallback: Nếu lệnh gọi webhook thất bại (khác 200 hoặc timeout), Middleware PHẢI thực hiện 3 lần retry tự động với exponential backoff. Nếu cả 3 lần retry đều thất bại, Middleware PHẢI đẩy cảnh báo công suất trực tiếp tới #dev_ops qua lệnh gọi native Slack API webhook. | Client confirmed 2026-09-18 |
| BR_9.8.9 | Chặn đáy khi trừ refund: Refund trừ current_mtd_volume theo GREATEST(0, current_mtd_volume - refund_amount) — giá trị không bao giờ dưới 0. current_mtd_volume là bộ đếm nội bộ để cân tải, không phải số dư ngân hàng; NMI vẫn xử lý refund bất kể giá trị này. | QnA_BACKGROUND_MECHANISMS_CRON.md [BG-024] — khách trả lời 2026-11-03 |
8. Tiêu chí chấp nhận
| ID | Kịch bản | Kết quả mong đợi |
|---|---|---|
| AC-9.8-01 | MID ở mức 79.9% → một giao dịch thành công đẩy lên 80.1% →warning_80_fired = FALSE | Webhook được bắn; warning_80_fired được đặt thành TRUE; nhận cảnh báo Slack tại #ops-treasury |
| AC-9.8-02 | MID đã ở mức 82% (warning_80_fired = TRUE) → xử lý giao dịch mới | Không bắn webhook; không có cảnh báo Slack; warning_80_fired vẫn là TRUE; trả về HTTP 200 |
| AC-9.8-03 | MID ở mức 79% → giao dịch đẩy lên 79.9% | Không bắn webhook; warning_80_fired vẫn là FALSE; trả về HTTP 200 |
| AC-9.8-04 | Cron cuối tháng chạy lúc 00:00 UTC ngày 1 | current_mtd_volume = 0.00 và warning_80_fired = FALSE cho toàn bộ MID |
| AC-9.8-05 | MID có is_active = FALSE nhận cập nhật thanh toán | Bỏ qua kiểm tra công suất; không bắn cảnh báo |
| AC-9.8-06 | monthly_limit = 0 hoặc NULL với một MID active | Trả về HTTP 200; không ném 500; ghi log NULL_OR_ZERO_MONTHLY_LIMIT_WARNING; gửi cảnh báo Slack tới #ops-treasury với nội dung "Configuration Error: MID [merchant_name] has an active status but a zero or null monthly limit." |
| AC-9.8-07 | Ops nâng monthly_limit của một MID (warning_80_fired = TRUE); payment/refund kế tiếp chạy kiểm tra công suất với volume < 80% hạn mức mới | warning_80_fired được reset về FALSE ở lần kiểm tra đó; không có cảnh báo Slack |
| AC-9.8-08 | Webhook thất bại ở lần đầu → retry → hết cả 3 lần retry | Thực hiện 3 lần retry với exponential backoff; cảnh báo Slack fallback được đẩy trực tiếp tới #dev_ops qua native Slack API |
| AC-9.8-09 | warning_80_fired = FALSE, monthly_limit là NULL, MID is_active = TRUE | Guard clause kích hoạt: bỏ qua kiểm tra công suất; ghi log NULL_OR_ZERO_MONTHLY_LIMIT_WARNING; gửi cảnh báo Slack #ops-treasury; không có lỗi chia cho 0 |
| AC-9.8-10 | Đã reset ngày 1 (current_mtd_volume = 0); refund một giao dịch $500 của tháng trước | current_mtd_volume = 0 (không âm); NMI xử lý refund bình thường |
| AC-9.8-11 | MID ở mức 85% (warning_80_fired = TRUE); refund kéo xuống 75% | warning_80_fired = FALSE; không có cảnh báo Slack |
| AC-9.8-12 | Sau AC-9.8-11, một giao dịch đẩy MID lên lại 81% | Webhook được bắn (cảnh báo lần 2 trong tháng); warning_80_fired = TRUE |
9. Lịch sử thay đổi
| Ngày | Phiên bản | Tóm tắt | Trước | Sau | Nguồn |
|---|---|---|---|---|---|
| 2026-11-05 | v1.1 → v1.2 | §2 Trigger (refund) · §3 Bước 1 (trừ refund) · Bước 2 (nhánh re-arm) · §6 Exc-1 · §7 cập nhật BR_9.8.1/2/6, thêm BR_9.8.9 · §8 cập nhật AC-9.8-07, thêm AC-9.8-10/11/12 · §6 Exc-3 · §3 bỏ Bước 3 (Zapier nhận webhook), Cảnh báo Slack đánh lại số Bước 4 → Bước 3 · bỏ chữ Zapier (cảnh báo = bắn webhook) | Chưa xử lý refund; cờ chỉ reset bởi cron hằng tháng hoặc khi nâng limit trên BPS (BR_9.8.6, 2026-09-18) | Refund: GREATEST(0, volume - refund); cờ được re-arm trong Check MID Capacity khi volume < 80%; không có trigger riêng | QnA_BACKGROUND_MECHANISMS_CRON.md [BG-024] [BG-025] — khách trả lời 2026-11-03 |
| 2026-09-18 | v1.0 → v1.1 | §4 Mô hình dữ liệu Q1 đã đóng · §6 Exc-2/Exc-3 đã giải quyết · §7 thêm BR_9.8.6/7/8 · §8 mở rộng AC-9.8-06 + thêm AC-9.8-07/08/09 | Q1: trường warning_80_fired chưa được xác nhận. Exc-2: guard chỉ ghi log cảnh báo, không có cảnh báo chủ động. Exc-3: [OPEN] — chưa quy định retry hay fallback. | Q1 đã đóng: warning_80_fired BOOLEAN NOT NULL DEFAULT FALSE được xác nhận cho Merchant_Routing_Config. Exc-2: limit null/zero nay kích hoạt cảnh báo Slack #ops-treasury (không dùng Freshdesk) ngoài cảnh báo phía server + HTTP 200. Exc-3: 3 lần retry với exponential backoff; khi hết retry, native Slack API fallback tới #dev_ops (bỏ qua Zapier). BR_9.8.6: reset thủ công khi nâng hạn mức. BR_9.8.7: guard null/zero + đặc tả cảnh báo Slack chủ động. BR_9.8.8: đặc tả retry + fallback. | Client confirmed 2026-09-18 |
| 2026-09-03 | v1.0 | Khởi tạo tài liệu | — | — | Zapier Integration V7.pdf §Flow 30; RFQ_ Stack Trading Prop Tech V7.pdf §Check MID Capacity |
UC_9.9 — Luồng 31: Giám sát Cạn kiệt Tổng hợp (Sev-1)
| Trường | Giá trị |
|---|---|
| BA phụ trách | linh.nguyen34 |
| Ngày tạo | 2026-09-03 |
| Phiên bản | v1.3 |
| Tài liệu tham chiếu | Zapier Integration V7.pdf (§Flow 31 lines 2471–2479) · RFQ_ Stack Trading Prop Tech V7.pdf (§Calculate Volume Pacing lines 1460–1464; §Merchant_Routing_Config lines 847–855) |
Danh mục UC
| UC_ID | Tên use case | Mô tả nghiệp vụ |
|---|---|---|
| UC_9.9 | Aggregate Exhaustion Monitor (Sev-1) | Hằng ngày lúc 11:00 PM CST: Zapier Flow 31 gọi GET /calculate-volume-pacing. Nếu khối lượng dự phóng cuối tháng trên toàn bộ MID đang active vượt tổng công suất tổng hợp, bắn cảnh báo SEV-1 Slack tới Operations. |
<a id="UC_9.9"></a>
UC_9.9 — Luồng 31: Giám sát Cạn kiệt Tổng hợp (Sev-1)
1. Tổng quan
| Trường | Nội dung |
|---|---|
| ID | UC_9.9 |
| Use Case | Aggregate Exhaustion Monitor (Sev-1) |
| Mô tả | Hệ thống phòng vệ dự báo ở cấp vĩ mô. Mỗi ngày lúc 11:00 PM CST, Zapier Flow 31 gọi GET /calculate-volume-pacing để tính khối lượng dự phóng cuối tháng trên toàn bộ MID đang active. Nếu dự phóng vượt tổng công suất tổng hợp, một cảnh báo SEV-1 CRITICAL được gửi tới kênh Slack Operations. Bổ trợ cho UC_9.8 (Flow 30) vốn giám sát từng MID riêng lẻ — luồng này giám sát xu hướng tổng hợp toàn hệ thống để phát hiện đột biến khối lượng trước khi xảy ra cạn kiệt cuối tháng. |
| Zapier Flow | Flow 31 (Aggregate Exhaustion Monitor) |
| Zapier Table | Merchant_Routing_Config (PostgreSQL — do Middleware truy vấn, không phải Zapier truy vấn trực tiếp) |
| 3rd Party | Slack (gửi cảnh báo SEV-1) |
2. Trigger
Loại: Theo lịch — Zapier Cron Job.
Lịch chạy: Hằng ngày lúc 11:00 PM CST.
(Nguồn: Zapier Integration V7.pdf §Flow 31 line 2473)
3. Luồng cơ bản
Bước 1 — Trigger theo lịch (Zapier)
Scheduler tích hợp sẵn của Zapier kích hoạt lúc 11:00 PM CST hằng ngày và gọi GET /calculate-volume-pacing trên Middleware.
(Nguồn: Zapier Integration V7.pdf §Flow 31 lines 2473–2474)
Bước 2 — Tính toán pacing (GET /calculate-volume-pacing)
Middleware thực thi thuật toán sau trên Merchant_Routing_Config:
2a. Tổng hợp (chỉ MID active):
SELECT
SUM(current_mtd_volume) AS total_mtd_volume,
SUM(monthly_limit) AS total_aggregate_capacity
FROM merchant_routing_config
WHERE is_active = TRUE;2b. Tính Khối lượng trung bình ngày (ADV):
N = current day of month (e.g. 15)
ADV = total_mtd_volume / N2c. Dự phóng khối lượng cuối tháng:
T = total days in current calendar month (e.g. 30 for September)
Projected_EOM_Volume = ADV × T2d. Kiểm tra cạn kiệt:
IF Projected_EOM_Volume >= total_aggregate_capacity:
projected_exhaustion = true
ELSE:
projected_exhaustion = false(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §Calculate Volume Pacing lines 1460–1464; Zapier Integration V7.pdf §Flow 31 lines 2474–2475)
Bước 3 — Zapier Filter
Zapier đánh giá response. Chỉ tiếp tục nếu projected_exhaustion = true. Nếu false, luồng kết thúc âm thầm — không gửi tin nhắn Slack.
(Nguồn: Zapier Integration V7.pdf §Flow 31 line 2475)
Bước 4 — Cảnh báo Slack SEV-1
Nếu projected_exhaustion = true, gửi cảnh báo SEV-1 CRITICAL tới #ops-treasury (hoặc kênh Operations được cấu hình).
Nội dung tin nhắn Slack:
🚨 [SEV-1 CRITICAL] AGGREGATE VOLUME PACING EXHAUSTION DETECTED
Current Day: [current_day_of_month] / [total_days_in_month]
Total MTD Volume: $[total_mtd_volume]
Average Daily Volume: $[average_daily_volume]/day
Projected EOM Volume: $[projected_eom_volume]
Total Aggregate Capacity: $[total_aggregate_capacity]
CRITICAL: Aggregate pacing indicates all merchant accounts will exhaust
capacity before month end. Immediate intervention required.(Nguồn: Zapier Integration V7.pdf §Flow 31 lines 2476–2479)
4. API Endpoint
GET /calculate-volume-pacing
| Trường | Giá trị |
|---|---|
| Method | GET |
| Auth | Zapier Webhook Signature / HMAC (internal — not public) |
| Bên gọi | Zapier Flow 31 (theo lịch hằng ngày) |
| Input | Không có |
Response payload (đường đi bình thường):
{
"calculation_date": "2026-09-03",
"current_day_of_month": 3,
"total_days_in_month": 30,
"total_mtd_volume": 45000.00,
"total_aggregate_capacity": 450000.00,
"average_daily_volume": 15000.00,
"projected_eom_volume": 450000.00,
"projected_exhaustion": true,
"active_mids_count": 4
}Response payload (Edge Case 2 — không có MID active):
{
"projected_exhaustion": false,
"total_aggregate_capacity": 0,
"active_mids_count": 0,
"status_code": "NO_ACTIVE_MIDS_WARNING",
"message": "All payment MIDs are currently inactive. Ops Config Warning dispatched."
}| Trường | Kiểu | Mô tả |
|---|---|---|
calculation_date | DATE | Ngày chạy phép tính |
current_day_of_month | INT | N — mẫu số cho phép tính ADV |
total_days_in_month | INT | T — hệ số nhân cho dự phóng EOM |
total_mtd_volume | DECIMAL | Tổng current_mtd_volume của mọi MID có is_active = TRUE |
total_aggregate_capacity | DECIMAL | Tổng monthly_limit của mọi MID có is_active = TRUE |
average_daily_volume | DECIMAL | total_mtd_volume / current_day_of_month |
projected_eom_volume | DECIMAL | average_daily_volume × total_days_in_month |
projected_exhaustion | BOOLEAN | true nếu projected_eom_volume >= total_aggregate_capacity; luôn là false khi guard clause kích hoạt |
active_mids_count | INT | Số MID có is_active = TRUE tại thời điểm tính; giá trị 0 kích hoạt guard clause |
status_code | STRING | Chỉ xuất hiện trên đường guard clause: "NO_ACTIVE_MIDS_WARNING" |
message | STRING | Giải thích dễ đọc, chỉ xuất hiện trên đường guard clause |
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §Calculate Volume Pacing lines 1460–1464)
5. Cổng an toàn & Trường hợp biên
Edge Case 1 — Chặn nhiễu đầu tháng (thời gian ân hạn 3 ngày dương lịch)
Vào ngày 1 hằng tháng, các giá trị current_mtd_volume vừa được reset về 0.00. ADV sẽ gần bằng 0 và Projected_EOM_Volume sẽ ở mức tối thiểu — phép toán tự nhiên chặn các false positive. Tuy nhiên, vài ngày đầu tháng có thể cho ra dự phóng gây hiểu lầm do mẫu số ADV có ít dữ liệu.
Hành vi đã xác nhận (thời gian ân hạn 3 ngày dương lịch — Option B):
Trong 3 ngày dương lịch đầu tiên của mỗi tháng (ngày 1, 2, 3), Middleware PHẢI bỏ qua hoàn toàn các phép tính pacing của Flow 31 và trả về projected_exhaustion = false vô điều kiện. Từ ngày 4 trở đi, phép toán pacing tiêu chuẩn tiếp tục.
IF current_day_of_month <= 3:
→ return { projected_exhaustion: false } // Grace period — skip pacing math
ELSE:
→ Execute standard ADV / Projected_EOM_Volume calculationLý do: Cổng dựa trên thời gian dễ dự đoán hơn ngưỡng khối lượng tối thiểu. Nó loại bỏ sự mơ hồ về việc một ngày khối lượng thấp là do chủ ý chặn hay do vấn đề cấu hình, và cho phép ngày 4 là điểm bắt đầu sạch của dữ liệu xu hướng đáng tin cậy.
(Nguồn: client confirmed 2026-09-18)
Edge Case 2 — Tất cả MID không active (total_aggregate_capacity = 0)
Nếu không có MID nào có is_active = TRUE, thì total_aggregate_capacity = 0. Nếu không có guard clause, sẽ xảy ra hai chế độ lỗi:
- Chia cho 0 / NaN / Infinity khi tính tỷ lệ khối lượng.
- SEV-1 dương tính giả (false-positive):
Projected_EOM_Volume (≥ 0) >= 0luôn làtrue→ bắn một cảnh báo CRITICAL sai. Điều này mô tả sai sự cố thành cạn kiệt khối lượng trong khi nguyên nhân thực là vấn đề cấu hình/vô hiệu hóa gateway.
Hành vi đã xác nhận (Guard Clause — phía server):
Step 1: Query active MIDs
activeMIDs = SELECT * FROM Merchant_Routing_Config WHERE is_active = TRUE
Step 2: Compute total_aggregate_capacity
total_aggregate_capacity = SUM(activeMIDs.monthly_limit)
GUARD CLAUSE:
IF activeMIDs.length = 0 OR total_aggregate_capacity = 0:
→ log WARN: "All payment MIDs inactive — suppressing SEV-1 exhaustion alert"
→ return { projected_exhaustion: false, status_code: "NO_ACTIVE_MIDS_WARNING",
active_mids_count: 0, total_aggregate_capacity: 0 }
→ Middleware MUST additionally dispatch a separate Ops Config Warning
to **`#ops-treasury`** (distinct from SEV-1, NOT routed to Freshdesk):
"🚨 CRITICAL CONFIGURATION ERROR: All MIDs are currently marked inactive."
Step 3 onwards: Standard pacing math — only executes when active_mids_count > 0Ma trận kịch bản cảnh báo:
| Kịch bản | MID active | total_aggregate_capacity | projected_exhaustion | Hành động của hệ thống |
|---|---|---|---|---|
| Bình thường | ≥ 1 | > 0 | false | Không cảnh báo |
| Pacing khối lượng cao | ≥ 1 | > 0 | true | SEV-1 tới #ops-treasury |
| Edge Case 2 | 0 | 0 | false | Chặn SEV-1. Ghi log WARNING. Gửi Ops Config Warning tới #ops-treasury (không phải SEV-1, không qua Freshdesk). |
(Nguồn: client confirmed 2026-09-17)
Edge Case 3 — Endpoint không khả dụng
Nếu GET /calculate-volume-pacing trả về khác 200 hoặc timeout, Flow 31 thực thi trình xử lý ngoại lệ hai giai đoạn:
Giai đoạn 1 — Tự động retry (backoff 3 lần): Zapier tự động retry 3 lần, mỗi lần cách nhau 5 phút (tổng cửa sổ: 15 phút). Tự phục hồi khi gặp lỗi mạng thoáng qua hoặc Middleware khởi động lại.
Giai đoạn 2 — Cảnh báo Slack SEV-2 (sau 3 lần retry thất bại):
Nếu cả 3 lần retry thất bại, Zapier kích hoạt cảnh báo SEV-2 WARNING tới #ops-treasury và #dev_ops.
Nội dung tin nhắn SEV-2:
🟡 [SEV-2 WARNING] Volume Pacing Monitoring Endpoint Unavailable
• Event: Zapier Flow 31 failed to fetch MID volume pacing metrics.
• Target Endpoint: GET /calculate-volume-pacing
• Status: Failed after 3 retries (Non-200 / Timeout).
• Impact: Daily aggregate merchant capacity check was SKIPPED tonight.
• Action Required: @dev_ops please inspect Middleware Hub logs in AWS CloudWatch.Phân nhánh lỗi của Flow 31:
Zapier Flow 31: GET /calculate-volume-pacing
│
├─ HTTP 200 OK
│ ├─ projected_exhaustion = true → SEV-1 Slack Alert (#ops-treasury)
│ └─ projected_exhaustion = false → Pass (no alert)
│
└─ Non-200 / Timeout
└─ Auto-retry 3×, 5 min intervals
└─ Persistent fail (all 3 retries exhausted)
└─ SEV-2 Slack Alert (#ops-treasury + #dev_ops)(Nguồn: client confirmed 2026-09-17)
6. Quy tắc nghiệp vụ
| Rule ID | Quy tắc | Nguồn |
|---|---|---|
| BR_9.9.1 | Flow 31 chạyhằng ngày lúc 11:00 PM CST qua trigger theo lịch của Zapier. KHÔNG được kích hoạt bởi sự kiện thanh toán và KHÔNG phải là cron của Middleware. | Zapier Integration V7.pdf §Flow 31 line 2473 |
| BR_9.9.2 | Mẫu số ADV là ngày hiện tại trong tháng tại thời điểm tính. Trong 3 ngày dương lịch đầu tiên của mỗi tháng (current_day_of_month <= 3), Middleware PHẢI bỏ qua hoàn toàn phép tính pacing và trả về projected_exhaustion = false vô điều kiện — thời gian ân hạn 3 ngày dương lịch ngăn cảnh báo SEV-1 dương tính giả do mẫu số ADV ở mức nhiễu khi bắt đầu chu kỳ mới. Phép toán pacing tiêu chuẩn tiếp tục từ ngày 4 trở đi. | RFQ_ Stack Trading Prop Tech V7.pdf §Calculate Volume Pacing lines 1460–1464; client confirmed 2026-09-18 |
| BR_9.9.3 | Chỉ các MID có is_active = TRUE được đưa vào cả hai phép tổng hợp total_mtd_volume và total_aggregate_capacity. | RFQ_ Stack Trading Prop Tech V7.pdf §Merchant_Routing_Config lines 847–855 |
| BR_9.9.4 | Zapier PHẢI chặn đầu ra Slack khi projected_exhaustion = false. Cảnh báo SEV-1 chỉ bắn khi và chỉ khi projected_exhaustion = true. | Zapier Integration V7.pdf §Flow 31 line 2475 |
| BR_9.9.5 | Cảnh báo SEV-1 chỉ mang tính thông tin. Nó KHÔNG tự động vô hiệu hóa MID, định tuyến lại lưu lượng hay sửa Merchant_Routing_Config. Việc khắc phục do Ops thực hiện thủ công. | Zapier Integration V7.pdf §Flow 31 lines 2476–2479 |
| BR_9.9.6 | Zapier Flow 31 trên Production PHẢI chỉ gọi endpoint Middleware Production. Cấm gọi chéo môi trường (prod → staging DB). | Client spec 2026-09-03 |
| BR_9.9.7 | Reset hằng tháng (dùng chung với UC_9.8 / BR_9.8.4): current_mtd_volume được reset về 0.00 cho toàn bộ MID vào 00:00 UTC ngày 1 hằng tháng. Việc này reset đường cơ sở ADV cho chu kỳ tính toán tiếp theo của Flow 31. | RFQ_ Stack Trading Prop Tech V7.pdf §Merchant_Routing_Config line 222 |
| BR_9.9.8 | Guard Clause — Công suất tổng hợp bằng 0: Nếu active_mids_count = 0 HOẶC total_aggregate_capacity = 0, GET /calculate-volume-pacing PHẢI trả về projected_exhaustion = false ngay lập tức mà không thực thi phép toán pacing. Ngăn (a) lỗi chia cho 0 và (b) cảnh báo SEV-1 dương tính giả do phép so sánh luôn đúng Projected_EOM_Volume >= 0. Middleware PHẢI ghi log WARNING và gửi một Ops Config Warning riêng tới Slack #ops-treasury (KHÔNG phải SEV-1, KHÔNG qua Freshdesk). Nội dung cảnh báo: "🚨 CRITICAL CONFIGURATION ERROR: All MIDs are currently marked inactive." Response bao gồm status_code: "NO_ACTIVE_MIDS_WARNING". Nguyên nhân gốc (tất cả MID không active) là vấn đề cấu hình, không phải sự kiện cạn kiệt khối lượng — loại cảnh báo PHẢI khác tương ứng. | Client confirmed 2026-09-17; channel confirmed 2026-09-18 |
| BR_9.9.9 | Flow 31 Endpoint Failure — Retry + SEV-2: Nếu GET /calculate-volume-pacing trả về khác 200 hoặc timeout, Zapier PHẢI retry 3 lần cách nhau 5 phút (Giai đoạn 1). Nếu cả 3 lần retry thất bại, Zapier kích hoạt cảnh báo SEV-2 WARNING tới #ops-treasury và #dev_ops (Giai đoạn 2). Tin nhắn SEV-2 phải bao gồm: tên endpoint, trạng thái lỗi, tuyên bố tác động (kiểm tra hằng ngày bị bỏ qua), và yêu cầu hành động @dev_ops kèm tham chiếu AWS CloudWatch. | Client confirmed 2026-09-17 |
7. Mối quan hệ với UC_9.8 (Flow 30)
| Khía cạnh | UC_9.8 — Flow 30 | UC_9.9 — Flow 31 |
|---|---|---|
| Phạm vi | MID riêng lẻ | Tất cả MID active (tổng hợp) |
| Trigger | Thời gian thực, theo từng giao dịch thanh toán | Theo lịch, hằng ngày 11:00 PM CST |
| Ngưỡng | 80% monthly_limit của một MID | 100% tổng công suất tổng hợp (EOM dự phóng) |
| Loại cảnh báo | Warning | SEV-1 CRITICAL |
| Mục đích | Phát hiện một MID chạy quá nóng | Phát hiện đột biến khối lượng toàn hệ thống trước cuối tháng |
| Khắc phục | Liên hệ processor để nâng hạn mức cho một MID | Khẩn cấp: liên hệ tất cả processor + onboard các MID dự phòng + điều chỉnh trọng số round-robin |
8. Tiêu chí chấp nhận
| ID | Kịch bản | Kết quả mong đợi |
|---|---|---|
| AC-9.9-01 | Ngày 15 của tháng 30 ngày;total_mtd_volume = 300,000; total_aggregate_capacity = 400,000 → ADV = 20,000; EOM dự phóng = 600,000 | projected_exhaustion = true; cảnh báo SEV-1 Slack được gửi tới #ops-treasury |
| AC-9.9-02 | Ngày 15 của tháng 30 ngày;total_mtd_volume = 150,000; total_aggregate_capacity = 400,000 → ADV = 10,000; EOM dự phóng = 300,000 | projected_exhaustion = false; không gửi cảnh báo Slack |
| AC-9.9-03 | Ngày 1 của tháng; total_mtd_volume = 1,000 (vừa reset); current_day_of_month = 1 | projected_exhaustion = false (trong thời gian ân hạn 3 ngày — bỏ qua phép toán pacing); không có cảnh báo Slack |
| AC-9.9-04 | total_aggregate_capacity = 0 (tất cả MID không active — active_mids_count = 0) | Guard clause kích hoạt: projected_exhaustion = false; status_code = "NO_ACTIVE_MIDS_WARNING"; ghi log WARNING; gửi Ops Config Warning (không phải SEV-1); không có lỗi 500; KHÔNG thực thi phép toán pacing |
| AC-9.9-05 | projected_exhaustion = true | Tin nhắn Slack chứa đủ 7 trường:current_day_of_month, total_days_in_month, total_mtd_volume, average_daily_volume, projected_eom_volume, total_aggregate_capacity và nhãn SEV-1 |
| AC-9.9-06 | Cron chạy lúc 11:00 PM CST vào một ngày bất kỳ | Flow 31 thực thi; gọi endpoint Production (không phải staging) |
| AC-9.9-07 | Ngày 20;projected_exhaustion = false | Không có tin nhắn Slack; Zapier filter dừng luồng âm thầm |
9. Câu hỏi mở
| ID | Ưu tiên | Câu hỏi | Giả định |
|---|---|---|---|
Đã giải quyết 2026-09-18: Option B được xác nhận — thời gian ân hạn 3 ngày dương lịch. current_day_of_month <= 3 bỏ qua hoàn toàn phép toán pacing. Không có volume floor. Đã cập nhật BR_9.9.2 + Edge Case 1 + AC-9.9-03. | |||
total_aggregate_capacity = 0 (all MIDs inactive)? | Đã giải quyết 2026-09-17: Guard clause — trả về projected_exhaustion = false + status_code: "NO_ACTIVE_MIDS_WARNING" + ghi log WARN + gửi Ops Config Warning tới #ops-treasury. Phép toán pacing bị chặn. Đã thêm BR_9.9.8. | ||
GET /calculate-volume-pacing returns non-200 or times out? | Đã giải quyết 2026-09-17: backoff 3 lần retry (cách nhau 5 phút) → SEV-2 tới #ops-treasury + #dev_ops. Đã thêm BR_9.9.9. Đã ghi nhận Edge Case 3. |
10. Lịch sử thay đổi
| Ngày | Phiên bản | Tóm tắt | Trước | Sau | Nguồn |
|---|---|---|---|---|---|
| 2026-09-18 | v1.2 → v1.3 | Viết lại Edge Case 1 (ân hạn 3 ngày thay thế volume floor $5,000); kênh Ops Config Warning của Edge Case 2 được chốt là #ops-treasury; cập nhật BR_9.9.2; cập nhật BR_9.9.8 kèm kênh + nội dung cảnh báo; cập nhật AC-9.9-03; đóng Q1 + Q3 trong Câu hỏi mở; tăng phiên bản | Edge Case 1: $5,000 volume floor, [OPEN — Q1]. BR_9.9.2: logic volume floor. AC-9.9-03: "below $5,000 floor". Kênh Edge Case 2: "TBD by Ops team". Q1 + Q3: mở. | Edge Case 1: ân hạn 3 ngày dương lịch — current_day_of_month <= 3 bỏ qua hoàn toàn phép toán pacing. BR_9.9.2: cổng dựa trên thời gian. AC-9.9-03: logic ân hạn. Kênh Edge Case 2: #ops-treasury (KHÔNG phải Freshdesk). Q1 + Q3: đã giải quyết. | Client confirmed 2026-09-18 |
| 2026-09-17 | v1.0 → v1.1 | Mở rộng Edge Case 2 + BR_9.9.8 + AC-9.9-04 + response payload + đóng Q2 | Edge Case 2: [OPEN — Q2] — hành vi guard chưa được quy định. AC-9.9-04: stub tối thiểu. Response payload: 8 trường. | Edge Case 2: đặc tả guard clause đầy đủ — kiểm tra active_mids_count = 0 trước phép toán pacing, status code NO_ACTIVE_MIDS_WARNING, Ops Config Warning (không phải SEV-1), thêm pseudocode + ma trận cảnh báo. Đã thêm BR_9.9.8. AC-9.9-04: mở rộng chi tiết guard clause. Response payload: 11 trường (thêm active_mids_count, status_code, message). Đã đóng Q2. | Client confirmed 2026-09-17 |
| 2026-09-03 | v1.0 | Khởi tạo tài liệu | — | — | Zapier Integration V7.pdf §Flow 31; RFQ_ Stack Trading Prop Tech V7.pdf §Calculate Volume Pacing |
UC_9.11 — Luồng 34: Đồng bộ Sổ cái Lương Rippling
| Trường | Giá trị |
|---|---|
| UC_ID | UC_9.11 |
| Phiên bản | v1.1 |
| Cập nhật lần cuối | 2026-11-04 |
| BA PiC | linh.nguyen34 |
| Danh mục | BACKGROUND MECHANISMS & CRON JOBS |
| Mô-đun | Luồng 34 (UC_4.15.2) |
| Chức năng | Đồng bộ Sổ cái Lương Rippling |
| Tài liệu tham chiếu | Zapier Integration V7.pdf (§Flow 34; §7.2 Flow 34 Payroll Trigger — Option A/B) · RFQ_ Stack Trading Prop Tech V7.pdf (§Record Immutable Ledger POST /api/ledger/record; §3.6.1 Smart Contract Architecture; §Audit Requirements CFTC 1.31) · StackTrading_Backend_Technical_Proposal_v1.6.docx (§Option B provider list) · QnA_STAGE5_PAYOUT_WITHDRAWAL.md (A-33) |
1. Danh mục UC
| UC_ID | Chức năng | Mô tả |
|---|---|---|
| UC_9.11 | Luồng 34 — Đồng bộ Sổ cái Lương Rippling | Khi Rippling thanh toán xong một kỳ lương hai tuần/hàng tháng cho trader W-2 đang hoạt động, Zapier gọi POST /api/ledger/record để mint một event W2 Payroll Executed trên contract StackTradingLedger của Polygon PoS. Middleware lưu tx_hash trả về vào PostgreSQL. Các lần Rippling gửi trùng được loại bỏ (dedup) theo provider_event_id / receipt_hash. |
UC_9.11 — Luồng 34: Đồng bộ Sổ cái Lương Rippling
| Trường | Giá trị |
|---|---|
| Use Case | Đồng bộ Sổ cái Lương Rippling |
| Mô tả | Công bố bằng chứng chi trả lương W-2 (lương cơ bản + phúc lợi) lên Sổ cái Bất biến (Immutable Ledger). Mỗi kỳ lương Rippling định kỳ theo lịch (hai tuần/hàng tháng) đã thanh toán cho một trader W-2 đang hoạt động tạo ra đúng một event CorporateMetricVerified trên Polygon PoS, với Rippling Transaction ID làm verificationHash. Các kỳ lương off-cycle bị bỏ qua. |
| Tác nhân | Hệ thống (Rippling → Zapier → Middleware Hub → Polygon PoS) |
| Zapier Flow | Flow 34 |
| Nguồn trigger | Rippling webhook — Payroll Run Cleared (PAYROLL_CLEARED) |
| 3rd Party | Rippling · Zapier · Alchemy RPC · Polygon PoS |
| Middleware Endpoint | POST /api/ledger/record |
| UC liên quan | UC_7.4 Immutable Ledger (Payout) · UC_4.15.2 Career & HR Widget · Flow 5 (thưởng hiệu suất), Flow 9 (severance), Flow 17 (vesting của Pod Leader) — mỗi luồng tự ghi khoản chi trả W-2 off-cycle của mình lên sổ cái, xem BR_9.11.6 |
2. Trigger
Event: Rippling gửi webhook khi một kỳ lương hai tuần hoặc hàng tháng định kỳ theo lịch được cleared (tiền đã giải ngân).
Các kỳ lương off-cycle (thưởng hiệu suất Flow 5, severance Flow 9, vesting Pod Leader Flow 17 — đều qua Rippling Off-Cycle Payroll API) không được Luồng 34 xử lý, kể cả khi Rippling gửi cùng một webhook. Xem Alt-3.
| Nguồn | Event | Đường chuyển phát |
|---|---|---|
| Rippling | Payroll Run Cleared (PAYROLL_CLEARED) | Option A — trực tiếp đến Zapier (Rippling không nằm trong danh sách provider hardened-ingress của Option B) |
(Nguồn: Zapier Integration V7.pdf §Flow 34; §7.2 Option A/B · StackTrading_Backend_Technical_Proposal_v1.6.docx §Option B provider list — Rippling được đánh dấu "—")
⚠️ Option B (
POST /webhooks/payroll/rippling→POST /zapier/events/payroll_cleared) được mô tả trong Zapier Integration V7.pdf §7.2 nhưng không nằm trong phạm vi Option B đã được chấp thuận (8 provider: NMI, Triple-A, Skrill, Nomupay, EBANX, Dusupay, Veriff, Dots). Xem Q1.
3. Điều kiện tiên quyết
| ID | Điều kiện tiên quyết |
|---|---|
| P1 | Webhook subscription của Rippling cho Payroll Run Cleared được cấu hình trỏ tới catch hook của Zapier Flow 34. |
| P2 | Payload chứa Rippling Transaction ID (dùng làm receipt_hash) và số tiền lương (Total_Amount). |
| P3 | Worker trên Rippling được ánh xạ tới một người dùng Stack Trading (trader_id) có Employment_Type = 'W-2' và trạng thái đang hoạt động. |
| P4 | Contract StackTradingLedger đã được triển khai trên Polygon PoS Mainnet; private key của owner wallet phía server khả dụng cho Middleware (môi trường bảo mật). |
4. Hậu điều kiện
| # | Hậu điều kiện |
|---|---|
| 1 | Một event CorporateMetricVerified được phát trên Polygon PoS với metricCategory = "W2 Payroll Executed". |
| 2 | tx_hash của Polygon được ghi vào PostgreSQL (bản ghi sổ cái) theo trader_id + receipt_hash. |
| 3 | receipt_hash / provider_event_id được đánh dấu đã xử lý — mọi lần gửi lại đều là no-op. |
| 4 | Việc thực thi được ghi vào BPS_Audit_Log. |
| 5 | Event hiển thị trên GET /public/ledger (ticker Live Payout Ledger) sau khi cache được làm mới. |
| 6 | Zap kết thúc. |
5. Luồng cơ bản
Bước 1 — Nhận webhook (Zapier)
Zapier catch hook nhận payload Rippling Payroll Run Cleared.
Bước 2 — Filter: chỉ trader W-2 đang hoạt động, chỉ kỳ lương định kỳ (Zapier)
- Ánh xạ Rippling worker sang
trader_id. - Chỉ tiếp tục nếu trader là trader W-2 đang hoạt động của Stack Trading (
Employment_Type = 'W-2', tài khoản active). - Chỉ tiếp tục nếu kỳ lương là kỳ lương định kỳ theo lịch (không phải off-cycle). Off-cycle → dừng (xem Alt-3).
- Ngược lại → dừng (xem Alt-2).
- ⚠️ Trường trong payload Rippling dùng để phân biệt kỳ lương định kỳ với kỳ lương off-cycle chưa có trong nguồn — Dev cần xác nhận với Rippling API.
(Nguồn: Zapier Integration V7.pdf §Flow 34 — "Ensure payload corresponds to an active Stack Trading W2 Trader Only"; QnA_BACKGROUND_MECHANISMS_CRON.md [BG-016] — bảng tính của khách hàng 2026-11-04)
Bước 3 — Kiểm tra dedup
Trước khi gọi sổ cái, kiểm tra provider_event_id (Rippling event / transaction ID) với dedup store. Nếu đã xử lý → Alt-1.
(Nguồn: Zapier Integration V7.pdf §7.2 Option A — "Store provider_event_id in DB to dedup (required for payroll/ledger events)")
Bước 4 — Gọi Middleware POST /api/ledger/record
| Trường | Giá trị |
|---|---|
trader_id | UUID trader toàn cục |
metric_category | "W2 Payroll Executed" (cố định) |
metric_value | "$[Total_Amount] Base + Benefits Paid" — số tiền định dạng 2 chữ số thập phân kèm dấu phân cách hàng nghìn, ví dụ "$4,500.00 Base + Benefits Paid" |
date | Hôm nay (ISO 8601 UTC) |
receipt_hash | Rippling Transaction ID |
(Nguồn: Zapier Integration V7.pdf §Flow 34)
Bước 5 — Middleware mint trên Polygon
- Xác thực chữ ký Zapier JWT. Từ chối nếu không hợp lệ (Exc-1).
- Khởi tạo Ethers.js wallet phía server bằng private key từ môi trường bảo mật.
- Thực thi
contract.recordMetric(_traderId, _metricCategory, _metricValue, _timestamp, _verificationHash)qua Alchemy RPC, với ánh xạ:trader_id → _traderId,metric_category → _metricCategory,metric_value → _metricValue,date → _timestamp,receipt_hash → _verificationHash. - Chờ block confirmation.
- Ghi
tx_hashvào PostgreSQL; log vàoBPS_Audit_Log. - Trả về
HTTP 200 { "status": "success", "tx_hash": "0x..." }.
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §Record Immutable Ledger; §3.6.1 Smart Contract Architecture)
Bước 6 — Kết thúc Zap.
6. Luồng thay thế
Alt-1 — Gửi trùng
provider_event_id/receipt_hashđã được xử lý.- Middleware không gọi lại
recordMetric(); trả vềHTTP 200cùngtx_hashđã lưu trước đó. - Không có event sổ cái thứ hai; không có dòng ticker thứ hai.
Alt-2 — Payload không phải trader W-2 đang hoạt động
- Filter thất bại (contractor, đã chấm dứt, worker không xác định, nhân viên không phải trader).
- Zap dừng ở Bước 2. Không gọi sổ cái. Không có lỗi.
Alt-3 — Rippling báo một kỳ lương off-cycle
- Zap dừng ở Bước 2. Không gọi sổ cái. Không có lỗi.
- Các khoản chi trả W-2 off-cycle đã được chính luồng của chúng ghi nhận qua
POST /api/ledger/record: Flow 5 (Performance Bonus, Path C), Flow 9 (Severance, Path C), Flow 17 (Pod Leader Vesting, nhánh US W-2). Ghi lại lần nữa trong Luồng 34 sẽ tạo bản ghi on-chain trùng lặp.
(Nguồn: QnA_BACKGROUND_MECHANISMS_CRON.md [BG-016] — bảng tính của khách hàng 2026-11-04; Zapier Integration V7.pdf §Flow 5 Path C, §Flow 9 Path C; RFQ_ Stack Trading Prop Tech V7.pdf §Process Pod Vesting — Immutable Ledger Logging)
7. Luồng ngoại lệ
Exc-1 — Zapier JWT không hợp lệ / hết hạn
- Middleware trả về
HTTP 401; không gọi contract; log vàoBPS_Audit_Logvớiexecution_status = REJECTED.
Exc-2 — Lỗi Polygon RPC / giao dịch
recordMetric()thất bại hoặc không nhận được confirmation.- Middleware trả về non-2xx; không ghi nhận là đã xử lý (để retry có thể mint).
- Áp dụng Zapier retry (xem Q3). Khi thất bại cuối cùng → cảnh báo Slack tới Ops (kênh theo slack_channel_usage.md, xem Q3).
Exc-3 — Không thể ánh xạ trader_id từ Rippling worker
- Xử lý như Alt-2 (dừng) và ghi log vào
BPS_Audit_Logvới giá trịUNMAPPED_RIPPLING_WORKERđể xem xét thủ công. (Giả định của BA — xem Q4)
8. Quy tắc nghiệp vụ
BR_9.11.1: Phạm vi — Chỉ trader W-2 đang hoạt động
Luồng 34 chỉ ghi nhận lương cho trader W-2 đang hoạt động của Stack Trading. Contractor quốc tế được xử lý bởi Luồng 36 (UC_9.13).
(Nguồn: Zapier Integration V7.pdf §Flow 34)
BR_9.11.2: Ánh xạ sổ cái cố định
metric_category luôn là "W2 Payroll Executed". Định dạng metric_value là "$[Total_Amount] Base + Benefits Paid", số tiền định dạng 2 chữ số thập phân (ví dụ "$2,500.00 Base + Benefits Paid"). receipt_hash luôn là Rippling Transaction ID.
(Nguồn: Zapier Integration V7.pdf §Flow 34; QnA_BACKGROUND_MECHANISMS_CRON.md [BG-016])
BR_9.11.3: Một kỳ lương = Một event sổ cái (idempotency)
provider_event_id phải được lưu và kiểm tra trước khi mint. Các lần retry của Rippling không bao giờ được tạo ra event on-chain thứ hai. Bản trùng trả về HTTP 200 cùng tx_hash gốc.
(Nguồn: Zapier Integration V7.pdf §7.2 Option A)
BR_9.11.4: Ghi sổ cái chỉ dành cho Owner, chỉ dạng Event
Contract không lưu state; dữ liệu chỉ được ghi dưới dạng event log CorporateMetricVerified. Chỉ owner wallet mới được gọi recordMetric().
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §3.6.1)
BR_9.11.5: tx_hash được lưu trước khi trả response thành công
Middleware chỉ trả về thành công cho Zapier sau khi có block confirmation và sau khi tx_hash được ghi vào PostgreSQL.
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §Record Immutable Ledger — "Await confirmation and write the Polygon transaction hash to PostgreSQL")
BR_9.11.6: Luồng 34 chỉ ghi lương định kỳ — Các khoản chi trả off-cycle do chính luồng của chúng ghi nhận
Luồng 34 chỉ ghi nhận lương W-2 định kỳ theo lịch (lương cơ bản, phúc lợi). Các khoản chi trả W-2 off-cycle được ghi nhận bởi luồng kích hoạt chúng, qua POST /api/ledger/record: Flow 5 (Performance Bonus), Flow 9 (Severance), Flow 17 (Pod Leader Vesting). Luồng 34 lọc bỏ các kỳ lương off-cycle để mỗi khoản thanh toán tạo ra đúng một bản ghi sổ cái.
(Nguồn: QnA_BACKGROUND_MECHANISMS_CRON.md [BG-016] — bảng tính của khách hàng 2026-11-04, thay thế giả định trong QnA_STAGE5_PAYOUT_WITHDRAWAL.md A-33 rằng Luồng 34 ghi nhận các khoản chi trả off-cycle Path C; Zapier Integration V7.pdf §Flow 5, §Flow 9; RFQ_ Stack Trading Prop Tech V7.pdf §Process Pod Vesting)
BR_9.11.7: Lưu trữ audit
Mọi lần thực thi đều được ghi vào BPS_Audit_Log. Thời hạn lưu trữ: 5 năm + 1 tháng (CFTC 1.31 / NFA); các object cũ hơn 25 tháng được chuyển sang cold storage.
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §Audit Requirements)
9. Tiêu chí chấp nhận
| AC_ID | Luồng | Cho trước | Thì | Loại |
|---|---|---|---|---|
| AC-9.11-01 | Happy path | Payroll Run Cleared cho trader W-2 đang hoạt động, $4,500.00, transaction ID mới | Một event CorporateMetricVerified với metricCategory = "W2 Payroll Executed", metricValue = "$4,500.00 Base + Benefits Paid", verificationHash = Rippling Transaction ID; tx_hash được lưu; HTTP 200 | Integration |
| AC-9.11-02 | Trùng lặp | Cùng một Rippling event được gửi hai lần | Lần gọi thứ hai trả về HTTP 200 cùng tx_hash gốc; chỉ có một event on-chain | Integration |
| AC-9.11-03 | Filter — contractor | Payload của worker không phải W-2 | Zap dừng ở filter; không gọi sổ cái | Integration |
| AC-9.11-04 | Filter — inactive | Payload của trader W-2 đã chấm dứt | Zap dừng; không gọi sổ cái | Integration |
| AC-9.11-05 | JWT sai | Request có chữ ký không hợp lệ | HTTP 401; không gọi contract; dòng audit REJECTED | Security |
| AC-9.11-06 | Lỗi RPC | Alchemy trả về lỗi | Trả về non-2xx; event không được đánh dấu đã xử lý; retry mint đúng một lần | Integration |
| AC-9.11-07 | Public ledger | AC-9.11-01 hoàn tất | Event xuất hiện trong GET /public/ledger recent_events sau khi cache được làm mới | Integration |
| AC-9.11-08 | Filter — off-cycle | Kỳ lương off-cycle của Rippling (ví dụ severance) được cleared cho trader W-2 đang hoạt động | Zap dừng ở filter; không gọi sổ cái; Luồng 34 không tạo event on-chain nào | Integration |
| AC-9.11-09 | Định dạng số tiền | Kỳ lương định kỳ được cleared, $2,500 | metricValue = "$2,500.00 Base + Benefits Paid" (2 chữ số thập phân) | Integration |
10. Tham chiếu API
POST /api/ledger/record
| Trường | Giá trị |
|---|---|
| Caller | Zapier (Luồng 34) |
| Auth | Chữ ký Zapier JWT |
Request:
{
"trader_id": "94a2b1c8-3f4e-4b21-a123-890abcdef123",
"metric_category": "W2 Payroll Executed",
"metric_value": "$4,500.00 Base + Benefits Paid",
"date": "2026-03-15T18:00:00Z",
"receipt_hash": "rip_pay_8830192a7c41f"
}Response (HTTP 200):
{ "status": "success", "tx_hash": "0x..." }| Trường | Kiểu | Bắt buộc | Mô tả |
|---|---|---|---|
trader_id | STRING | Có | UUID trader toàn cục |
metric_category | STRING | Có | "W2 Payroll Executed" |
metric_value | STRING | Có | "$[Total_Amount] Base + Benefits Paid" |
date | STRING | Có | ISO 8601 UTC |
receipt_hash | STRING | Có | Rippling Transaction ID |
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §Record Immutable Ledger)
11. Câu hỏi mở
| ID | Ưu tiên | Câu hỏi | Giả định |
|---|---|---|---|
| Q1 | H | Rippling không nằm trong danh sách provider Option B. Luồng 34 được chuyển phát qua Option A (Rippling → Zapier trực tiếp) hay nên bổ sung Rippling vào Option B (POST /webhooks/payroll/rippling)? | Option A; dedup thực hiện trong Middleware tại POST /api/ledger/record. |
| Q2 | M | Một lần chạy off-cycle của Rippling (severance, client nói Luồng 34 bao gồm severance — A-33) có cũng kích hoạt Payroll Run Cleared không? Nếu có, metric_value vẫn ghi "Base + Benefits Paid"? | ✅ Đã trả lời 2026-11-04 — Luồng 34 bỏ qua các kỳ lương off-cycle; xem Alt-3, BR_9.11.6 ([BG-016]). |
| Q3 | M | Số lần Zapier retry / backoff và kênh Slack khi thất bại cuối cùng không được nêu trong V7. | Zapier tự động replay mặc định; cảnh báo tới kênh Ops. |
| Q4 | L | Một Rippling worker không ánh xạ được nên phát cảnh báo Ops hay chỉ ghi dòng audit? | Chỉ ghi dòng audit. |
| Q5 | L | Total_Amount là lương gộp (gross) hay thực nhận (net)? Tiền tệ luôn là USD? | Gross, USD. |
12. Lịch sử thay đổi
| Ngày | Phiên bản | Thay đổi | Trước | Sau | Nguồn |
|---|---|---|---|---|---|
| 2026-11-04 | v1.1 | §1 Mô tả, UC liên quan; §2 Trigger; §5 Bước 2 (filter off-cycle), Bước 4 định dạng metric_value; §6 Alt-3 (mới); §8 BR_9.11.2, BR_9.11.6 (viết lại); §9 AC-9.11-08/09 (mới); §11 Q2 | Luồng 34 được giả định cũng ghi nhận các kỳ lương off-cycle, gồm severance (A-33); BR_9.11.6 = chỉ so sánh Luồng 34 với Luồng 5 | Luồng 34 = chỉ lương định kỳ theo lịch; các khoản chi trả W-2 off-cycle do Flow 5 / Flow 9 / Flow 17 ghi nhận; metric_value 2 chữ số thập phân | Bảng tính của khách hàng 2026-11-04 [BG-016]; Zapier Integration V7.pdf §Flow 5, §Flow 9; RFQ V7 §Process Pod Vesting |
| 2026-10-27 | v1.0 | Tạo tài liệu ban đầu | — | Đặc tả đầy đủ: trigger, filter W-2, dedup, mint sổ cái, BR_9.11.1–7, AC-9.11-01–07 | Zapier Integration V7.pdf §Flow 34, §7.2; RFQ_ Stack Trading Prop Tech V7.pdf §Record Immutable Ledger, §3.6.1; QnA A-33 |
UC_9.12 — Luồng 35: Đồng bộ Sổ cái PTO Rippling
| Trường | Giá trị |
|---|---|
| UC_ID | UC_9.12 |
| Phiên bản | v1.2 |
| Cập nhật lần cuối | 2026-11-05 (v1.2 — bảng tính của khách hàng 2026-11-04: chỉ số ngày nguyên; không ghi bút toán điều chỉnh khi hủy) |
| BA PiC | linh.nguyen34 |
| Danh mục | BACKGROUND MECHANISMS & CRON JOBS |
| Mô-đun | Luồng 35 (UC_4.15.2) |
| Chức năng | Đồng bộ Sổ cái PTO Rippling |
| Tài liệu tham chiếu | CR-20261027-001 (Flow 13 hybrid — trader W-2 yêu cầu nghỉ phép trong Rippling) · Zapier Integration V7.pdf (§Flow 35; §Flow 13 Time-Off Management) · RFQ_ Stack Trading Prop Tech V7.pdf (§Record Immutable Ledger POST /api/ledger/record; §3.6.1 Smart Contract Architecture; §Audit Requirements) |
1. Danh mục UC
| UC_ID | Chức năng | Mô tả |
|---|---|---|
| UC_9.12 | Luồng 35 — Đồng bộ Sổ cái PTO Rippling | Khi Rippling phê duyệt yêu cầu nghỉ phép của trader W-2, Zapier gọi POST /api/ledger/record để mint một event Corporate Benefits ([Days_Approved] Days PTO Approved (Paid Time Off)) trên Polygon PoS. Chỉ ghi sổ cái — việc gia hạn stagnation và khóa tài khoản thuộc Luồng 13 (UC_9.2). |
UC_9.12 — Luồng 35: Đồng bộ Sổ cái PTO Rippling
| Trường | Giá trị |
|---|---|
| Use Case | Đồng bộ Sổ cái PTO Rippling |
| Mô tả | Công bố bằng chứng nghỉ phép có lương (phúc lợi doanh nghiệp) lên Sổ cái Bất biến (Immutable Ledger). Mỗi yêu cầu nghỉ phép được Rippling phê duyệt cho trader W-2 đang hoạt động tạo ra đúng một event CorporateMetricVerified với Rippling Request ID làm verificationHash. |
| Tác nhân | Hệ thống (Rippling → Zapier → Middleware Hub → Polygon PoS) |
| Zapier Flow | Flow 35 |
| Nguồn trigger | Rippling webhook — Time Off Request Approved |
| 3rd Party | Rippling · Zapier · Alchemy RPC · Polygon PoS |
| Middleware Endpoint | POST /api/ledger/record |
| UC liên quan | UC_9.2 Flow 13 Time-Off Management (Path A được kích hoạt bởi cùng webhook Rippling này) · UC_9.11 · UC_4.15.2 |
2. Trigger
Event: Webhook Rippling Time Off Request Approved → catch hook của Zapier Flow 35.
Upstream: trader W-2 yêu cầu nghỉ phép ngay trong Rippling và Rippling phê duyệt (CR-20261027-001). Cùng một webhook được phân nhánh tới hai Zap độc lập: Flow 35 (UC này — sổ cái) và Flow 13 Path A (UC_9.2 — gia hạn stagnation_deadline, khóa tài khoản, thông báo). Stack Trading không tạo bản ghi nghỉ phép trong Rippling.
(Nguồn: Zapier Integration V7.pdf §Flow 35 · CR-20261027-001 · UC_9.2 §2.1)
3. Điều kiện tiên quyết
| ID | Điều kiện tiên quyết |
|---|---|
| P1 | Webhook subscription của Rippling cho Time Off Request Approved trỏ tới Zapier Flow 35. |
| P2 | Payload chứa Rippling Request ID và số ngày đã được phê duyệt (Days_Approved). |
| P3 | Worker được ánh xạ tới một trader W-2 đang hoạt động của Stack Trading (Employment_Type = 'W-2'). |
| P4 | Contract StackTradingLedger và owner wallet khả dụng (xem UC_9.11 P4). |
4. Hậu điều kiện
| # | Hậu điều kiện |
|---|---|
| 1 | Một event CorporateMetricVerified với metricCategory = "Corporate Benefits". |
| 2 | tx_hash được lưu trong PostgreSQL; việc thực thi được ghi vào BPS_Audit_Log. |
| 3 | Rippling Request ID được đánh dấu đã xử lý (không mint trùng). |
| 4 | Zap kết thúc. Luồng này không thay đổi stagnation_deadline hay trạng thái giao dịch. |
5. Luồng cơ bản
Bước 1 — Nhận webhook (Zapier).
Bước 2 — Filter: trader W-2 đang hoạt động. Chỉ tiếp tục nếu payload tương ứng với trader W-2 đang hoạt động của Stack Trading; ngược lại dừng (Alt-2).
(Nguồn: Zapier Integration V7.pdf §Flow 35)
Bước 3 — Kiểm tra dedup trên Rippling Request ID. Nếu đã xử lý → Alt-1.
Bước 4 — Gọi POST /api/ledger/record
| Trường | Giá trị |
|---|---|
trader_id | UUID trader toàn cục |
metric_category | "Corporate Benefits" (cố định) |
metric_value | "[Days_Approved] Days PTO Approved (Paid Time Off)" — Days_Approved là số nguyên (chỉ tính ngày trọn), ví dụ "3 Days PTO Approved (Paid Time Off)" |
date | Hôm nay (ISO 8601 UTC) |
receipt_hash | Rippling Request ID |
(Nguồn: Zapier Integration V7.pdf §Flow 35)
Bước 5 — Middleware mint trên Polygon — giống hệt UC_9.11 Bước 5 (xác thực JWT → Ethers.js wallet → recordMetric() qua Alchemy → chờ confirmation → lưu tx_hash → HTTP 200).
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §Record Immutable Ledger)
Bước 6 — Kết thúc Zap.
6. Luồng thay thế
Alt-1 — Gửi trùng: trả về HTTP 200 cùng tx_hash đã lưu; không mint lần hai.
Alt-2 — Không phải trader W-2 đang hoạt động: Zap dừng ở filter; không gọi sổ cái.
7. Luồng ngoại lệ
Exc-1 (JWT không hợp lệ → HTTP 401) và Exc-2 (lỗi Polygon RPC → non-2xx, không đánh dấu đã xử lý, Zapier retry) — giống UC_9.11 §7.
Exc-3 — Kỳ nghỉ sau đó bị hủy trong Rippling
- Event on-chain là bất biến và không thể đảo ngược. Không ghi bút toán sổ cái điều chỉnh.
- Việc mở lại tài khoản giao dịch và điều chỉnh
stagnation_deadlinedo Ops xử lý thủ công — xem UC_9.2 Alt-5.
(Nguồn: QnA_BACKGROUND_MECHANISMS_CRON.md [BG-017], [BG-002] — bảng tính của khách hàng 2026-11-04)
8. Quy tắc nghiệp vụ
BR_9.12.1: Chỉ ghi sổ cái
Luồng 35 chỉ ghi event PTO lên Sổ cái Bất biến (Immutable Ledger). Việc gia hạn stagnation_deadline, TRIM_TO_COMPLIANCE và khóa thực thi (Rithmic / MT5 / TraderEvolution) thuộc Luồng 13 (UC_9.2 Steps A-3+) và không được thực thi lại tại đây.
(Nguồn: Zapier Integration V7.pdf §Flow 35 — hành động duy nhất là POST /api/ledger/record rồi "Terminate Zap"; §Flow 13)
BR_9.12.2: Ánh xạ sổ cái cố định
metric_category = "Corporate Benefits"; metric_value = "[Days_Approved] Days PTO Approved (Paid Time Off)", với Days_Approved là số nguyên — PTO chỉ được tính theo ngày trọn (không có nửa ngày); receipt_hash = Rippling Request ID.
(Nguồn: Zapier Integration V7.pdf §Flow 35; QnA_BACKGROUND_MECHANISMS_CRON.md [BG-018] — bảng tính của khách hàng 2026-11-04)
BR_9.12.3: Phạm vi — Chỉ W-2 đang hoạt động
Thời gian tạm nghỉ (hiatus) của contractor quốc tế là không lương và không được Luồng 35 ghi nhận.
(Nguồn: Zapier Integration V7.pdf §Flow 35; §Flow 13 Path B)
BR_9.12.4: Một lần phê duyệt = Một event sổ cái
Rippling Request ID là khóa idempotency (chống trùng lặp). Giống BR_9.11.3.
BR_9.12.5: Lưu trữ audit
Giống BR_9.11.7 (5 năm + 1 tháng; > 25 tháng → cold storage).
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §Audit Requirements)
9. Tiêu chí chấp nhận
| AC_ID | Luồng | Cho trước | Thì | Loại |
|---|---|---|---|---|
| AC-9.12-01 | Happy path | Rippling phê duyệt 3 ngày PTO cho trader W-2 đang hoạt động | Một event: metricCategory = "Corporate Benefits", metricValue = "3 Days PTO Approved (Paid Time Off)", verificationHash = Request ID; tx_hash được lưu | Integration |
| AC-9.12-02 | Trùng lặp | Cùng Request ID được gửi hai lần | Chỉ một event on-chain; lần gọi thứ hai HTTP 200 cùng tx_hash gốc | Integration |
| AC-9.12-03 | Contractor | Payload của contractor quốc tế | Zap dừng; không gọi sổ cái | Integration |
| AC-9.12-04 | Không có side effect | AC-9.12-01 hoàn tất | stagnation_deadline và trạng thái giao dịch trên broker không bị Luồng 35 thay đổi (nếu có thay đổi thì chỉ đến từ Luồng 13) | Integration |
| AC-9.12-05 | JWT sai | Chữ ký không hợp lệ | HTTP 401; không mint | Security |
| AC-9.12-06 | Số ngày nguyên | Rippling phê duyệt 2 ngày PTO cho trader W-2 đang hoạt động | metricValue = "2 Days PTO Approved (Paid Time Off)" — giá trị số nguyên, không có phần thập phân | Integration |
10. Tham chiếu API
POST /api/ledger/record — schema như UC_9.11 §10.
{
"trader_id": "94a2b1c8-3f4e-4b21-a123-890abcdef123",
"metric_category": "Corporate Benefits",
"metric_value": "3 Days PTO Approved (Paid Time Off)",
"date": "2026-03-15T18:00:00Z",
"receipt_hash": "rip_pto_99120482"
}11. Câu hỏi mở
| ID | Ưu tiên | Câu hỏi | Giả định |
|---|---|---|---|
| Q1 | M | Nếu một PTO đã được phê duyệt sau đó bị hủy/rút ngắn trong Rippling, có nên mint một event sổ cái điều chỉnh (ví dụ "PTO Cancelled") không? | ✅ Đã trả lời 2026-11-04 — không ghi bút toán sổ cái điều chỉnh; Ops xử lý thủ công (Exc-3, [BG-017]). |
| Q2 | — | ✅ Đã đóng bởi CR-20261027-001 — toàn bộ PTO của W-2 được yêu cầu trực tiếp trong Rippling; Luồng 35 ghi nhận mọi lần phê duyệt. | |
| Q3 | L | Days_Approved có thể là số lẻ (nửa ngày)? Định dạng? | ✅ Đã trả lời 2026-11-04 — chỉ số ngày nguyên, không có nửa ngày (BR_9.12.2, [BG-018]). |
12. Lịch sử thay đổi
| Ngày | Phiên bản | Thay đổi | Trước | Sau | Nguồn |
|---|---|---|---|---|---|
| 2026-11-05 | v1.2 | Header; §5 dòng metric_value; §7 Exc-3; §8 BR_9.12.2; §9 AC-9.12-06 (mới); §11 Q1, Q3 | Giả định có thể có nửa ngày (0.5 Days…); bút toán điều chỉnh khi hủy còn mở | Chỉ số ngày nguyên; không ghi bút toán sổ cái điều chỉnh, Ops xử lý thủ công (UC_9.2 Alt-5) | Bảng tính của khách hàng 2026-11-04 [BG-017] [BG-018] [BG-002] |
| 2026-10-27 | v1.1 | §1 Tài liệu tham chiếu, UC liên quan; §2 Trigger upstream; đóng Q2 | Upstream được mô tả là "Flow 13 Step A-2 ghi nhận kỳ nghỉ trong Rippling" | W-2 yêu cầu nghỉ phép trong Rippling; cùng một webhook kích hoạt Flow 35 và Flow 13 Path A | CR-20261027-001 |
| 2026-10-27 | v1.0 | Tạo tài liệu ban đầu | — | Đặc tả đầy đủ: trigger, filter W-2, dedup, mint sổ cái, BR_9.12.1–5, AC-9.12-01–05 | Zapier Integration V7.pdf §Flow 35, §Flow 13; RFQ_ Stack Trading Prop Tech V7.pdf §Record Immutable Ledger |
UC_9.13 — Luồng 36: Đồng bộ Sổ cái Retainer Dots
| Trường | Giá trị |
|---|---|
| UC_ID | UC_9.13 |
| Phiên bản | v1.0 |
| Cập nhật lần cuối | 2026-10-27 |
| BA PiC | linh.nguyen34 |
| Danh mục | BACKGROUND MECHANISMS & CRON JOBS |
| Mô-đun | Luồng 36 (UC_4.15.2) |
| Chức năng | Đồng bộ Sổ cái Retainer Dots |
| Tài liệu tham chiếu | Zapier Integration V7.pdf (§Flow 36; §7.3 Flow 36 Retainer Trigger — Option B) · RFQ_ Stack Trading Prop Tech V7.pdf (§Record Immutable Ledger; §3.6.1; §Audit Requirements) · StackTrading_Backend_Technical_Proposal_v1.6.docx (§Option B — Dots ✓) |
1. Danh mục UC
| UC_ID | Chức năng | Mô tả |
|---|---|---|
| UC_9.13 | Luồng 36 — Đồng bộ Sổ cái Retainer Dots | Khi Dots xử lý retainer hàng tháng cho contractor quốc tế đang hoạt động (Level 6+), event đi vào qua hardened ingress của Middleware Hub (Option B), được phát tới Zapier dưới dạng RETAINER_PROCESSED, và Zapier gọi POST /api/ledger/record để mint một event Payroll Executed ($[Total_Amount] Retainer Paid ([billing_country])) trên Polygon PoS. |
UC_9.13 — Luồng 36: Đồng bộ Sổ cái Retainer Dots
| Trường | Giá trị |
|---|---|
| Use Case | Đồng bộ Sổ cái Retainer Dots |
| Mô tả | Công bố bằng chứng thanh toán retainer cho contractor quốc tế lên Sổ cái Bất biến (Immutable Ledger). Một retainer Dots đã xử lý = một event CorporateMetricVerified với Dots Transaction ID làm verificationHash. |
| Tác nhân | Hệ thống (Dots → Middleware Hub → Zapier → Middleware Hub → Polygon PoS) |
| Zapier Flow | Flow 36 |
| Nguồn trigger | Event của Middleware Hub RETAINER_PROCESSED (nguồn: Dots, Monthly Retainer Processed) |
| 3rd Party | Dots · Zapier · Alchemy RPC · Polygon PoS |
| Middleware Endpoints | POST /webhooks/retainer/dots (ingress) · POST /zapier/events/retainer_processed (emit) · POST /api/ledger/record |
| UC liên quan | UC_9.11 · UC_9.2 (hiatus Path B tạm ngưng / tiếp tục retainer) · UC_4.15.2 |
2. Trigger
| Giai đoạn | Thành phần | Hành động |
|---|---|---|
| Ingress | POST /webhooks/retainer/dots | Xác thực chữ ký Dots / allowlist; thực thi idempotency (chống trùng lặp) qua provider_event_id; ghi vào event_log trước khi emit. |
| Emit | POST /zapier/events/retainer_processed | Phát event chuẩn hóa RETAINER_PROCESSED (source: Dots) tới Zapier. |
| Zap | Flow 36 | Được kích hoạt bởi event của Middleware Hub. |
(Nguồn: Zapier Integration V7.pdf §7.3 Option B · StackTrading_Backend_Technical_Proposal_v1.6.docx §Option B provider list — Dots ✓)
3. Điều kiện tiên quyết
| ID | Điều kiện tiên quyết |
|---|---|
| P1 | Dots webhook được cấu hình trỏ tới POST /webhooks/retainer/dots. |
| P2 | Payload chứa Dots Transaction ID, Total_Amount, và tham chiếu người nhận có thể ánh xạ về trader_id. |
| P3 | Trader là contractor quốc tế đang hoạt động ở Level 6+. |
| P4 | billing_country khả dụng trên bảng Users. |
| P5 | Contract sổ cái và owner wallet khả dụng (xem UC_9.11 P4). |
4. Hậu điều kiện
| # | Hậu điều kiện |
|---|---|
| 1 | Event Dots được lưu bền vững trong event_log (durable, có thể replay). |
| 2 | Một event CorporateMetricVerified với metricCategory = "Payroll Executed". |
| 3 | tx_hash được lưu trong PostgreSQL; việc thực thi được ghi vào BPS_Audit_Log. |
| 4 | Zap kết thúc. |
5. Luồng cơ bản
Bước 1 — Ingress (Middleware). Nhận Dots webhook tại POST /webhooks/retainer/dots → xác thực chữ ký → dedup trên provider_event_id (trùng → Alt-1) → ghi event_log.
Bước 2 — Emit (Middleware). POST /zapier/events/retainer_processed với payload đã chuẩn hóa.
(Nguồn: Zapier Integration V7.pdf §7.3 Option B)
Bước 3 — Filter (Zapier). Chỉ tiếp tục nếu payload tương ứng với contractor quốc tế đang hoạt động của Stack Trading (Level 6+). Ngược lại dừng (Alt-2).
(Nguồn: Zapier Integration V7.pdf §Flow 36)
Bước 4 — Gọi POST /api/ledger/record
| Trường | Giá trị |
|---|---|
trader_id | UUID trader toàn cục |
metric_category | "Payroll Executed" (cố định) |
metric_value | "$[Total_Amount] Retainer Paid ([billing_country])" — ví dụ "$2,500.00 Retainer Paid (UK)" |
date | Hôm nay (ISO 8601 UTC) |
receipt_hash | Dots Transaction ID |
(Nguồn: Zapier Integration V7.pdf §Flow 36)
Bước 5 — Middleware mint trên Polygon — giống UC_9.11 Bước 5.
Bước 6 — Kết thúc Zap.
6. Luồng thay thế
Alt-1 — Dots gửi trùng: dedup tại ingress trên provider_event_id trả về HTTP 200 cho Dots; không emit gì tới Zapier.
Alt-2 — Không phải contractor quốc tế đang hoạt động L6+: Zap dừng ở filter; không gọi sổ cái.
7. Luồng ngoại lệ
Exc-1 — Chữ ký Dots không hợp lệ: ingress từ chối (HTTP 401); không có dòng event_log; dòng audit REJECTED.
Exc-2 — Zapier JWT không hợp lệ / lỗi Polygon RPC: giống UC_9.11 Exc-1 / Exc-2. Vì event đã nằm trong event_log, Ops có thể replay.
8. Quy tắc nghiệp vụ
BR_9.13.1: Phạm vi — contractor quốc tế đang hoạt động, Level 6+
Dưới Level 6, trader theo mô hình thưởng theo yêu cầu (on-demand bonus; không có retainer). Lương W-2 của Mỹ thuộc Luồng 34.
(Nguồn: Zapier Integration V7.pdf §Flow 36)
BR_9.13.2: Ánh xạ sổ cái cố định
metric_category = "Payroll Executed"; metric_value = "$[Total_Amount] Retainer Paid ([billing_country])"; receipt_hash = Dots Transaction ID.
(Nguồn: Zapier Integration V7.pdf §Flow 36)
BR_9.13.3: Hardened Ingress (Option B) là bắt buộc đối với Dots
Dots webhook đi vào qua Middleware Hub (xác thực chữ ký, dedup provider_event_id, ghi event_log trước khi emit). Dots không được gọi Zapier trực tiếp.
(Nguồn: Zapier Integration V7.pdf §7.3 · StackTrading_Backend_Technical_Proposal_v1.6.docx §Option B)
BR_9.13.4: Một retainer = Một event sổ cái
Dedup tại ingress (provider_event_id) và tại POST /api/ledger/record (receipt_hash). Không có bản ghi sổ cái trùng lặp.
(Nguồn: Zapier Integration V7.pdf §7.3 — "Ensure downstream actions are idempotent (no duplicate ledger entries / payouts / accounting entries)")
BR_9.13.5: Lưu trữ audit
Giống BR_9.11.7.
9. Tiêu chí chấp nhận
| AC_ID | Luồng | Cho trước | Thì | Loại |
|---|---|---|---|---|
| AC-9.13-01 | Happy path | Retainer Dots $2,500.00 được xử lý cho contractor L6+ đang hoạt động, billing_country = UK | Có dòng event_log; event Zapier được emit; một event on-chain "$2,500.00 Retainer Paid (UK)", verificationHash = Dots Transaction ID | Integration |
| AC-9.13-02 | Trùng lặp | Dots retry cùng một event | Ingress trả về HTTP 200; không emit lần hai; một event on-chain | Integration |
| AC-9.13-03 | Dưới L6 / W-2 | Payload của trader L5 hoặc W-2 | Zap dừng; không gọi sổ cái | Integration |
| AC-9.13-04 | Chữ ký Dots sai | Chữ ký không hợp lệ | HTTP 401; không có dòng event_log | Security |
| AC-9.13-05 | Replay | RPC thất bại cho event X | Ops replay X từ event_log; kết quả là đúng một event on-chain | Integration |
10. Tham chiếu API
POST /api/ledger/record — schema như UC_9.11 §10.
{
"trader_id": "94a2b1c8-3f4e-4b21-a123-890abcdef123",
"metric_category": "Payroll Executed",
"metric_value": "$2,500.00 Retainer Paid (UK)",
"date": "2026-03-31T18:00:00Z",
"receipt_hash": "dots_txn_441029381"
}11. Câu hỏi mở
| ID | Ưu tiên | Câu hỏi | Giả định |
|---|---|---|---|
| Q1 | M | Định dạng hiển thị billing_country — ISO alpha-2 (GB) hay nhãn (UK)? Ví dụ trong V7 hiển thị UK. | ISO alpha-2 từ bảng Users; GB hiển thị nguyên trạng. Cần xác nhận. |
| Q2 | M | Retainer một lần theo tỷ lệ (prorated; tăng Level 7+ giữa tháng) — có kích hoạt cùng Dots event và được ghi nhận không? | Có, ghi nhận như một event riêng. |
| Q3 | L | Luồng 36 có cập nhật bản ghi invoice nào không (ví dụ đánh dấu paid)? Không có trong V7. | Không — chỉ ghi sổ cái. |
12. Lịch sử thay đổi
| Ngày | Phiên bản | Thay đổi | Trước | Sau | Nguồn |
|---|---|---|---|---|---|
| 2026-10-27 | v1.0 | Tạo tài liệu ban đầu | — | Đặc tả đầy đủ: ingress Option B, filter contractor L6+, mint sổ cái, BR_9.13.1–5, AC-9.13-01–05 | Zapier Integration V7.pdf §Flow 36, §7.3; Backend Proposal v1.6 §Option B |
UC_9.14 — Luồng 32: Velocity Reward Engine
| Trường | Giá trị |
|---|---|
| UC_ID | UC_9.14 |
| Phiên bản | v1.1 |
| Cập nhật lần cuối | 2026-11-05 |
| BA PiC | linh.nguyen34 |
| Danh mục | STAGE 9: GROWTH & REFERRAL |
| Mô-đun | Referral Loyalty Loop |
| Chức năng | Luồng 32 — Velocity Reward Engine |
| Tài liệu tham chiếu | RFQ_ Stack Trading Prop Tech V7.pdf (§Velocity Reward Engine) · Zapier Integration V7.pdf (§Flow 32) |
1. Danh mục UC
| UC_ID | Chức năng | Mô tả |
|---|---|---|
| UC_9.14 | Luồng 32 — Velocity Reward Engine | Phát hành mã khuyến mãi EVAL_L1 giảm giá 100% tại các mốc giới thiệu (3 và 6 lượt giới thiệu thành công). Được kích hoạt bởi Everflow S2S webhook (EV_MILESTONE_3_REFS / EV_MILESTONE_6_REFS). Thực thi pipeline 4 bước: cổng idempotency (chống trùng lặp) → kiểm tra sanctions → tạo mã khuyến mãi → email Klaviyo → thông báo Discord. Có audit trail tuân thủ CFTC/NFA. |
UC_9.14 — Luồng 32: Velocity Reward Engine
| Trường | Giá trị |
|---|---|
| Use Case | Velocity Reward Engine |
| Mô tả | Tại các mốc giới thiệu 3 hoặc 6 lượt giới thiệu thành công, Everflow gửi một S2S webhook (EV_MILESTONE_3_REFS hoặc EV_MILESTONE_6_REFS). Middleware thực thi: (1) cổng idempotency qua PostgreSQL SELECT ... FOR UPDATE + UNIQUE index trên provider_event_id, (2) sàng lọc sanctions đối chiếu Compliance_geo_restrictions, (3) tạo mã khuyến mãi (EVAL_L1, giảm giá 100%) và chèn vào bảng promo_codes, (4) email Klaviyo Velocity_Reward_Unlocked, (5) thông báo Discord tới #trading-floor-announcements qua Redis queue với giới hạn 5 msg/5 s. Audit được ghi vào bps_audit_logs và sao chép sang S3 Glacier Object Lock (WORM) để lưu trữ 5 năm theo CFTC/NFA. |
| Zapier Flow | Flow 32 (Velocity Reward Engine) |
| Nguồn trigger | Everflow S2S Webhook |
| 3rd Party | Everflow · Klaviyo · Discord Bot API |
| Middleware Endpoint | POST /api/referral/velocity-reward |
| Release | Release 2 (QnA_BACKGROUND_MECHANISMS_CRON.md [BG-012] — client sheet 2026-11-04) |
2. Trigger
Sự kiện: Middleware nhận Everflow S2S webhook tại POST /api/referral/velocity-reward.
| Tên sự kiện | Mốc | Ý nghĩa |
|---|---|---|
EV_MILESTONE_3_REFS | 3 lượt giới thiệu thành công | Referrer đạt bậc velocity 3 lượt giới thiệu |
EV_MILESTONE_6_REFS | 6 lượt giới thiệu thành công | Referrer đạt bậc velocity 6 lượt giới thiệu |
Everflow gửi webhook khi số lượt giới thiệu của origin_user_id vượt ngưỡng mốc. Middleware là điểm vào duy nhất — luồng không đi qua Zapier.
(Nguồn: Zapier Integration V7.pdf §Flow 32)
3. Điều kiện tiên quyết
| ID | Điều kiện tiên quyết |
|---|---|
| P1 | Nhận được Everflow S2S webhook với provider_event_id, origin_user_id và event_type hợp lệ (EV_MILESTONE_3_REFS hoặc EV_MILESTONE_6_REFS) |
| P2 | provider_event_id chưa từng được xử lý (cổng idempotency chưa bị kích hoạt) |
| P3 | origin_user_id ứng với một user đã biết trong hệ thống |
| P4 | billing_country / billing_region của origin_user_id không có trong Compliance_geo_restrictions (sanctions hợp lệ) |
4. Hậu điều kiện
| # | Hậu điều kiện |
|---|---|
| 1 | Bản ghi khóa idempotency được ghi: provider_event_id được đánh dấu là đã xử lý trong bảng idempotency |
| 2 | Bản ghi mã khuyến mãi được chèn vào bảng promo_codes: applicable_product_id = 'EVAL_L1', discount_percent = 100, is_used = false, origin_user_id được gán |
| 3 | Email giao dịch Klaviyo Velocity_Reward_Unlocked được gửi tới origin_user_id, chứa mã khuyến mãi |
| 4 | Tin nhắn Discord được broadcast tới #trading-floor-announcements qua Redis queue |
| 5 | Bản ghi audit được ghi vào bps_audit_logs |
| 6 | Bản ghi audit được sao chép sang S3 Glacier Object Lock (WORM) — lưu trữ 5 năm + 1 tháng |
5. Luồng cơ bản
Bước 1 — Cổng Idempotency
Middleware lấy khóa mức hàng (row-level lock) trước mọi thao tác ghi:
BEGIN;
SELECT id FROM idempotency_log
WHERE provider_event_id = :provider_event_id
FOR UPDATE;- Nếu hàng tồn tại: trả về
HTTP 200 OKvới{ "status": "duplicate", "skipped": true }— không xử lý tiếp. - Nếu hàng không tồn tại: chèn hàng mới với
provider_event_id,origin_user_id,event_type,received_at; giữ khóa cho đến khi commit ở Bước 3.
UNIQUE index trên provider_event_id là lớp bảo vệ cứng chống chèn đồng thời.
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §Velocity Reward Engine)
Bước 2 — Cổng Sanctions
SELECT 1 FROM Compliance_geo_restrictions
WHERE country_code = :billing_country
AND (region_code = :billing_region OR region_code IS NULL);- Nếu tìm thấy hàng: trả về
HTTP 403 Forbiddenvới{ "error": "SANCTIONS_BLOCKED", "user_id": :origin_user_id }. Hủy luồng. Ghi sự kiện sanctions-block vàobps_audit_logs. - Nếu không tìm thấy hàng: chuyển sang Bước 3.
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §Velocity Reward Engine)
Bước 3 — Tạo mã khuyến mãi
INSERT INTO promo_codes (
code,
applicable_product_id,
discount_percent,
is_used,
origin_user_id,
event_type,
created_at
) VALUES (
:generated_code, -- cryptographically random alphanumeric, 16 chars
'EVAL_L1',
100,
false,
:origin_user_id,
:event_type,
NOW()
);
COMMIT; -- releases FOR UPDATE lock from Step 1generated_code là chuỗi chữ-số ngẫu nhiên mật mã học dài 16 ký tự, duy nhất nhờ ràng buộc UNIQUE trên cột code.
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §Velocity Reward Engine)
Bước 4 — Email Klaviyo
Middleware gọi Klaviyo Transactional API:
- Template:
Velocity_Reward_Unlocked - Đến: địa chỉ email của
origin_user_id - Các trường payload:
promo_code,event_type,milestone_count(3 hoặc 6),applicable_product(EVAL_L1)
Khi Klaviyo API lỗi: áp dụng retry với exponential backoff (xem Exc-1). Không chặn việc gửi Discord khi các retry Klaviyo đang diễn ra.
(Nguồn: Zapier Integration V7.pdf §Flow 32)
Bước 5 — Thông báo Discord
Đưa tin nhắn Discord vào Redis queue cho #trading-floor-announcements:
- Giới hạn tốc độ: 5 tin nhắn mỗi 5 giây (do queue consumer thực thi).
- Triển khai: chỉ dùng
fetch()gốc — axios bị cấm tuyệt đối. - Nội dung tin nhắn: Thông báo thành tích đạt mốc của
origin_user_id(tên hiển thị hoặc handle ẩn danh theo chính sách cộng đồng).
Queue consumer lấy tin ra khỏi queue và gọi Discord Bot API trong giới hạn tốc độ cho phép.
(Nguồn: Zapier Integration V7.pdf §Flow 32)
Bước 6 — Audit Log
Ghi vào bps_audit_logs:
{
"event_type": "VELOCITY_REWARD_ISSUED",
"origin_user_id": "<id>",
"provider_event_id": "<id>",
"milestone": 3,
"promo_code": "<code>",
"timestamp": "<ISO-8601>"
}Sao chép sang S3 Glacier Object Lock (WORM). Thời gian lưu trữ: 5 năm + 1 tháng. 2 năm đầu lưu ở tầng truy cập tức thì. Sau 2 năm: Glacier Deep Archive. Theo yêu cầu lưu trữ hồ sơ của CFTC/NFA.
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §Velocity Reward Engine)
6. Luồng thay thế
Alt-1 — Sự kiện trùng lặp (provider_event_id đã được xử lý)
- Cổng idempotency phát hiện hàng đã tồn tại (Bước 1).
- Trả về
HTTP 200 OK:{ "status": "duplicate", "skipped": true }. - Không tạo mã khuyến mãi. Không gửi email. Không gửi Discord. Không có sự kiện audit (sự kiện đã được ghi ở lần xử lý đầu tiên).
- Luồng kết thúc bình thường.
7. Luồng ngoại lệ
Exc-1 — Klaviyo API lỗi (Bước 4)
Middleware áp dụng exponential backoff:
delay(attempt) = 5s × 2^attempt
attempt 1 → 10s
attempt 2 → 20s
attempt 3 → 40sSau 3 lần retry thất bại (tổng cửa sổ backoff ~70s):
- Ghi một entry
WARNvàobps_audit_logs:{ "event": "KLAVIYO_RETRY_EXHAUSTED", "promo_code": "<code>", "origin_user_id": "<id>" }. - Gửi cảnh báo Slack SEV-2 tới
#dev_ops:"⚠️ [SEV-2] Velocity Reward email failed after 3 retries for user <id>. Promo code <code> was issued but email not delivered. Manual resend required." - Thông báo Discord (Bước 5) vẫn tiếp tục bất kể Klaviyo lỗi — mã khuyến mãi đã được commit.
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §Velocity Reward Engine)
Exc-2 — Discord API lỗi (Bước 5)
Redis queue consumer retry theo hướng dẫn rate-limit của Discord Bot. Nếu kênh Discord không khả dụng (non-2xx sau các lần retry):
- Ghi
WARNvàobps_audit_logs. - Cảnh báo Slack tới
#dev_ops:"⚠️ [SEV-2] Discord broadcast failed for Velocity Reward event <provider_event_id>.". - Không chặn luồng — mã khuyến mãi và email không bị ảnh hưởng.
Exc-3 — Bị chặn bởi sanctions (Bước 2)
- Trả về
HTTP 403 Forbidden. - Ghi bản ghi sanctions-block vào
bps_audit_logsvớievent_type = 'VELOCITY_REWARD_SANCTIONS_BLOCKED'. - Không tạo mã khuyến mãi. Không gửi email. Không gửi Discord.
- Việc sao chép sự kiện chặn sang S3 Glacier vẫn được áp dụng (audit trail CFTC/NFA).
Exc-4 — Xung đột khi INSERT promo_codes (ràng buộc UNIQUE trên code)
- Middleware tạo lại
codengẫu nhiên mới và retry INSERT (tối đa 3 lần). - Nếu cả 3 lần retry đều thất bại: trả về
HTTP 500, ghi logERROR, gửi cảnh báo Slack SEV-2 tới#dev_ops. - Xác suất xung đột với chuỗi chữ-số ngẫu nhiên 16 ký tự là không đáng kể nhưng vẫn phải có cơ chế bảo vệ này.
8. Quy tắc nghiệp vụ
BR_9.14.1: Idempotency là bắt buộc — PostgreSQL FOR UPDATE + UNIQUE Index
Mọi Everflow webhook đến PHẢI được kiểm tra xem đã xử lý chưa bằng SELECT ... FOR UPDATE trên bảng idempotency trước khi ghi bất cứ thứ gì. Ràng buộc UNIQUE trên provider_event_id là lớp bảo vệ cứng. Sự kiện trùng lặp PHẢI trả về HTTP 200 OK một cách im lặng — không phải HTTP 4xx — để tránh retry storm từ Everflow.
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §Velocity Reward Engine)
BR_9.14.2: Cổng Sanctions chạy trước khi tạo mã khuyến mãi
Việc kiểm tra Compliance_geo_restrictions trên billing_country/billing_region PHẢI chạy trước khi bất kỳ mã khuyến mãi nào được ghi vào promo_codes. User bị sanctions tuyệt đối không được nhận mã khuyến mãi hay email, kể cả khi sự kiện mốc của Everflow là hợp lệ.
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §Velocity Reward Engine)
BR_9.14.3: Tham số mã khuyến mãi là cố định
Mọi mã khuyến mãi do Flow 32 phát hành PHẢI có:
applicable_product_id = 'EVAL_L1'discount_percent = 100is_used = false(trạng thái ban đầu)
Không có sản phẩm hay giá trị giảm giá nào khác hợp lệ cho luồng này.
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §Velocity Reward Engine)
BR_9.14.4: Triển khai Discord — chỉ dùng native fetch(), không dùng axios
Mọi lời gọi Discord API trong Flow 32 PHẢI dùng fetch() API gốc. axios bị cấm tuyệt đối. Quy tắc này áp dụng cho mọi lời gọi Discord Bot từ Middleware trong luồng này.
(Nguồn: Zapier Integration V7.pdf §Flow 32)
BR_9.14.5: Giới hạn tốc độ Discord — 5 tin nhắn mỗi 5 giây qua Redis Queue
Các broadcast Discord PHẢI được đưa vào Redis queue và tiêu thụ với tốc độ tối đa 5 tin nhắn mỗi 5 giây. Cấm các lời gọi Discord đồng bộ trực tiếp bỏ qua queue để tránh bị ban do vượt rate-limit của API.
(Nguồn: Zapier Integration V7.pdf §Flow 32)
BR_9.14.6: Audit Trail CFTC/NFA — S3 Glacier WORM, lưu trữ 5 năm + 1 tháng
Mọi sự kiện audit (phát hành thưởng, sanctions block, hết số lần retry) PHẢI được ghi vào bps_audit_logs và sao chép sang S3 Glacier Object Lock (WORM). Thời gian lưu trữ: 5 năm + 1 tháng. 2 năm đầu ở tầng truy cập tức thì. Sau 2 năm: Glacier Deep Archive. Chính sách Object Lock cấm xóa thủ công.
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §Velocity Reward Engine)
BR_9.14.7: Exponential Backoff — Cơ số 5s, 3 lần Retry, SEV-2 khi cạn Retry
Lỗi API bên thứ ba (Klaviyo, Discord) PHẢI áp dụng exponential backoff: delay = 5s × 2^attempt (10s, 20s, 40s). Sau 3 lần retry thất bại, gửi cảnh báo Slack SEV-2 tới #dev_ops. Luồng không bị crash — các bước phía sau đã hoàn tất không bị rollback.
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §Velocity Reward Engine)
BR_9.14.8: Chỉ có hai sự kiện mốc hợp lệ
Chỉ EV_MILESTONE_3_REFS và EV_MILESTONE_6_REFS là trigger hợp lệ cho Flow 32. Mọi giá trị event_type khác nhận được tại POST /api/referral/velocity-reward PHẢI trả về HTTP 400 Bad Request và được ghi log là INVALID_EVENT_TYPE. Mỗi mốc (3 và 6) phát hành đúng một mã EVAL_L1 100% dùng một lần.
(Nguồn: Zapier Integration V7.pdf §Flow 32; QnA_STAGE1_CHECKOUT_ONBOARDING.md STAGE1-064; UC_9.x_Client_Questions.md F32-01 — mốc 3/6 được xác nhận 2026-11-05)
9. Tiêu chí chấp nhận
| AC_ID | Luồng | Cho trước | Thì | Loại |
|---|---|---|---|---|
| AC-9.14-01 | Happy path — mốc 3 lượt giới thiệu | Webhook EV_MILESTONE_3_REFS hợp lệ, sanctions hợp lệ, lần xuất hiện đầu tiên | Mã khuyến mãi được chèn (EVAL_L1, 100%); email Velocity_Reward_Unlocked được gửi; broadcast Discord được đưa vào queue; audit log được ghi; trả về HTTP 200 OK | Integration |
| AC-9.14-02 | Happy path — mốc 6 lượt giới thiệu | Webhook EV_MILESTONE_6_REFS hợp lệ, sanctions hợp lệ, lần xuất hiện đầu tiên | Giống AC-9.14-01; event_type = 'EV_MILESTONE_6_REFS' trong bản ghi promo_codes | Integration |
| AC-9.14-03 | Idempotency — sự kiện trùng lặp | Cùng một provider_event_id nhận được hai lần | Lời gọi thứ hai trả về HTTP 200 { "status": "duplicate", "skipped": true }; không có mã khuyến mãi thứ hai; không có email thứ hai | Integration |
| AC-9.14-04 | Trùng lặp đồng thời | Hai request đồng thời với cùng provider_event_id | Chỉ đúng một mã khuyến mãi được tạo; transaction thứ hai bị chặn bởi SELECT ... FOR UPDATE + ràng buộc UNIQUE | Integration |
| AC-9.14-05 | Sanctions block | billing_country của user khớp Compliance_geo_restrictions | Trả về HTTP 403; không có mã khuyến mãi; không có email; sự kiện sanctions-block có trong bps_audit_logs | Integration |
| AC-9.14-06 | Cạn retry Klaviyo | Klaviyo trả về non-2xx cho cả 3 lần retry | Cảnh báo Slack SEV-2 tới #dev_ops; mã khuyến mãi đã commit vẫn hợp lệ; broadcast Discord vẫn được gửi | Integration |
| AC-9.14-07 | Giới hạn tốc độ Discord | 10 sự kiện thưởng đến trong vòng 1 giây | Redis queue consumer gửi ≤ 5 tin nhắn mỗi 5 s; không có HTTP 429 từ Discord | Integration |
| AC-9.14-08 | Discord dùng native fetch | Lời gọi Discord trong triển khai Flow 32 | Không có import axios trong đường đi của request | Code review |
| AC-9.14-09 | event_type không hợp lệ | Webhook với event_type = 'EV_UNKNOWN' | HTTP 400 Bad Request; INVALID_EVENT_TYPE được ghi log; không tạo mã khuyến mãi | Integration |
| AC-9.14-10 | Audit WORM | Sự kiện thưởng được xử lý thành công | Hàng bps_audit_logs có mặt; xác nhận đã sao chép S3 Glacier Object Lock; Object Lock mode = COMPLIANCE | Integration |
10. Mô hình dữ liệu
Bảng: promo_codes
| Cột | Kiểu | Ràng buộc | Mô tả |
|---|---|---|---|
id | BIGSERIAL | PK | Khóa chính tự tăng |
code | VARCHAR(32) | UNIQUE NOT NULL | Chuỗi chữ-số ngẫu nhiên mật mã học 16 ký tự |
applicable_product_id | VARCHAR(64) | NOT NULL | Cố định: 'EVAL_L1' cho Flow 32 |
discount_percent | SMALLINT | NOT NULL | Cố định: 100 cho Flow 32 |
is_used | BOOLEAN | NOT NULL DEFAULT false | Mã đã được sử dụng hay chưa |
origin_user_id | BIGINT | FK → users.id | Referrer nhận được phần thưởng |
event_type | VARCHAR(64) | NOT NULL | 'EV_MILESTONE_3_REFS' hoặc 'EV_MILESTONE_6_REFS' |
created_at | TIMESTAMPTZ | NOT NULL DEFAULT NOW() | Thời điểm phát hành mã |
used_at | TIMESTAMPTZ | NULLABLE | Thời điểm sử dụng mã |
Bảng: idempotency_log (logic — có thể trùng với bảng idempotency hiện có)
| Cột | Kiểu | Ràng buộc | Mô tả |
|---|---|---|---|
id | BIGSERIAL | PK | — |
provider_event_id | VARCHAR(128) | UNIQUE NOT NULL | ID sự kiện Everflow — khóa idempotency |
origin_user_id | BIGINT | NOT NULL | — |
event_type | VARCHAR(64) | NOT NULL | — |
received_at | TIMESTAMPTZ | NOT NULL DEFAULT NOW() | — |
11. Tham chiếu API
POST /api/referral/velocity-reward
| Trường | Giá trị |
|---|---|
| Method | POST |
| Auth | Chữ ký HMAC của Everflow webhook (header: X-Everflow-Signature) |
| Caller | Everflow S2S webhook |
| Content-Type | application/json |
Request payload:
{
"provider_event_id": "ef_evt_abc123xyz",
"origin_user_id": 10042,
"event_type": "EV_MILESTONE_3_REFS",
"referral_count": 3,
"billing_country": "US",
"billing_region": "TX"
}Response — Happy path (HTTP 200 OK):
{
"status": "success",
"promo_code": "ABCD1234EFGH5678",
"applicable_product": "EVAL_L1",
"discount_percent": 100,
"email_dispatched": true,
"discord_queued": true
}Response — Sự kiện trùng lặp (HTTP 200 OK):
{
"status": "duplicate",
"skipped": true
}Response — Sanctions block (HTTP 403 Forbidden):
{
"error": "SANCTIONS_BLOCKED",
"user_id": 10042
}Response — event_type không hợp lệ (HTTP 400 Bad Request):
{
"error": "INVALID_EVENT_TYPE",
"received": "EV_UNKNOWN",
"accepted": ["EV_MILESTONE_3_REFS", "EV_MILESTONE_6_REFS"]
}| Trường request | Kiểu | Bắt buộc | Mô tả |
|---|---|---|---|
provider_event_id | STRING | Có | ID sự kiện duy nhất của Everflow — khóa idempotency |
origin_user_id | INT | Có | User ID của referrer |
event_type | STRING | Có | EV_MILESTONE_3_REFS hoặc EV_MILESTONE_6_REFS |
referral_count | INT | Có | Số mốc giới thiệu hiện tại (3 hoặc 6) |
billing_country | STRING | Có | Mã quốc gia ISO 3166-1 alpha-2 dùng cho kiểm tra sanctions |
billing_region | STRING | Không | Mã vùng/bang ISO 3166-2 dùng cho kiểm tra sanctions |
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §Velocity Reward Engine)
12. Câu hỏi mở
| ID | Ưu tiên | Câu hỏi | Giả định |
|---|---|---|---|
| Q1 | M | Broadcast Discord nên hiển thị tên hiển thị của user, bí danh, hay hoàn toàn ẩn danh? | Giả định của BA: handle ẩn danh hoặc chỉ tên riêng; cần xác nhận với khách về chính sách cộng đồng |
| Q2 | L | idempotency_log là bảng riêng cho Flow 32 hay là bảng idempotency dùng chung toàn hệ thống hiện có? | Giả định của BA: bảng dùng chung toàn hệ thống, lọc theo tiền tố event_type là EV_ |
| Q3 | M | Template ID / slug Klaviyo của Velocity_Reward_Unlocked là gì? | Đang dùng tên placeholder; cần xác nhận với Marketing/cấu hình Klaviyo |
| Q4 | L | Việc sao chép sang S3 Glacier là đồng bộ (trong request) hay bất đồng bộ (background job)? | Giả định của BA: background job bất đồng bộ; audit log ghi đồng bộ, việc sao chép S3 được đưa vào queue |
13. Lịch sử thay đổi
| Ngày | Phiên bản | Thay đổi | Trước | Sau | Nguồn |
|---|---|---|---|---|---|
| 2026-11-05 | v1.1 | §1 dòng Release; BR_9.14.8 (một mã cho mỗi mốc + nguồn) | Chưa nêu Release; mô hình mốc 3/6 so với tin nhắn ghim (pinned) 2/6 + gói (bundle) chưa được giải quyết (F32-01) | Release 2; mốc 3/6 đã được xác nhận, mỗi mốc một mã EVAL_L1 — câu trả lời về bundle (F32-02) không áp dụng | Client sheet 2026-11-04 [BG-012]; F32-01 đóng 2026-11-05; STAGE1-064 |
| 2026-10-01 | v1.0 | Tạo tài liệu ban đầu | — | Đặc tả đầy đủ: pipeline 4 bước, cổng idempotency, kiểm tra sanctions, tạo mã khuyến mãi, email Klaviyo, broadcast Discord, audit trail CFTC/NFA | RFQ_ Stack Trading Prop Tech V7.pdf §Velocity Reward Engine; Zapier Integration V7.pdf §Flow 32 |
UC_9.15 — Luồng 33: Xác minh Amplify Bounty
| Trường | Giá trị |
|---|---|
| UC_ID | UC_9.15 |
| Phiên bản | v1.1 |
| Cập nhật lần cuối | 2026-11-05 |
| BA PiC | linh.nguyen34 |
| Danh mục | BACKGROUND MECHANISMS & CRON JOBS |
| Mô-đun | Luồng 33 (4.9.3/4.9.4) |
| Chức năng | Amplify Bounty |
| Tài liệu tham chiếu | Zapier Integration V7.pdf (§Flow 33 Amplify Verification) · RFQ_ Stack Trading Prop Tech V7.pdf (§2.3 Discord Bot — Application Commands; §API GET /bounty/check-url, POST /generate-promo-code; §Audit Requirements) |
1. Danh mục UC
| UC_ID | Chức năng | Mô tả |
|---|---|---|
| UC_9.15 | Luồng 33 — Xác minh Amplify Bounty | Trader gửi URL TikTok / YouTube Shorts qua /submit-bounty [url] trong #bounty-submissions. Bot kiểm tra định dạng URL và giới hạn 1 lần/24h, sau đó Zapier chạy quy trình audit 5 bước: (1) URL chưa từng được dùng, (2) lấy Views/Likes/Comments, (3) Views ≥ 50,000 VÀ (Likes+Comments)/Views ≥ 0.015, (4) Gemini 1.5 Pro audit bot-farm trên văn bản đã ẩn PII, (5) PASS → promo code Associate Track giảm 100% + DM + email; FAIL → DM kèm lý do. |
UC_9.15 — Luồng 33: Xác minh Amplify Bounty
| Trường | Giá trị |
|---|---|
| Use Case | Xác minh & Trao thưởng Amplify Bounty |
| Mô tả | Thưởng cho việc quảng bá video ngắn một cách tự nhiên và xác thực. Ngưỡng định lượng lọc các nội dung có độ tiếp cận thấp; phân tích pháp chứng (forensic) của Gemini 1.5 Pro loại bỏ tương tác nhân tạo (bot-farm). Mỗi URL chỉ được thưởng một lần. |
| Tác nhân | Trader (thành viên Discord) · Hệ thống (Discord Bot, Zapier, Middleware, Gemini 1.5 Pro) |
| Zapier Flow | Flow 33 |
| Nguồn trigger | Discord Bot webhook từ /submit-bounty |
| 3rd Party | Discord · TikTok API · YouTube Data API · Google Gemini 1.5 Pro · Google Sheets · AWS SES (email PASS — chỉ cho user đã liên kết) |
| Middleware Endpoints | GET /bounty/check-url · POST /generate-promo-code |
| UC liên quan | UC_9.14 (dùng chung POST /generate-promo-code) · UC_2.7.8 Apply Promo Code (sử dụng mã) |
2. Trigger
Trader chạy slash command Discord /submit-bounty [url] trong #bounty-submissions. Sau khi các kiểm tra phía bot đạt (§5 Bước 1–2), bot gửi webhook tới Zapier Flow 33.
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §2.3; Zapier Integration V7.pdf §Flow 33)
3. Điều kiện tiên quyết
| ID | Điều kiện tiên quyết |
|---|---|
| P1 | #bounty-submissions: Send Messages bị tắt cho @everyone; Use Application Commands được bật. |
| P2 | Người gửi là thành viên của Discord server Stack Trading. Không bắt buộc liên kết tài khoản Stack Trading (QnA [BG-013] — client sheet 2026-11-04). |
| P3 | Video ở chế độ công khai trên TikTok hoặc YouTube Shorts. |
| P4 | Đã có cấu hình Gemini 1.5 Pro enterprise với điều khoản lưu giữ dữ liệu được xác định rõ. |
4. Hậu điều kiện
| Kết quả | Hậu điều kiện |
|---|---|
| PASS | Promo code được tạo (dùng một lần, giảm 100% Associate Track, origin_user_id được ghi log); URL được ghi nhận trong bounty_submissions là đã thưởng; DM + email được gửi; AUDIT_PASS được lưu trong Google Sheets. |
| URL trùng lặp | DM được gửi; không ghi nhận phần thưởng nào. |
| Math failure | DM kèm các chỉ số hiện tại; không thưởng; URL không bị đánh dấu là đã thưởng (trader có thể gửi lại). |
| AI failure | DM từ chối; không thưởng; AUDIT_FAIL được lưu trong Google Sheets. |
| Mọi kết quả | Lần thực thi được ghi log vào BPS_Audit_Log. |
5. Luồng cơ bản
Bước 1 — Kiểm tra định dạng URL (Discord Bot, phía client)
Bot kiểm tra đầu vào là URL TikTok hoặc YouTube Shorts hợp lệ trước khi chấp nhận. Không hợp lệ → Alt-1.
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §2.3 — Validation)
Bước 2 — Rate limit (Discord Bot)
Mỗi discord_user_id chỉ được gửi một lần trong 24 giờ. Vượt quá → Alt-2.
Triển khai (giả định của BA): Redis SET bounty_rate:{discord_user_id} 1 NX EX 86400; key đã tồn tại → từ chối.
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §2.3 — Rate Limiting)
Bước 3 — Webhook tới Zapier
Payload: discord_user_id, url, platform (tiktok | youtube_shorts), timestamp.
Bước 4 — Audit Bước 1: URL chưa từng được dùng
Zapier gọi GET /bounty/check-url?url=[URL-encoded]. Middleware truy vấn bounty_submissions.
is_unique = false→ dừng; DM: "This link has already been used to claim a reward." (Alt-3)
(Nguồn: Zapier Integration V7.pdf §Flow 33 Step 1; RFQ V7 §API Check Bounty URL)
Bước 5 — Audit Bước 2: lấy chỉ số
Zapier lấy Views, Likes, Comments từ TikTok / YouTube API. Khi API lỗi, retry 3 lần với exponential backoff (trong vòng 10 giây). Mọi lần retry đều thất bại → Exc-1.
(Nguồn: QnA_BACKGROUND_MECHANISMS_CRON.md [BG-015] — client sheet 2026-11-04)
Bước 6 — Audit Bước 3: bộ lọc định lượng (Zapier Filter)
| Điều kiện | Ngưỡng |
|---|---|
Views | ≥ 50,000 |
(Likes + Comments) / Views | ≥ 0.015 (1.5%) |
Phải đạt cả hai. Nếu không → Alt-4 (Math Failure).
(Nguồn: Zapier Integration V7.pdf §Flow 33 Step 3)
Bước 7 — Audit Bước 4: audit pháp chứng bằng Gemini 1.5 Pro
- Ẩn PII — loại bỏ email, số điện thoại, định danh tài khoản, thông tin thanh toán, họ tên đầy đủ khỏi toàn bộ văn bản trước khi gửi tới LLM.
- Đầu vào — mô tả video + 10 comment hàng đầu.
- Prompt (tác vụ suy luận chuyên sâu, không phải bộ phân loại spam cơ bản): "Analyze these comments for bot-farm patterns (repetitive emojis, generic praise, identical phrasing). Return 'AUDIT_PASS' or 'AUDIT_FAIL'."
- Lưu trữ — chỉ lưu
AUDIT_PASS/AUDIT_FAILđược rút ra trong Google Sheets (không lưu comment gốc). AUDIT_FAIL→ Alt-5.
(Nguồn: Zapier Integration V7.pdf §Flow 33 Step 4 — LLM Analysis (Privacy-Safe))
Bước 8 — Audit Bước 5: trao thưởng (khi PASS)
- Gọi
POST /generate-promo-code(type= Associate Track 100%,origin_user_id= trader đã gửi). - Ghi nhận URL là đã thưởng trong
bounty_submissions. - DM trader: "Success. Your impact is verified. Your free Associate Track code is [promo_code]. It has also been sent to your email."
- Truy vấn
Userstheodiscord_user_id. Nếu tồn tại user đã liên kết có email → gửi mã tới email đó qua AWS SES. Nếu không có tài khoản liên kết → bỏ qua email; DM Discord là kênh gửi duy nhất.
(Nguồn: Zapier Integration V7.pdf §Flow 33 Step 5; QnA_BACKGROUND_MECHANISMS_CRON.md [BG-013], [BG-014] — client sheet 2026-11-04)
6. Luồng thay thế
| ID | Điều kiện | Hành động của hệ thống | Thông báo cho trader (Discord) |
|---|---|---|---|
| Alt-1 | Không phải URL TikTok / YouTube Shorts | Bot từ chối; không có webhook | Ephemeral error (nội dung — xem Q5) |
| Alt-2 | Đã gửi trong vòng 24h | Bot từ chối; không có webhook | Ephemeral "one submission per 24 hours" (nội dung — xem Q5) |
| Alt-3 | URL đã được thưởng | Dừng tại Bước 4 | "This link has already been used to claim a reward." |
| Alt-4 | Math failure | Dừng tại Bước 6; không thưởng | "Submission logged. You currently have [Views] views and a [Ratio] engagement rate. This bounty requires 50,000 views and 1.5 percent engagement. Resubmit once your metrics update." |
| Alt-5 | AUDIT_FAIL | Dừng tại Bước 7; không thưởng; lưu AUDIT_FAIL | "Submission rejected. Our system detected non-organic engagement patterns. We only reward authentic traffic. Repeated bot-farming attempts will result in an Amplify Program ban." |
(Nguồn: Zapier Integration V7.pdf §Flow 33 Steps 1, 5 — Communication Matrix)
7. Luồng ngoại lệ
Exc-1 — Social API không khả dụng sau 3 lần retry / URL không hợp lệ, ở chế độ riêng tư hoặc đã bị xóa: không thưởng; lần gửi này được tính vào rate limit 24h của discord_user_id đó; DM thông báo cho user rằng xác minh thất bại và có thể gửi lại sau 24 giờ (nội dung — xem Q5); ghi log vào BPS_Audit_Log. (Nguồn: QnA [BG-015] — client sheet 2026-11-04)
Exc-2 — Gemini timeout / đầu ra sai định dạng (không đúng chính xác AUDIT_PASS / AUDIT_FAIL): coi là không đạt (fail-closed); không thưởng; ghi log để kiểm tra thủ công. (Giả định của BA)
Exc-3 — POST /generate-promo-code thất bại sau khi PASS: không đánh dấu URL là đã thưởng; cảnh báo Ops tới #ops-marketing; DM trader thông báo đang chờ phát hành thủ công. (Giả định của BA)
Exc-4 — DM Discord bị chặn bởi cài đặt quyền riêng tư của user: user đã liên kết → email vẫn được gửi khi PASS; ghi log lỗi DM. User chưa liên kết → cũng không có email, nên mã không được gửi tới user; ghi log để Ops theo dõi xử lý.
8. Quy tắc nghiệp vụ
BR_9.15.1: Chỉ nhận đầu vào có cấu trúc
#bounty-submissions chỉ chấp nhận slash command /submit-bounty (Send Messages tắt cho @everyone, Use Application Commands bật).
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §2.3)
BR_9.15.2: Nền tảng được hỗ trợ
Chỉ chấp nhận URL TikTok và YouTube Shorts; được bot kiểm tra trước khi gửi.
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §2.3)
BR_9.15.3: Một lần gửi mỗi 24 giờ cho mỗi discord_user_id
Rate limit 24h và số lần vi phạm chống bot (anti-bot strike) được theo dõi theo discord_user_id (dù đã liên kết hay chưa). Các lần xác minh thất bại (lỗi API sau khi retry, URL không hợp lệ/riêng tư) cũng được tính.
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §2.3; QnA_BACKGROUND_MECHANISMS_CRON.md [BG-013], [BG-015] — client sheet 2026-11-04)
BR_9.15.4: Một phần thưởng cho mỗi URL
GET /bounty/check-url trả về is_unique = false cho mọi URL đã được thưởng. Chỉ các URL đã được thưởng mới chặn việc gửi lại.
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §API Check Bounty URL)
BR_9.15.5: Ngưỡng định lượng
Views ≥ 50,000 VÀ (Likes + Comments) / Views ≥ 0.015. Cả hai đều bao gồm giá trị biên.
(Nguồn: Zapier Integration V7.pdf §Flow 33 Step 3)
BR_9.15.6: Quyền riêng tư với LLM
PII được ẩn trước khi bất kỳ văn bản nào đến LLM; chỉ lưu kết quả PASS/FAIL được rút ra (Google Sheets); cấu hình LLM enterprise với thời hạn lưu giữ được xác định rõ.
(Nguồn: Zapier Integration V7.pdf §Flow 33 Step 4)
BR_9.15.7: Định nghĩa phần thưởng
Dùng một lần, giảm 100% Associate Track, có thể chuyển nhượng, origin_user_id được ghi log để audit. (usage_limit = 1 theo QnA STAGE1-074.)
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §API Generate Promo Code · QnA_STAGE1_CHECKOUT_ONBOARDING.md STAGE1-074)
BR_9.15.8: Nội dung thông báo được cố định
Nội dung DM Discord cho Alt-3/4/5 và PASS tuân theo Communication Matrix của V7 nguyên văn (tiếng Anh).
(Nguồn: Zapier Integration V7.pdf §Flow 33 Step 5)
9. Tiêu chí chấp nhận
| AC_ID | Luồng | Cho trước | Thì | Loại |
|---|---|---|---|---|
| AC-9.15-01 | Happy path | URL TikTok mới, 80,000 views, 1,000 likes, 300 comments (ER 1.625%), Gemini AUDIT_PASS | Promo code được tạo; URL được đánh dấu đã thưởng; PASS DM kèm mã; email được gửi; AUDIT_PASS trong Sheets | E2E |
| AC-9.15-02 | Biên | Views = 50,000; ER = đúng 1.5% | Đạt Bước 6 | Integration |
| AC-9.15-03 | Math fail — views | 49,999 views | Alt-4 DM kèm views và ratio thực tế; không có mã | Integration |
| AC-9.15-04 | Math fail — ER | 100,000 views, 1,400 likes+comments | Alt-4 DM; không có mã | Integration |
| AC-9.15-05 | AI fail | Gemini AUDIT_FAIL | Alt-5 DM; không có mã; AUDIT_FAIL trong Sheets | Integration |
| AC-9.15-06 | URL trùng lặp | URL đã được thưởng trước đó | Alt-3 DM; Bước 5–8 không được thực thi | Integration |
| AC-9.15-07 | Rate limit | Cùng một user gửi hai lần trong 24h | Lần thứ hai bị bot từ chối; không có Zapier webhook | Integration |
| AC-9.15-08 | URL không hợp lệ | URL Instagram Reel | Bot từ chối; không có webhook | Integration |
| AC-9.15-09 | PII | Comment chứa địa chỉ email | Văn bản gửi tới Gemini đã được ẩn email | Security |
| AC-9.15-10 | Channel ACL | @everyone đăng văn bản thường trong #bounty-submissions | Không được phép | Config |
| AC-9.15-11 | User chưa liên kết | discord_user_id chưa liên kết với tài khoản Stack Trading nào; bài gửi đạt mọi bước audit | Promo code được tạo; PASS DM kèm mã; không gửi email | Integration |
| AC-9.15-12 | Lỗi API | Social API lỗi ở cả 3 lần retry | DM thông báo xác minh thất bại / gửi lại sau 24h; không thưởng; lần gửi thứ hai trong vòng 24h bị bot từ chối | Integration |
10. Tham chiếu API
GET /bounty/check-url
| Param | Kiểu | Mô tả |
|---|---|---|
url | STRING (URL-encoded) | URL video đã gửi |
Response: { "is_unique": true }
POST /generate-promo-code
Request (theo V7: type, origin_user_id):
{
"type": "ASSOCIATE_TRACK_100",
"origin_user_id": "94a2b1c8-3f4e-4b21-a123-890abcdef123"
}Response: { "promo_code": "STRING" }
Các giá trị enum của
typekhông được định nghĩa trong V7 —ASSOCIATE_TRACK_100là placeholder do BA đặt (xem Q3).
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §API)
11. Câu hỏi mở
| ID | Ưu tiên | Câu hỏi | Giả định |
|---|---|---|---|
| Q1 | H | Thành viên Discord có discord_user_id chưa được liên kết với tài khoản Stack Trading có được gửi không? Ai nhận mã / email? | ✅ Đã trả lời 2026-11-04 — bất kỳ ai trong Discord server đều có thể gửi; luôn gửi DM, chỉ gửi email nếu đã liên kết ([BG-013]). |
| Q2 | H | Có giới hạn số phần thưởng cho mỗi trader không (ví dụ 1 lần/30 ngày)? V7 không đề cập — chỉ có 1 lần gửi/24h và 1 phần thưởng/URL. | Không giới hạn ngoài các quy tắc của V7. |
| Q3 | M | Các giá trị type cho /generate-promo-code và applicable_product_id đích cho Associate Track (kích thước tài khoản / thị trường nào)? | Một mã dùng được cho Level 1 Associate Track, mọi thị trường. |
| Q4 | M | Kênh nào gửi email PASS — Klaviyo event (như Velocity_Reward_Unlocked) hay AWS SES? Tên template/nội dung? | ✅ Đã trả lời 2026-11-04 — AWS SES, chỉ cho user đã liên kết ([BG-014]). |
| Q5 | L | Nội dung thông báo cho URL không hợp lệ, rate-limit và lỗi API (không có trong V7). | BA soạn; client phê duyệt. |
| Q6 | L | Nếu audit thất bại ở Bước 2 (lỗi API), rate limit 24h có còn áp dụng không? | ✅ Đã trả lời 2026-11-04 — có, các lần thất bại được tính vào giới hạn 24h; retry 3 lần trước ([BG-015]). |
| Q7 | M | Xung đột chính sách ban: DM trong Zapier V7 nói "Repeated bot-farming attempts will result in an Amplify Program ban", nhưng nội dung ghim trong #amplify-intel (Discord Server Details.md — không có trong _SOURCE_MANIFEST.yaml) nói "immediate and permanent ban" ngay lần phát hiện đầu tiên. Áp dụng cái nào, và việc ban là tự động hay thủ công? | Theo cách diễn đạt của Zapier V7 (lặp lại → ban); việc ban do Ops thực hiện thủ công; lịch sử AUDIT_FAIL hiển thị trong Sheets. |
| Q8 | M | DM PASS của V7 ghi "It has also been sent to your email" — sai với người gửi chưa liên kết (không có email được gửi, [BG-013]). Có dùng nội dung DM khác cho user chưa liên kết không? (BR_9.15.8: nội dung là cố định, không được thay đổi khi chưa có phê duyệt của client) | Giữ nội dung V7 cho đến khi client xác nhận. |
12. Lịch sử thay đổi
| Ngày | Phiên bản | Thay đổi | Trước | Sau | Nguồn |
|---|---|---|---|---|---|
| 2026-11-05 | v1.1 | Header; §1 3rd Party; §3 P2; §5 Bước 5 (retry), Bước 8.4 (email); §7 Exc-1, Exc-4; §8 BR_9.15.3; §9 AC-9.15-11/12 (mới); §11 Q1, Q4, Q6, Q8 (mới) | Bắt buộc liên kết; kênh email còn mở (giả định Klaviyo); quy tắc 24h khi lỗi API còn mở | Không bắt buộc liên kết; luôn gửi DM, email AWS SES chỉ khi đã liên kết; retry social API 3 lần; các lần thất bại được tính vào giới hạn 24h | Client sheet 2026-11-04 [BG-013] [BG-014] [BG-015] |
| 2026-10-27 | v1.0 | Khởi tạo tài liệu | — | Đặc tả đầy đủ: kiểm tra phía bot, audit 5 bước, Communication Matrix, BR_9.15.1–8, AC-9.15-01–10 | Zapier Integration V7.pdf §Flow 33; RFQ_ Stack Trading Prop Tech V7.pdf §2.3, §API |
UC_9.16 — Luồng 38: Chặn Hỗ trợ & Tạo Ticket Freshdesk
| Trường | Giá trị |
|---|---|
| UC_ID | UC_9.16 |
| Phiên bản | v1.1 |
| Cập nhật lần cuối | 2026-11-05 |
| BA PiC | linh.nguyen34 |
| Danh mục | BACKGROUND MECHANISMS & CRON JOBS |
| Mô-đun | Luồng 38 |
| Chức năng | Support Interception |
| Tài liệu tham chiếu | Zapier Integration V7.pdf (§Flow 38 Support Interception & Zendesk Creation) · RFQ_ Stack Trading Prop Tech V7.pdf (§2.3 Automated Support Interception & Routing) · CR-20260720-004 (Freshdesk replaces Zendesk) · CR-20260818-002 (monitor #live-broadcast-chat) · QnA_STAGE2_COMMUNITY_LIVE_BROADCAST.md (DCS-BIZ-21, PIT-BIZ-03) · list_email.md (FD-01) |
1. Danh mục UC
| UC_ID | Chức năng | Mô tả |
|---|---|---|
| UC_9.16 | Luồng 38 — Support Interception | Node.js Discord Bot giám sát #the-pit, #verify và #live-broadcast-chat. Các tin nhắn chứa từ khóa trigger vận hành sẽ đi qua cổng sentiment Gemini (light). Nếu là Complaint: bot xóa tin nhắn, đăng placeholder công khai gắn thẻ user, và gửi webhook tới Zapier; Zapier tra cứu email đã đăng ký của user theo discord_id và tạo ticket Freshdesk mức ưu tiên High; Freshdesk gửi email xác nhận. |
UC_9.16 — Luồng 38: Chặn Hỗ trợ & Tạo Ticket Freshdesk
| Trường | Giá trị |
|---|---|
| Use Case | Support Interception |
| Mô tả | Chuyển các khiếu nại vận hành (slippage, lag, frozen platform, …) ra khỏi các kênh Discord công khai vào một ticket Freshdesk riêng tư, ngăn chặn sự lan truyền cảm xúc tiêu cực công khai mà vẫn hỗ trợ được trader. |
| Tác nhân | Trader (thành viên Discord) · Hệ thống (Discord Bot, Gemini, Middleware Hub, Zapier, Freshdesk) |
| Zapier Flow | Flow 38 |
| Nguồn trigger | Webhook từ Middleware Hub / Discord Bot — "Negative Operational Sentiment Detected" |
| 3rd Party | Discord · Google Gemini (light model — cùng model với "The Desk Manager") · Freshdesk |
| UC liên quan | UC_4.5.2 The Pit · UC_4.9.1 / UC_4.14.1 Discord Chat Stream (#live-broadcast-chat) |
2. Trigger
- Bot listener: bất kỳ tin nhắn mới nào trong kênh được giám sát có chứa từ khóa trigger.
- Zapier trigger: webhook từ Middleware Hub (Discord Bot) sau khi cổng sentiment trả về
Complaint. Payload:discord_id,message_text,timestamp.
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §2.3; Zapier Integration V7.pdf §Flow 38)
Các kênh được giám sát
| Kênh | Nguồn |
|---|---|
#the-pit | RFQ V7 §2.3 |
#verify | RFQ V7 §2.3 |
#live-broadcast-chat | CR-20260818-002 (CR thắng V7) |
3. Điều kiện tiên quyết
| ID | Điều kiện tiên quyết |
|---|---|
| P1 | Bot có quyền Read Message History và Manage Messages trong tất cả các kênh được giám sát. |
| P2 | Danh sách từ khóa trigger đã được cấu hình (xem BR_9.16.2). |
| P3 | URL webhook Zapier Flow 38 đã được cấu hình trong bot / Middleware. |
| P4 | Thông tin xác thực Freshdesk API đã được cấu hình trong Zapier. |
4. Hậu điều kiện (nhánh Complaint)
| # | Hậu điều kiện |
|---|---|
| 1 | Tin nhắn gốc bị xóa khỏi kênh công khai. |
| 2 | Phản hồi placeholder công khai được đăng, gắn thẻ user (xem Q1 cho #live-broadcast-chat). |
| 3 | Ticket Freshdesk được tạo: Requester = email đã đăng ký, Description = message_text, Priority = High. |
| 4 | Freshdesk gửi email xác nhận cho trader. |
| 5 | Lần thực thi được ghi log vào BPS_Audit_Log. |
5. Luồng cơ bản
Bước 1 — Bộ lọc sơ bộ theo từ khóa (Bot)
Bot quét từng tin nhắn mới trong các kênh được giám sát. Không có từ khóa trigger → bỏ qua (Alt-1).
Bước 2 — Cổng sentiment LLM (Bot → Gemini light model)
Tin nhắn được gửi tới Gemini để phân loại ý định là Complaint hoặc Positive/Neutral (ngăn false positive như "Stack Trading has no slippage!"). Không phải Complaint → bỏ qua (Alt-2).
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §2.3 — LLM Sentiment Gate)
Prompt tham chiếu (BA soạn thảo — không phải do client cung cấp):
You are an operations sentiment classifier for Stack Trading. Classify the user's intent:
1. "Complaint": reports an actual operational issue, technical glitch, execution failure, or frustration with system performance.
2. "Positive/Neutral": compliments the system, discusses normal market conditions, or uses the keyword in a non-complaint context.
Output ONLY JSON: {"intent": "Complaint" | "Positive/Neutral", "confidence_score": float}Bước 3 — Xóa tin nhắn (Bot) — ngay lập tức.
Bước 4 — Placeholder công khai (Bot)
"@[User], to protect your account details and resolve this immediately, we have opened a secure Freshdesk support ticket for you. Please check your registered email."
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §2.3 — "Zendesk" được thay bằng "Freshdesk" theo CR-20260720-004)
Bước 5 — Webhook tới Zapier
{ "discord_id": "123456789012345678", "discord_username": "trader_jane", "channel_name": "the-pit", "message_text": "MT5 frozen again, lost my entry", "timestamp": "2026-10-27T09:15:00Z" }Bước 6 — Tra cứu (Zapier)
Truy vấn PostgreSQL Users theo discord_id → email đã đăng ký. Không tìm thấy → Exc-1.
Bước 6.5 — Một ticket mỗi ngày UTC (Zapier / Middleware)
Kiểm tra ngày tạo ticket Freshdesk gần nhất (UTC) của discord_id này. Nếu trong ngày đã có ticket được tạo (trước 00:00 UTC), không tạo ticket thứ hai — hoặc thêm tin nhắn dưới dạng note vào ticket của ngày đó, hoặc bỏ qua (Dev chọn phương án đơn giản hơn). Tin nhắn Discord vẫn bị xóa (Bước 3). Xem BR_9.16.7.
Bước 7 — Tạo ticket Freshdesk (Zapier → Freshdesk POST /api/v2/tickets)
| Trường Freshdesk | Giá trị | Nguồn |
|---|---|---|
email (Requester) | Email đã đăng ký | V7 Flow 38 |
subject | Stack Trading Support Ticket Created - Discord Escalation | V7 Flow 38 (email subject) — xem Q3 |
description | message_text + channel_name, discord_username, discord_id, thời điểm gửi tin nhắn | V7 Flow 38; [BG-019] |
priority | 3 (High) | V7 Flow 38 |
status | 2 (Open) | Giả định của BA |
tags | ["discord-escalation", channel_name] | [BG-019] — client sheet 2026-11-04 |
custom_fields.cf_discord_user_id | discord_id | Giả định của BA |
Bước 8 — Email xác nhận (Freshdesk)
Subject: Stack Trading Support Ticket Created - Discord Escalation
Hello,
We intercepted your recent message in the Stack Trading Discord regarding an operational issue. To protect your privacy and ensure our team can assist you securely, we have converted your message into a high-priority support ticket. Your original message: "[Insert message_text payload]"
Our team is reviewing this now and will reply to this email directly. Please reply to this email with any additional context or screenshots.
Regards,
(Nguồn: Zapier Integration V7.pdf §Flow 38 — Notify; gửi qua Freshdesk theo CR-20260720-004)
6. Luồng thay thế
Alt-1 — Không có từ khóa trigger: không thực hiện hành động nào.
Alt-2 — Gemini trả về Positive/Neutral: tin nhắn được giữ nguyên; không tạo ticket.
Alt-3 — Tin nhắn trong #live-broadcast-chat: cùng pipeline. Release 1/2: chỉ cần xóa tin nhắn trong Discord là đủ. Việc gỡ tin nhắn khỏi Live Chat Feed của dashboard (DISCORD_CHAT_STREAM) là hạng mục Day 2, theo một trong hai cách: (1) lọc trước — giữ lại tin nhắn chứa từ khóa trigger để kiểm tra bằng Gemini trước khi đẩy vào DISCORD_CHAT_STREAM, và không bao giờ đẩy nếu bị gắn cờ; hoặc (2) delete event — đưa message_id vào payload của stream và phát delete event khi bot xóa tin nhắn. Hành vi placeholder đang chờ Q1.
(Nguồn: CR-20260818-002; QnA_BACKGROUND_MECHANISMS_CRON.md [BG-020] — client sheet 2026-11-04)
7. Luồng ngoại lệ
Exc-1 — discord_id chưa được liên kết với user nào
- Không tạo ticket Freshdesk (email placeholder sẽ bị bounce và Ops không thể phản hồi).
- Bot vẫn xóa tin nhắn và phản hồi công khai, hướng dẫn user liên kết tài khoản hoặc gửi email tới
support@stacktrading.com(chưa có nội dung thông báo). - Đăng lên Slack
#ops-generalkèmdiscord_username,channel_name,message_text.
(Nguồn: QnA_BACKGROUND_MECHANISMS_CRON.md [BG-021] — client sheet 2026-11-04)
Exc-2 — Gemini không khả dụng / timeout
- Retry lời gọi Gemini một lần. Vẫn không khả dụng / timeout → fail-open: giữ nguyên tin nhắn, không tạo ticket, ghi log cảnh báo. Không bao giờ fail-closed (một sự cố gián đoạn sẽ xóa hàng loạt tin nhắn chat bình thường và làm ngập Freshdesk).
(Nguồn: QnA_BACKGROUND_MECHANISMS_CRON.md [BG-022] — client sheet 2026-11-04)
Exc-3 — Bot không thể xóa (thiếu quyền / tin nhắn đã bị xóa): tiếp tục Bước 5–8 (ticket vẫn được tạo); ghi log cảnh báo.
Exc-4 — Freshdesk API thất bại: Zapier retry; nếu thất bại lần cuối thì cảnh báo Ops (#ops-general). Placeholder công khai đã được đăng — ticket phải được tạo thủ công.
8. Quy tắc nghiệp vụ
BR_9.16.1: Các kênh được giám sát
#the-pit, #verify, #live-broadcast-chat.
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §2.3 · CR-20260818-002)
BR_9.16.2: Từ khóa trigger
Ví dụ trong V7: slippage, lag, frozen. Danh sách đầy đủ chưa được xác nhận (PIT-BIZ-03 còn mở). Cấu hình đề xuất (BA soạn thảo): ["slippage", "lag", "frozen", "scam", "missing payout", "Rithmic down", "MT5 down", "TradingView down", "connection dropped"]. Phải điều khiển bằng cấu hình (Ops có thể chỉnh sửa), không phân biệt chữ hoa/thường.
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §2.3 · QnA PIT-BIZ-03 — open)
BR_9.16.3: Cổng LLM là bắt buộc
Chỉ khớp từ khóa thì không bao giờ xóa tin nhắn; chỉ intent = "Complaint" mới kích hoạt việc chặn. Dùng cùng light Gemini model với "The Desk Manager".
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §2.3)
BR_9.16.4: Nền tảng ticketing là Freshdesk
Mọi tham chiếu tới Zendesk trong V7 Flow 38 được thay bằng Freshdesk.
(Nguồn: CR-20260720-004)
BR_9.16.5: Ánh xạ ticket
Requester = email đã đăng ký (tra cứu theo discord_id); Description = message_text gốc; Priority = High.
(Nguồn: Zapier Integration V7.pdf §Flow 38)
BR_9.16.6: Thứ tự hành động
Xóa → placeholder → webhook. Việc xóa phải diễn ra ngay lập tức ("instantly") và không chờ ticket.
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §2.3 — Execution)
BR_9.16.7: Một ticket cho mỗi discord_id mỗi ngày UTC
Các khiếu nại lặp lại từ cùng một discord_id trong một ngày lịch UTC tạo ra tối đa một ticket Freshdesk (các tin nhắn bổ sung được thêm dưới dạng note hoặc bỏ qua). Khiếu nại sau 00:00 UTC sẽ tạo ticket mới. Mọi tin nhắn khiếu nại vẫn bị xóa trong Discord.
(Nguồn: QnA_BACKGROUND_MECHANISMS_CRON.md [BG-023] — client sheet 2026-11-04)
9. Tiêu chí chấp nhận
| AC_ID | Luồng | Cho trước | Thì | Loại |
|---|---|---|---|---|
| AC-9.16-01 | Happy path | User đã liên kết đăng "MT5 frozen again" trong #the-pit; Gemini → Complaint | Tin nhắn bị xóa; placeholder gắn thẻ user; ticket Freshdesk (Requester = email đã đăng ký, Priority High, description = văn bản gốc); nhận được email xác nhận | E2E |
| AC-9.16-02 | False positive | "Stack Trading has no slippage!" | Gemini → Positive/Neutral; tin nhắn được giữ nguyên; không tạo ticket | Integration |
| AC-9.16-03 | Không có từ khóa | "Good morning traders" | Không gửi tới Gemini | Integration |
| AC-9.16-04 | #verify | Complaint trong #verify | Giống AC-9.16-01 | Integration |
| AC-9.16-05 | #live-broadcast-chat | Complaint trong lúc phát sóng trực tiếp | Tin nhắn bị xóa trong Discord; ticket được tạo. Gỡ khỏi Live Chat Feed của dashboard = Day 2 (không kiểm thử trong Release 1/2) | E2E |
| AC-9.16-06 | Kênh không được giám sát | Complaint trong #general | Không có hành động | Integration |
| AC-9.16-07 | User chưa liên kết | discord_id không có trong Users | Tin nhắn bị xóa; không tạo ticket Freshdesk; phản hồi công khai (liên kết tài khoản / gửi email tới support@stacktrading.com); cảnh báo Slack #ops-general | Integration |
| AC-9.16-08 | Thương hiệu Freshdesk | AC-9.16-01 | Placeholder và email ghi "Freshdesk", không bao giờ ghi "Zendesk" | Content |
| AC-9.16-09 | Lặp lại trong cùng ngày | User đã liên kết đăng khiếu nại thứ hai trong cùng ngày UTC | Tin nhắn bị xóa; không tạo ticket thứ hai (thêm note hoặc bỏ qua) | Integration |
| AC-9.16-10 | Ngày UTC kế tiếp | Cùng user khiếu nại lại sau 00:00 UTC | Ticket mới được tạo | Integration |
| AC-9.16-11 | Gemini gián đoạn | Lời gọi Gemini thất bại, retry cũng thất bại | Tin nhắn được giữ nguyên; không tạo ticket; ghi log cảnh báo | Integration |
10. Câu hỏi mở
| ID | Ưu tiên | Câu hỏi | Giả định |
|---|---|---|---|
| Q1 | H | Trong #live-broadcast-chat, bot có vẫn đăng placeholder công khai trong lúc phát sóng trực tiếp không? (DCS-BIZ-21a — còn mở) | Có, giống các kênh khác. |
| Q2 | M | Ticket có nên được gắn tag kênh nguồn (ví dụ live-broadcast) để ưu tiên trong giờ phát sóng không? (DCS-BIZ-21c) | ✅ Đã trả lời 2026-11-04 — tags ["discord-escalation", channel_name] + ngữ cảnh trong description ([BG-019]). |
| Q3 | M | V7 Flow 38 định nghĩa một email xác nhận tùy chỉnh; list_email.md FD-01 là auto-reply "New Ticket Created" chung của Freshdesk. Gửi cái nào (hay cả hai)? | Template tùy chỉnh của Flow 38 thay thế FD-01 cho tag discord-escalation ([BG-019]). |
| Q4 | H | Xử lý discord_id chưa liên kết (email placeholder + cảnh báo Ops) — xác nhận hay bỏ ticket? | ✅ Đã trả lời 2026-11-04 — không tạo ticket; phản hồi công khai + Slack #ops-general ([BG-021]). |
| Q5 | M | Race: tin nhắn đến dashboard qua WebSocket trước khi bot xóa — có cần event thu hồi (retract) không? (CR-20260818-002 Q4) | ✅ Đã trả lời 2026-11-04 — Release 1/2: chỉ xóa trong Discord; gỡ khỏi dashboard = Day 2 ([BG-020]). |
| Q6 | M | Gemini không khả dụng: fail-open hay fail-closed? | ✅ Đã trả lời 2026-11-04 — retry một lần, sau đó fail-open; không bao giờ fail-closed ([BG-022]). |
| Q7 | L | Danh sách từ khóa trigger cuối cùng cho từng kênh (PIT-BIZ-03, DCS-BIZ-21b). | Danh sách nháp ở BR_9.16.2, dùng chung cho mọi kênh. |
| Q8 | L | Các khiếu nại lặp lại của cùng một user trong khoảng thời gian ngắn nên tạo một ticket hay nhiều ticket? | ✅ Đã trả lời 2026-11-04 — một ticket cho mỗi discord_id mỗi ngày UTC (BR_9.16.7, [BG-023]). |
11. Lịch sử thay đổi
| Ngày | Phiên bản | Thay đổi | Trước | Sau | Nguồn |
|---|---|---|---|---|---|
| 2026-11-05 | v1.1 | Header; §5 payload Bước 5, Bước 6.5 (mới), Bước 7 description/tags; §6 Alt-3; §7 Exc-1, Exc-2; §8 BR_9.16.7 (mới); §9 AC-9.16-05/07, AC-9.16-09–11 (mới); §10 Q2, Q4, Q5, Q6, Q8 | Tags Discord_Escalation (giả định của BA); chưa liên kết → ticket placeholder; một ticket cho mỗi tin nhắn; bắt buộc gỡ khỏi dashboard; giả định Gemini fail-open | Tags discord-escalation + kênh; description kèm channel/username/id/timestamp; chưa liên kết → không tạo ticket, phản hồi công khai + Slack; 1 ticket / discord_id / ngày UTC; gỡ khỏi dashboard = Day 2; Gemini retry một lần rồi fail-open | Client sheet 2026-11-04 [BG-019]–[BG-023] |
| 2026-10-27 | v1.0 | Khởi tạo tài liệu | — | Đặc tả đầy đủ: 3 kênh được giám sát, từ khóa + cổng Gemini, xóa/placeholder/webhook, ticket + email Freshdesk, BR_9.16.1–6, AC-9.16-01–08 | RFQ V7 §2.3; Zapier V7 §Flow 38; CR-20260720-004; CR-20260818-002 |
UC_9.17 — Luồng 39: Gửi lại Hướng dẫn Dữ liệu Thị trường BPS
| Trường | Giá trị |
|---|---|
| BA phụ trách | linh.nguyen34 |
| Ngày tạo | 2026-08-26 |
| Phiên bản | v1.4 |
| Cập nhật lần cuối | 2026-11-05 — Pop-up hiển thị mỗi lần vào tab "Command" trên Dashboard khi thỏa thuận CME chưa ký; dừng khi Signed_Completely = Yes; trader tự phục vụ gọi POST /resend-rtrader-instructions. |
| Tài liệu tham chiếu | Zapier Integration V7.pdf (Flow 39 lines 2614–2623) · RFQ_ Stack Trading Prop Tech V7.pdf (BPS Endpoints — Resend R|Trader Instructions lines 2941–2944) |
Danh mục UC
| UC_ID | Tên Use Case | Mô tả nghiệp vụ |
|---|---|---|
| UC_9.17 | Luồng 39 — Gửi lại Hướng dẫn Dữ liệu Thị trường BPS | BPS Admin kích hoạt gửi lại email thiết lập R|Trader Pro cho trader Futures chưa ký các thỏa thuận subscriber CME/CBOT/NYMEX/COMEX. Middleware gửi webhook RESEND_RTRADER_INSTRUCTIONS tới Zapier, Zapier gửi email qua AWS SES. Phạm vi: chỉ Futures. Có ba câu hỏi mở đang chờ Rithmic làm rõ (xem §6). |
Lịch sử thay đổi
| Ngày | Phiên bản | Mục cập nhật | Trước | Sau | Ghi chú |
|---|---|---|---|---|---|
| 2026-08-26 | v1.1 | Tạo ban đầu + phân nhánh phân loại SIM vs Live (CRITICAL). §1 bối cảnh nghiệp vụ. Template SES-39A/SES-39B. BR_9.17.7. Exc-5. AC-9.17-07/08. | — | UC_9.17 được soạn | Nguồn: Zapier V7 Flow 39 + bảng BPS Endpoint trong v7_full.txt + phân tích nghiệp vụ do user cung cấp 2026-08-26. |
| 2026-10-20 | v1.1 → v1.2 | Bổ sung §1b Giao diện dành cho Trader; §3 Luồng cơ bản cập nhật đường kích hoạt trader tự phục vụ; bổ sung BR_9.17.9; bổ sung AC-9.17-16/17. | Chưa có tài liệu về giao diện dành cho trader. Flow 39 chỉ do admin kích hoạt. | Bổ sung modal "Action Required: Exchange Agreements Pending" trên Trader Dashboard làm điểm vào thứ hai. Nhấn "Resend Instructions" gọi cùng endpoint POST /bps/resend-rtrader-instructions. | UI do khách cung cấp 2026-10-20. |
| 2026-10-27 | v1.2 → v1.3 | §1 bảng tham số; §3 Bước 2 xác định phase; §3 bảng phân nhánh template; Exc-7 | account_status Live ghi dạng Active_L1_DMA → Active_L24_DMA / Active_Lx_DMA | account_status = 'Active_DMA' (current_level 1–24) | Khách xác nhận 2026-09-14 — account_status là enum cố định; level lưu ở current_level (UC_5.8 BR_5.8.9) |
| 2026-11-05 | v1.3 → v1.4 | §1b điều kiện hiển thị, hành vi Cancel, endpoint Resend, Completion Gate (mới); §2 Điểm vào B; BR_9.17.10/11 (mới); cập nhật AC-9.17-16/16b/17, thêm AC-9.17-18 | Modal hiển thị một lần ở lần đăng nhập Dashboard đầu tiên (cme_modal_acknowledged); trader tự phục vụ gọi POST /bps/resend-rtrader-instructions | Modal hiển thị mỗi lần vào tab "Command" khi chưa ký; đóng modal không lưu trạng thái; dừng khi Signed_Completely = Yes; trader tự phục vụ gọi POST /resend-rtrader-instructions | Flow 39 update 2026-11-05 |
Thuật ngữ
Đối với toàn bộ thuật ngữ dùng chung của dự án, tham khảo: Project Glossary.
UC_9.17 — Luồng 39: Gửi lại Hướng dẫn Dữ liệu Thị trường BPS
1. Tổng quan
| Trường | Nội dung |
|---|---|
| ID | UC_9.17 |
| Use Case | Gửi lại Hướng dẫn Dữ liệu Thị trường BPS |
| Mô tả | Cho phép BPS Admin/Support kích hoạt thủ công việc gửi lại email thiết lập R|Trader Pro cho trader Futures có luồng dữ liệu thị trường bị khóa do chưa ký thỏa thuận subscriber CME. Admin gọi BPS endpoint POST /bps/resend-rtrader-instructions với user_id của trader. Middleware gửi webhook RESEND_RTRADER_INSTRUCTIONS tới Zapier (Flow 39), Zapier gửi email AWS SES chứa thông tin đăng nhập và hướng dẫn từng bước để ký thỏa thuận. |
| Zapier Flow | Flow 39 (BPS Resend Market Data Instructions) |
| Backend Endpoint | POST /bps/resend-rtrader-instructions — gửi webhook RESEND_RTRADER_INSTRUCTIONS tới Zapier |
| Zapier Table | Bảng Users (user_id, platform_username, asset_class) |
| 3rd Party | Zapier (Flow 39) · AWS SES (gửi email — SES-44 cho SIM, SES-45 cho Live) · Rithmic (nguồn thông tin đăng nhập platform) |
1b. Giao diện dành cho Trader — Modal "Action Required: Exchange Agreements Pending"
Flow 39 có hai điểm vào: BPS Admin (§2 Trigger) và trader tự phục vụ qua modal trên Dashboard bên dưới.
Đặc tả modal (UI do khách cung cấp 2026-10-20):
| Thành phần | Nội dung |
|---|---|
| Tiêu đề | Action Required: Exchange Agreements Pending |
| Nội dung | You must log into the R|Trader Pro desktop terminal to digitally sign your CME exchange agreements. Your data feeds cannot activate until this step is complete. |
| Nút chính | Resend Instructions |
| Nút phụ | Cancel |
Điều kiện hiển thị: Modal được hiển thị mỗi lần trader mở hoặc chuyển tới tab "Command" trên Dashboard khi TẤT CẢ các điều kiện sau đúng:
account_status IN ('SIM_ACTIVE', 'Active_DMA')(tài khoản Futures)- Các thỏa thuận CME Market Data Agreements chưa được ký (
Signed_CompletelykhácYes)
Cơ chế hiển thị giống Soft Breach modal. (Nguồn: Flow 39 update 2026-11-05)
Hành vi nút "Resend Instructions":
- Trader nhấn → frontend Dashboard gọi
POST /resend-rtrader-instructions(kích hoạt Flow 39) vớiuser_idcủa chính trader (xác thực bằng Dashboard session JWT — không phải admin token). - Middleware áp dụng cùng các gate check như đường BPS Admin (asset class, kiểm tra credential, rate limit, kiểm tra phase).
- Khi thành công: nút hiển thị trạng thái xác nhận ngắn (ví dụ: "Instructions sent"); modal vẫn có thể đóng bằng Cancel.
- Khi thất bại (rate limit
429, sai phase422, v.v.): modal hiển thị thông báo lỗi inline; không tự động đóng.
Nút "Cancel" / tắt modal: Đóng modal. Không gọi API và không lưu trạng thái nào. Luồng dữ liệu vẫn bị khóa. Modal xuất hiện lại mỗi lần trader quay lại tab "Command" khi các thỏa thuận vẫn chưa được ký.
Điều kiện ngưng hiển thị (Completion Gate): Ngay khi backend xác nhận qua Rithmic rằng trader đã ký xong, hệ thống lưu Signed_Completely = Yes vào CSDL. Từ đó hệ thống dừng kiểm tra Rithmic và không hiển thị modal ở các lần vào tab "Command" sau. (Nguồn: Flow 39 update 2026-11-05)
Lưu ý — các trường admin_id và reason: BPS endpoint yêu cầu admin_id và reason cho audit log. Với các lời gọi trader tự phục vụ, admin_id PHẢI được đặt thành một giá trị hệ thống dành riêng (ví dụ "SYSTEM_TRADER_SELF_SERVICE") và reason thành một chuỗi cố định (ví dụ "Trader self-initiated via Dashboard modal"). Điều này giữ nguyên việc ghi BPS_Audit_Log mà không cần trader nhập liệu.
Ảnh chụp màn hình (khách cung cấp 2026-10-20):

(Nguồn: UI do khách cung cấp 2026-10-20)
1a. Bối cảnh nghiệp vụ
Vì sao trader Futures phải ký thỏa thuận sàn giao dịch: Với các tài khoản Futures dùng Rithmic, CME Group (và các sàn thành viên CBOT, NYMEX, COMEX) bắt buộc mọi trader phải ký điện tử Subscriber Agreement trước khi luồng giá real-time được kích hoạt. Cho đến khi ký, Rithmic chặn luồng dữ liệu ở cấp hạ tầng bất kể trạng thái tài khoản. Trader dùng NinjaTrader 8, Quantower, ATAS, Sierra Chart hoặc MotiveWave sẽ thấy "No Market Data" hoặc biểu đồ trống cho đến khi hoàn tất ký. Dashboard của Stack Trading không thể theo dõi trạng thái ký của Rithmic theo thời gian thực — nghĩa là support phải chủ động gửi lại hướng dẫn khi trader báo thiếu dữ liệu.
Thời điểm gửi email hướng dẫn ký — theo từng phase:
Phase A — SIM (Level 0 Associate Track):
Được kích hoạt ngay sau khi thanh toán SIM challenge thành công. Middleware đặt account_status = 'Guest' và gửi Welcome_Sim_Challenge qua AWS SES. Email chứa thông tin đăng nhập Rithmic Paper + khối hướng dẫn CME bắt buộc: "You must download R|Trader Pro and log in using your provided credentials to activate your CME data feeds, digitally sign your exchange agreements..." Trader phải tải R|Trader Pro (desktop hoặc web), đăng nhập bằng thông tin SIM và ký với tư cách Non-Professional. Luồng giá trên tất cả terminal giao dịch vẫn bị khóa cho đến khi hoàn tất ký.
Phase B — Live Onboarding (Level 1, 2 hoặc 5):
Email hướng dẫn ký (Live_Onboarding_Credentials) được gửi khi hoàn tất Flow 3H — sau toàn bộ chuỗi Stage 3: Veriff KYC → thiết lập payout Dots → ký hợp đồng ICA Rippling → chọn/thanh toán Market Data (Flow 3F/3G) → tài khoản Live được Middleware cấp phát qua Rithmic Live FCM API. Trader phải đăng nhập vào Live gateway của R|Trader Pro và ký các thỏa thuận CME với tư cách Professional trước khi mở terminal giao dịch.
(Nguồn: Zapier Integration V7.pdf Flow 1 Step 8 lines 983–1001; Flow 3H lines 1264–1332; RFQ_ Stack Trading Prop Tech V7.pdf §122 Steps 2/4/6/8)
Vì sao phân loại Non-Pro và Pro quan trọng: CME Group phân biệt hai loại subscriber với mức phí và nghĩa vụ pháp lý khác nhau:
| Tham số | SIM (Level 0) | Live (Level 1+) |
|---|---|---|
account_status | SIM_ACTIVE | Active_DMA (current_level 1–24) |
| Rithmic Gateway | Paper Trading Gateway | Live / Ironbeam Gateway |
| Phân loại CME | Non-Professional (bắt buộc) | Professional (bắt buộc) |
| Phí | $18.00/exchange/tháng — do công ty chi trả, trader trả $0.00; chính sách CME "4 exchanges billed as 3" | $137.50/exchange/tháng — trừ từ tài khoản của trader (BR_5.6.7) |
| Ký thỏa thuận | Bắt buộc trước khi luồng dữ liệu được kích hoạt | Bắt buộc trước khi giao dịch live bắt đầu |
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §315, §317, §44, §356)
Lý do phân tách: tài khoản SIM chạy trên Rithmic Paper Trading Gateway (tách biệt khỏi live FCM/Ironbeam để tránh phí định tuyến live). CME phân loại tài khoản trên Paper server là mục đích cá nhân/giáo dục — Non-Professional là bắt buộc theo luật. Tài khoản Live vận hành bằng vốn tự doanh của công ty — CME bắt buộc Professional bất kể lý lịch cá nhân. Nếu trader Live ký dưới Non-Professional → vi phạm kiểm toán CME, công ty phải chịu trách nhiệm.
Quy trình ký thỏa thuận — phụ thuộc hệ điều hành:
| HĐH | Phương thức | Các bước chính |
|---|---|---|
| Windows — Option A | R|Trader Pro Desktop (.msi) | Cài đặt → đăng nhập bằng platform_username + mật khẩu Dashboard → Rithmic tự động hiển thị pop-up Wizard (khóa terminal) → chọn phân loại → nhập tên pháp lý KYC → ký CME, CBOT, NYMEX, COMEX → pop-up đóng, luồng dữ liệu kích hoạt |
| Windows — Option B | R|Trader Pro Web (trình duyệt) | Giống luồng macOS bên dưới |
| macOS | R|Trader Pro Web (trình duyệt) | Mở Safari/Chrome/Firefox → R|Trader Pro Web → đăng nhập → chọn đúng Gateway (Paper cho SIM, Live cho Live DMA) → ký 4 thỏa thuận online → luồng dữ liệu mở khóa trên toàn hệ thống; kết nối MotiveWave hoặc platform native macOS khác |
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §276, §314, §315, §356, §486)
Biện pháp bảo vệ vận hành (cấp Rithmic Gateway):
- Khóa cấp Gateway: Rithmic tạm dừng luồng giá real-time ở cấp hạ tầng cho đến khi cả 4 thỏa thuận được ký. Dashboard Stack Trading KHÔNG theo dõi việc ký theo thời gian thực (không cần polling — Rithmic tự mở khóa).
- Giới hạn 1 session + Plug-In Mode: Rithmic áp dụng 1 session đồng thời cho mỗi tài khoản. Trader phải bật "Allow Plug-In Connection" trong R|Trader Pro Desktop trước khi kết nối NinjaTrader 8 (qua chế độ "Connect using R|Trader Pro") — tránh bị đá session khi chạy đồng thời cả hai ứng dụng.
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §315)
Xử lý lỗi — chọn sai phân loại khi ký:
Phân loại CME Professional/Non-Professional là yêu cầu pháp lý nghiêm ngặt với hệ quả phí đáng kể (Non-Pro: $18/exchange/tháng; Pro: $137.50/exchange/tháng). Stack Trading đã thiết kế các cơ chế kiểm soát sau:
Trường hợp A — Trader Live (Professional) vô tình chọn "Non-Pro":
Điều này không thể xảy ra. Vì trader vận hành vốn tự doanh của công ty theo Independent Contractor Agreement với Stack Trading Brokerage LLC, CME bắt buộc theo luật phân loại Professional. Tại Flow 3H, Middleware thực thi khóa cứng: gọi Rithmic Live FCM API và cấu hình Trader ID profile với Is_Professional = True. Khi trader đăng nhập R|Trader Pro Live, Rithmic chỉ hiển thị form ký Professional — không có tùy chọn Non-Pro nào hiển thị trên giao diện.
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf lines 1141–1146; Zapier Integration V7.pdf lines 1274–1285)
Trường hợp B — Trader SIM (Non-Professional) vô tình chọn "Professional":
Điều này CÓ THỂ xảy ra. Giá trị mặc định trong DB cho SIM là Is_Professional = False (công ty chi trả chi phí Non-Pro). Nếu trader đăng nhập R|Trader Pro SIM và chọn "Professional", Rithmic ghi nhận Trader ID là Professional — CME sẽ cố tính phí $137.50/tháng cho mỗi exchange thay vì mức Non-Pro do công ty chi trả.
Phát hiện: GET /api/bps/check-market-data-status liên tục đối chiếu entitlement trong DB (Non-Pro) với trạng thái chữ ký thực tế từ Rithmic API. Nếu phát hiện không khớp (Rithmic báo Professional trong khi DB báo Non-Pro) → hệ thống nâng cờ cảnh báo.
Phương án khắc phục:
- Tự động (Flow 39): Ops dùng
POST /bps/resend-rtrader-instructions. Webhook này gửi lại email hướng dẫn VÀ đặt lại/vô hiệu hóa trạng thái chữ ký CME sai trên Rithmic gateway — cho phép trader đăng nhập lại và ký lại đúng với tư cách Non-Professional. - Thủ công (R|Admin): Ops có thể truy cập phần mềm middle-office R|Admin của Rithmic để chỉnh phân loại của trader từ Professional về Non-Professional.
(Nguồn: khách xác nhận 2026-09-09) Flow 39 chỉ áp dụng cho trader có
asset_class = 'Futures'. Trader Forex (MT5/TE) dùng luồng giá LP phi tập trung — không cần thỏa thuận CME/Rithmic.asset_class IN ('Forex_MT5', 'Forex_TE')→ từ chối tại gate của Middleware, không gửi webhook.
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf §314, §315)
Lưu ý về trigger — chỉ thủ công (đang chờ): Flow 39 hiện được tài liệu hóa là hành động do admin kích hoạt thủ công. Việc có nên tự động kích hoạt sau khi cấp phát Rebuy (khi sub-account mới được gán cho User ID hiện có) hay không vẫn là câu hỏi mở — xem F39-Q1/Q2 trong §6.
2. Trigger
Điểm vào A — BPS Admin: BPS Admin nhấn "Resend R|Trader Instructions" trong BPS Admin Panel cho một trader cụ thể.
Điểm vào B — Trader tự phục vụ (UI 2026-10-20): Trader nhấn "Resend Instructions" trên modal "Action Required: Exchange Agreements Pending" trên Dashboard của mình.
Điểm vào A gọi POST /bps/resend-rtrader-instructions; Điểm vào B (pop-up trên Dashboard) gọi POST /resend-rtrader-instructions. Cả hai đi theo cùng các gate check của Middleware và cùng đường dispatch.
- Admin gửi
POST /bps/resend-rtrader-instructionsvớiuser_idtrong request body. - Middleware xác thực request, gửi webhook event
RESEND_RTRADER_INSTRUCTIONStới Zapier. - Zapier Flow 39 được kích hoạt khi nhận webhook.
(Nguồn: Zapier Integration V7.pdf Flow 39 line 2616 — "Trigger: Webhook RESEND_RTRADER_INSTRUCTIONS received from Middleware Hub"; RFQ_ Stack Trading Prop Tech V7.pdf BPS Endpoint table lines 2941–2944)
Đặc tả BPS Endpoint:
| Trường | Giá trị |
|---|---|
| Method | POST |
| Path | /bps/resend-rtrader-instructions |
| Input | user_id, admin_id, reason |
| Response | { "status": "success" } |
| Tác động | Gửi webhook RESEND_RTRADER_INSTRUCTIONS tới Zapier |
(Nguồn: RFQ_ Stack Trading Prop Tech V7.pdf lines 2941–2944 — path /bps/resend-rtrader-instructions)
⚠️ Sai lệch path endpoint: Đặc tả do user cung cấp dùng
/api/bps/rtrader-instructions. Nguồn V7 dùng/bps/resend-rtrader-instructions. Nguồn V7 là chuẩn — SotaTek phải triển khai/bps/resend-rtrader-instructions. Xác nhận với khách trước khi build.
RBAC gate: Yêu cầu tối thiểu token support Level_1 trở lên (bất kỳ JWT admin/support agent đã xác thực). KHÔNG yêu cầu vai trò cao hơn (Level_2 / Admin) cho endpoint này — khác với các hành động phá hủy (Hard Reset, Force Session Kick, Full Refund, Admin Platform Reassignment) vốn yêu cầu Level_2. Xác thực JWT trả về 401 nếu không hợp lệ/hết hạn; agent Level_1 được phép.
admin_id và reason là các trường bắt buộc trong request body — mọi BPS POST endpoint đều bắt buộc tham số reason.
(Nguồn: khách xác nhận 2026-08-26 — Stack Trading V7 BPS administrative security protocol)
3. Luồng cơ bản
Bước 1 — Admin tra cứu và thao tác
- Admin tìm trader trong BPS Admin Panel theo
user_idhoặc email. - BPS Panel hiển thị nút "Resend R|Trader Instructions" — luôn hiển thị và luôn nhấn được, bất kể trạng thái ký hiện tại của trader. Nút KHÔNG bị ẩn hay vô hiệu hóa dựa trên đầu ra của
GET /bps/trader/market-data-status. - BPS Panel có thể gọi
GET /bps/trader/market-data-statusở nền (non-blocking) để hiển thị nút ở một trong ba trạng thái:- Amber/Alert — phát hiện không khớp:
db_entitlementscó luồng exchange nhưngrithmic_statuscủa exchange đó =false→ tooltip:"Discrepancy Detected: Trader has unsigned agreements. Click to resend." - Muted/Success — đồng bộ hoàn toàn: mọi db entitlement khớp với chữ ký Rithmic đang hoạt động → tooltip:
"All agreements successfully signed. Click to force resend if needed." - Neutral/Gray — chưa tải xong so sánh hoặc Rithmic API không khả dụng → nút vẫn hoạt động đầy đủ; admin vẫn có thể kích hoạt resend
- Amber/Alert — phát hiện không khớp:
- Nhấn nút mở modal xác nhận hiển thị: địa chỉ email đích + ô nhập text "Reason for Resending" bắt buộc (cần cho việc ghi
BPS_Audit_Log). - Admin điền
reasonvà xác nhận → kích hoạtPOST /bps/resend-rtrader-instructions.
(Nguồn: khách xác nhận 2026-08-26 — BPS Endpoints Passage 105/677; GET /bps/trader/market-data-status chỉ mang tính thông tin/chẩn đoán, không phải cổng hạn chế)
Bước 2 — Middleware xử lý
- Middleware nhận
POST /bps/resend-rtrader-instructions. - Gate check 1 — Auth: Xác thực Admin JWT. Nếu không hợp lệ hoặc hết hạn →
401 Unauthorized, không gửi webhook (xem Exc-5). - Gate check 2 — Asset class:
asset_class = 'Futures'. Nếu là Forex → trả về400 Bad Request, không gửi webhook (xem Exc-2). - Gate check 3 — Tra cứu user: Xác nhận
user_idtồn tại trong PostgreSQL. Nếu không tìm thấy →404 Not Found(xem Exc-1). - Gate check 4 — Kiểm tra credential: Xác nhận
platform_username IS NOT NULL AND platform_username != ''. Nếu tài khoản Rithmic chưa được cấp phát →422 Unprocessable Entityvới thông báo"Rithmic account has not been provisioned yet". Không gửi webhook (xem Exc-6). - Gate check 5 — Rate limit & dedup:
- Dedup (cửa sổ idempotency): Nếu một request giống hệt cho cùng
user_idđã được xử lý trong 5 phút gần nhất → trả về200 OK(thành công im lặng) mà không gửi webhook mới. Ngăn việc click đúp ngoài ý muốn mà không hiển thị lỗi gây nhầm lẫn cho admin. - Giới hạn hằng ngày: Nếu trader đã nhận 3–5 lần resend thành công trong 24 giờ gần nhất → trả về
429 Too Many Requestsvới thông báo:"Resend limit reached for this user. Please try again tomorrow."Không gửi webhook (xem Exc-8). (Nguồn: khách xác nhận 2026-09-09)
- Dedup (cửa sổ idempotency): Nếu một request giống hệt cho cùng
- Xác định phase: Truy vấn
account_statustừ bảng Users để xác định template email sẽ dùng:account_status = 'SIM_ACTIVE'(Level 0) →template = 'SES-44'(Non-Professional)account_status = 'Active_DMA'(current_level >= 1) →template = 'SES-45'(Professional)- Mọi
account_statuskhác (ví dụGuest,Failed,Terminated,Locked,SUSPENDED_DEFENSE,In_Defense,Active_Challenge) → trả về422 Unprocessable Entityvới thông báo:"Trader is not in an active phase eligible for R|Trader instructions resend". Không gửi webhook. Lý do: các tài khoản ngoàiSIM_ACTIVE/Active_DMAkhông có credential Rithmic đang hoạt động (sub-account chưa được gán hoặc bị soft-lock để dừng phí platform $25/tháng); gửi hướng dẫn đăng nhập cho tài khoản không hoạt động sẽ gây nhầm lẫn cho trader và làm quá tải support desk.
- Middleware ghi một audit entry bất biến vào
BPS_Audit_Log:{ admin_id, action: "RESEND_RTRADER_INSTRUCTIONS", target_user_id, reason, template_used, timestamp }. Bắt buộc theo yêu cầu lưu trữ hồ sơ CFTC/NFA — mọi BPS POST endpoint đều thực thi điều này. (Nguồn: khách xác nhận 2026-08-26) - Middleware gửi webhook
RESEND_RTRADER_INSTRUCTIONStới Zapier vớiuser_id,platform_username,email,template.
Bước 3 — Gửi mail (Flow 39)
- Zapier Flow 39 được kích hoạt khi nhận webhook
RESEND_RTRADER_INSTRUCTIONS. - Zapier đọc trường
templatetừ payload webhook để chọn đúng template SES. - Zapier gọi AWS SES để gửi email tới địa chỉ email đã đăng ký của trader.
Phân nhánh template email:
| Điều kiện | Template | Phân loại CME |
|---|---|---|
account_status = 'SIM_ACTIVE' (Level 0) | SES-44 — RTrader_Setup_NonPro | Non-Professional |
account_status = 'Active_DMA' (current_level >= 1) | SES-45 — RTrader_Setup_Pro | Professional (bắt buộc — vốn tự doanh) |
Về đặc tả nội dung email đầy đủ và lý do tuân thủ, xem SES-44 và SES-45 trong list_email.md.
(Nguồn: Zapier Integration V7.pdf Flow 39 lines 2619–2623; phân loại Non-Pro vs. Pro: phân tích nghiệp vụ do user cung cấp 2026-08-26)
RÀNG BUỘC BẢO MẬT QUAN TRỌNG:
platform_password_ciphertextKHÔNG BAO GIỜ được trả về cho Zapier và KHÔNG BAO GIỜ được đưa vào bất kỳ email nào. Mật khẩu chỉ trader truy cập được quaGET /platform-credentialstrên Dashboard sau khi xác thực Auth0 OTP (hiển thị một lần). Ràng buộc này áp dụng cho TẤT CẢ các luồng email, bao gồm Flow 39.
Hậu điều kiện: Trader nhận được email. Bản thân Flow 39 không kích hoạt thay đổi trạng thái DB nào trong bảng Users. Trạng thái luồng dữ liệu vẫn bị khóa cho đến khi trader hoàn tất việc ký trong R|Trader Pro — việc cập nhật trạng thái diễn ra qua cơ chế callback riêng của Rithmic (ngoài phạm vi Flow 39).
4. Luồng ngoại lệ
Exc-1 — Không tìm thấy user_id
- Middleware tra cứu
user_idkhông có bản ghi. - Trả về
404 Not Foundcho BPS Admin Panel. - Không gửi webhook. Không gửi email.
- Admin kiểm tra lại
user_idvà thử lại.
Exc-2 — Trader Forex (ngoài phạm vi)
- Middleware phát hiện
asset_class IN ('Forex_MT5', 'Forex_TE'). - Trả về
400 Bad Requestvới thông báo:"Flow 39 is scoped to Futures traders only. Forex traders use MT5/TE platform — no CME agreements required." - Không gửi webhook. Không gửi email.
Exc-3 — Gửi email SES thất bại (Hard Bounce / Complaint)
- AWS SES trả về Bounce (email không tồn tại) hoặc Complaint (trader báo spam) qua SNS webhook tới Middleware.
- Mức độ nghiêm trọng: SEV-2 —
#ops-alerts. Chỉ ảnh hưởng đường gửi của một trader. - Cập nhật DB: Middleware tự động đặt
email_delivery_status = 'Bounced'hoặc'Complaint'+email_banned = TRUEtrên bản ghi của trader. - BPS Panel: Banner đỏ hiển thị trên màn hình chi tiết của trader:
⚠️ Email Delivery Blocked: Hard Bounce / Complaint Registered. - Trạng thái nút: "Resend R|Trader Instructions" bị vô hiệu hóa/làm xám vĩnh viễn cho đến khi Admin cập nhật địa chỉ email hợp lệ mới cho trader — ngăn spam SES và bảo vệ Sender Reputation của nền tảng (SPF/DKIM/DMARC). Cố ý không tự động retry khi Hard Bounce; retry một địa chỉ chết khiến AWS SES hạ điểm sender của Stack Trading và có thể đình chỉ tài khoản SES. (Nguồn: khách xác nhận 2026-09-09 — Stage 3 - KYC - DOT's Feedback.pdf Category B Row 4; Zapier Integration V7.pdf pp.102, 166, 228; RFQ_ Stack Trading Prop Tech V7.pdf §1.0)
Exc-4 — Zapier không nhận được webhook
- Middleware gửi webhook
RESEND_RTRADER_INSTRUCTIONSnhưng Zapier trả về 5xx hoặc timeout (> 10s). - Mức độ nghiêm trọng: SEV-1 —
#ops-alerts. Nâng lên SEV-1 vì đây là kết nối bị hỏng giữa Middleware và bus tích hợp Zapier chính. - Retry: Middleware retry 3× với Exponential Backoff. Nếu cạn mọi retry → trạng thái được ghi là
Failed_Exhaustedtrong bảngfailed_webhooks_queue. - BPS Panel: Admin theo dõi lỗi tại tab "Failed Webhooks Monitor" trong BPS Admin Console. Mỗi hàng log hiển thị: timestamp, Trader ID, tên flow (Resend R|Trader Instructions — Flow 39), số lần retry (3/3), mã lỗi Zapier.
- Khôi phục: Một nút 🔄 Replay Webhook xuất hiện cạnh entry log. Khi Zapier được khôi phục, Admin nhấn nút để Middleware đẩy lại payload thủ công — không cần trader khởi động lại quy trình. (Nguồn: khách xác nhận 2026-09-09 — Zapier Integration V7.pdf pp.146–147 [Webhook Reliability: provider_event_id + correlation_id for replay/dedup], p.147 [Break-glass runbook mandate], p.198 [failed_webhooks_queue + Replay]; RFQ_ Stack Trading Prop Tech V7.pdf p.13 [CloudWatch → Slack SEV-1 alerting])
Exc-5 — Admin JWT không hợp lệ hoặc hết hạn
- Xác thực JWT của Middleware thất bại (token hết hạn, sai định dạng hoặc vai trò RBAC không đủ).
- Trả về
401 Unauthorized. - Không tiếp tục các gate check. Không gửi webhook. Không gửi email.
- Admin xác thực lại và thử lại.
Exc-6 — Tài khoản Rithmic chưa được cấp phát
- Middleware phát hiện
platform_username IS NULL OR platform_username = ''của trader đích. - Trả về
422 Unprocessable Entityvới thông báo:"Rithmic account has not been provisioned yet". - Không gửi webhook. Không gửi email.
- Admin kiểm tra trạng thái cấp phát của trader và escalate lên đội BE nếu việc cấp phát thất bại ở upstream.
Exc-7 — Trader không ở phase đang hoạt động
- Middleware phát hiện
account_statusKHÔNG phảiSIM_ACTIVEhoặcActive_DMA(ví dụGuest,Failed,Terminated,Locked,SUSPENDED_DEFENSE,In_Defense,Active_Challenge). - Trả về
422 Unprocessable Entityvới thông báo:"Trader is not in an active phase eligible for R|Trader instructions resend". - Không gửi webhook. Không gửi email.
- Lý do: các tài khoản ở những trạng thái này không có credential Rithmic đang hoạt động (sub-account chưa được gán hoặc bị soft-lock). Gửi hướng dẫn đăng nhập cho tài khoản không hoạt động thì không có template hợp lệ để chọn và gây nhầm lẫn cho trader + làm quá tải support desk. Áp dụng khớp template theo kiểu best-effort hoặc mặc định SES-45 có nguy cơ gửi sai hướng dẫn phân loại CME — vi phạm quy định. (Nguồn: khách xác nhận 2026-08-26 — RFQ_ Stack Trading Prop Tech V7.pdf §508, §516, §521, §522)
Exc-8 — Vượt giới hạn resend hằng ngày
- Middleware phát hiện trader đã nhận 3–5 lần resend thành công trong 24 giờ gần nhất.
- Trả về
429 Too Many Requestsvới thông báo:"Resend limit reached for this user. Please try again tomorrow." - Không gửi webhook. Không gửi email.
- Admin phải chờ cửa sổ 24 giờ được đặt lại trước khi thử lại. (Nguồn: khách xác nhận 2026-09-09)
BR_9.17.1 — Phạm vi chỉ Futures
Flow 39 chỉ áp dụng cho trader có asset_class = 'Futures'. Trader Forex (MT5/TE) không dùng Rithmic, không có thỏa thuận subscriber CME, và bị loại trừ rõ ràng. Middleware phải gate theo asset_class trước khi gửi webhook.
(Nguồn: Zapier Integration V7.pdf Flow 39 — các thỏa thuận CME/CBOT/NYMEX/COMEX là đặc thù của Rithmic)
BR_9.17.2 — Chỉ admin kích hoạt thủ công (phạm vi hiện tại) Flow 39 hiện là hành động BPS do admin kích hoạt thủ công. Hành vi tự động kích hoạt sau khi cấp phát Rebuy là câu hỏi mở đang chờ (F39-Q1/F39-Q2). Cho đến khi Rithmic xác nhận phạm vi gắn kết của thỏa thuận CME (User ID hay sub-account), không được triển khai auto-trigger. (Xem §6 — F39-Q1, F39-Q2)
BR_9.17.3 — Không bao giờ đưa mật khẩu vào email
Mật khẩu platform Rithmic của trader KHÔNG ĐƯỢC đưa vào bất kỳ payload email nào. Email phải hướng trader đến luồng lấy credential trên Dashboard (GET /platform-credentials sau Auth0 OTP). Đây là ràng buộc bảo mật tuyệt đối áp dụng cho mọi luồng.
BR_9.17.4 — Nội dung email: bắt buộc 4 thỏa thuận exchange Email phải bao gồm hướng dẫn ký cho cả 4 sàn CME Group: CME, CBOT, NYMEX, COMEX. Bỏ sót bất kỳ thỏa thuận nào dẫn đến việc kích hoạt luồng dữ liệu không đầy đủ cho exchange tương ứng. (Nguồn: Zapier Integration V7.pdf Flow 39 lines 2622–2623)
BR_9.17.5 — Bắt buộc ghi BPS_Audit_Log (tuân thủ CFTC/NFA)
Mỗi lần gọi POST /bps/resend-rtrader-instructions phải ghi một bản ghi bất biến vào bảng audit BPS trong PostgreSQL. Các trường bắt buộc: admin_id, user_id, action (RESEND_RTRADER_INSTRUCTIONS), timestamp, reason, transcript_url. Điều này đáp ứng yêu cầu lưu trữ hồ sơ của CFTC và NFA cho mọi hành động override quản trị và hỗ trợ được thực hiện qua Support Screen.
(Nguồn: khách xác nhận 2026-08-26 — mọi BPS POST endpoint đều thực thi giao thức audit)
BR_9.17.6 — [PENDING F39-Q1] CME agreement carry-over khi Rebuy
Nếu Rithmic xác nhận các thỏa thuận CME gắn với User ID (không phải sub-account), trader không cần ký lại sau Rebuy. Trong trường hợp này, Flow 39 KHÔNG nên tự động kích hoạt sau Rebuy, và việc kiểm tra không khớp GET /bps/trader/market-data-status là chỉ báo duy nhất để gửi lại. Nếu thỏa thuận gắn với sub-account → bắt buộc auto-trigger. Việc triển khai bị chặn chờ phản hồi từ Rithmic.
BR_9.17.7 — Template email phải khớp với phase tài khoản của trader
Middleware PHẢI xác định đúng template trước khi gửi webhook. Gửi SES-44 (hướng dẫn Non-Professional) cho trader Live DMA là rủi ro tuân thủ CME. Gửi SES-45 (hướng dẫn Professional) cho trader SIM sẽ gây nhầm lẫn không cần thiết. Việc chọn template là không thể thương lượng và phải dựa trên account_status tại thời điểm gọi.
BR_9.17.8 — Rate limiting: cửa sổ dedup + giới hạn hằng ngày
Middleware PHẢI thực thi hai lớp chống spam trên POST /bps/resend-rtrader-instructions:
- Cửa sổ dedup (Idempotency): Các request giống hệt cho cùng
user_idtrong cửa sổ 5 phút bị chặn im lặng — Middleware trả về200 OKmà không gửi webhook mới. Ngăn kịch bản click đúp ngoài ý muốn. - Giới hạn hằng ngày: Tối đa 3–5 lần resend thành công cho mỗi trader trong cửa sổ trượt 24 giờ. Vượt giới hạn trả về
429 Too Many Requestsvới thông báo:"Resend limit reached for this user. Please try again tomorrow."Không gửi webhook.
Lý do: bảo vệ sender reputation của SES (tỷ lệ bounce/complaint cao do gửi lặp lại tới cùng một địa chỉ có thể khiến AWS đình chỉ tài khoản) và ngăn các vòng lặp lỗi của admin làm ngập inbox của trader. (Nguồn: khách xác nhận 2026-09-09)
BR_9.17.9 — Trader tự phục vụ: admin_id và reason phải do hệ thống điền (UI 2026-10-20)
Khi Flow 39 được kích hoạt qua modal trên Dashboard của trader (nút "Resend Instructions"), frontend Dashboard KHÔNG ĐƯỢC yêu cầu trader nhập admin_id hay reason. Các trường này bắt buộc theo endpoint để tuân thủ BPS_Audit_Log. Frontend PHẢI tự động điền:
admin_id = "SYSTEM_TRADER_SELF_SERVICE"reason = "Trader self-initiated via Dashboard modal"
Điều này giữ nguyên audit trail bắt buộc mà không để lộ các trường admin cho trader.
BR_9.17.10 — Pop-up hiển thị mỗi lần vào tab "Command"
Với tài khoản Futures (account_status IN ('SIM_ACTIVE', 'Active_DMA')) có CME Market Data Agreements chưa ký, pop-up "Action Required: Exchange Agreements Pending" PHẢI hiển thị mỗi lần trader mở hoặc chuyển tới tab "Command". Tắt pop-up không lưu trạng thái; pop-up xuất hiện lại ở lần vào tiếp theo.
(Nguồn: Flow 39 update 2026-11-05)
BR_9.17.11 — Completion Gate: Signed_Completely
Khi backend xác nhận qua Rithmic rằng việc ký đã hoàn tất, hệ thống PHẢI lưu Signed_Completely = Yes. Khi cờ đã được đặt, hệ thống dừng kiểm tra ký trên Rithmic và không bao giờ hiển thị lại pop-up cho trader đó.
(Nguồn: Flow 39 update 2026-11-05)
6. Câu hỏi mở (đang chờ Rithmic làm rõ)
| # | Câu hỏi | Tác động | Trạng thái |
|---|---|---|---|
| F39-Q1 | Các thỏa thuận subscriber CME/CBOT/NYMEX/COMEX — gắn với Rithmic User ID hay sub-account? Sau Rebuy (sub-account mới, cùng User ID), trader có cần ký lại không? Trạng thái "Data Feed Locked" có xuất hiện lại cho sub-account mới không? | Quyết định liệu có cần auto-trigger Flow 39 sau Rebuy hay không. Ảnh hưởng BR_9.17.2 và BR_9.17.6. | 🔴 Mở — chặn thiết kế UX rebuy |
| F39-Q2 | Khi một sub-account mới được cấp phát vào User ID hiện có (kịch bản rebuy), R|Trader Pro có tự động nhắc lại màn hình ký thỏa thuận ở lần đăng nhập kế tiếp của trader — hay luồng dữ liệu chuyển thẳng sang Active vì User ID đã ký? | Quyết định liệu Flow 39 có cần auto-trigger trong chuỗi rebuy Flow 3H hay chỉ thủ công là đủ. | 🔴 Mở — chặn thiết kế luồng rebuy |
| F39-Q3 | Nếu GET /bps/trader/market-data-status trả về trạng thái luồng dữ liệu không khớp với DB entitlement (ví dụ DB báo CME=Active nhưng Rithmic báo CME=false), có API nào để force-sync hoặc re-provision luồng dữ liệu trực tiếp không — tách biệt với việc gửi email resend cho trader? | Quyết định liệu nút "Resend Instructions" có đủ để khắc phục không khớp, hay cũng cần một hành động admin force-sync. | 🟡 Mở — ảnh hưởng phạm vi BPS panel |
7. Tiêu chí chấp nhận
| ID | Kịch bản | Kết quả mong đợi |
|---|---|---|
| AC-9.17-01 | Admin gọi POST /bps/resend-rtrader-instructions với user_id hợp lệ của trader Futures SIM | Webhook RESEND_RTRADER_INSTRUCTIONS được gửi với template = 'SES-44'; Flow 39 gửi email SES-44 (hướng dẫn Non-Professional); trả về response { "status": "success" } |
| AC-9.17-02 | Admin gọi endpoint với user_id hợp lệ của trader Live DMA (Level 1+) | Webhook RESEND_RTRADER_INSTRUCTIONS được gửi với template = 'SES-45'; Flow 39 gửi email SES-45 (hướng dẫn Professional kèm cảnh báo tuân thủ); trả về response { "status": "success" } |
| AC-9.17-03 | Trader SIM nhận email (SES-44) | Subject = "Stack Trading: Market Data & R|Trader Pro Setup"; chứa platform_username; chứa link tải R|Trader Pro; hướng dẫn chọn Non-Professional; KHÔNG chứa mật khẩu |
| AC-9.17-04 | Trader Live DMA nhận email (SES-45) | Subject = "Stack Trading: Market Data & R|Trader Pro Setup"; chứa platform_username; chứa link tải R|Trader Pro; hướng dẫn chọn Professional; chứa cảnh báo tuân thủ CME; KHÔNG chứa mật khẩu |
| AC-9.17-05 | Admin gọi endpoint với user_id của trader Forex_MT5 hoặc Forex_TE | Middleware trả về 400 Bad Request; không gửi webhook; không gửi email |
| AC-9.17-06 | Admin gọi endpoint với user_id không tồn tại | Middleware trả về 404 Not Found; không gửi webhook; không gửi email |
| AC-9.17-07 | Admin gọi endpoint với JWT hết hạn hoặc không hợp lệ | Middleware trả về 401 Unauthorized; không gửi webhook; không gửi email |
| AC-9.17-08 | Admin gọi endpoint cho trader có platform_username là NULL hoặc rỗng | Middleware trả về 422 Unprocessable Entity với thông báo "Rithmic account has not been provisioned yet"; không gửi webhook; không gửi email |
| AC-9.17-09 | Flow 39 được kích hoạt — trạng thái DB sau khi thực thi | Bản thân Flow 39 không sửa trường nào trong bảng Users; trạng thái luồng dữ liệu không đổi cho đến khi trader hoàn tất việc ký trong R|Trader Pro |
| AC-9.17-10 | Admin gọi endpoint | Audit entry được ghi vào BPS_Audit_Log với admin_id, user_id, action: "RESEND_RTRADER_INSTRUCTIONS", reason, transcript_url, timestamp — bắt buộc theo yêu cầu lưu trữ hồ sơ CFTC/NFA |
| AC-9.17-11 | Admin gọi endpoint cho trader có account_status là Failed, Terminated, Guest, Locked, SUSPENDED_DEFENSE, In_Defense hoặc Active_Challenge | Middleware trả về 422 Unprocessable Entity với thông báo "Trader is not in an active phase eligible for R|Trader instructions resend"; không gửi webhook; không gửi email |
| AC-9.17-12 | Admin kích hoạt resend cho cùng user_id hai lần trong vòng 5 phút | Request thứ hai trả về 200 OK (thành công im lặng); không gửi webhook mới; không gửi email trùng lặp |
| AC-9.17-13 | Admin kích hoạt resend cho trader đã nhận số lần resend tối đa hằng ngày (3–5) trong 24 giờ | Middleware trả về 429 Too Many Requests với thông báo "Resend limit reached for this user. Please try again tomorrow."; không gửi webhook; không gửi email |
| AC-9.17-14 | AWS SES trả về Hard Bounce cho email của trader | email_delivery_status = 'Bounced' + email_banned = TRUE được đặt trên bản ghi trader; banner đỏ ⚠️ Email Delivery Blocked: Hard Bounce / Complaint Registered hiển thị trong BPS Panel; nút "Resend" bị làm xám; cảnh báo SEV-2 gửi tới #ops-alerts |
| AC-9.17-15 | Middleware gửi webhook nhưng Zapier trả về 5xx / timeout | Middleware retry 3× với exponential backoff; khi cạn retry: trạng thái Failed_Exhausted được ghi vào failed_webhooks_queue; entry hiển thị trong tab "Failed Webhooks Monitor" của BPS với nút 🔄 Replay Webhook; cảnh báo SEV-1 gửi tới #ops-alerts |
| AC-9.17-16 | Trader Futures có thỏa thuận CME chưa ký mở tab "Command" | Modal "Action Required: Exchange Agreements Pending" hiển thị với các nút "Resend Instructions" và "Cancel" |
| AC-9.17-16b | Trader tắt modal, rời tab "Command" rồi quay lại (thỏa thuận vẫn chưa ký) | Modal hiển thị lại |
| AC-9.17-17 | Trader nhấn "Resend Instructions" trên modal Dashboard | POST /resend-rtrader-instructions được gọi với admin_id = "SYSTEM_TRADER_SELF_SERVICE" và reason = "Trader self-initiated via Dashboard modal"; áp dụng cùng các gate check như đường admin; khi thành công: nút hiển thị trạng thái xác nhận; khi 429: lỗi inline hiển thị trong modal |
| AC-9.17-18 | Backend nhận xác nhận từ Rithmic rằng trader đã ký toàn bộ thỏa thuận CME | Lưu Signed_Completely = Yes; lần vào tab "Command" tiếp theo không hiển thị modal và không kích hoạt kiểm tra Rithmic |