Terawallet插件并发下单致负余额,禁止负余额过滤器无效求助
解决Woo Wallet并发下单导致负余额的问题
问题根因
你使用的woo_wallet_disallow_negative_transaction过滤器仅在交易发起前做静态余额校验,但并发场景下两个请求会同时通过检查——第一个请求尚未完成余额扣减时,第二个请求读取到的仍是初始的20美元余额,最终导致超额扣减出现负余额。
修复方案
需要在余额扣减环节加入数据库级锁机制,确保同一用户的钱包操作串行执行,同时强化实时余额校验逻辑。
步骤1:替换原有过滤器,添加并发安全校验
移除之前的两个过滤器,改用以下代码:
add_filter('woo_wallet_disallow_negative_transaction', '__return_true', 10); add_action('woo_wallet_before_debit', 'secure_wallet_debit_concurrent_check', 10, 3); function secure_wallet_debit_concurrent_check($amount, $user_id, $note) { global $wpdb; // 对应Woo Wallet的钱包数据表,默认前缀为wp_ $wallet_table = $wpdb->prefix . 'woo_wallet'; // 数据库行锁:锁定当前用户的钱包记录,直到当前事务完成 $user_wallet = $wpdb->get_row($wpdb->prepare("SELECT * FROM $wallet_table WHERE user_id = %d FOR UPDATE", $user_id)); if (!$user_wallet) { wp_die('钱包不存在'); } // 实时校验:确保扣减后余额不小于0 if ($user_wallet->balance - $amount < 0) { wp_die('余额不足,无法完成支付'); } }
步骤2:代码逻辑说明
FOR UPDATE语句:在数据库层面锁定当前用户的钱包行,其他并发请求必须等待当前事务结束才能读取/修改该记录,从根源避免竞态条件。woo_wallet_before_debit钩子:在实际执行余额扣减前触发,做最终的实时余额校验,确保扣减操作安全。
步骤3:前置拦截优化(可选)
在 checkout 环节提前拦截,避免用户走到支付步骤:
add_action('woocommerce_checkout_process', 'pre_check_wallet_balance'); function pre_check_wallet_balance() { if (isset($_POST['payment_method']) && $_POST['payment_method'] === 'woo_wallet') { $user_id = get_current_user_id(); $cart_total = WC()->cart->total; $wallet_balance = woo_wallet()->wallet->get_balance($user_id); if ($wallet_balance < $cart_total) { wc_add_notice('钱包余额不足,无法完成订单', 'error'); } } }
注意事项
- 代码需添加到主题的
functions.php文件或自定义插件中,禁止直接修改Woo Wallet核心文件。 - 测试并发场景:可通过Postman等工具模拟同时发起的支付请求,验证余额不会出现负数。
内容的提问来源于stack exchange,提问作者Ahmed Salah
相关产品推荐
相关产品推荐

