You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WooCommerce基于用户角色的购物车折扣功能出现空订单问题

WooCommerce基于用户角色折扣功能的空订单问题修复

问题场景

我用以下代码实现了WooCommerce基于用户角色的折扣功能,核心逻辑包括:

  • 登录用户根据角色获取对应折扣并修改购物车商品价格
  • 购物车商品名称追加折扣比例显示
  • 订单完成后自动升级用户折扣角色

功能整体正常,但出现偶发异常:访客添加商品到购物车后进入结账页,看到需登录才能享受折扣,注册账号返回后购物车为空,同时系统生成无邮箱、姓名及商品信息的空订单。

原实现代码

//DISCOUNT
add_filter('woocommerce_add_cart_item_data', 'add_items_default_price_as_custom_data', 20, 3 );
function add_items_default_price_as_custom_data( $cart_item_data, $product_id, $variation_id ){
   
  
   if ( !is_user_logged_in() ) { return;}
   
    $product_id = $variation_id > 0 ? $variation_id : $product_id;

    //get discount from role
    $user = wp_get_current_user(); 
    $user_role = $user->roles[0];
    
    $page = jet_engine()->options_pages->registered_pages['shop-opt'];

    // give a discount depending on the role
    switch ($user_role) {   
        case 'customer':          
            $discount_percentage = 0;               
            break;  
          
        case 'discount1':         
            $discount_percentage = $page->get( 'diskont-riven-1' );                        
            break;
            
        case 'discount2':
            $discount_percentage =  $page->get( 'diskont-riven-2' );      
            break; 

        case 'discount3':
            $discount_percentage =  $page->get( 'diskont-riven-3' );        
            break; 
    }
    
    // The WC_Product Object
    $product = wc_get_product($product_id);

    $price = (float) $product->get_price();

    // Set the Product default base price as custom cart item data
    $cart_item_data['base_price'] = $price;

    // Only for non on sale products
    if( ! $product->is_on_sale() ){
        // Set the Product discounted price as custom cart item data
        $cart_item_data['new_price'] = $price * (100 - $discount_percentage) / 100;

        // Set the percentage as custom cart item data
        $cart_item_data['percentage'] = $discount_percentage;
    }

    if( $product->is_on_sale() ){
        // Set the Product discounted price as custom cart item data
        $cart_item_data['new_price'] = $price;       
    }
    return $cart_item_data;
}

// Display the product name with the discount percentage
add_filter( 'woocommerce_cart_item_name', 'append_percetage_to_item_name', 20, 3 );
function append_percetage_to_item_name( $product_name, $cart_item, $cart_item_key ){
    if ( !is_user_logged_in() ) {
        return;
    }

    if( isset($cart_item['percentage']) && isset($cart_item['base_price']) ) {
        if( $cart_item['data']->get_price() != $cart_item['base_price'] )
            $product_name .= ' <em>(' . $cart_item['percentage'] . '% discount)</em>';
    }
    return $product_name;
}

add_action( 'woocommerce_before_calculate_totals', 'custom_discounted_cart_item_price', 20, 1 );
function custom_discounted_cart_item_price( $cart ) {
    if ( !is_user_logged_in() ) {
        return;
    }

    if ( is_admin() && ! defined( 'DOING_AJAX' ) )
        return;

    if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
        return;

    // Loop through cart items
    foreach ( $cart->get_cart() as $cart_item ) {
        // Set cart item discounted price
        $cart_item['data']->set_price($cart_item['new_price']);
    }
}

// CHECK/ADD  ROLE after buuing
add_action( 'woocommerce_order_status_completed', 'order_completed', 1);
function order_completed($order_id) {
    $order = wc_get_order( $order_id );   
    $user_id = $order->get_customer_id();   

    if ( $user_id == ''){
        return ;
    }
    $user = new WP_User( $user_id);
    $user_role = $user->roles[0];

    switch ($user_role) {   
        case 'customer':          
            $user->set_role( 'discount1' );                        
            break;  
          
        case 'discount1':         
            $user->set_role( 'discount2' );                        
            break;
            
        case 'discount2':
            $user->set_role( 'discount3' );       
            break; 
    } 
}

问题根源分析

  1. 购物车项数据损坏:add_items_default_price_as_custom_data函数中,未登录时直接return;,但该过滤器要求返回完整的$cart_item_data数组。直接return会导致未登录用户添加的购物车项数据为空,后续登录切换用户会话时,WooCommerce无法正确识别和迁移这些损坏的购物车项。
  2. 空订单触发:损坏的购物车项在结账流程中被错误处理,系统尝试创建订单但缺少必要的商品和用户信息,最终生成空订单。

修复方案

核心修改点

  • 未登录用户调用add_items_default_price_as_custom_data时,返回原始$cart_item_data而非直接return
  • 确保用户登录后,访客购物车能正确合并并重新计算折扣价格
  • 增加购物车会话迁移后的折扣重计算逻辑

修复后的完整代码

