SRS: BPS — Manual Payout Hold (UC 10.4.6)
| Field | Value |
|---|---|
| BA in Charge | Anh Hoang |
| Date Created | 2026-11-04 |
| Version | v1.9 |
| Document References | Zapier Integration V7.pdf (Break-Glass Operations — Manual Payout Holds ("Do Not Pay" list) · Operational Security Requirement · §Flow 5, Flow 9, Flow 17, Flow 36)ADMIN BPS OPERATIONS.xlsx (tab "QnA (Lily)" — Manual Payout Hold: RBAC, Input, Output, Logic Requirements)QnA_BPS_AnhHoang_UC_10.2-10.4.md (AH-HOLD-01 → AH-HOLD-19) |
UC_10.4.6 — Manual Payout Hold
🇻🇳 Section này viết bằng tiếng Việt để BA review. Mọi ID, anchor, tên field/API và text trả về giữ nguyên tiếng Anh.
1. Overview
| Field | Content |
|---|---|
| ID | UC_10.4.6 |
| Use Case | Manual Payout Hold |
| Description | Operations / Compliance dùng endpoint này để chặn ngay mọi khoản tiền trả ra cho một trader, cả lúc vận hành bình thường (VD đang điều tra nghi gian lận hoặc vi phạm rule) và khi có sự cố (break-glass). |
| Phạm vi | Endpoint thêm / gỡ hold, và quy tắc các luồng trả tiền phải kiểm tra hold. Không gồm: khoá tài khoản trading (Admin làm riêng nếu cần), lương cơ bản W-2 (US) định kỳ, vì khoản này chạy trực tiếp trong Rippling và do HR quản lý trong Rippling; UI trên Trader Dashboard, màn hình BPS Admin. |
| Zapier Flow | Không có Zap riêng. Hold được kiểm tra trong Flow 5, Flow 9, Flow 17 và cron International Retainer (Flow 36) |
| Zapier Table | — (đọc / ghi bảng PostgreSQL Users, BPS_Audit_Log) |
| API | BPS endpoint Manual Payout Hold (POST) |
| Cron Job task | Không có |
| 3rd Party | Slack (hold chặn lệnh gọi Dots, Rippling) |
2. Trigger
- Operations / Compliance cần chặn tiền trả ra cho 1 trader (VD đang điều tra nghi gian lận). Level 2 Admin thêm hold trên BPS Admin console (màn hình quản trị nội bộ của Stack Trading, do team Stack Trading tự làm. Admin đăng nhập màn này để tìm trader và bấm các thao tác hỗ trợ; mỗi thao tác gọi tới endpoint do Sotatek làm).
- Điều tra xong, Level 2 Admin gỡ hold.
3. Pre-conditions
- Admin đã đăng nhập BPS Admin console với role Level 2 Admin hoặc System Admin (BR_10.4.6.1).
- Trader đã có trong
Users.
4. Post-conditions
- Thêm hold:
Users.payout_hold = TRUE. Từ lúc này, mọi khoản payout tự động chưa gửi đi của trader đều bị giữ lại. - Gỡ hold:
Users.payout_hold = FALSE. Các khoản đang giữ được trả lại theo BR_10.4.6.7. - Trạng thái tài khoản trading không đổi. Trader không nhận email, Dashboard không đổi gì.
BPS_Audit_Logcó 1 dòng;#ops-generalcó tin báo.
5. Basic Flow
5.1 Add/remove a trader from a payout hold list (Logic Requirement 1)
Stage 1 — Nhận request
-
BPS Admin console gọi Manual Payout Hold (
POST, path do BE xác định) với headerAuthorization: Bearer <access-token>(mã đăng nhập của Admin, cách hoạt động xem BR_10.4.6.1) và input. Nguồn ghi Input là "None"; input dưới đây theo câu trả lời của khách:Input Bắt buộc Value user_id✅ UUID của trader action✅ 'HOLD'(thêm hold) hoặc'RELEASE'(gỡ hold)reason✅ Lý do Admin nhập (xem §5.3) transcript_url✅ URL tới Freshdesk ticket admin_idlấy từ JWT (hoặc payload).Output Khi nào 200 OKThêm / gỡ hold thành công -
Endpoint xác thực token, kiểm tra role trong JWT phải là
Level_2hoặcSystem_Admin, và kiểm tra input theo BR_10.4.6.1. Không đạt thì xem E_10.4.6.1, E_10.4.6.2, E_10.4.6.3.
Stage 2 — Kiểm tra điều kiện hợp lệ để Admin thêm / gỡ hold
- Endpoint tìm trader theo
user_id. Không thấy → E_10.4.6.4. - Endpoint đọc
Users.payout_hold(BR_10.4.6.4):action = 'HOLD'mà trader đang bị hold → E_10.4.6.5.action = 'RELEASE'mà trader không bị hold → E_10.4.6.6.
Stage 3 — Thêm / gỡ hold
HOLD→Users.payout_hold = TRUE.RELEASE→Users.payout_hold = FALSE, các khoản đang giữ được trả lại theo BR_10.4.6.7. Trạng thái tài khoản trading không đổi (BR_10.4.6.8).
Stage 4 — Ghi log và trả kết quả
- Endpoint ghi
BPS_Audit_Logvớiaction = 'PAYOUT_HOLD'hoặc'PAYOUT_RELEASE'(BR_10.4.6.2). - Endpoint trả
200 OK. - Endpoint gửi tin
✅ OPERATIONS ACTION PERFORMEDvào#ops-general(BR_10.4.6.3). Không gửi email cho trader.
5.2 Halt payout triggers and prevent payout execution even if prior events replay (Logic Requirement 2)
Stage 5 — Các luồng trả tiền kiểm tra hold
-
Trước khi gửi một khoản payout sang Dots API hoặc Rippling Off-Cycle API, luồng trả tiền kiểm tra trader có đang bị hold không (
Users.payout_hold). Đang bị hold thì không gửi khoản đó (BR_10.4.6.5). Vì kiểm tra ngay lúc sắp gửi tiền, nên dù một sự kiện trả tiền cũ bị chạy lại, khoản đó vẫn bị chặn (E_10.4.6.8). Các luồng có bước kiểm tra này:Luồng Chỗ kiểm tra Flow 5 — thưởng lên cấp, Data Refund, hoàn phí Sierra Chart Step 2 Compliance Check Flow 9 — Severance Trước lệnh trả tiền Flow 17 — Pod Leader Carry Vesting Trong POST /process-pod-vesting(UC_8.2.12)Flow 36 — Dots retainer hằng tháng của International Contractor Trong cron International Retainer (UC_8.3.1)
Stage 6 — Giữ khoản tiền đến hạn
payout_hold = TRUE→ luồng bỏ qua khoản của trader này và giữ khoản đó ở trạng thái chờ (BR_10.4.6.6):- Flow 17: bỏ qua leader này (giống leader bị lỗi routing Dots), giữ nguyên
Pending_Carry_Balance, không đưa về 0. - Flow 5, Flow 9, Flow 36: bản ghi payout giữ
status = 'pending'/'held'trong bảngpayouts, không huỷ, không xoá. - ⚠️ Dev kiểm tra: schema Zapier V7 §3.1 chưa có bảng
payouts; nơi lưu payout đang chờ khách trả lời ở STAGE5-039. - Gửi cảnh báo vào
#ops-treasurycho mỗi khoản bị bỏ qua do hold.
- Flow 17: bỏ qua leader này (giống leader bị lỗi routing Dots), giữ nguyên
- Khoản đã gửi sang Dots / Rippling trước lúc thêm hold thì không chặn được nữa; hệ thống không huỷ hay đảo lại khoản đó, Stack Trading tự chấp nhận (E_10.4.6.7).
5.3 Require a reason code and supervisor approval policy (if applicable) (Logic Requirement 3)
Stage 7 — Lý do và phê duyệt
- Không có danh sách reason code cố định:
reasonlà text tự do, bắt buộc, đã kiểm ở bước 2. Không có quy trình duyệt của người thứ 2 (supervisor): 1 lần gọi của Level 2 Admin / System Admin là có hiệu lực ngay (BR_10.4.6.1).
6. Exceptional Flows
Mọi trường hợp thất bại ở §5.1 đều ghi BPS_Audit_Log (BR_10.4.6.2) và báo #ops-general (BR_10.4.6.3).
| Exception ID | Exception | Condition | System Handling / Fallback |
|---|---|---|---|
| E_10.4.6.1 | Token không hợp lệ | Không có token, hoặc token không xác thực được. | Từ chối, không thực thi. |
| E_10.4.6.2 | Không đủ quyền | JWT có role Level_1. | Trả 403 Forbidden. |
| E_10.4.6.3 | Input không hợp lệ | Thiếu reason hoặc transcript_url, hoặc action khác 'HOLD' / 'RELEASE'. | Trả 400 Bad Request. Không kiểm tra nội dung reason. |
| E_10.4.6.4 | Không tìm thấy trader | user_id không có trong Users. | Trả 404. |
| E_10.4.6.5 | Thêm hold khi đang hold | action = 'HOLD' và payout_hold = TRUE. | Trả 409 "Trader is already on payout hold". |
| E_10.4.6.6 | Gỡ hold khi chưa hold | action = 'RELEASE' và payout_hold = FALSE. | Trả 409 "Trader is not on payout hold". |
| E_10.4.6.7 | Thêm hold sau khi tiền đã gửi đi | VD trader lên cấp lúc 10:00, hệ thống đã gửi lệnh trả thưởng sang Dots; 10:05 Admin mới thêm hold. | Hold chỉ chặn khoản chưa gửi. Khoản đã gửi sang Dots / Rippling không bị huỷ hay đảo lại; Stack Trading tự chấp nhận khoản đó. |
| E_10.4.6.8 | Sự kiện cũ chạy lại | Webhook trả tiền cũ chạy lại (replay) trong lúc trader đang bị hold. | Bị chặn, vì hold được kiểm tra ngay trước lệnh gọi Dots / Rippling, không phải lúc nhận sự kiện (BR_10.4.6.5). |
| E_10.4.6.9 | Ghi BPS_Audit_Log lỗi | Insert vào BPS_Audit_Log không thành công. | Ghi lỗi vào server log (CloudWatch), không chặn response. |
7. Business Rules
| BR_ID | Rule Description |
|---|---|
| BR_10.4.6.1 | Xác thực, quyền và input: Endpoint chỉ nhận request từ BPS Admin có token trong header Authorization: Bearer <access-token>. Cách hoạt động:1. Admin đăng nhập BPS Admin console, Auth0 (dịch vụ đăng nhập) trả về một mã đăng nhập (token).2. Admin bấm thao tác, BPS gửi yêu cầu sang endpoint, kèm mã đăng nhập đó.3. Endpoint kiểm tra mã: mã đúng thì biết ai đang thao tác (VD adam@stacktrading.com) và làm tiếp; mã sai hoặc hết hạn thì từ chối.admin_id lấy từ JWT (hoặc payload).Quyền gọi theo role:- Level 1 Admin: ❌ không được gọi, endpoint trả 403 Forbidden.- Level 2 Admin: ✅ được gọi.- System Admin: ✅ được gọi.Endpoint tự kiểm tra role trong JWT (RBAC Gate). Không có quy trình duyệt 2 người: 1 lần gọi của Level 2 / System Admin là có hiệu lực ngay. user_id, action, reason, transcript_url bắt buộc; reason là text tự do. |
| BR_10.4.6.2 | Audit log: Mỗi request, thành công hay thất bại, ghi 1 dòng vào BPS_Audit_Log (PostgreSQL) và server log (CloudWatch) với đủ 6 field: admin_id, user_id, action ('PAYOUT_HOLD' hoặc 'PAYOUT_RELEASE'), timestamp, reason, transcript_url, kèm kết quả success / failure. BPS_Audit_Log cũng là lịch sử thêm / gỡ hold của trader.⚠️ Dev kiểm tra: bảng bps_audit_logs chưa có cột transcript_url và kết quả.Insert lỗi thì không chặn luồng (E_10.4.6.9). |
| BR_10.4.6.3 | Slack: Mỗi request từ BPS Admin báo kết quả vào #ops-general (Ref: slack_channel_usage.md) theo mẫu Slack BPS, gồm 2 loại tin:- Thành công: dòng đầu ✅ OPERATIONS ACTION PERFORMED.- Thất bại (bất kỳ bước nào không thực hiện được): dòng đầu 🚨 FAILED OPERATION ACTION.Các dòng sau giống nhau ở cả 2 loại:- WHEN: thời điểm thực hiện, dạng ISO timestamp- BY WHO: email của admin, lấy từ token- ACTION: Manual Payout Hold- ACCOUNT: email của trader- CHANGE: payout_hold trước và sau: thêm hold FALSE to TRUE, gỡ hold TRUE to FALSE- REASON: giá trị reason; không có thì ghi NULL- TICKET: mã ticket kèm link Freshdesk (transcript_url); không có thì ghi NULLVD tin thành công:✅ OPERATIONS ACTION PERFORMEDWHEN: 2026-11-04T09:30:00ZBY WHO: adam@stacktrading.comACTION: Manual Payout HoldACCOUNT: hana@sotatek.comCHANGE: FALSE to TRUEREASON: Fraud investigationTICKET: 12345 https://stacktrading.freshdesk.com/a/tickets/12345Tin thất bại có cùng các dòng, chỉ đổi dòng đầu. BPS_Audit_Log ghi đúng kết quả thành công / thất bại tương ứng. Khoản payout bị bỏ qua do hold thì báo riêng vào #ops-treasury (§5.2 bước 10). |
| BR_10.4.6.4 | Nơi lưu hold: Cột boolean Users.payout_hold (mặc định FALSE). Không tạo bảng riêng; lịch sử thêm / gỡ lấy từ BPS_Audit_Log. Hold không tự hết hạn, không có cron nhắc: còn hiệu lực tới khi admin gọi action = 'RELEASE'. |
| BR_10.4.6.5 | Hold chặn những khoản nào, kiểm tra ở đâu: Chặn mọi payout tự động do backend / Zapier kích hoạt:- Flow 5: thưởng lên cấp (Promotion Bonuses), Data Refund, hoàn phí Sierra Chart.- Flow 9: Severance.- Flow 17: Pod Leader Carry Vesting.- Dots retainer định kỳ của International Contractor (Flow 36).Lương cơ bản W-2 (US) định kỳ chạy trực tiếp trong Rippling và do HR quản lý trong Rippling, không do backend / Zapier kích hoạt.Cách chặn: ngay trước khi gửi khoản tiền sang Dots API hoặc Rippling Off-Cycle API, luồng kiểm tra Users.payout_hold; đang hold thì không gửi. Khoản đã gửi sang Dots / Rippling trước đó thì không chặn được, Stack Trading tự chấp nhận. Vì kiểm tra ngay lúc sắp gửi tiền nên sự kiện trả tiền cũ chạy lại cũng bị chặn. |
| BR_10.4.6.6 | Khoản đến hạn trong lúc hold: Giữ lại, không huỷ, không xoá. Flow 17 giữ nguyên Pending_Carry_Balance; các luồng còn lại giữ bản ghi payout ở status = 'pending' / 'held'. Hold chỉ chặn khoản chưa gửi đi; khoản đã gửi sang Dots / Rippling thì không đảo lại. |
| BR_10.4.6.7 | Sau khi gỡ hold: Day 1 không làm cơ chế trả gộp mới.- Carry của Pod Leader được trả ở kỳ Flow 17 tiếp theo (ngày 1 hằng tháng).- Bản ghi payout đang treo trong bảng payouts: cách trả lại đang chờ chốt (§8 Open Items #1). |
| BR_10.4.6.8 | Những gì hold không động tới: Trạng thái tài khoản trading (trader vẫn trade bình thường); email cho trader; UI Trader Dashboard (tab Billing, Career & HR); ledger công khai Polygon (chỉ ghi khi payout thực sự đã được trả, không ghi sự kiện hold). |
8. Open Items
| # | Hạng mục | Trạng thái |
|---|---|---|
| 1 | Trả lại khoản payout đang treo sau khi gỡ hold. Adrian: chạy lại Flow 5 cho bản ghi đó, hoặc để Operations xử lý tay. | ⏳ Cần BA chốt |
9. Phân chia trách nhiệm
| Hạng mục | Sotatek build | Stack Trading Ops | Bên thứ 3 |
|---|---|---|---|
Endpoint Manual Payout Hold: RBAC gate, kiểm tra, cập nhật Users.payout_hold, audit log, Slack | ✅ | — | — |
Kiểm tra hold trước lệnh trả tiền ở Flow 5, Flow 9, Flow 17, Flow 36; giữ khoản ở trạng thái chờ; cảnh báo #ops-treasury | ✅ | — | — |
| Quyết định thêm / gỡ hold, điều tra | — | ✅ (Operations / Compliance) | — |
| Lương cơ bản W-2 (US) định kỳ | — | ✅ (HR) | Rippling |
Changelog
| Date | Version | Updated item | Before | After | Notes |
|---|---|---|---|---|---|
| 2026-11-04 | v1.9 | §5 tên Stage kiểm tra | "Kiểm tra" | "Kiểm tra" → "Kiểm tra điều kiện hợp lệ để Admin thêm / gỡ hold" | BA review 2026-11-04: tên Stage kiểm tra ghi rõ là kiểm tra điều kiện hợp lệ cho thao tác của Admin |
| 2026-11-04 | v1.8 | §1 Phạm vi, §5 bước 9 và 11, E_10.4.6.7, BR_10.4.6.5, §9 | Bước 9 viết khó hiểu ("đọc Users.payout_hold ngay trước khi gọi…"); ghi "Không chặn lương W-2 US chạy trong Rippling" | Bước 9 viết lại: trước khi gửi khoản payout sang Dots / Rippling thì kiểm tra hold, đang hold thì không gửi; khoản đã gửi trước đó thì Stack Trading tự chấp nhận. Phần W-2 ghi đúng ý khách: lương cơ bản W-2 (US) định kỳ chạy trực tiếp trong Rippling, do HR quản lý, không do backend / Zapier kích hoạt | BA review 2026-11-04; AH-HOLD-08, AH-HOLD-11 |
| 2026-11-04 | v1.7 | §5 bước 1 | Ghi header Authorization: Bearer <access-token> nhưng không trỏ tới phần giải nghĩa | Thêm "(mã đăng nhập của Admin, cách hoạt động xem BR_10.4.6.1)" ngay sau header | BA review 2026-11-04 |
| 2026-11-04 | v1.6 | §1 Overview (thêm dòng Cron Job task), §3 Pre-conditions, §5 Basic Flow, BR phân quyền, toàn file | §5 chia Stage theo cách BA tự đặt (Nhận request / Kiểm tra / Thực hiện), không theo Logic Requirements của nguồn; người thực hiện ghi là "Ops"; quyền ghi chung chung | §5 chia mục 5.x theo đúng Logic Requirements ở cột B của tab "QnA (Lily)", mỗi mục có Stage và bước đánh số liên tục; phần khách trả lời thêm ghi ngay trong bước tương ứng; người thực hiện ghi là "Admin"; quyền ghi theo từng role (Level 1 Admin, Level 2 Admin, System Admin) | BA duyệt khuôn UC_10.2.4, 2026-11-04 |
| 2026-11-04 | v1.5 | BR_10.4.6.1 | Giải nghĩa token viết liền 1 đoạn, còn chữ "header", "Bearer" | Viết lại thành 3 bước dễ hiểu: Admin đăng nhập → Auth0 trả mã đăng nhập (token); BPS gửi yêu cầu kèm mã; endpoint kiểm tra mã, đúng thì biết ai thao tác và làm tiếp, sai / hết hạn thì từ chối | BA review 2026-11-04 |
| 2026-11-04 | v1.4 | BR_10.4.6.1 | Ghi header Authorization: Bearer ..., không giải nghĩa | Thêm giải nghĩa: token Auth0 cấp khi Admin đăng nhập BPS, BPS gửi kèm mỗi request, endpoint kiểm tra token và đọc ra Admin nào đang gọi | BA review 2026-11-04; nguồn: câu trả lời của Adam (tab "QnA (Lily)", cột D dòng 3) |
| 2026-11-04 | v1.3 | §2 Trigger | Ghi "BPS Admin console", không giải nghĩa | Thêm giải nghĩa trong ngoặc ở lần nhắc đầu tiên: màn hình quản trị nội bộ của Stack Trading do team Stack Trading tự làm, Admin dùng để tìm trader và bấm thao tác, mỗi thao tác gọi endpoint do Sotatek làm | BA review 2026-11-04 |
| 2026-11-04 | v1.2 | §1 Overview — Description | BA tự tóm tắt luồng xử lý của endpoint | Ghi đúng câu trả lời của khách cho câu hỏi Purpose (AH-HOLD-01) | BA yêu cầu: Description = câu trả lời Purpose trong file Q&A |
| 2026-11-04 | v1.1 | BR_10.4.6.3 (Slack), §8 Open Items | Chỉ ghi gửi #ops-general theo mẫu Slack BPS chung; mẫu chưa có (Open Item) | Ghi đủ mẫu tin thành công (✅ OPERATIONS ACTION PERFORMED) và thất bại (🚨 FAILED OPERATION ACTION): WHEN, BY WHO, ACTION Manual Payout Hold, ACCOUNT, CHANGE, REASON, TICKET (không có thì NULL). Đóng Open Item mẫu Slack | Mẫu Slack BPS của BA Chloe, BA cung cấp 2026-11-04 |
| 2026-11-04 | v1.0 | Toàn bộ file | — | Tạo mới UC_10.4.6 theo khuôn UC_1.8.7, dựa trên runbook Manual Payout Holds của Zapier V7 và câu trả lời của khách | QnA_BPS_AnhHoang_UC_10.2-10.4.md (AH-HOLD-01 → 19) |