提问者:小点点

Woocommerce使用ajax对购物车的总价进行折扣


我的目标是:使用AJAX在购物车的总价格上打10%的折扣。

上下文:此折扣仅适用于来自给定网站的特定用户。想法是在我添加到购物车页面的输入字段中获取他们的会员编号。使用API,我将检查这个数字并应用折扣或不应用折扣(考虑到进一步解释的问题,我不会在这里详细介绍这个检查部分,只关注问题)。

这个想法:

  1. 创建一个插件,该插件将在购物车页面中添加一个带有验证按钮的输入字段

问题:所以我使用AJAX来验证这个数字,然后在购物车的总价格上打10%的折扣。问题是我找不到如何更新总数。

我的JS:

(function($) {
    $("body").on("click", ".updateCart", function(e) {
        e.preventDefault();
        var form = $('#cartAjaxTcs');
        var value = form.serialize();
        $.ajax({
            type:'POST',
            data: {
                action: 'test',
                number: value
            },
            url: ajaxurl,
            success: function(value) {
                jQuery("[name='update_cart']").removeAttr('disabled');
                jQuery("[name='update_cart']").trigger("click");
                console.log(value);
            },
            error:function(){
                console.log('error');
            }
        });
    });
})( jQuery );

我的PHP:


<?php

/*
  Plugin Name: discount Plugin
  Description: Apply 10% discount on cart's price
  Author: Aurélien
  Version: 1.0.0
 */