// DISCOUNT: 基于用户角色设置购物车项折扣数据
add_filter('woocommerce_add_cart_item_data', 'add_items_default_price_as_custom_data', 20, 3 );
function add_items_default_price_as_custom_data( $cart_item_data, $product_id, $variation_id ){
    // 未登录时返回原始购物车数据,避免数据损坏
    if ( !is_user_logged_in() ) { 
        return $cart_item_data;
    }
   
    $product_id = $variation_id > 0 ? $variation_id : $product_id;

    // 获取当前用户角色
    $user = wp_get_current_user(); 
    $user_role = $user->roles[0];
    
    $page = jet_engine()->options_pages->registered_pages['shop-opt'];

    // 根据角色设置折扣比例
    switch ($user_role) {   
        case 'customer':          
            $discount_percentage = 0;               
            break;  
          
        case 'discount1':         
            $discount_percentage = $page->get( 'diskont-riven-1' );                        
            break;
            
        case 'discount2':
            $discount_percentage =  $page->get( 'diskont-riven-2' );      
            break; 

        case 'discount3':
            $discount_percentage =  $page->get( 'diskont-riven-3' );        
            break; 
        default:
            $discount_percentage = 0;
            break;
    }
    
    // 获取产品对象
    $product = wc_get_product($product_id);
    if ( !$product ) return $cart_item_data;

    $price = (float) $product->get_price();

    // 存储产品基准价格
    $cart_item_data['base_price'] = $price;

    // 非促销商品设置折扣后价格
    if( ! $product->is_on_sale() ){
        $cart_item_data['new_price'] = $price * (100 - $discount_percentage) / 100;
        $cart_item_data['percentage'] = $discount_percentage;
    } else {
        // 促销商品保持原价
        $cart_item_data['new_price'] = $price;       
    }

    // 添加唯一键确保购物车项数据被正确识别
    $cart_item_data['unique_key'] = md5( microtime().rand() );
    
    return $cart_item_data;
}

// 购物车商品名称显示折扣比例
add_filter( 'woocommerce_cart_item_name', 'append_percetage_to_item_name', 20, 3 );
function append_percetage_to_item_name( $product_name, $cart_item, $cart_item_key ){
    if ( !is_user_logged_in() ) {
        return $product_name;
    }

    if( isset($cart_item['percentage']) && isset($cart_item['base_price']) ) {
        if( $cart_item['data']->get_price() != $cart_item['base_price'] )
            $product_name .= ' <em>(' . $cart_item['percentage'] . '% discount)</em>';
    }
    return $product_name;
}

// 计算购物车总价时应用折扣价格
add_action( 'woocommerce_before_calculate_totals', 'custom_discounted_cart_item_price', 20, 1 );
function custom_discounted_cart_item_price( $cart ) {
    if ( !is_user_logged_in() ) {
        return;
    }

    if ( is_admin() && ! defined( 'DOING_AJAX' ) )
        return;

    if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
        return;

    // 遍历购物车项设置折扣价格
    foreach ( $cart->get_cart() as $cart_item ) {
        if( isset($cart_item['new_price']) ){
            $cart_item['data']->set_price($cart_item['new_price']);
        }
    }
}

// 用户登录后重新计算购物车折扣
add_action( 'wp_login', 'recalculate_cart_after_login', 10, 2 );
function recalculate_cart_after_login( $user_login, $user ) {
    if ( WC()->cart->is_empty() ) return;

    // 重新获取购物车项并更新折扣数据
    foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
        $product_id = $cart_item['variation_id'] > 0 ? $cart_item['variation_id'] : $cart_item['product_id'];
        // 调用折扣数据设置函数更新购物车项
        $updated_data = add_items_default_price_as_custom_data( $cart_item, $product_id, $cart_item['variation_id'] );
        WC()->cart->cart_contents[$cart_item_key] = array_merge( $cart_item, $updated_data );
    }

    // 重新计算购物车总价
    WC()->cart->calculate_totals();
}

// 订单完成后升级用户折扣角色
add_action( 'woocommerce_order_status_completed', 'order_completed_upgrade_user_role', 1);
function order_completed_upgrade_user_role($order_id) {
    $order = wc_get_order( $order_id );   
    if ( !$order ) return;

    $user_id = $order->get_customer_id();   
    if ( empty($user_id) ){
        return ;
    }

    $user = new WP_User( $user_id);
    if ( !$user || !$user->exists() ) return;

    $user_role = $user->roles[0];

    switch ($user_role) {   
        case 'customer':          
            $user->set_role( 'discount1' );                        
            break;  
          
        case 'discount1':         
            $user->set_role( 'discount2' );                        
            break;
            
        case 'discount2':
            $user->set_role( 'discount3' );       
            break; 
    } 
}

修复说明

  1. 修复了未登录用户购物车项数据损坏的问题,确保访客添加的商品能正常保存
  2. 添加wp_login钩子,用户登录后自动重新计算购物车折扣,保证访客购物车迁移后能正确应用折扣
  3. 增加了产品对象和用户存在性的校验,避免空值报错
  4. 为购物车项添加唯一键,确保WooCommerce能正确识别和处理购物车数据

内容的提问来源于stack exchange,提问作者user3331122

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 09:52:33