fix: move withdrawal commission deduction from application to approval
Build docker and publish / build (20.15.1) (push) Failing after 8m28s

- commissionWithdrawLogic: remove upfront commission deduction;
  balance check now includes sum of all pending withdrawals to prevent
  double-spending; transaction only creates the withdrawal record (status=0)
- approveWithdrawal: add FOR UPDATE lock on user row, balance check before
  deducting, atomic commission decrement and commission log inside one
  transaction; clear user cache after commit
- rejectWithdrawal: remove commission refund and log — commission was never
  deducted on application under the new flow
- add migration 02150: refund commission for existing status=0 withdrawals
  that were deducted under the old logic; includes rollback script

Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
2026-05-24 19:38:59 -07:00
parent 3bbce5ce84
commit 80751eb8ef
7 changed files with 243 additions and 58 deletions
@@ -0,0 +1,19 @@
-- Rollback: re-deduct commission for users with pending (status=0) withdrawals.
-- This re-applies the OLD behaviour where commission is deducted on application.
-- Only run this if you are rolling back to the old code; do NOT run against
-- the new code or commission will be double-deducted on approval.
UPDATE `user` u
JOIN (
SELECT user_id, COALESCE(SUM(amount), 0) AS pending_total
FROM user_withdrawal
WHERE status = 0
GROUP BY user_id
) p ON u.id = p.user_id
SET u.commission = u.commission - p.pending_total
WHERE p.pending_total > 0;
-- Remove the migration log entries written by the up migration.
DELETE FROM system_log
WHERE type = 3
AND content LIKE '%migration: refund pending withdrawal commission (HIF-22)%';
@@ -0,0 +1,45 @@
-- Migration: refund commission for existing pending (status=0) withdrawals
--
-- Under the old logic, commission was deducted when a withdrawal was submitted.
-- Under the new logic, commission is only deducted on approval.
-- This migration refunds the deducted amounts back to each user so that
-- the system is in a consistent state before the new code is deployed.
--
-- Idempotency: the UPDATE only touches rows whose commission would need
-- to increase, and each execution produces the same result because
-- COALESCE(SUM(amount),0) is deterministic given the same pending set.
-- Running this script multiple times is safe only if no new pending
-- withdrawals are created between runs; deploy new code immediately after.
-- Step 1: refund commission for all users with pending withdrawals.
UPDATE `user` u
JOIN (
SELECT user_id, COALESCE(SUM(amount), 0) AS pending_total
FROM user_withdrawal
WHERE status = 0
GROUP BY user_id
) p ON u.id = p.user_id
SET u.commission = u.commission + p.pending_total
WHERE p.pending_total > 0;
-- Step 2: write a migration log entry for each refunded user.
INSERT INTO system_log (type, date, object_id, content, created_at)
SELECT
3 AS type,
DATE(NOW()) AS date,
p.user_id AS object_id,
JSON_OBJECT(
'type', 99,
'amount', p.pending_total,
'order_no', '',
'timestamp', UNIX_TIMESTAMP(NOW()) * 1000,
'note', 'migration: refund pending withdrawal commission (HIF-22)'
) AS content,
NOW() AS created_at
FROM (
SELECT user_id, COALESCE(SUM(amount), 0) AS pending_total
FROM user_withdrawal
WHERE status = 0
GROUP BY user_id
HAVING pending_total > 0
) p;