WooCommerce自定义Ajax加购:自定义价格与数据显示问题
解决WooCommerce自定义Ajax加入购物车的问题(带定制参数与动态价格)
问题核心
固定价格产品Ajax加购正常,但带用户定制参数(价格随尺寸变化)的产品加购失败,返回Error: not added!;同时需要实现:购物车显示正确总价,展示指定定制字段,隐藏生产用参数,且参数仅来自用户输入。
错误排查与完整修复方案
1. 前端JS代码修复(解决即时值获取与语法错误)
原JS存在页面加载时提前获取输入值、选择器语法错误、硬编码用户输入值的问题,修复后代码如下:
jQuery( function($) { if (typeof wc_params_custom === 'undefined') return false; const PANEL_WIDTH = 24; $('.custom.add-to-cart').on('click', function(e) { e.preventDefault(); // 实时获取输入值,修复选择器引号错误 let height = $('.height').val().trim(); let width = $('.width').val().trim(); // 验证输入有效性 if (!height || !width || isNaN(height) || isNaN(width)) { alert('请输入有效的尺寸数值'); return; } // 计算定制参数 let totalArea = ((height / 12) * (width / 12)).toFixed(2); let cost = (totalArea * 7.95).toFixed(2); let numPanels = (width / PANEL_WIDTH).toFixed(1); // 动态获取用户选择的单位(示例,需配合页面单选框) let heightUnit = $('input[name="height-unit"]:checked').val() || 'in'; let widthUnit = $('input[name="width-unit"]:checked').val() || 'in'; let enteredHeight = `${height} ${heightUnit === 'in' ? 'in.' : 'ft'}`; let enteredWidth = `${width} ${widthUnit === 'in' ? 'in.' : 'ft'}`; $.ajax({ type: 'POST', url: wc_params_custom.ajax_url, data: { 'action' :'custom_add_to_cart', 'nonce' : wc_params_custom.nonce, 'product_id' : wc_params_custom.product_id, // 从本地化数据获取当前产品ID 'quantity' : 1, 'height' : `${height} in.`, 'width' : `${width} in.`, 'totalArea' : `${totalArea} sq ft`, 'cost' : cost, // 仅传数字,方便后端计算 'numPanels' : numPanels, 'enteredHeight' : enteredHeight, 'enteredWidth' : enteredWidth }, success: function (response) { $(document.body).trigger("wc_fragment_refresh"); console.log(response); }, error: function (error) { console.log(error); } }); }); });
2. 后端PHP代码修复(解决加购验证与价格覆盖)
原PHP存在价格传递格式错误、缺少价格覆盖逻辑、硬编码产品ID的问题,修复后代码(子主题functions.php):
// 注册并本地化脚本 add_action('wp_enqueue_scripts', 'enqueue_custom_add_to_cart_scripts'); function enqueue_custom_add_to_cart_scripts() { if(function_exists('is_product') && is_product()) { wp_enqueue_script('custom_add_to_cart', get_stylesheet_directory_uri() . '/js/add-to-cart-ajax.js', array('jquery'), '1.0', true); wp_localize_script('custom_add_to_cart', 'wc_params_custom', array( 'ajax_url' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('nonce_custom_atc'), 'product_id' => get_the_ID() // 动态获取当前产品ID )); } } // Ajax加购处理 add_action('wp_ajax_custom_add_to_cart', 'custom_add_to_cart'); add_action('wp_ajax_nopriv_custom_add_to_cart', 'custom_add_to_cart'); function custom_add_to_cart() { $cart_item_key = null; // 完善参数验证 if (isset($_POST['nonce']) && wp_verify_nonce($_POST['nonce'], 'nonce_custom_atc') && isset($_POST['product_id']) && $_POST['product_id'] > 0 && isset($_POST['quantity']) && $_POST['quantity'] > 0 && isset($_POST['cost']) && is_numeric($_POST['cost'])) { $product_id = intval($_POST['product_id']); $quantity = intval($_POST['quantity']); $custom_cost = floatval($_POST['cost']); // 添加商品到购物车,存储定制参数 $cart_item_key = WC()->cart->add_to_cart( $product_id, $quantity, 0, array(), array( 'height' => isset($_POST['height']) ? sanitize_text_field($_POST['height']) : '', 'width' => isset($_POST['width']) ? sanitize_text_field($_POST['width']) : '', 'totalArea' => isset($_POST['totalArea']) ? sanitize_text_field($_POST['totalArea']) : '', 'custom_cost' => $custom_cost, 'numPanels' => isset($_POST['numPanels']) ? sanitize_text_field($_POST['numPanels']) : '', 'enteredHeight' => isset($_POST['enteredHeight']) ? sanitize_text_field($_POST['enteredHeight']) : '', 'enteredWidth' => isset($_POST['enteredWidth']) ? sanitize_text_field($_POST['enteredWidth']) : '' ) ); } wp_die($cart_item_key ? "Cart item key: {$cart_item_key}" : "Error: not added!"); } // 覆盖购物车商品价格 add_action('woocommerce_before_calculate_totals', 'custom_cart_item_price', 10, 1); function custom_cart_item_price($cart) { if (is_admin() && !defined('DOING_AJAX')) return; foreach ($cart->get_cart() as $cart_item_key => $cart_item) { if (isset($cart_item['custom_cost']) && $cart_item['custom_cost'] > 0) { $cart_item['data']->set_price($cart_item['custom_cost']); } } } // 在购物车/订单页面显示用户可见的定制字段 add_filter('woocommerce_get_item_data', 'display_custom_cart_item_data', 10, 2); function display_custom_cart_item_data($item_data, $cart_item) { if (isset($cart_item['enteredHeight'])) { $item_data[] = array('key' => '输入高度', 'value' => $cart_item['enteredHeight']); } if (isset($cart_item['enteredWidth'])) { $item_data[] = array('key' => '输入宽度', 'value' => $cart_item['enteredWidth']); } if (isset($cart_item['totalArea'])) { $item_data[] = array('key' => '总面积', 'value' => $cart_item['totalArea']); } if (isset($cart_item['numPanels'])) { $item_data[] = array('key' => '面板数量', 'value' => $cart_item['numPanels']); } return $item_data; } // 向订单添加生产用隐藏参数(仅后台可见) add_action('woocommerce_checkout_create_order_line_item', 'add_custom_order_item_meta', 10, 4); function add_custom_order_item_meta($item, $cart_item_key, $cart_item, $order) { if (isset($cart_item['height'])) { $item->add_meta_data('_production_height', $cart_item['height'], true); // true表示用户端隐藏 } if (isset($cart_item['width'])) { $item->add_meta_data('_production_width', $cart_item['width'], true); } }
3. HTML结构优化
添加动态产品ID隐藏域与单位选择控件(可选):
<input type="hidden" class="product-id" value="<?php echo get_the_ID(); ?>"> <input class="height" type="number" placeholder="输入高度(英寸)" min="1" /> <input class="width" type="number" placeholder="输入宽度(英寸)" min="1" /> <!-- 可选:单位切换控件 --> <div class="unit-select"> <label><input type="radio" name="height-unit" value="in" checked> 英寸</label> <label><input type="radio" name="height-unit" value="ft"> 英尺</label> </div> <div class="unit-select"> <label><input type="radio" name="width-unit" value="in" checked> 英寸</label> <label><input type="radio" name="width-unit" value="ft"> 英尺</label> </div> <button class="custom add-to-cart">加入购物车</button>
关键逻辑说明
- 实时值获取:点击按钮时才获取用户输入,确保拿到最新数据,同时修复选择器语法错误。
- 价格控制:后端接收数字格式成本,通过
woocommerce_before_calculate_totals钩子覆盖产品价格,保证购物车总价正确。 - 字段权限控制:
- 用户可见字段:通过
woocommerce_get_item_data在购物车/订单页面展示指定参数。 - 生产用字段:通过
add_meta_data的true参数,将height/width设为后台可见的订单元数据,用户无法查看。
- 用户可见字段:通过
- 参数验证:前后端均添加输入有效性校验,避免无效数据提交。
内容的提问来源于stack exchange,提问作者jstrother
相关产品推荐
相关产品推荐

