title: Woocommerce apply discount to cart's total price with ajax
body: <p><strong>My Goal :</strong> Apply a 10% discount on total cart's price using AJAX.</p>
  <p><strong>The context :</strong> This discount must be applied only for specific users coming from a given website. The idea is to get their membership number in an input field I added in cart's page. With an API I will check this number and apply the discount or not (considering the issue explained further, I won't detail this checking part here and focus only with the problem).</p>
  <p><strong>The idea :</strong></p>
  <ol>
  <li>Create a plugin which will add an input field in cart's page with a validation button.</li>
  <li>Make an AJAX call when the button is clicked</li>
  <li>Apply discount if the user membership number is good (I won't deal with API checking here since it's not the problem)</li>
  </ol>
  <p><strong>The issue :</strong> So I'm using AJAX to validate this number and then apply a 10% discount on the total cart's price. The problem is I can't find how to update the total.</p>
  <p><strong>My JS:</strong></p>
  <pre><code>(function($) {
      $(&quot;body&quot;).on(&quot;click&quot;, &quot;.updateCart&quot;, 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(&quot;[name='update_cart']&quot;).removeAttr('disabled');
                  jQuery(&quot;[name='update_cart']&quot;).trigger(&quot;click&quot;);
                  console.log(value);
              },
              error:function(){
                  console.log('error');
              }
          });
      });
  })( jQuery );
  </code></pre>
  <p><strong>My PHP:</strong></p>
  <pre><code>
  &lt;?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() {
              ?&gt;
              &lt;form id=&quot;cartAjaxTcs&quot; class=&quot;discount_input_wrapper&quot; method=&quot;post&quot; action=&quot;/wp-admin/admin-post.php&quot;&gt;
                  &lt;div class=&quot;discount_form_field&quot;&gt;
                      &lt;input type=&quot;text&quot; name=&quot;number&quot; id=&quot;number&quot; placeholder=&quot;&lt;?php echo 'My number, ie : 4GG524G42';?&gt;&quot;&gt;
                      &lt;label for=&quot;number&quot;&gt;&lt;?php echo 'Membership number';?&gt;&lt;/label&gt;
                  &lt;/div&gt;
                  &lt;button class=&quot;updateCart&quot; type=&quot;submit&quot;&gt;&lt;?php echo 'Update cart';?&gt;&lt;/button&gt;
              &lt;/form&gt;
              &lt;?php
  
          }
  
   
  
      Discount_Plugin::init();
      add_action( 'wp_ajax_test', 'test' );
      add_action( 'wp_ajax_nopriv_test', 'test' );
  
      function test()
      {
        //Solution 1 or solution 2
      }
  
  }
  </code></pre>
  <p><strong>My 1st solution :</strong> I tried to directly reduce total price like this. It changes the total price but not display it, even in backend cart's report where the total price is still the same than before discount.</p>
  <pre><code>function test()
  {
      //This is displayed in console
      echo 'test-init';
  
      if (!DOING_AJAX){
          return;
      }
  
      $total = (int) WC()-&gt;cart-&gt;get_totals()['total']; 
      $total *= 0.9;
      WC()-&gt;cart-&gt;set_total($total);
  
      wp_die();
  }
  
  
  </code></pre>
  <p><strong>My 2nd solution:</strong>
  I tried to trigger the &quot;update cart&quot; button (already present on the page) and use <em>woocommerce_before_calculate_totals</em> hook to update the price but it is not even called.</p>
  <p>Obviously, no hook or filter are exectuted inside my AJAX's action function. I have tested with a simple filter to add a class to the body and it doesn't work inside my action function but works perfectly outside. I put some flags to see if my function was called by AJAX and it is!</p>
  <pre><code>//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';
  }
  </code></pre>
  <hr />
  <p><strong>EDIT :</strong></p>
  <p>I re-wrote my explanations to make them a bit more understandable with additional code:</p>
  <p>Here are some tries with a negative fee try using <code>woocommerce_cart_calculate_fees</code> hook:</p>
  <p>The callback function of <em>woocommerce_cart_calculate_fees</em> hook works perfectly if I use <em>add_action</em> outside of my <em>AJAX action function</em> like this :</p>
  <pre><code>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() &amp;&amp; ! defined( 'DOING_AJAX' )))
          return;
  
      $percentage = 0.1;
      $surcharge = ( $woocommerce-&gt;cart-&gt;cart_contents_total + $woocommerce-&gt;cart-&gt;shipping_total ) * $percentage;
      $woocommerce-&gt;cart-&gt;add_fee( 'discount', -$surcharge, true, '' );
  
  }
  
  function test(){
     
     //My AJAX action function
  
  }
  </code></pre>
  <p>So this work, the negatif fee is applied but when the page is loaded and that's not what I would like since I want to apply the negative fee when I trigger <em>update cart button</em>, so the idea is to integrate the <em>add_action</em> inside my <em>AJAX action function</em> like this :</p>
  <pre><code>add_action( 'wp_ajax_test', 'test' );
  add_action( 'wp_ajax_nopriv_test', 'test' );
  
  function woocommerce_custom_surcharge() {
      global $woocommerce;
  
      if ( ( is_admin() &amp;&amp; ! defined( 'DOING_AJAX' )))
          return;
  
      $percentage = 0.1;
      $surcharge = ( $woocommerce-&gt;cart-&gt;cart_contents_total + $woocommerce-&gt;cart-&gt;shipping_total ) * $percentage;
      $woocommerce-&gt;cart-&gt;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
  }
  </code></pre>
  <p>The callback function <em>woocommerce_custom_surcharge</em> isn't called at all. Both my flags are displayed in chrome console so it means my action function is correctly called. So my question is : <strong>how to make this add_action work inside my action function?</strong></p>