My Goal : Apply a 10% discount on total cart's price using AJAX.
The context : 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).
The idea :
- Create a plugin which will add an input field in cart's page with a validation button.
- Make an AJAX call when the button is clicked
- Apply discount if the user membership number is good (I won't deal with API checking here since it's not the problem)
The issue : 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.
My 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 );
My 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
}
}
My 1st solution : 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.
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();
}
My 2nd solution: I tried to trigger the "update cart" button (already present on the page) and use woocommerce_before_calculate_totals hook to update the price but it is not even called.
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!
//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';
}
EDIT :
I re-wrote my explanations to make them a bit more understandable with additional code:
Here are some tries with a negative fee try using woocommerce_cart_calculate_fees
hook:
The callback function of woocommerce_cart_calculate_fees hook works perfectly if I use add_action outside of my AJAX action function like this :
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
}
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 update cart button, so the idea is to integrate the add_action inside my AJAX action function like this :
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
}
The callback function woocommerce_custom_surcharge 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 : how to make this add_action work inside my action function?