I would like to move the coupon field at checkout to the woocommerce_review_order_before_payment hook.
Having the coupon field at the top of the page negatively affects conversions as users immediately try looking for a coupon code and abandon checkout if they fail to find one.
I read online that it's not that simple because the coupon field is also a form. And placing the coupon field anywhere inside the checkout form causes the "Apply Coupon" to submit the order form instead of applying the coupon.
I also read online that there are working solutions to fix this issue. But there are no tutorials on how to do it even though people have been asking this same question for years.
Could someone please give a step by step tutorial on how to properly move the coupon field and end this issue once and for all?
You could adapt something like Move coupon form before subtotal in WooCommerce checkout answer code, but it will not work for many reasons…
Revisited updated answer (simplified without Ajax, just like WooCommerce default one):
// Just hide default woocommerce coupon field
add_action( 'woocommerce_before_checkout_form', 'hide_checkout_coupon_form', 5 );
function hide_checkout_coupon_form() {
echo '<style>.woocommerce-form-coupon-toggle {display:none;}</style>';
}
// Add a custom coupon field before checkout payment section
add_action( 'woocommerce_review_order_before_payment', 'woocommerce_checkout_coupon_form_custom' );
function woocommerce_checkout_coupon_form_custom() {
echo '<div class="checkout-coupon-toggle"><div class="woocommerce-info">' . sprintf(
__("Have a coupon? %s"), '' . __("Click here to enter your code") . ''
) . '</div></div>';
echo '<div class="coupon-form" style="margin-bottom:20px;" style="display:none !important;">
<p>' . __("If you have a coupon code, please apply it below.") . '</p>
<p class="form-row form-row-first woocommerce-validated">
<input type="text" name="coupon_code" class="input-text" placeholder="' . __("Coupon code") . '" id="coupon_code" value="">
</p>
<p class="form-row form-row-last">
<button type="button" class="button" name="apply_coupon" value="' . __("Apply coupon") . '">' . __("Apply coupon") . '</button>
</p>
<div class="clear"></div>
</div>';
}
// jQuery code
add_action( 'wp_footer', 'custom_checkout_jquery_script' );
function custom_checkout_jquery_script() {
if ( is_checkout() && ! is_wc_endpoint_url() ) :
?>
<script type="text/javascript">
jQuery( function($){
$('.coupon-form').css("display", "none"); // Be sure coupon field is hidden
// Show or Hide coupon field
$('.checkout-coupon-toggle .show-coupon').on( 'click', function(e){
$('.coupon-form').toggle(200);
e.preventDefault();
})
// Copy the inputed coupon code to WooCommerce hidden default coupon field
$('.coupon-form input[name="coupon_code"]').on( 'input change', function(){
$('form.checkout_coupon input[name="coupon_code"]').val($(this).val());
// console.log($(this).val()); // Uncomment for testing
});
// On button click, submit WooCommerce hidden default coupon form
$('.coupon-form button[name="apply_coupon"]').on( 'click', function(){
$('form.checkout_coupon').submit();
// console.log('click: submit form'); // Uncomment for testing
});
});
</script>
<?php
endif;
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
Original first answer:
You will need something completely custom, to be able to make work a coupon input field just before checkout payment section. It additionally requires Ajax and jQuery code as follows:
// Remove default coupon field
remove_action( 'woocommerce_before_checkout_form', 'woocommerce_checkout_coupon_form', 10 );
// Add a custom coupon field before checkout payment section
add_action( 'woocommerce_review_order_before_payment', 'woocommerce_checkout_coupon_form_custom' );
function woocommerce_checkout_coupon_form_custom() {
echo '<div class="coupon-form" style="margin-bottom:20px;">
<p>' . __("If you have a coupon code, please apply it below.") . '</p>
<p class="form-row form-row-first woocommerce-validated">
<input type="text" name="coupon_code" class="input-text" placeholder="' . __("Coupon code") . '" id="coupon_code" value="">
</p>
<p class="form-row form-row-last">
<button type="button" class="button" name="apply_coupon" value="' . __("Apply coupon") . '">' . __("Apply coupon") . '</button>
</p>
<div class="clear"></div>
</div>';
}
// jQuery - Send Ajax request
add_action( 'wp_footer', 'custom_checkout_jquery_script' );
function custom_checkout_jquery_script() {
if ( is_checkout() && ! is_wc_endpoint_url() ) :
?>
<script type="text/javascript">
jQuery( function($){
if (typeof wc_checkout_params === 'undefined')
return false;
var couponCode = '';
$('input[name="coupon_code"]').on( 'input change', function(){
couponCode = $(this).val();
});
$('button[name="apply_coupon"]').on( 'click', function(){
$.ajax({
type: 'POST',
url: wc_checkout_params.ajax_url,
data: {
'action': 'apply_checkout_coupon',
'coupon_code': couponCode,
},
success: function (response) {
$(document.body).trigger("update_checkout"); // Refresh checkout
$('.woocommerce-error,.woocommerce-message').remove(); // Remove other notices
$('input[name="coupon_code"]').val(''); // Empty coupon code input field
$('form.checkout').before(response); // Display notices
// console.log(response); // Uncomment for testing
}
});
});
});
</script>
<?php
endif;
}
// Ajax receiver function
add_action( 'wp_ajax_apply_checkout_coupon', 'apply_checkout_coupon_ajax_receiver' );
add_action( 'wp_ajax_nopriv_apply_checkout_coupon', 'apply_checkout_coupon_ajax_receiver' );
function apply_checkout_coupon_ajax_receiver() {
if ( isset($_POST['coupon_code']) && ! empty($_POST['coupon_code']) ) {
WC()->cart->add_discount( wc_format_coupon_code( wp_unslash( $_POST['coupon_code'] ) ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
} else {
wc_add_notice( WC_Coupon::get_generic_coupon_error( WC_Coupon::E_WC_COUPON_PLEASE_ENTER ), 'error' );
}
wc_print_notices();
wp_die();
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
In case anyone else runs into this issue...
When i tried implementing the code I found an issue that when I hit "dummy" form, that it tried and submitted the order.
To get around it, modified the button as a span and styled the span to look and act like a button.
// Just hide default woocommerce coupon field
add_action( 'woocommerce_before_checkout_form', 'hide_checkout_coupon_form', 5 );
function hide_checkout_coupon_form() {
echo '<style>.woocommerce-form-coupon-toggle {display:none;}</style>';
}
// Add a custom coupon field before checkout payment section
add_action( 'woocommerce_review_order_before_payment', 'woocommerce_checkout_coupon_form_custom' );
function woocommerce_checkout_coupon_form_custom() {
echo '<div class="checkout-coupon-toggle"><div class="woocommerce-info">' . sprintf(
__("Have a coupon? %s"), '' . __("Click here to enter your code") . ''
) . '</div></div>';
echo '<div class="coupon-form" style="margin-bottom:20px;" style="display:none !important;">
<p>' . __("If you have a coupon code, please apply it below.") . '</p>
<p class="form-row form-row-first woocommerce-validated">
<input type="text" name="coupon_code" class="input-text" placeholder="' . __("Coupon code") . '" id="coupon_code" value="">
</p>
<p class="form-row form-row-last">
<span class="button" name="apply_coupon" value="' . __("Apply coupon") . '">' . __("Apply coupon") . '</span>
</p>
<div class="clear"></div>
</div>';
}
// jQuery code
add_action( 'wp_footer', 'custom_checkout_jquery_script' );
function custom_checkout_jquery_script() {
if ( is_checkout() && ! is_wc_endpoint_url() ) :
?>
<script type="text/javascript">
jQuery( function($){
$('.coupon-form').css("display", "none"); // Be sure coupon field is hidden
// Show or Hide coupon field
$('.checkout-coupon-toggle .show-coupon').on( 'click', function(e){
$('.coupon-form').toggle(200);
e.preventDefault();
})
// Copy the inputed coupon code to WooCommerce hidden default coupon field
$('.coupon-form input[name="coupon_code"]').on( 'input change', function(){
$('form.checkout_coupon input[name="coupon_code"]').val($(this).val());
// console.log($(this).val()); // Uncomment for testing
});
// On button click, submit WooCommerce hidden default coupon form
$('.coupon-form span[name="apply_coupon"]').on( 'click', function(){
$('form.checkout_coupon').submit();
// console.log('click: submit form'); // Uncomment for testing
});
});
</script>
<?php
endif;
}
Related
I have this field at woocommerce checkout:
<input type="text" class="input-text wfacp-form-control" name="zipcode" id="zipcode" placeholder="" value="">
I am trying to fire action, when this field is changed.
Code i tried is this:
add_action('wp_head', function() {
if ( is_checkout ) {
?><script>
jQuery('input#zipcode').change(function() {
alert('Changed!')
});
</script><?php
}
});
Unfortunatelly it's not working. Can someone tip me to the right direction?
thanks
The biggest flaw in your code is that is_checkout should be is_checkout(). Also the way you apply jQuery is wrong
So you get:
// jQuery code
function action_wp_head() {
if ( is_checkout() ) {
?>
<script type="text/javascript">
jQuery(function($) {
// Selector
$( 'input#zipcode' ).change(function() {
alert( 'Changed!' );
});
});
</script>
<?php
}
}
add_action( 'wp_head', 'action_wp_head' );
I moved the Woocommerce coupon form by editing the review-order.php based on this method
I would like to know if it's possible to make the coupon code apply with AJAX (without reloading the page) like in the cart page. I don't know where to start, please help.
as per your shared link, if you follow the same means you are using the coupon form inside the checkout form, so you should remove the coupon form tag and then use it.
Copy woocommerce review-order.php and past inside your active then woocommerce folder.
Open review-order.php and past coupon HTML inside table structure like this:
<tr class="coupon_checkout">
<td colspan="2">
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
if ( ! wc_coupons_enabled() ) {
return;
}
?>
<i class="fa fa-plus"></i> REDEEM A PROMO CODE/GIFT VOUCHER
<div class="checkout_coupon" method="post" style="display:none">
<p class="form-row form-row-first">
<input type="text" name="coupon_code" class="input-text" placeholder="<?php esc_attr_e( 'Coupon code', 'woocommerce' ); ?>" id="checkout_coupon_code" value="" />
</p>
<p class="form-row form-row-last">
<input id="checkout_apply_coupon" type="button" class="button" name="apply_coupon" value="<?php esc_attr_e( 'Apply Coupon', 'woocommerce' ); ?>" />
</p>
</div>
</td>
</tr>
Add jQuery code either your custom.js file or directly on the footer page like this:
<script>
jQuery(document).on('click','#checkout_apply_coupon', function() {
// Get the coupon code
var code = jQuery( '#checkout_coupon_code').val();
var button = jQuery( this );
data = {
action: 'ajaxapplucoupon',
coupon_code: code
};
button.html( 'wait.');
// Send it over to WordPress.
jQuery.post( wc_checkout_params.ajax_url, data, function( returned_data ) {
if( returned_data.result == 'error' ) {
jQuery( 'p.result' ).html( returned_data.message );
} else {
setTimeout(function(){
//reload with ajax
jQuery(document.body).trigger('update_checkout');
button.html( 'Apply');
}, 2000);
console.log( returned_data+code );
}
})
});
</script>
As I have tested on my checkout page it's working perfectly like this:
https://www.loom.com/share/7dfc833895d248f191ba327cf5290403
Optional (if not setup wp_localize_script yet then add into functions.php)
function custom_enqueue() {
wp_enqueue_script( 'ajax-script', get_template_directory_uri() . '/js/custom.js', array('jquery') ); // optional - if you want to add custom.js then goto theme directory- > js -> and create/add custom.js file
wp_localize_script( 'ajax-script', 'wc_checkout_params', array( 'ajax_url' => admin_url( 'admin-ajax.php' ) ) ); // setup ajax call url
}
add_action( 'wp_enqueue_scripts', 'custom_enqueue' );
You can modify the snippet below to match your styles.
Place the coupon form below in review-order.php or include it from a separate file
<form class="checkout_coupon m-0 p-0 border-0 woocommerce-form-coupon grid grid-cols-3" action="<?php echo esc_url(wc_get_cart_url()); ?>" method="post">
<input type="text" name="coupon_code" class="input-text col-span-2" placeholder="<?php esc_attr_e('Coupon code', 'woocommerce'); ?>" id="coupon_code" value=""/>
<button type="submit" class="theme-button-secondary" name="apply_coupon"><i class="fas fa-arrow-right"></i></button>
</form>
<script>
jQuery(document).on('submit', 'form.checkout_coupon', function (e) {
e.preventDefault()
var form = jQuery(this)
form.block({message: null, overlayCSS: {background: '#FFF', opacity: 0.6}})
jQuery.post(wc_checkout_params.ajax_url, {
action: 'ajax_apply_coupon',
coupon_code: form.find('[name="coupon_code"]').val()
}).done(function () {
jQuery(document.body).trigger('update_checkout')
form.unblock()
}).fail(function (data) {
jQuery(document.body).trigger('update_checkout')
form.unblock()
})
})
</script>
If you want to include it from different file use this
add_action('woocommerce_review_order_after_cart_contents', function () {
if (is_checkout()) {
wc_get_template('checkout/coupon.php');
}
});
Add your ajax handler with the coupon logic in your function.php
function ajax_apply_coupon()
{
$coupon_code = null;
if (!empty($_POST['coupon_code'])) {
$coupon_code = sanitize_key($_POST['coupon_code']);
}
$coupon_id = wc_get_coupon_id_by_code($coupon_code);
if (empty($coupon_id)) {
wc_add_notice(__('Sorry, there has been an error.', 'woocommerce'), 'error');
wp_send_json_error(['message' => __('Sorry, there has been an error.', 'woocommerce')], 400);
}
if (!WC()->cart->has_discount($coupon_code)) {
WC()->cart->add_discount($coupon_code);
}
wp_send_json_success(['message' => __('Coupon code applied successfully.', 'woocommerce')], 200);
}
add_action('wp_ajax_ajax_apply_coupon', 'ajax_apply_coupon');
add_action('wp_ajax_nopriv_ajax_apply_coupon', 'ajax_apply_coupon');
I have this button here. The use of this button is to add to cart a product has a product id of 237, variation id of 208673, and attribute_pa_option of bluetooth. Is there a way to AJAX this?
<div class="btnss">
<span class="price">
<span class="woocommerce-Price-amount amount">6,999
<span class="woocommerce-Price-currencySymbol">kr</span>
</span>
</span>
<div class="quantity buttons_added">
<input type="button" value="-" class="minus">
<label class="screen-reader-text" for="quantity_5b101f605f067">Quantity</label>
<input type="number" id="quantity_5b101f605f067" class="input-text qty text" step="1" min="1" max="" name="quantity" value="1" title="Qty" size="4" pattern="[0-9]*" inputmode="numeric" aria-labelledby="">
<input type="button" value="+" class="plus">
</div>
Add to cart
</div>
To make it work I use a custom ajax add-to-cart for product variations exclusively.
1). I have first changed a bit your button html:
<div class="btnss">
<span class="price">
<span class="woocommerce-Price-amount amount">6,999
<span class="woocommerce-Price-currencySymbol">kr</span>
</span>
</span>
<div class="quantity buttons_added">
<input type="button" value="-" class="minus">
<label class="screen-reader-text" for="quantity_5b101f605f067">Quantity</label>
<input type="number" id="quantity_5b101f605f067" class="input-text qty text" step="1" min="1" max="" name="quantity" value="1" title="Qty" size="4" pattern="[0-9]*" inputmode="numeric" aria-labelledby="">
<input type="button" value="+" class="plus">
</div>
Add to cart
</div>
As you will see I don't use the button href attribute, as I post the data through ajax.
For your attributes, if you have more than one, you will separate each pair by a coma like:
data-variation="pa_option=bluetooth,pa_color=red-shiny"
2). The PHP (Wordpress-Ajax) and the jQuery (Ajax) code:
// Wordpress Ajax php: Adding variation to cart
add_action( 'wp_ajax_nopriv_variation_to_cart', 'product_variation_add_to_cart' );
add_action( 'wp_ajax_variation_to_cart', 'product_variation_add_to_cart' );
function product_variation_add_to_cart() {
if( isset($_POST['pid']) && $_POST['pid'] > 0 ){
$product_id = (int) $_POST['pid'];
$variation_id = (int) $_POST['vid'];
$quantity = (int) $_POST['qty'];
$attributes = explode(',', $_POST['var']);
$variation = array();
foreach($attributes as $values){
$values = explode('=', $values);
$variation['attributes_'.$values[0]] = $values[1];
}
WC()->cart->add_to_cart( $product_id, $quantity, $variation_id, $variation );
echo true;
}
die(); // To avoid server error 500
}
// The Jquery ajax script
add_action( 'wp_footer', 'custom_product_variation_script' );
function custom_product_variation_script() {
// HERE set the page or the post ID
$the_id = 102;
if( ! ( is_page($the_id) || is_single($the_id) ) ) return;
$view_cart = 'View cart';
$adding = __('Adding to cart…', 'woocommerce');
?>
<script type="text/javascript">
jQuery( function($){
// wc_add_to_cart_params is required to continue
if ( typeof wc_add_to_cart_params === 'undefined' )
return false;
var a = 'a.button.ajax.variation',
b = $(a).html(),
c = '<?php echo $view_cart; ?>',
d = '<?php echo $adding; ?>';
// Sync the data-quantity attribute
$('input.minus,input.plus').on( 'click blur', function(){
$(a).attr('data-quantity',$('input.qty').val());
});
$('input.qty').on('input click blur', function(){
$(a).attr('data-quantity',$('input.qty').val());
})
$(a).on('click', function(e){
e.preventDefault();
$('a.wc-forward').remove();
$(a).html(d);
// The Ajax request
$.ajax({
type: 'POST',
url: wc_add_to_cart_params.ajax_url,
data: {
'action': 'variation_to_cart',
'pid' : $(a).attr('data-product_id'),
'vid' : $(a).attr('data-variation_id'),
'qty' : $(a).attr('data-quantity'),
'var' : $(a).attr('data-variation'),
},
success: function (response) {
if(response){
// Update button and refresh minicart fragments
setTimeout(function(){
$(a).addClass('added').html(b).after(c);
$(document.body).trigger('added_to_cart').trigger('wc_fragment_refresh');
}, 500);
}
},
error: function (error) {
$(a).addClass('failed').html('Add to cart failed!');
console.log(error);
}
});
});
});
</script>
<?php
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
You could use this plugin ("Woocommerce Ajax add to cart for variable products") to Ajaxify the "Add to cart" button which is supposed to be pressed after selecting variation's attributes (e.g. Color) and quantity on the front-end. Without this plugin by default, the page refreshes when you press "Add to cart"
I'm trying to check inserted value for billing_email field in WooCommerce checkout to know if it is existed or not.
Here is code in functions.php
add_action('wp_enqueue_scripts', 'live_validation' );
add_action('wp_ajax_validate_email', 'validate_email_input');
add_action('wp_ajax_nopriv_validate_email', 'validate_email_input');
function live_validation() {
wp_enqueue_script( "validate_email", get_stylesheet_directory_uri() . '/check-email.js', array( 'jquery' ) );
wp_localize_script( "validate_email", "validateEmail", array( 'ajaxurl' => admin_url( 'admin-ajax.php' ) ) );
}
function validate_email_input() {
global $wpdb;
$email = $_POST['billing_email'];
if ( email_exists($email) ) {
echo 'existed';
} else {
echo 'not exist';
}
exit;
}
And this is js file
jQuery(document).ready(function($) {
$('input[name=billing_email]').change(function() {
var input_value = $(this).val();
$.post( validateEmail.ajaxurl, { action:'validate_email', billing_email:input_value }, function(data) {
$('.message').html(data);
});
});
});
HTML form
<p class="form-row form-row form-row form-row-first validate-required woocommerce-validated" id="billing_email_field">
<label for="billing_email" class="">Email <abbr class="required" title="required">*</abbr></label>
<input type="text" class="input-text " name="billing_email" id="billing_email" placeholder="Email" value="abc#gmail.com"></p>
<span class="message"></span>
I can see admin-ajax file fired when input value of billing_email changed, but the notification didn't show.
Please help. Thanks.
I am trying to add my own "Add Media" Button in my own plugin form pages, I created a plugin and I have a form in add-new.php file here is the code:
<div class="wrap">
<h1><?php _e( 'Add Deal', 'webdevs' ); ?></h1>
<form action="" method="post">
<!-- I NEED TO CHANGE THIS TO SHOW "ADD MEDIA BUTTON" -->
<input id="upload_image" type="text" size="36" name="upload_image" value="" />
<input id="upload_image_button" type="button" value="Upload Image" />
<?php wp_nonce_field( 'deal-new' ); ?>
<?php submit_button( __( 'Add Deal', 'webdevs' ), 'primary', 'submit_deal' ); ?>
</form>
How to add the html code and handle it in php
please help
Thnaks
I have created a custom image upload with metabox, you can set below code as per your need.
What you need is to call new WP's thickbox media uploader CSS and JS on your page. you have to modify condition of adding script for plugin page.
Condition to modify if( 'post' !== $typenow ).
What it will do?
It will allow you to open wordpress media uploader and send your selected image url to textbox then you can save the url in post_meta using update_post_meta() or wherever you want to save it. you can get the url from
Supposeable variable :
$content_img = $_POST['content_img'];
Html
<p>
<label><b>Upload Content Image</b></label><br/>
<input class="upload_image" name="content_img" type="text" readonly="readonly" value="<?php echo $content_img ?>"/>
<input type="button" value="Upload" class="button button-primary button-large" onclick="upload_new_img(this)"/>
Remove
</p>
Admin functions.php
// Enqueue script in admin
function my_admin_scripts() {
# Not our screen, bail out
if( 'post.php' !== $hook )
return;
# Not our post type, bail out
global $typenow;
if( 'post' !== $typenow )
return;
wp_enqueue_media('media-upload');
wp_enqueue_media('thickbox');
wp_register_script('my-upload', get_stylesheet_directory_uri().'/js/metabox.js', array('jquery','media-upload','thickbox'));
wp_enqueue_media('my-upload');
}
// Call thickbox CSS
function my_admin_styles() {
wp_enqueue_style('thickbox');
}
add_action('admin_enqueue_scripts', 'my_admin_scripts');
add_action('admin_enqueue_scripts', 'my_admin_styles');
Custom JS metabox.js
function upload_new_img(obj)
{
var file_frame;
var img_name = jQuery(obj).closest('p').find('.upload_image');
if ( file_frame ) {
file_frame.open();
return;
}
file_frame = wp.media.frames.file_frame = wp.media(
{
title: 'Select File',
button: {
text: jQuery( this ).data( 'uploader_button_text' )
},
multiple: false
}
);
file_frame.on('select', function() {
attachment = file_frame.state().get('selection').first().toJSON();
var newwurl = attachment.url.split('/wp-content');
img_name[0].value = '/wp-content'+newwurl[1];
file_frame.close();
// jQuery('.upload_image').val(attachment.url);
});
file_frame.open();
}
function remove_image(obj) {
var img_name;
if (jQuery(obj).closest('p').find('.upload_image').length > 0) {
img_name = jQuery(obj).closest('p').find('.upload_image');
} else {
img_name = jQuery(obj).closest('td').find('.upload_image');
}
if (typeof img_name != "undefined") {
img_name.val('');
}
}