WooCommerce购物车添加自定义计算价格遇阻,求技术解决
问题排查与解决方案
先梳理下你代码里的几个核心问题,这些是导致自定义费用无法正确计入总计的关键:
1. 语法错误导致代码整体失效
你的代码最后一行是未闭合的add_action('woocommerce_before_cart_item_quantity_z,这会让PHP解析失败,后续所有钩子逻辑都无法执行,得先把这个残缺代码删掉或者补全。
2. 原生Session的使用冲突
WooCommerce自带一套会话管理机制,直接用PHP原生session_start()和$_SESSION很容易和WooCommerce的session冲突,导致自定义费用数据丢失。应该改用WooCommerce官方推荐的WC()->session来存储临时数据。
3. 费用计算方式不合理
你用woocommerce_before_calculate_totals修改产品本身的价格,不仅会让用户混淆原始价格和额外费用的区分,还可能在WooCommerce的一些流程中被重置。正确做法是用woocommerce_cart_calculate_fees钩子单独添加自定义费用,这样能在总计前显示独立的费用行,也更贴合WooCommerce的费用计算逻辑。
4. woocommerce_cart_calculate_fees钩子未正确取数
你之前尝试这个钩子但显示$0,大概率是因为没从购物车项中正确读取到自定义费用值,或者数据在传递过程中丢失了。
修复后的完整代码
<?php /** * Check if WooCommerce is active */ if ( class_exists( 'WooCommerce' ) ) { // Enqueue styles and scripts add_action('woocommerce_single_product_summary','extra_enqueue_scripts'); function extra_enqueue_scripts() { wp_enqueue_style('custom_style', plugins_url('/assets/css/custom_style.css', __FILE__)); wp_register_script( 'item_add', plugins_url('/assets/js/item_add.js', __FILE__), array('jquery'), false, true ); wp_enqueue_script('item_add'); wp_localize_script( 'item_add', 'wdm_object', array( 'ajax_file_path' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('extra_custom_nonce') // 添加nonce验证,提升安全性 )); } // Add custom field before add-to-cart button add_action( 'woocommerce_before_add_to_cart_button', 'extra_add_custom_field', 0 ); function extra_add_custom_field() { $currency = get_woocommerce_currency_symbol(); echo "<div class='custom-text text'><p>Extra Charge ($currency):</p> <input type='text' name='custom_price' value='' placeholder='e.g. 10' title='Custom Text' class='custom_price text_custom text'> </div>"; } // AJAX callback to save custom fee to WC session add_action('wp_ajax_extra_add_user_custom_data_options_callback', 'extra_add_user_custom_data_options_callback'); add_action('wp_ajax_nopriv_extra_add_user_custom_data_options_callback', 'extra_add_user_custom_data_options_callback'); function extra_add_user_custom_data_options_callback() { // 验证nonce,防止CSRF攻击 if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'extra_custom_nonce')) { wp_send_json_error('Invalid nonce'); } if (isset($_POST['custom_price'])) { $custom_fee = (float)sanitize_text_field($_POST['custom_price']); // 使用WooCommerce session存储数据 WC()->session->set('user_custom_data', $custom_fee); wp_send_json_success(); } wp_send_json_error('No custom fee provided'); } // Add custom fee data to cart item add_filter('woocommerce_add_cart_item_data','extra_add_custom_field_data', 10, 2); function extra_add_custom_field_data($cart_item_data, $product_id) { $custom_fee = WC()->session->get('user_custom_data'); if (!empty($custom_fee)) { $cart_item_data['user_custom_data_value'] = $custom_fee; // 添加唯一键,确保每次添加都生成新的购物车项(如需支持修改数量保留费用可调整) $cart_item_data['unique_key'] = md5(microtime().rand()); // 清除session中的数据,避免重复添加 WC()->session->__unset('user_custom_data'); } return $cart_item_data; } // Retrieve custom fee data from session for cart items add_filter('woocommerce_get_cart_item_from_session', 'extra_get_cart_items_from_session', 10, 3 ); function extra_get_cart_items_from_session($item, $values, $key) { if (isset($values['user_custom_data_value'])) { $item['user_custom_data_value'] = $values['user_custom_data_value']; } return $item; } // Display custom fee in cart/checkout item details add_filter('woocommerce_cart_item_name', 'extra_display_custom_fee_in_cart', 10, 3); add_filter('woocommerce_checkout_cart_item_quantity', 'extra_display_custom_fee_in_cart', 10, 3); function extra_display_custom_fee_in_cart($display_text, $cart_item, $cart_item_key) { if (isset($cart_item['user_custom_data_value']) && $cart_item['user_custom_data_value'] > 0) { $currency = get_woocommerce_currency_symbol(); $custom_fee = $cart_item['user_custom_data_value']; $display_text .= "<br><small>" . __('Extra Charge:', 'woocommerce') . " {$currency}{$custom_fee}</small>"; } return $display_text; } // Calculate and add custom fee to cart totals add_action('woocommerce_cart_calculate_fees', 'extra_add_custom_fee_to_totals'); function extra_add_custom_fee_to_totals($cart) { if (is_admin() && !defined('DOING_AJAX')) return; $total_custom_fee = 0; // 遍历所有购物车项,累加自定义费用 foreach ($cart->get_cart() as $cart_item) { if (isset($cart_item['user_custom_data_value'])) { $total_custom_fee += $cart_item['user_custom_data_value']; } } if ($total_custom_fee > 0) { // 添加自定义费用到总计,第一个参数是费用名称,第二个是金额 $cart->add_fee(__('Extra Charge', 'woocommerce'), $total_custom_fee); } } // Remove custom fee data when cart item is removed add_action('woocommerce_cart_item_removed', 'extra_remove_custom_fee_on_item_remove', 10, 2); function extra_remove_custom_fee_on_item_remove($cart_item_key, $cart) { // 费用实时计算,移除项后下一次计算会自动扣除,无需额外操作 } }
关键修改说明
- 替换原生Session为WC Session:用
WC()->session->set()和WC()->session->get()存储临时数据,避免冲突,保证数据可靠传递。 - 添加Nonce验证:在AJAX请求中加入nonce,防止恶意请求,提升安全性。
- 改用
woocommerce_cart_calculate_fees钩子:这个钩子专门用于添加自定义费用,会在购物车总计前显示独立的“额外费用”行,金额正确计入总计。 - 优化购物车项数据存储:给带自定义费用的购物车项添加唯一键,确保每次添加都是独立项(如需支持修改数量保留费用可按需调整)。
- 修复显示逻辑:用
woocommerce_cart_item_name钩子在商品名称下方显示额外费用,让用户清晰看到费用构成。
前端JS代码调整(item_add.js)
记得在AJAX请求中加入nonce参数:
jQuery(document).ready(function($) { $('form.cart').on('submit', function(e) { var customPrice = $('.custom_price').val(); if (customPrice) { $.ajax({ url: wdm_object.ajax_file_path, type: 'POST', data: { action: 'extra_add_user_custom_data_options_callback', custom_price: customPrice, nonce: wdm_object.nonce // 传递nonce }, success: function(response) { if (response.success) { $('form.cart').submit(); } else { alert('Error: ' + response.data); } }, error: function() { alert('Failed to save custom charge'); } }); e.preventDefault(); } }); });
修改后,自定义费用会显示在购物车每个商品的名称下方,同时在总计区域单独列出“额外费用”,金额也会正确计入购物车总计。
内容的提问来源于stack exchange,提问作者DHL17
相关产品推荐
相关产品推荐

