skip to Main Content

I am trying to create a custom checkout radio button that calculates restaurant tip in percentage.

For the radio button, the static value is working fine.

However, I want to get the subtotal and calculate certain percentage on custom radio button click.

This is my code

add_action( 'woocommerce_after_checkout_billing_form', 'add_box_option_to_checkout' );
function add_box_option_to_checkout( $checkout ) {

$chosen = WC()->session->get( 'tip' );
    $chosen = empty( $chosen ) ? WC()->checkout->get_value( 'tip' ) : $chosen;
    $chosen = empty( $chosen ) ? '0' : $chosen;
    
    $total = WC()->cart->get_subtotal();
$fivetip = $total * 0.05;

    woocommerce_form_field( 'tip',  array(
        'type'      => 'radio',
        'class'     => array( 'form-row-wide', 'update_totals_on_change' ),
        'options'   => array(
            $fivetip  => '5%',
            '10.00'     => '10%',
            '15.00'     => '15%',
        ),
    ), $chosen );
    
        woocommerce_form_field( 'add_tip', array(
        'type'          => 'text',
        'class'         => array('add_tipform-row-wide'),
        'placeholder'   => __('Enter Custom Tip Amount') 
        ), $checkout->get_value( 'add_tip' ));
    
}


add_action( 'woocommerce_cart_calculate_fees', 'checkout_tip_fee', 20, 1 );
function checkout_tip_fee( $cart ) {
    if ( $radio = WC()->session->get( 'tip' ) ) {
        $cart->add_fee( 'Tip', $radio );
    }
}

add_action( 'woocommerce_checkout_update_order_review', 'checkout_tip_choice_to_session' );

function checkout_tip_choice_to_session( $posted_data ) {
    parse_str( $posted_data, $output );
    if ( isset( $output['tip'] ) ){
        WC()->session->set( 'tip', $output['tip'] );
    }
}

2

