Move coupon form before payment section in WooCommerce checkout
Asked Answered
N

3

6

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?

Narco answered 12/1, 2021 at 21:12 Comment(0)
H
17

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.

Horan answered 13/1, 2021 at 5:44 Comment(7)
Hi @LoicTheAztec. Thank you for your answer. The code works is currently the best solution for this issue you can find online. But it could be better. Is it possible to make the coupon field into an accordion (click the text to reveal; click again to hide)? Having the coupon field always open might still distract a lot of customers from what's important, which is filling in the billing details, because the coupon field takes up a lot of screen real estate.Narco
Hey @LoicTheAztec, sorry to bother you but I have a question. How can I make the coupon form take 100% of the width but keeping the button in 1 line as well. Currently, on desktop, the coupon form takes ~70% of the width and on mobile it takes 100% of the width but the button jumps below the coupon field.Narco
@Horan if I set the width of the coupon field to 100%, the field stretches out but the button jumps below the field, it does not stay in 1 line no matter which element of the coupon form I target.Narco
@Narco Is better to ask a new question providing everything involved: CSS generated html excerpt, a live link and clear explanations (I never answer CSS related questions personally)Horan
Hi @LoicTheAztec, for the simplified version of your code, is it possible to give the coupon form a specific CSS class based on if the coupon code is correct or incorrect?Narco
@Narco No because we can't get any information related to the coupon validity on (or after) coupon submission.Horan
Is this supposed to validate the coupon, just like the default behaviour? For me this doesn't throw see any errors, but not sure if I have a conflict or it's intended.Gerenuk
I
1

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

Intercross answered 1/4, 2023 at 15:41 Comment(0)
N
0

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;
}
Nikaniki answered 14/7, 2022 at 2:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.