/**
* Check if WooCommerce is active
**/
if ( in_array( 'woocommerce/woocommerce.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) ) {

    class Discount_Plugin{

        public static function init() {

            add_action('wp_enqueue_scripts',  __CLASS__ .'::callback_for_setting_up_scripts');

            //Add input field
            add_action('woocommerce_cart_collaterals', __CLASS__.'::order_comments_custom_cart_field');

        }

        public static function callback_for_setting_up_scripts() {
            $path = '/wp-content/plugins/discount-plugin/assets/css/style.css';
            wp_register_style( 'discountStyle', $path );
            wp_enqueue_style( 'discountStyle' );
            wp_enqueue_script( 'ajax-script', plugins_url( 'assets/js/discount-plugin.js', __FILE__ ), array('jquery'), '1.0', true );
            wp_localize_script( 'ajax-script', 'ajaxurl', admin_url( 'admin-ajax.php' ) );
        }

        public static function order_comments_custom_cart_field() {
            ?>
            <form id="cartAjaxTcs" class="discount_input_wrapper" method="post" action="/wp-admin/admin-post.php">
                <div class="discount_form_field">
                    <input type="text" name="number" id="number" placeholder="<?php echo 'My number, ie : 4GG524G42';?>">
                    <label for="number"><?php echo 'Membership number';?></label>
                </div>
                <button class="updateCart" type="submit"><?php echo 'Update cart';?></button>
            </form>
            <?php

        }

 

    Discount_Plugin::init();
    add_action( 'wp_ajax_test', 'test' );
    add_action( 'wp_ajax_nopriv_test', 'test' );

    function test()
    {
      //Solution 1 or solution 2
    }

}

我的第一个解决方案:我试图像这样直接降低总价格。它会更改总价,但不会显示它,即使在后端购物车的报告中,总价仍然与折扣前相同。

function test()
{
    //This is displayed in console
    echo 'test-init';

    if (!DOING_AJAX){
        return;
    }

    $total = (int) WC()->cart->get_totals()['total']; 
    $total *= 0.9;
    WC()->cart->set_total($total);

    wp_die();
}


我的第二个解决方案是:我试图触发“更新购物车”按钮(已经出现在页面上),并使用woocommerce\u before\u calculate\u totals钩子更新价格,但它甚至没有被调用。

显然,在我的AJAX操作函数中没有执行任何钩子或过滤器。我已经用一个简单的过滤器进行了测试,将一个类添加到主体中,它在我的动作函数内部不起作用,但在外部却可以完美地工作。我放置了一些标志,以查看我的函数是否由AJAX调用,并且它是!

//This works well! If I put the same filter in my 'test' function it doesn't work
add_filter( 'body_class', function( $classes ) {
    return array_merge( $classes, array( 'test-class' ) );
} );

function test()
{
    //This is displayed in console
    echo'test-init';

    if (!DOING_AJAX){
        return;
    }
    
    //This does't work
    add_filter( 'body_class', function( $classes ) {
      return array_merge( $classes, array( 'test-class2' ) );
    } );
    
    //The callback function isn't called
    add_action( 'woocommerce_before_calculate_totals', 'update_price' );

    //This is displayed in console
    echo 'test';

    wp_die();
}

//Not called
function update_price(){
    echo 'update';
}

编辑:

我重新编写了我的解释,通过附加代码使它们更易于理解:

以下是一些使用woocommerce\u cart\u calculate\u feeshook的负费用尝试:

如果我在AJAX操作函数之外使用add\u操作,那么woocommerce\u cart\u calculate\u fees hook的回调函数将非常有效,如下所示:

add_action( 'wp_ajax_test', 'test' );
add_action( 'wp_ajax_nopriv_test', 'test' );

add_action( 'woocommerce_cart_calculate_fees','woocommerce_custom_surcharge' );
function woocommerce_custom_surcharge() {
    global $woocommerce;

    if ( ( is_admin() && ! defined( 'DOING_AJAX' )))
        return;

    $percentage = 0.1;
    $surcharge = ( $woocommerce->cart->cart_contents_total + $woocommerce->cart->shipping_total ) * $percentage;
    $woocommerce->cart->add_fee( 'discount', -$surcharge, true, '' );

}

function test(){
   
   //My AJAX action function

}

所以这项工作,应用了否定费,但当页面加载时,这不是我想要的,因为我想在触发更新购物车按钮时应用否定费,所以想法是将add_操作集成到我的AJAX操作函数中,如下所示:

add_action( 'wp_ajax_test', 'test' );
add_action( 'wp_ajax_nopriv_test', 'test' );

function woocommerce_custom_surcharge() {
    global $woocommerce;

    if ( ( is_admin() && ! defined( 'DOING_AJAX' )))
        return;

    $percentage = 0.1;
    $surcharge = ( $woocommerce->cart->cart_contents_total + $woocommerce->cart->shipping_total ) * $percentage;
    $woocommerce->cart->add_fee( 'discount', -$surcharge, true, '' );

}

//My AJAX action function
function test(){

   echo 'test1'; //flag 1
   add_action( 'woocommerce_cart_calculate_fees','woocommerce_custom_surcharge' );
   echo 'test2'; //flag 2
}

根本不调用woocommerce_custom_surcharge回调函数。我的两个标志都显示在chrome控制台中,所以这意味着我的操作函数被正确调用。所以我的问题是:如何让这个add_action在我的动作函数中工作?


共1个答案

匿名用户

您错过了使用WC会话变量来设置一些数据并在折扣函数中使用它...下面的第一个函数处理您的设置,它会加载到其他函数的任何地方。

// Settings
function get_membership_settings(){
    $discount_percentage = 1; // the discount percentage: 1% here

    return array(
        'percentage'       => $discount_percentage,
        'field_key'        => 'membership_number', // Field "name" key (or id)
        'field_type'       => 'text',
        'field_label'      =>  __('Membership number', 'woocommerce'),
        'button_text'      =>  __('Apply membership number', 'woocommerce'),
        'discount_text'    =>  sprintf( __('Membership discount%s', 'woocommerce'), ' ('.$discount_percentage.' %)' ), // for negative fee
        'valid_message'    => __('Number is valid (text message).', 'woocommerce'),
        'unvalid_message'  => __('Number not valid (text message).', 'woocommerce'),
        'empty_field_text' => __('Please enter your membership number.', 'woocommerce'),
     );
}

// Settings + Membership number
function get_membership_data(){
    $settings      = get_membership_settings();// Load settings
    $field_key     = $settings['field_key']; // The field Id

    $user_value    = get_user_meta( get_current_user_id(), $field_key, true ); // Get "membership number" from user data
    $session_value = WC()->session->get($field_key); // Get "membership number" from session variable

    // Set "membership number" in the array
    $settings['field_value'] = empty($session_value) ? $user_value : $session_value;

    return $settings;
}

购物车上显示的字段(见最后的截图):

// Display a text input field on cart (+ javascript)
add_action('woocommerce_cart_contents', 'display_field_membership_number', 100 );
function display_field_membership_number(){
    extract(get_membership_data()); // Load data and settings

    echo '<tr><td colspan="6" class="membership" style="padding:0;border-top:16px solid #FFF;">
        <style>.message.off,label.hidden{display:none}.membership .message{margin-left:20px}</style>
        <div id="'.$field_key.'-wrapper">
            <label for="'.$field_key.'" class="hidden"> '.$field_label.'&nbsp;<abbr class="required" title="required">*</abbr></label>
            <input type="'.$field_type.'" class="input-'.$field_type.'" name="'.$field_key.'" id="'.$field_key.'" placeholder="'.$field_label.'" value="'.$field_value.'">
            <button type="button" class="button">'.$button_text.'</button>
            <span class="message off"></span>
        </div>
    </td></tr>
    <tr><td colspan="6" style="padding:0"></td></tr>';
}

jQuery/Ajax代码:

// Function that send the Ajax request | jQuery + Ajax
add_action('wp_footer', 'membership_number_js_script');
function membership_number_js_script() {
    if( ! is_cart() ) return; // Only on cart

    $field_key = get_membership_settings()['field_key']; // Load field key id

    // jQuery Ajax code
    ?>
    <script type="text/javascript">
    jQuery( function($){
        if (typeof woocommerce_params === 'undefined')
            return false;

        var s = '#<?php echo $field_key; ?>-wrapper';

        $(s+' button').click( function(){
            var value = $(s+' input').val();

            // Function that handle the display of the message
            function handleDisplayMessage( selector, response, error = false ) {
                if ( ! error ) {
                    $.each( $.parseJSON(response), function(index, value){
                        displayMessage( selector, value, index );
                    });
                } else {
                    displayMessage( selector, response, 0 );
                }
            }

            // Function that display a message
            function displayMessage( selector, response, type ) {
                $(selector).hide('0').removeClass('off').html(response).css('color', (type == 1 ? '#03C03C' : '#DC143C')).show();
                setTimeout(function() {
                    $(selector).hide();
                }, 3000 );
            }

            $.ajax({
                type: 'POST',
                url: wc_cart_params.ajax_url,
                data: {
                    'action': '<?php echo $field_key; ?>',
                    '<?php echo $field_key; ?>': value,
                },
                success: function (response) {
                    handleDisplayMessage( (s+' .message'), response );
                    $(document.body).trigger("added_to_cart"); // refresh cart
                },
                error:function(error){
                    handleDisplayMessage( (s+' .message'), ('A problem occured (error: '+error+')'), true );
                }
            });
        });
    });
    </script>
    <?php
}

PHP WordPress Ajax接收器函数:

// Get the ajax request and set value to WC session (and the field validation)
add_action( 'wp_ajax_membership_number', 'set_membership_number_and_validation' );
add_action( 'wp_ajax_nopriv_membership_number', 'set_membership_number_and_validation' );
function set_membership_number_and_validation() {
    extract(get_membership_settings()); // Load and extract settings

    if( isset($_POST[$field_key]) && ! empty($_POST[$field_key]) ) {

        ## HERE BELOW, SET YOUR CODE (that checks membership number for validation)

        $validation = true; // "true" when validated (or false if not)

        // Set membership number to a WC session variable
        WC()->session->set($field_key, $_POST[$field_key]);

        // Send response back
        echo json_encode( ($validation ? [1 => $valid_message] : [0 => $unvalid_message]) );
    } else {
        // Send response back
        echo json_encode( [0 => $empty_field_text] );
    }
    die();
}

折扣有多种方式(这里有两种,使用不同的挂钩):

// 1. Percentage discount for Membership (with a negative fee)
add_action( 'woocommerce_cart_calculate_fees', 'add_membership_discount' );
function add_membership_discount( $cart ) {
    if ( is_admin() && ! defined( 'DOING_AJAX' ) )
        return;

    extract(get_membership_data()); // Load and extract settings + membership numver

    if( ! empty($field_value) ) {
        // Calculation
        $discount = ( $cart->get_cart_contents_total() + $cart->get_shipping_total() ) * $percentage / 100;

        $cart->add_fee( $discount_text, -$discount ); // Add a discount
    }
}
// 2. Percentage discount for Membership (on cart items price)
add_action( 'woocommerce_before_calculate_totals', 'add_membership_cart_item_discount' );
function add_membership_cart_item_discount( $cart ) {
    if ( is_admin() && ! defined( 'DOING_AJAX' ) )
        return;

    if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
        return;

    extract(get_membership_data()); // Load and extract settings + membership numver

    if( ! empty($field_value) ) {

        // Loop through cart items
        foreach( $cart->get_cart() as $cart_item ){
            // Get the real WC_Product Object
            $product = wc_get_product($cart_item['data']->get_id());

            $price = $product->get_price(); // The real product price

            $discounted_price = $price * ( 1 - ( $percentage / 100 ) ); // Calculation

            $cart_item['data']->set_price($discounted_price); // Set the discounted price
        }
    }
}

所有代码functions.php活动子主题(或活动主题)的文件中。测试和工作。

类似的线程:

  • 客人客户查看WooCommerce订单账单电子邮件的第一个订单折扣
  • WooCommerce结账页面中使用AJAX和Fee API的动态折扣