2

I am trying to make a ajax function to make users pay half the price of total amount, on custom cash on delivery method. When user selects yes or no radio button total amount is changed accordingly.

This: Link is a great example what I am trying to follow but I need to make it as a ajax call.

Here's how I added new fields before payment block:

add_action("woocommerce_review_order_before_payment", "new_buttons");
function new_buttons(){
 echo '<div id="cash-on-delivery-wrap" class="cash-on-delivery-wrap">';
 echo '<h5>Cash on delivery: </h5><div style="clear:both"></div>';
 echo '<h6>Pay half 50%</h6><div style="clear:both"></div>';
 echo '<div class="cod_button_wrap">';
 echo '<label><input type=radio value="no" name="new-cod" checked/>No</label>';
 echo '<label><input type=radio value="yes" name="new-cod" />Yes</label>';
 echo '</div>';
 echo '</div>';
}

Here is the JS:

jQuery(document).ready(function(){
  jQuery("form.checkout").on("change", "#cash-on-delivery-wrap input", 
   function(){
     var data = {
        action: 'change_cod',
        security: wc_checkout_params.update_order_review_nonce,
        post_data: jQuery( 'form.checkout' ).serialize()
     };

     jQuery.post( ajaxurl, data, function( response )
     {
        jQuery( 'body' ).trigger( 'update_checkout' );
     });
   });
});

Here is the function:

function custom_cart_total() {
 $current_state = $_POST['post_data'];
 if ( is_admin() && ! defined( 'DOING_AJAX' ) )
 return;
 if($current_state=='yes'){
    WC()->cart->total *= 0.50;
 }else{
    WC()->cart->total;
 }
 exit;
}
add_action( 'wp_ajax_nopriv_change_cod', 'custom_cart_total' );
add_action( 'wp_ajax_change_cod', 'custom_cart_total' ); 

Cant seem to make it work, what am I missing here.

LoicTheAztec
  • 229,944
  • 23
  • 356
  • 399
mysticalghoul
  • 632
  • 2
  • 10
  • 23

1 Answers1

1

Note: The code in the linked answer, is only changing the displayed total amount in cart and checkout, but doesn't change it for real.

This answer is also changing the displayed checkout total amount. We need another function hooked in the order creation process, to update the total amount.

For Wordpress Ajax you need to register your script in an external JS file that you will upload in your active theme folder inside a js subfolder. Let say that this external file name will be pay_half.js.

1) Here is the function that will do that registration and will enable WordPress Ajax functionality:

add_action( 'wp_enqueue_scripts', 'ajax_change_shipping' );
function ajax_change_shipping() {
    // Only on front-end and checkout page
    if( is_admin() || ! is_checkout() ) return;

    // Get the Path to the active theme or child theme or plugin folder
    # $path = plugin_dir_url( __FILE__ ); // A plugin
    # $path = get_template_directory_uri(); // A Normal theme
    $path = get_stylesheet_directory_uri(); // A child theme

    // Define the subfolder name
    $subfolder = 'js';

    // Define the file name
    $filename = 'pay_half.js';

    // Reference name of the script (should be unique)
    $handle = 'pay-half';

    // Set the ajaxurl parameter used in your script
    $data = array(
        'ajaxurl' => admin_url( 'admin-ajax.php' ),
    );

    // The variable name whichwill contain the data
    $name = 'pay_half';

    // Registering the javascript file and enqueues it.
    wp_enqueue_script( $handle, $path."/$subfolder/$filename", array( 'jquery' ), '1.0', true );

    // Localizing the registered script (Here using Ajax)
    wp_localize_script( $handle, $name, $data );
}

Code goes in function.php file of your active child theme (or theme) or also in any plugin file.


2) Now the Javascript/jQuery external file (named: pay_half.js):

jQuery(document).ready(function ($) {
    var selectHalf = '#cash-on-delivery-wrap input[type="radio"]',
        paymentMethod = 'input[name^="payment_method"]',
        codWrap = $('.cash-on-delivery-wrap'),
        cartGTotal = $('input#cart_gtotal').val(),
        codPartial;

    // Detecting payment method on load to show/hide cod custom options
    if( $(paymentMethod+':checked').val() == 'cod' )
        codWrap.show("fast");
    else
        codWrap.hide("fast");

    // Live detecting choosen payment method to show/hide cod custom options
    $( 'form.checkout' ).on( 'change', 'input[name^="payment_method"]', function() {
        if ( $(paymentMethod+':checked').val() == 'cod' ) {
            codWrap.show("fast");
        } else {
            codWrap.hide("fast");
            $('#cash-on-delivery-wrap input#cod-options_no').prop('checked', true);
        }
        $(document.body).trigger("update_checkout");
        // console.log($(paymentMethod+':checked').val());
    });

    // The "Cod" custom options (ajax)
    $(selectHalf).click(function(){
        if($(selectHalf+':checked' ).val() == 'yes' ) codPartial = 'yes';
        else codPartial = 'no';

        $.ajax({ // This does the ajax request
            url: pay_half.ajaxurl,
            type : 'post',
            data: {
                'action':'cod_partial_payment', // Name of the php function
                'cod_partial' : codPartial       // Passing this variable to the PHP function
            },
            success:function(data) {
                // Displaying the price (Ajax)
                $( 'table.shop_table > tfoot > tr.order-total > td > strong > span' ).html(data.price_html);
                if(codPartial == 'yes')
                    $('input#cart_remaining').val(data.price_remaining);
                else
                    $('input#cart_remaining').val(0);
                $(document.body).trigger("wc_fragment_refresh");
                console.log(data);
            },
            error: function(error){
                console.log(error);
            }
        });
    });
});

