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

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>

关键逻辑说明

  1. 实时值获取:点击按钮时才获取用户输入,确保拿到最新数据,同时修复选择器语法错误。
  2. 价格控制:后端接收数字格式成本,通过woocommerce_before_calculate_totals钩子覆盖产品价格,保证购物车总价正确。
  3. 字段权限控制:
    • 用户可见字段:通过woocommerce_get_item_data在购物车/订单页面展示指定参数。
    • 生产用字段:通过add_meta_data的true参数,将height/width设为后台可见的订单元数据,用户无法查看。
  4. 参数验证:前后端均添加输入有效性校验,避免无效数据提交。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 17:08:15