Appearance
Module Overview — Affiliate Approval And Payout
1. Scope
Flow này không nằm gọn trong một page hay một bảng. Nó cắt qua:
- frontend affiliate routes cho approval, list, order, payment, customer detail subpages,
- backend
ecommerce-apiactions choaffiliateUserRegistration,affiliateUserChangeStatus,changeCustomerCommissionPercent,changeAffiliateWalletType, - Hasura metadata của domain
ecommerce, - wallet queries cho
wallet_balance,transaction_request,transaction, - report và user drill-down dùng lại cùng data surfaces.
Mental model nên giữ từ đầu:
affiliate_userlà hồ sơ và quyền CTV,order_affiliatelà ledger cấp order, chưa phải payout,invoice_affiliatelà ledger cấp payment/invoice,transaction_requestmới là runtime boundary để commission vào ví hoặc rút tiền ra khỏi ví.
2. Bức tranh kiến trúc
text
Affiliate registration
-> affiliate_user insert
-> by customer/anonymous: pending inactive
-> by staff/user: auto-activate ngay nếu đủ tag
Affiliate approval
-> affiliateUserChangeStatus(type=user)
-> create/find customer account
-> activate/revoke affiliate_user
-> write affiliate_action_log
Order commission
-> order insert / referral handling tạo order_affiliate + invoice_affiliate + transaction_request draft/pending
-> staff approve/reject order_affiliate
-> staff approve/reject invoice_affiliate
-> approve invoice => flip transaction_request status success + credit commission
Affiliate payout
-> customer detail withdraw form
-> refundRequestCreate on wallet transaction_request
-> history cards đọc transaction + transaction_request + wallet_stats3. Frontend Surface
3.1 Route families
| Family | Route group | Boundary |
|---|---|---|
| Approval shell | /affiliate/approval | Queue các CTV chưa active |
| Affiliate list | /affiliate/list | Danh sách CTV active, edit, referral detail |
| Order shell | /affiliate/order | Commission cấp order, approve/reject, detail |
| Payment shell | /affiliate/payment | Commission cấp invoice/payment, approve/reject, detail |
| Customer embedded views | user/customer-detail/collaborators/* | Reuse order/payment/withdraw flows trong customer workspace |
3.2 Runtime map thực tế
| Boundary | Ghi chú |
|---|---|
| Root redirect | POS branch có thể vào thẳng list, admin flow mặc định vào approval queue |
| Create/edit reuse | ListCreate được mount cho cả approval edit, create mới và edit affiliate |
| Action popup reuse | Cả user/order/invoice đều dùng chung mutation ChangeAffiliateUserStatus, chỉ khác type và new_status |
| Payment vs withdraw | PaymentList chỉ là invoice commission; withdraw history lại đọc transaction_request riêng |
4. Backend Boundary Map
| Lớp | File chính | Vai trò |
|---|---|---|
| Action | affiliate_user_registration.go | Đăng ký CTV theo role caller, create account nếu cần |
| Action | affiliate_user_change_status.go | State machine cho user, order, invoice; ghi action log |
| Action | change_customer_commission_percent.go | Sửa commission percent trên order/invoice chưa approve |
| Action | change_affiliate_wallet_type.go | Đổi ví nhận commission và recalc order/invoice chưa approve |
| Event | order_insert.go | Tạo order_affiliate khi order đủ điều kiện referral/affiliate |
| Wallet boundary | refundRequestCreate + wallet queries | Tạo withdraw request và đọc lịch sử rút tiền |
5. Data Layer Map
| Layer | Object chính | Vai trò |
|---|---|---|
| Affiliate profile | affiliate_user | Hồ sơ CTV, KYC, bank info, active/tag state |
| Referral linkage | affiliate_user_log | Ai được affiliate bởi ai, thời điểm nào |
| Approval/action audit | affiliate_action_log | History approve/revoke/reject/update percent |
| Order commission | order_affiliate | Pending/approved/rejected commission cấp order |
| Invoice commission | invoice_affiliate | Pending/approved/rejected commission cấp payment |
| Config | affiliate_config | Enable matrix theo order_kind và payment_method_id |
| Wallet runtime | transaction_request, transaction, wallet_balance, wallet_stats | Commission materialization và payout/withdraw |
6. Semantic Rules Cần Nhớ
affiliateUserRegistrationcó hai semantics khác nhau:user/adminpath auto-activate, còncustomer/anonymouspath chỉ insert pending record.affiliateUserChangeStatuslà state engine chung nhưng không đối xứng hoàn toàn:userhỗ trợapprovevàrevoke,orderhỗ trợapprove,cancel,reject,unreject,invoicehỗ trợapprove,reject,unreject, còncancelbị chặn.
- Approve
invoice_affiliatemới là điểm commission đi vào wallet, vì handler gọiAddAffiliateCommissionvà settransaction_request.status = success. - Withdraw history của affiliate không đọc
invoice_affiliate; FE lọctransaction_requesttheotype = Wvàbehavior_id = refund_collaborator. - Các list order/payment cố ý vẫn hiển thị hồ sơ inactive nếu commission đã được approve, để giữ historical visibility.
7. Rủi ro / Findings
| ID | Mức | Finding |
|---|---|---|
| F-01 | P0 | affiliateUserRegistration cho role user/admin auto-activate ngay (is_active = true, approved_at = now), nên approval queue không phải là cửa duy nhất để một CTV đi vào trạng thái active. |
| F-02 | P0 | Approve invoice phụ thuộc invoice_affiliate.transaction_id đã tồn tại sẵn; nếu transaction request thiếu hoặc lệch state, flow commission materialization sẽ fail ở AddAffiliateCommission. |
| F-03 | P1 | Cùng một output code như ALREADY_APPROVED được reuse cho user/order/invoice, nên nếu chỉ nhìn response code từ FE sẽ thiếu ngữ cảnh reference type. |
| F-04 | P1 | PaymentList và OrderList cố tình include affiliate đã inactive nếu record commission đã approved; đây là semantics lịch sử đúng, nhưng rất dễ bị hiểu nhầm là bug filter. |
| F-05 | P1 | Withdraw/payout chạy ở wallet boundary với refund_collaborator, nên reconciliation nếu chỉ test trên affiliate payment pages sẽ thiếu một nửa runtime thật. |
| F-06 | P2 | CreateCustomer set một số default cứng như gender = male và wallet_receive_commission = commission, cho thấy affiliate approval còn gánh side effects account bootstrap ngoài phạm vi affiliate thuần. |