skip to Main Content

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?

3

Answers


  1. 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"), '<a href="#" class="show-coupon">' . __("Click here to enter your code") . '</a>'
        ) . '</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.

    Login or Signup to reply.
  2. 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"), '<a href="#" class="show-coupon">' . __("Click here to enter your code") . '</a>'
        ) . '</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;
    }
    
    Login or Signup to reply.
  3. I updated the code to display the coupon form in a more clean look.
    New coupon form look

    1. Use this custom CSS code.
    .woocommerce .woocommerce-info {
    border: 0px;
    background: none;
    margin:0px;
    padding-left:42px;
    padding-bottom:10px;
    padding-top:10px;}
        
    .woocommerce .woocommerce-info:before {
    color: #a1a1a1;
    content: "f145";
    font-family: "fontawesome";
    padding-top: 0.8em;}
    
    .woocommerce-info .show-coupon{
    float:none !important;
    font-weight:400 !important;}
    
    .coupon-form .form-row.form-row-first{
    width:30%;margin-right:20px;margin-left:20px}
    
    #coupon_code.input-text{
    border-radius:4px;}
    
    .coupon-form .form-row.form-row-last{
    float:left;}
    
    .coupon-form .form-row.form-row-last .button{
    font-size: 12px;
    font-weight:600;
    text-transform:uppercase;
    font-family:montserrat;
    color:#000;
    position: absolute;
    padding-top: 10px;}
    
    .coupon-form .form-row.form-row-last .button:hover{text-decoration:underline;}
    1. Update only this php portion.
    // 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(
            '<a href="#" class="show-coupon">' . __("Have a promo code?") . '</a>'
        ) . '</div></div>';
    
        echo '<div class="coupon-form" style="margin-bottom:0px;" style="display:none !important;">
            <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>';
    }
    1. This portion is a test version and only optional.
      Now if you want to change the position of the entire coupon form you have to update this woocommerce hook ‘woocommerce_review_order_before_payment’

    Changing hooks works but somehow causes the coupon form to duplicate and stay on top.
    I’m still working on a solution and update this once I learn.

    Changing hook live example

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search