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'] ); } }
Answer
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).
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:
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> </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.'> '.$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']; }