I have a specific coupon in WooCommerce with the following properties:
Coupon Type: Percent
Discount Value: 100%
Applicable to: Only one specific product
Minimum Order Value: $300
Goal: When a user enters this specific coupon code, the product associated with the coupon should be automatically added to the cart if it’s not already there. If the minimum order value of $300 is not met, a relevant error message should be shown, and the product should not be added.
Challenges and Attempts: I've tried using a combination of PHP functions and JavaScript to intercept the coupon application process, add the product to the cart, and apply the coupon. My current approach involves custom PHP functions hooked to template_redirect and JavaScript that intercepts form submissions and makes AJAX calls. Despite these attempts, the product sometimes doesn't get added to the cart, or the coupon is not applied correctly. In some cases, applying the coupon again causes the product to be removed.
Desired Outcome:
- Automatic product addition: When the specific coupon code is entered, the associated product should be added to the cart if it's not already there.
- Minimum order value enforcement: If the cart total does not meet the $300 minimum, a relevant error message should be shown, and the coupon/product logic should not proceed.
Seamless coupon application: Ensure that the coupon is applied correctly after the product is added, without timing conflicts or inconsistencies.
Here is last code attempt:
// Enqueue the JavaScript file and pass the custom AJAX URL to it
add_action('wp_enqueue_scripts', 'cns_enqueue_coupon_script');
function cns_enqueue_coupon_script() {
wp_enqueue_script(
'cns-coupon-handler',
get_stylesheet_directory_uri() . '/assets/js/cns-coupon-handler.js', // Path for the child theme
['jquery'],
time(), // Use the current time to prevent caching during development
true
);
// Pass the custom AJAX URL to the script
wp_localize_script('cns-coupon-handler', 'cns_ajax_object', [
'ajax_url' => home_url('/?cns-ajax=apply_coupon_prevalidation'),
]);
}
// Handle the custom AJAX endpoint for coupon pre-validation
add_action('template_redirect', 'cns_apply_coupon_prevalidation');
function cns_apply_coupon_prevalidation() {
// Check if the custom AJAX request is being triggered
if (isset($_GET['cns-ajax']) && $_GET['cns-ajax'] === 'apply_coupon_prevalidation') {
// Load the WooCommerce cart if it is not initialized
if (!WC()->cart) {
wc_load_cart();
}
// Retrieve and sanitize the coupon code from the request
$coupon_code = isset($_GET['coupon_code']) ? sanitize_text_field($_GET['coupon_code']) : '';
if (empty($coupon_code)) {
// Return an error if no coupon code is provided
wp_send_json_error(['message' => 'Invalid coupon code.'], 400);
exit;
}
try {
// Retrieve the WooCommerce coupon object
$coupon = new WC_Coupon($coupon_code);
// Check if the coupon is of type 'percent' with a 100% discount
if ($coupon->get_discount_type() === 'percent' && $coupon->get_amount() == 100) {
// Get the product IDs associated with this coupon
$product_ids = $coupon->get_product_ids();
if (empty($product_ids)) {
// If no products are associated, return success but do nothing
wp_send_json_success(['message' => 'No specific product associated with this coupon.']);
exit;
}
$product_added = false;
// Loop through the product IDs associated with the coupon
foreach ($product_ids as $product_id) {
// Retrieve the actual product ID in case it's a variation
$real_product_id = wc_get_product($product_id)->get_id();
// Check if the product is already in the cart
$product_in_cart = false;
foreach (WC()->cart->get_cart() as $cart_item) {
if ($cart_item['product_id'] == $real_product_id) {
$product_in_cart = true;
break;
}
}
// Add the product to the cart if it is not already there
if (!$product_in_cart) {
WC()->cart->add_to_cart($real_product_id);
$product_added = true;
}
}
if ($product_added) {
// Recalculate the cart totals and save the session
WC()->cart->calculate_totals();
WC()->session->set('cart', WC()->cart->get_cart());
wp_send_json_success(['message' => 'Product added to cart.']);
exit;
} else {
// If the product is already in the cart, return success
wp_send_json_success(['message' => 'Product already in cart.']);
exit;
}
} else {
// If the coupon is valid but not relevant, return success without any changes
wp_send_json_success(['message' => 'Coupon is valid but not relevant for this logic.']);
exit;
}
} catch (Exception $e) {
// Handle errors and return a JSON error response
wp_send_json_error(['message' => 'An error occurred: ' . $e->getMessage()], 500);
exit;
}
}
}
cns-coupon-handler.js:
jQuery(document).ready(function($) {
// Intercept the coupon form submission
$('#coupon_code').closest('form').on('submit', function(e) {
e.preventDefault(); // Prevent the default form submission
var $form = $(this);
var couponCode = $('#coupon_code').val();
if (couponCode) {
// Send a request to the custom endpoint to validate and add the product
$.ajax({
url: cns_ajax_object.ajax_url,
method: 'GET',
data: { coupon_code: couponCode },
success: function(response) {
if (response.success) {
console.log('Pre-validation response:', response);
// Re-trigger the WooCommerce coupon process after a delay
setTimeout(function() {
$form.off('submit').submit(); // Submit the form again to continue WooCommerce logic
}, 500); // Delay to allow cart updates to complete
} else {
console.error('Error from pre-validation:', response);
alert(response.data.message || 'An error occurred during coupon pre-validation.');
}
},
error: function(xhr, status, error) {
console.error('Error from custom API:', error);
alert('An error occurred while adding the product. Please try again.');
}
});
} else {
$form.off('submit').submit(); // Submit the form if no coupon code is provided
}
});
// Re-bind the function after WooCommerce updates the cart
$(document).on('updated_wc_div', function() {
$('#coupon_code').closest('form').off('submit').on('submit', function(e) {
e.preventDefault();
var $form = $(this);
var couponCode = $('#coupon_code').val();
if (couponCode) {
$.ajax({
url: cns_ajax_object.ajax_url,
method: 'GET',
data: { coupon_code: couponCode },
success: function(response) {
if (response.success) {
console.log('Pre-validation response:', response);
setTimeout(function() {
$form.off('submit').submit(); // Submit the form again
}, 500);
} else {
console.error('Error from pre-validation:', response);
alert(response.data.message || 'An error occurred during coupon pre-validation.');
}
},
error: function(xhr, status, error) {
console.error('Error from custom API:', error);
alert('An error occurred while adding the product. Please try again.');
}
});
} else {
$form.off('submit').submit();
}
});
});
});
Unfortunately, it doesn't work: I am not able to intercept and stop the coupon submission event.
What should I change to achieve this functionality reliably for WooCommerce? Or are there specific hooks, filters, or techniques that would ensure the custom logic runs in the correct sequence without interfering with WooCommerce's standard coupon application process?