Checkout input text field that set a discount in WooCommerce

I am using Checkbox field that add / remove a custom fee in WooCommerce answer code which is working perfectly on my shop, to set a discount (a negative fee).

But I want to set the fee amount using an input field. My goal is to reduce the total amount of purchases by the inputed value in a num/text input field. I don’t want to use checkbox, I want to use input or number filed for determine amount of the fee.

Is it possible ? How can i do this?

Answer

To enable a checkout input text field that set a custom discount in WooCommerce, few changes are required as follows:

// Display an input text field after billing fields
add_action( 'woocommerce_after_checkout_billing_form', 'add_custom_checkout_text_field', 20 );
function add_custom_checkout_text_field(){

    woocommerce_form_field( 'custom_discount', array(
        'type'  => 'text',
        'label' => __('Add a discount amount'),
        'class' => array( 'form-row-wide' ),
    ), WC()->session->get('custom_discount') );
}

// Remove "(optional)" label on checkbox field
add_filter( 'woocommerce_form_field' , 'remove_order_comments_optional_fields_label', 10, 4 );
function remove_order_comments_optional_fields_label( $field, $key, $args, $value ) {
    // Only on checkout page for Order notes field
    if( 'custom_discount' === $key && is_checkout() ) {
        $optional = '&nbsp;<span class="optional">(' . esc_html__( 'optional', 'woocommerce' ) . ')</span>';
        $field = str_replace( $optional, '', $field );
    }
    return $field;
}

// Ajax / jQuery script
add_action( 'wp_footer', 'custom_discount_script' );
function custom_discount_script() {
    // On checkoutpage
    if( ( is_checkout() && ! is_wc_endpoint_url() ) ) :
    ?>
    <script type="text/javascript">
    jQuery( function($){
        if (typeof woocommerce_params === 'undefined')
            return false;

        $(document.body).on('input change', 'input[name=custom_discount]', function(){
            $.ajax({
                type: 'POST',
                url: woocommerce_params.ajax_url,
                data: {
                    'action': 'custom_discount',
                    'custom_discount': $(this).val(),
                },
                success: function (result) {
                    $('body').trigger('update_checkout');
                    console.log(result);
                },
            });
        });
    });
    </script>
    <?php
    endif;
}

// Get the ajax request and set value to WC session
add_action( 'wp_ajax_custom_discount', 'get_ajax_custom_discount' );
add_action( 'wp_ajax_nopriv_custom_discount', 'get_ajax_custom_discount' );
function get_ajax_custom_discount() {
    if ( isset($_POST['custom_discount']) ) {
        $discount = $_POST['custom_discount'] ? floatval($_POST['custom_discount']) : '';
        WC()->session->set('custom_discount', $discount );
        // echo WC()->session->get('custom_discount');
    }
    die();
}

// Add / Remove a custom discount
add_action( 'woocommerce_cart_calculate_fees', 'add_remove_custom_discount', 10, 1 );
function add_remove_custom_discount( $cart ) {
    // Only on checkout
    if ( ( is_admin() && ! defined( 'DOING_AJAX' ) ) || is_cart() )
        return;

    $discount = (float) WC()->session->get('custom_discount');

    if( $discount > 0 ) {
        $cart->add_fee( __( 'Custom Discount', 'woocommerce'), -$discount );
    }
}

// Reset WC Session custom discount variable
add_action( 'woocommerce_checkout_order_created', 'reset_wc_session_custom_discount_variable' );
function reset_wc_session_custom_discount_variable( $order ) {
    if ( WC()->session->__isset('custom_discount') ) {
        WC()->session->__unset('custom_discount');
    }
}

Code goes in functions.php file of the active child theme (or active theme). Tested and works.