WooCommerce 新客首单优惠券自动校验代码失效问题排查
WooCommerce新客首单优惠券校验代码不生效问题修复
问题背景
需要实现WooCommerce新客首单优惠券自动校验逻辑:自动判断用户是否为首次下单的新客,符合条件则允许使用指定优惠券,非新客则自动移除优惠券并弹出错误提示。将自定义PHP代码加入子主题functions.php后,在WordPress 6.0、WooCommerce 6.5.1环境下始终无法正常生效。
原实现代码如下:
add_action('woocommerce_after_checkout_validation','check_new_customer_coupon', 0); function check_new_customer_coupon(){ global $woocommerce; // 可自行修改优惠券编码 $new_cust_coupon_code = 'new2022'; $has_apply_coupon = false; foreach ( WC()->cart->get_coupons() as $code => $coupon ) { if($code == $new_cust_coupon_code) { $has_apply_coupon = true; } } if($has_apply_coupon) { if(is_user_logged_in()) { $user_id = get_current_user_id(); // 查询所有订单 $customer_orders = get_posts( array( 'meta_key' => '_customer_user', 'meta_value' => $user_id, 'post_type' => 'shop_order', 'numberposts'=> -1 ) ); if(count($customer_orders) > 0) { $has_ordered = false; $statuses = array('wc-failed', 'wc-cancelled', 'wc-refunded'); // 遍历订单,排除失败、取消、退款状态的订单 foreach($customer_orders as $tmp_order) { $order = wc_get_order($tmp_order->ID); if(!in_array($order->get_status(), $statuses)) { $has_ordered = true; } } // 老用户移除优惠券 if($has_ordered == true) { WC()->cart->remove_coupon( $new_cust_coupon_code ); wc_add_notice( sprintf( "Coupon code: %s is only applicable for new customer." , $new_cust_coupon_code), 'error' ); return false; } } else { // 无历史订单判定为新客 return true; } } else { // 未登录用户直接判定为新客 return true; } } }
代码失效核心原因
- 钩子选择错误:
woocommerce_after_checkout_validation触发时,购物车优惠券的计算逻辑已经执行完成,此时移除优惠券不会重新计算购物车金额,也无法阻断不符合条件的订单提交。同时WooCommerce 3.0+版本开始,直接用get_posts查询shop_order文章类型会漏掉HPOS高性能存储模式下的订单数据,且全量查询订单的写法性能很差。 - 未覆盖游客校验场景:原代码默认所有未登录用户都是新客,实际上游客下单是通过账单邮箱关联历史订单的,同一游客使用相同邮箱重复下单时,原代码会错误判定为新客,存在逻辑漏洞。
- 触发时机过晚:仅在提交结账的最后节点做校验,用户在购物车页、结账页加载阶段就能看到优惠券抵扣金额,直到提交订单才提示无法使用,体验很差。
修正后可直接使用的代码
将以下代码替换原代码加入子主题functions.php即可,注意修改第一行的优惠券编码为你自己的新客券编码:
// 配置项:修改为你的新客首单优惠券编码 define('NEW_CUSTOMER_COUPON_CODE', 'new2022'); // 购物车/结账页加载时校验优惠券有效性 add_action('woocommerce_before_cart', 'validate_new_customer_coupon'); add_action('woocommerce_before_checkout_form', 'validate_new_customer_coupon'); // 提交订单时二次校验,防止绕过前端校验 add_action('woocommerce_checkout_process', 'validate_new_customer_coupon'); // 手动应用优惠券时即时校验 add_filter('woocommerce_coupon_is_valid', 'check_coupon_valid_for_new_customer', 10, 2); function check_coupon_valid_for_new_customer($is_valid, $coupon) { if($coupon->get_code() !== NEW_CUSTOMER_COUPON_CODE) return $is_valid; $has_valid_order = false; if(is_user_logged_in()) { $customer_id = get_current_user_id(); // 用WooCommerce内置函数查询用户有效订单,兼容HPOS模式 $customer_orders = wc_get_orders([ 'customer_id' => $customer_id, 'status' => array_diff(array_keys(wc_get_order_statuses()), ['wc-failed', 'wc-cancelled', 'wc-refunded']), 'limit' => 1, 'return' => 'ids' ]); $has_valid_order = !empty($customer_orders); } else { // 游客场景通过账单邮箱校验历史订单 $billing_email = isset($_POST['billing_email']) ? sanitize_email($_POST['billing_email']) : WC()->session->get('billing_email'); if($billing_email) { $guest_orders = wc_get_orders([ 'billing_email' => $billing_email, 'status' => array_diff(array_keys(wc_get_order_statuses()), ['wc-failed', 'wc-cancelled', 'wc-refunded']), 'limit' => 1, 'return' => 'ids' ]); $has_valid_order = !empty($guest_orders); } } if($has_valid_order) { $is_valid = false; add_filter('woocommerce_coupon_error', 'show_new_customer_coupon_error', 10, 3); } return $is_valid; } function show_new_customer_coupon_error($err, $err_code, $coupon) { if($coupon->get_code() === NEW_CUSTOMER_COUPON_CODE) { $err = sprintf('优惠券码 %s 仅限首次下单的新客户使用。', NEW_CUSTOMER_COUPON_CODE); } return $err; } function validate_new_customer_coupon() { if(!WC()->cart->has_discount(NEW_CUSTOMER_COUPON_CODE)) return; $has_valid_order = false; if(is_user_logged_in()) { $customer_id = get_current_user_id(); $customer_orders = wc_get_orders([ 'customer_id' => $customer_id, 'status' => array_diff(array_keys(wc_get_order_statuses()), ['wc-failed', 'wc-cancelled', 'wc-refunded']), 'limit' => 1, 'return' => 'ids' ]); $has_valid_order = !empty($customer_orders); } else { $billing_email = WC()->customer->get_billing_email(); if($billing_email) { $guest_orders = wc_get_orders([ 'billing_email' => $billing_email, 'status' => array_diff(array_keys(wc_get_order_statuses()), ['wc-failed', 'wc-cancelled', 'wc-refunded']), 'limit' => 1, 'return' => 'ids' ]); $has_valid_order = !empty($guest_orders); } } if($has_valid_order) { WC()->cart->remove_coupon(NEW_CUSTOMER_COUPON_CODE); wc_add_notice(sprintf('优惠券码 %s 仅限首次下单的新客户使用,已自动移除。', NEW_CUSTOMER_COUPON_CODE), 'error'); } }
代码说明
- 兼容WooCommerce 3.0+到最新版本,支持HPOS高性能订单存储模式,不会出现订单查询遗漏问题
- 覆盖购物车页、结账页、提交订单、手动应用优惠券四个校验节点,不会出现校验绕过问题
- 同时支持登录用户和游客的新客身份判定,游客通过账单邮箱匹配历史有效订单
- 无效优惠券会被自动移除,同时展示对应错误提示,用户体验更流畅
内容的提问来源于stack exchange,提问作者Demis Cunningham
相关产品推荐
相关产品推荐

