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; } }
问题根源分析
- 购物车项数据损坏:
add_items_default_price_as_custom_data函数中,未登录时直接return;,但该过滤器要求返回完整的$cart_item_data数组。直接return会导致未登录用户添加的购物车项数据为空,后续登录切换用户会话时,WooCommerce无法正确识别和迁移这些损坏的购物车项。 - 空订单触发:损坏的购物车项在结账流程中被错误处理,系统尝试创建订单但缺少必要的商品和用户信息,最终生成空订单。
修复方案
核心修改点
- 未登录用户调用
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; } }
修复说明
- 修复了未登录用户购物车项数据损坏的问题,确保访客添加的商品能正常保存
- 添加
wp_login钩子,用户登录后自动重新计算购物车折扣,保证访客购物车迁移后能正确应用折扣 - 增加了产品对象和用户存在性的校验,避免空值报错
- 为购物车项添加唯一键,确保WooCommerce能正确识别和处理购物车数据
内容的提问来源于stack exchange,提问作者user3331122
相关产品推荐
相关产品推荐

