基于自定义输入字段更新WooCommerce产品价格的异常问题
问题:自定义字段导致购物车价格重复计算
我在单个产品页面添加了自定义数字输入字段,产品常规价格会乘以该字段输入的数值。比如基础价格10美元,用户输入2时,新价格变为20美元。产品页面价格更新正常,但进入购物车修改产品数量时,价格会被重复计算——已计算好的20美元,把数量从1改为5时,会再次乘以自定义数值2,最终价格变成40美元。
原使用代码:
add_action( 'woocommerce_after_add_to_cart_quantity', 'add_custom_input_number_field', 0 ); function add_custom_input_number_field() { echo '<div class="custom-input-number-field">'; echo '<label>' . __( 'Custom Input Number', 'woocommerce' ) . '</label>'; echo '<input type="number" required="true" placeholder="max. 30.500" class="custom-input-number" name="custom_input_number" min="1" step="0.001" />'; echo '</div>'; } add_filter( 'woocommerce_get_price_html', 'update_product_price_based_on_custom_input_on_product_page', 9999, 2 ); function update_product_price_based_on_custom_input_on_product_page( $price, $product ) { if ( is_product() && isset( $_POST['custom_input_number'] ) ) { $custom_input_value = (float) $_POST['custom_input_number']; $product_price = $product->get_price(); $new_price = $product_price * $custom_input_value; $price = wc_price( $new_price ) . $product->get_price_suffix(); } return $price; } add_filter( 'woocommerce_add_cart_item_data', 'save_custom_input_number_field_data', 10, 2 ); function save_custom_input_number_field_data( $cart_item_data, $product_id ) { if ( isset( $_POST['custom_input_number'] ) ) { $cart_item_data['custom_input_number'] = wc_clean( $_POST['custom_input_number'] ); $cart_item_data['custom_input_number'] = (float) $cart_item_data['custom_input_number']; } return $cart_item_data; } add_action( 'woocommerce_before_calculate_totals', 'update_product_price_based_on_custom_input_in_cart', 10, 1 ); function update_product_price_based_on_custom_input_in_cart( $cart ) { if ( is_admin() && ! defined( 'DOING_AJAX' ) ) { return; } foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) { if ( isset( $cart_item['custom_input_number'] ) ) { $custom_input_value = $cart_item['custom_input_number']; $product = $cart_item['data']; $product_price = $product->get_price(); $new_price = $product_price * $custom_input_value; $product->set_price( $new_price ); } } }
问题原因
- 购物车价格计算逻辑错误:
woocommerce_before_calculate_totals钩子每次触发(比如修改数量、更新购物车)时,$product->get_price()获取的是已经被修改后的价格,而非产品原始价格。这导致每次更新都会重复乘以自定义值,造成价格叠加计算。 - 产品页面价格更新依赖POST:原
woocommerce_get_price_html的实现只有在提交表单时才会更新价格,用户输入自定义值时无法实时看到价格变化,体验不佳。
修正方案
1. 保留自定义字段添加函数
这部分不需要修改,继续使用:
add_action( 'woocommerce_after_add_to_cart_quantity', 'add_custom_input_number_field', 0 ); function add_custom_input_number_field() { echo '<div class="custom-input-number-field">'; echo '<label>' . __( 'Custom Input Number', 'woocommerce' ) . '</label>'; echo '<input type="number" required="true" placeholder="max. 30.500" class="custom-input-number" name="custom_input_number" min="1" step="0.001" />'; echo '</div>'; }
2. 产品页面实时更新价格(JavaScript)
替换原woocommerce_get_price_html的PHP实现,用JS实时监听输入变化并更新价格:
add_action( 'woocommerce_single_product_summary', 'add_custom_price_update_script', 25 ); function add_custom_price_update_script() { global $product; $original_price = (float) $product->get_price(); ?> <script> jQuery(document).ready(function($) { const originalPrice = <?php echo $original_price; ?>; const priceElement = $('.price .woocommerce-Price-amount'); const suffix = '<?php echo esc_js($product->get_price_suffix()); ?>'; $('.custom-input-number').on('input', function() { const customValue = parseFloat($(this).val()) || 1; const newPrice = originalPrice * customValue; const formattedPrice = new Intl.NumberFormat('<?php echo get_locale(); ?>', { style: 'currency', currency: '<?php echo get_woocommerce_currency(); ?>' }).format(newPrice); priceElement.html(formattedPrice + suffix); }); }); </script> <?php }
3. 保存自定义值与产品原始价格
修改woocommerce_add_cart_item_data函数,同时保存产品的原始价格,确保后续计算时使用未修改的价格:
add_filter( 'woocommerce_add_cart_item_data', 'save_custom_input_and_original_price', 10, 3 ); function save_custom_input_and_original_price( $cart_item_data, $product_id, $variation_id ) { $product = wc_get_product( $variation_id ?: $product_id ); if ( isset( $_POST['custom_input_number'] ) ) { $cart_item_data['custom_input_number'] = (float) wc_clean( $_POST['custom_input_number'] ); // 保存产品原始价格,避免后续计算时使用修改后的值 $cart_item_data['original_product_price'] = (float) $product->get_price(); // 添加唯一键,确保每个自定义输入的商品都是独立的购物车项 $cart_item_data['unique_key'] = md5( microtime() . rand() ); } return $cart_item_data; }
4. 修正购物车价格计算逻辑
修改woocommerce_before_calculate_totals函数,使用保存的原始价格乘以自定义值,避免重复计算:
add_action( 'woocommerce_before_calculate_totals', 'update_cart_price_with_custom_input', 10, 1 ); function update_cart_price_with_custom_input( $cart ) { if ( is_admin() && ! defined( 'DOING_AJAX' ) ) { return; } // 防止循环调用 if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 ) { return; } foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) { if ( isset( $cart_item['custom_input_number'], $cart_item['original_product_price'] ) ) { $custom_input_value = $cart_item['custom_input_number']; $original_price = $cart_item['original_product_price']; $product = $cart_item['data']; // 用原始价格乘以自定义值,得到正确的单价 $new_price = $original_price * $custom_input_value; $product->set_price( $new_price ); } } }
5. 购物车页面显示自定义字段(可选)
如果需要在购物车页面显示用户输入的自定义值,可以添加以下代码:
add_filter( 'woocommerce_get_item_data', 'display_custom_input_in_cart', 10, 2 ); function display_custom_input_in_cart( $item_data, $cart_item ) { if ( isset( $cart_item['custom_input_number'] ) ) { $item_data[] = array( 'key' => __( 'Custom Input Number', 'woocommerce' ), 'value' => wc_clean( $cart_item['custom_input_number'] ), 'display' => '', ); } return $item_data; }
说明
- 保存原始价格到购物车项数据中,确保每次计算都基于产品的初始价格,避免重复叠加自定义值。
- 用JavaScript实现产品页面实时价格更新,提升用户体验。
- 添加唯一键确保每个自定义输入的商品作为独立购物车项,避免同一款产品不同自定义值的价格冲突。
内容的提问来源于stack exchange,提问作者n1k1dk
相关产品推荐
相关产品推荐