3) The Display of your custom fields (revisisted).

I have added 2 hidden fields with the total amount and the remaining amount to pay.

add_action( 'woocommerce_review_order_before_payment', 'cod_payment_options', 10 );
function cod_payment_options(){
    echo '<style>.cod-button-options label{display:inline-block; margin:0 6px;}</style>
    <div id="cash-on-delivery-wrap" class="cash-on-delivery-wrap">
        <h3>' . __( 'Cash on delivery option' ) . '</h3>';

    woocommerce_form_field( 'cod-options', array(
        'type'      => 'radio',
        'class'     => array('form-row-wide', 'cod-button-options'),
        'label'     => __( '<b>Pay half (50%): </b>' ),
        'required' => false,
        'options' => array(
            'no'  => __( 'No' ),
            'yes' => __( 'Yes' ),
        )
    ), 'no' );

    // Some additional hidden fields
    echo '<input type="hidden" id="cart_gtotal" name="cart_gtotal" value="'. WC()->cart->total .'">
        <input type="hidden" id="cart_remaining" name="cart_remaining" value="0" />
        </div>';
}

Code goes in function.php file of your active child theme (or theme) or also in any plugin file.


4) The driven php function (Wordpress Ajax):

add_action( 'wp_ajax_nopriv_cod_partial_payment', 'cod_partial_payment' );
add_action( 'wp_ajax_cod_partial_payment', 'cod_partial_payment' );
function cod_partial_payment() {
    if( ! isset($_POST['cod_partial']) ) return;

    $current_state = $_POST['cod_partial'];
    $remaining = 0;

    if( $current_state == 'yes' ){
        WC()->cart->total /= 2;
    }
    WC()->session->set( 'total', WC()->cart->total );

    $response = array(
        'price_html'        => wc_price( WC()->cart->total ),
        'price_remaining'   => WC()->cart->total,
    );

    header( 'Content-Type: application/json' );
    echo json_encode( $response );

    die(); // Always (to avoid an error 500)
}

Code goes in function.php file of your active child theme (or theme) or also in any plugin file.


5) All Other php functions (Update order amount, save metadata, display a message):

// Replacing the total amount when COD option is enabled
add_action( 'woocommerce_checkout_create_order', 'cod_options_change_order_total_ammount', 10, 2 );
function cod_options_change_order_total_ammount( $order, $data ) {
    if ( ! empty( $_POST['cod-options'] ) && $_POST['cod-options'] == 'yes' ) {
        $remaining = sanitize_text_field( $_POST['cart_remaining'] );
        $total = WC()->cart->total - floatval($remaining);
        WC()->session->set( 'total', $total );
        $order->set_total( $total );
    }
}


// Updating order meta data for Cod selected option
add_action( 'woocommerce_checkout_update_order_meta', 'cod_options_update_order_meta', 10, 1 );
function cod_options_update_order_meta( $order_id ) {
    if ( ! empty( $_POST['cod-options'] ) && $_POST['cod-options'] == 'yes' ) {
        update_post_meta( $order_id, '_cod_remaining_amount', sanitize_text_field( $_POST['cart_remaining'] ) );
        update_post_meta( $order_id, '_cod_partial_paid_amount', sanitize_text_field( $_POST['cart_gtotal'] - $_POST['cart_remaining'] ) );
    }
    update_post_meta( $order_id, '_cod_partial_payment_option', sanitize_text_field( $_POST['cod-options'] ) );
}

// Displaying the remaining amount to pay in a custom message on Order received page (thank you)
add_action( 'woocommerce_thankyou_cod', 'cod_options_woocommerce_thankyou', 10, 1 );
function cod_options_woocommerce_thankyou( $order_id ) {
    if( get_post_meta( $order_id, '_cod_partial_payment_option', true ) == 'yes' ){
        $ra = '<span style="color:#96588a;">'.wc_price( get_post_meta( $order_id, '_cod_remaining_amount', true )).'</span>';
        ?>
            <ul class="woocommerce-order-overview woocommerce-thankyou-cod-options order_details">
                <li class="woocommerce-order-overview__remaining_total order">
                <strong><?php echo __("There is a remaining amount of $ra to pay on this order."); ?></strong>
                </li>
            </ul>
        <?php
    }
}

Code goes in function.php file of your active child theme (or theme) or also in any plugin file.

This code is tested on Woocommerce 3+ and works.

LoicTheAztec
  • 229,944
  • 23
  • 356
  • 399