Skip to content

Type Deep Dive — Refund / Withdraw Request Family

1. Scope

File này chỉ nói về family đi qua wallet.transaction_request:

  • refund_order
  • refund_order_cosmetic
  • refund_topup
  • refund_collaborator
  • refund_commission như child request

Không bao gồm negative payment. Boundary đó được tách riêng tại type-negative-payment-boundary.md.

2. Frontend Entry Points

2.1 Route families

Route familyĐối tượngGhi chú
ROUTE_WITHDRAW_REQUEST*POSmoduleId = refund_request_management
ROUTE_REQUEST_MANAGEMENT_WITHDRAW*Admin / fund request managementmoduleId = fund_request_management

Tuy tên route là “withdraw”, page set này thực tế đang gánh toàn bộ refund family.

2.2 Page responsibilities

PageVai trò
WithdrawRequest.tsxList + load approvers
WithdrawRequestCreate.tsxTạo / sửa request
WithdrawRequestDetail.tsxXem chi tiết + approve/reject/cancel + tạo refund commission child

2.3 Query / mutation chính

OperationDùng ở đâuVai trò
GetTransactionRequestByPkDetailĐọc request
GetTransactionRequestRefundsDetailĐọc child refund
RefundRequestCreateCreate / editUpsert request
ChangeStatusTransactionRefundDetailApprove / reject / cancel
UploadFileRefundDetailUpload file chứng từ
GetOrderCommissionRefundDetail dialogNguồn tạo refund_commission

3. FE Runtime Flow

text
User mở list refund/withdraw
  -> query transaction_request theo family withdraw types
  -> mở detail
  -> FE tính canApprove từ approver chain
  -> approve/reject/cancel qua changeStatusTransaction
  -> reload detail + child refunds + logs

Nếu cần truy thu commission
  -> mở CommissionRefundConfirmForm
  -> query order_commission_refund
  -> tạo child request refund_commission với parent_id

4. Create / Update Rules

4.1 Upsert request

WithdrawRequestCreate.tsx đang upsert transaction_request với các default đáng chú ý:

  • type = "W"
  • status = "R"
  • default behavior_id = "refund_topup" nếu người dùng chưa chọn nhánh khác

Điểm này quan trọng vì:

  • page create không chỉ dành cho topup,
  • nhưng default hiện tại khiến người đọc code dễ hiểu nhầm scope create page.

4.2 List filter

List refund/withdraw đang lọc:

  • type = "W"
  • activity is null
  • behavior_id in WITHDRAW_REQUEST_TYPES

Nguồn: components/withdraw-request/WithdrawRequestTable.tsx

5. Approval Runtime Rules

FE hiện đang tự build approver chain từ useGetApproversQuery và map:

  • approver_step_1
  • approver_step_2

Logic này đang lặp ở:

  • WithdrawRequest.tsx
  • WithdrawRequestDetail.tsx
  • WithdrawRequestSelectStatus.tsx

Rule quan trọng

  • approve chỉ khả dụng khi request ở trạng thái R
  • FE đang xác định canApprove bằng created_by của request kết hợp approver chain

Rủi ro

Đây là một dấu hiệu bug/risk rõ:

  • actor được so bằng created_by của request,
  • thay vì current user hiện tại.

Hệ quả:

  • có thể bật/tắt sai nút approve,
  • khó đồng bộ với backend permission thật.

6. Backend Action Path

Action trung tâm: wallet-api/action/change_status_transaction_refund.go

Dispatch theo behavior

BehaviorHandler
refund_orderhandlerRefundOrder
refund_order_cosmetichandlerRefundOrderCosmetic
refund_collaboratorhandlerRefundCollaborator
refund_topuphandlerRefundTopup

Transition gate

  • action đọc request hiện tại,
  • check validStatusTransactionRefund,
  • lấy reviewers current/next step,
  • chỉ cho người tạo cancel, reviewer approve, reviewer chain reject.

Điểm cần ghi rõ

validateTransactionRequest() đang bị comment toàn bộ, nên gate cũ theo accounting/group hiện không còn hiệu lực.

7. Backend Event Path

Event quan trọng nhất: wallet-api/event/transaction_request_update.go

Đây là nơi nằm phần lớn side effects:

  • upsert / sync refund log,
  • update customer_revenue,
  • wallet credit cho khách khi refund vào ví,
  • refund point,
  • notification approved,
  • commission side effects.

Điều này có nghĩa:

  • đọc riêng action approve là chưa đủ,
  • mọi doc/test case phải mô hình hóa cả event path.

8. Variant Notes

8.1 refund_order

  • Áp cho service order.
  • Khi success có thể force complete hoặc cancel order tùy trạng thái order.
  • Sau approve còn có logic cancel các request refund khác cùng order/branch.

8.2 refund_order_cosmetic

  • Áp cho product order.
  • Khi success có thể đổi order sang canceled hoặc returned.
  • Có dấu hiệu semantics chưa sạch ở phần commission side effect.

8.3 refund_topup

  • Vẫn là wallet request family.
  • Có thể dính split giữa VNDVND_PROMOTION ở downstream logic.

8.4 refund_collaborator

  • Dùng cho nhánh collaborator / payout.
  • Có thêm approved notification riêng.

8.5 refund_commission

  • Không có route riêng.
  • Tạo từ dialog con CommissionRefundConfirmForm.
  • Là child request gắn parent_id về request cha.

9. Data Touchpoints

ObjectVai trò trong flow
wallet.transaction_requestRequest source of truth
wallet.transaction_request_userWallet legs sender/receiver
wallet.transaction_request_logLog action
wallet.transactionLedger when success
wallet.order_commission_refundSource để build child refund commission
wallet.wallet_reference_fileFile chứng từ
ecommerce.transaction_refund_logAudit/read model
ecommerce.orderRefund flags / amount / refunded_at

10. Rủi ro / Findings

IDMứcFinding
RR-01P1FE page set tên là “withdraw” nhưng đang gánh nhiều refund behaviors, dễ gây naming drift.
RR-02P1canApprove FE có dấu hiệu check sai actor.
RR-03P1reference_id không canonical nên downstream join rất dễ sai.
RR-04P1is_refunded_order có thể bị set sớm hơn success thật.
RR-05P1Cosmetic refund có semantics commission không đồng nhất với service refund.