Answers


  1. You can get it like this:

    1. Add fields to checkout
    2. Empty/deselect the fields based on the field valued (so as to leave only the radio button or the text field valued)
    3. Calculate the cost based on the field and add the fee to the cart

    So:

    // add custom fields to checkout
    add_action( 'woocommerce_after_checkout_billing_form', 'add_box_option_to_checkout' );
    function add_box_option_to_checkout( $checkout ) {
    
        woocommerce_form_field( 'tip',  array(
            'type'    => 'radio',
            'class'   => array( 'form-row-wide', 'update_totals_on_change' ),
            'options' => array(
                '5'  => '5%',
                '10' => '10%',
                '15' => '15%',
            ),
        ));
        
        woocommerce_form_field( 'add_tip', array(
            'type'        => 'text',
            'class'       => array('add_tipform-row-wide', 'update_totals_on_change'),
            'placeholder' => __('Enter Custom Tip Amount') 
        ));
    
    }
    

    Now a jQuery script will empty the text field when a radio button is selected and vice versa:

    // uncheck radio buttons when text field is changed
    add_action( 'wp_footer', 'uncheck_radio_buttons_when_text_field_is_changed' );
    function uncheck_radio_buttons_when_text_field_is_changed() {
        if ( is_checkout() && ! is_wc_endpoint_url() ) {
        ?>
        <script type="text/javascript">
        // deselect radio buttons when an amount is entered in the text field
        jQuery('#add_tip').keyup(function(){
            jQuery('#tip_field input[name=tip]').prop('checked', false);
        });
        // clears the text field when a percentage is selected
        jQuery('input[name=tip]').change(function(){
            jQuery('#add_tip').val('');
        });
        </script>
        <?php
        }
    }
    

    Finally it calculates the amount of the fee based on the selected value calculated on the subtotal (excluding taxes) of the cart.

    Make sure you use the same name for the fee to add to the cart in
    order to overwrite the same and display only one.

    // adds a custom fee based on the field valued
    add_action( 'woocommerce_cart_calculate_fees', 'add_custom_fee', 10, 1 );
    function add_custom_fee( $cart ) {
    
        // get the subtotal (excluding taxes)
        $subtotal = WC()->cart->subtotal_ex_tax;
    
        if ( ! $_POST || is_admin() || ! is_ajax() ) {
            return;
        }
    
        if ( isset( $_POST['post_data'] ) ) {
            parse_str( $_POST['post_data'], $post_data );
        } else {
            $post_data = $_POST;
        }
    
        // adds the fee based on the checked percentage
        if ( isset( $post_data['tip'] ) ) {
            $percentage = $post_data['tip'];
            $fee = $subtotal * $percentage / 100;
            if ( $fee > 0 ) {
                WC()->cart->add_fee( 'Tip', $fee, true );
            }
        }
    
        // adds the fee based on the amount entered
        if ( isset( $post_data['add_tip'] ) ) {
            $fee = (float) str_replace( ',', '.', $post_data['add_tip'] );
            if ( $fee > 0 ) {
                WC()->cart->add_fee( 'Tip', $fee, true );
            }
        }
    
    }
    

    The code has been tested and works. Add it to your active theme’s functions.php.

    Login or Signup to reply.
  2. The following is something advanced using Ajax and WC Sessions:

    It will add a custom a tip (as a custom fee) based on selected radio buttons options: fixed percentages options or custom option that will show a text field to allow customer to input a fixed amount.

    Displayed Fields are hand coded to get a better display than WooCommerce form fields for radio buttons (see the screenshots below).

    enter image description here

    How it works for the customer:

    On checkout page load, a Tip of 5% (a fee) is applied (selected by default). When changing the selected option to something else, the applied fee changes.

    If the "custom" option is selected, the fee is removed while a text field appears below:

    enter image description here
    Customer can input a fixed amount and a Tip (a fee) is applied with this amount.

    Here is the code:

    // Display custom checkout fields
    add_action( 'woocommerce_after_checkout_billing_form', 'add_box_option_to_checkout' );
    function add_box_option_to_checkout( ) {
        // Here set your radio button options (Values / Labels pairs)
        $options = array( '5'  => '5%', '10' => '10%', '15' => '15%', 'custom' => __('Custom', 'woocommerce') );
    
        // Radio button fields
        echo '<style> #add_tip_field.form-row label { display:inline-block; margin-left:6px; } </style>
        <p class="form-row form-row-wide" id="add_tip_field"><span class="woocommerce-input-wrapper">
        <label for="add_tip"><strong>'.__('Add a tip', 'woocommerce'). ':</strong>&nbsp;</label>';
    
        foreach ( $options as $value => $label_name ) {
            $checked = $value == '5' ? ' checked="checked"' : '';
    
            echo '<label for="add_tip_'.$value.'" class="radio ">
                <input type="radio" class="input-radio " value="'.$value.'" name="add_tip" id="add_tip_'.$value.'"'.$checked.'>&nbsp'.$label_name.'
            </label>';
        }
        echo '</span></p>';
    
        // Text field (hidden by default)
        echo '<p class="form-row form-row-wide" id="custom_tip_field" style="display:none""><span class="woocommerce-input-wrapper">
            <input type="text" class="input-text " name="custom_tip" id="custom_tip" value="" placeholder="'.__('Input a tip amount', 'woocommerce').'">
        </span></p>';
    }
    
    // jQuery / Ajax script
    add_action( 'woocommerce_after_checkout_form', 'wc_checkout_fee_script' );
    function wc_checkout_fee_script() {
        ?>
        <script type="text/javascript">
        jQuery( function($){
            if (typeof wc_checkout_params === 'undefined')
                return false;
    
            var addTip    = 'input[name="add_tip"]',
                customTip = 'input[name="custom_tip"]'
    
            function triggerAjaxEvent( amount, type = 'percent' ){
               $.ajax({
                    type: 'POST',
                    url: wc_checkout_params.ajax_url,
                    data: {
                        'action': 'tip_fee',
                        'amount': amount,
                        'type'  : type
                    },
                    success: function (result) {
                        $(document.body).trigger('update_checkout');
                        console.log(result);
                    },
                });
            }
    
            triggerAjaxEvent( $(addTip+':checked').val() );
    
            $('form.checkout').on('change', addTip, function() {
                var textField = $('#custom_tip_field'),
                    percent   = $(this).val();
    
                if( percent === 'custom' && textField.css('display') === 'none' ) {
                    textField.show(200);
                } else if ( percent !== 'custom' && textField.css('display') !== 'none' ) {
                    textField.hide(200, function(){
                        $(customTip).val('');
                    });
                }
                triggerAjaxEvent( percent );
            });
    
            $('form.checkout').on('input change', customTip, function() {
                triggerAjaxEvent( $(this).val(), 'custom' );
            });
        });
        </script>
        <?php
    }
    
    // Get Ajax request and save data to WC session
    add_action( 'wp_ajax_tip_fee', 'get_tip_fee' );
    add_action( 'wp_ajax_nopriv_tip_fee', 'get_tip_fee' );
    function get_tip_fee() {
        if ( isset($_POST['amount']) && isset($_POST['type']) ) {
            $fee  = is_numeric($_POST['amount']) && $_POST['amount'] > 0 ? floatval($_POST['amount']) : 0;
    
            WC()->session->set('fee_data', array(
                'type'   => esc_attr($_POST['type']),
                'amount' => $fee
            ) );
    
            print_r(WC()->session->get('fee_data'));
        }
        die();
    }
    
    
    // Add a dynamic fee from WC Session ajax data
    add_action( 'woocommerce_cart_calculate_fees', 'checkout_custom_tip_fee' );
    function checkout_custom_tip_fee( $cart ) {
        if ( is_admin() && !defined('DOING_AJAX') )
            return;
    
        $data = WC()->session->get('fee_data');
        $fee  = $total = 0;
    
        if ( isset($data['type']) && isset($data['amount']) ) {
            // 1. Fixed Fee amount
            if ( $data['type'] === 'custom' ) {
                $text  = $data['type'];
                $fee   = $data['amount'];
            } 
            // 2. Calculated percentage Fee amount
            elseif ( $data['type'] === 'percent' && $data['amount'] > 0 ) {
                $text  = $data['amount'] . '%';
    
                // Get cart subtotal excl. Taxes (discounted)
                foreach ( $cart->get_cart() as $cart_item ) {
                    $total = $cart_item['line_total'];
                }
                // Calculate fee
                $fee   = $total * $data['amount'] / 100 ;
            }
            // Add the fee
            if ( $fee > 0 ) {
                $cart->add_fee( sprintf( __('Tip (%s)', 'woocommerce' ), $text ), $fee );
            }
        }
    }
    

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


    Addition related to your comment:

    To use cart items total including taxes replace in last function:

                // Get cart subtotal excl. Taxes (discounted)
                foreach ( $cart->get_cart() as $cart_item ) {
                    $total = $cart_item['line_total'];
                }
    

    with

                // Get cart subtotal Incl. Taxes (discounted)
                foreach ( $cart->get_cart() as $cart_item ) {
                    $total = $cart_item['line_total'] + $cart_item['line_tax'];
                }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search