I posted a similar problem a few months back and I have been helped out there to solve it. It was working fine until Woocommerce version 3.1.2. I have been updating to latest version of WooCommerce hoping the problem will be solved.
I am adding dynamic fees based on the difference between customer's billing_area (customized dropdown) on checkout and the value of seller's billing_city (set in user profile). The code I was using as below -
This is the jQuery Script that runs to get the value of billing_area as changed:
add_action( 'woocommerce_after_checkout_form', 'custom_checkout_jquery_script', 10 );
function custom_checkout_jquery_script() {
?>
<script type="text/javascript">
(function($){
$( 'form.checkout' ).on( 'change', '#billing_area', function(){
var location = $('#billing_area option:selected').val();
document.cookie="cusarea="+location;
// Browser console output (Just for testing)
function readCookie(n){ for(var r=n+"=",t=document.cookie.split(";"),e=0;e<t.length;e++){
for(var i=t[e];" "==i.charAt(0);)i=i.substring(1,i.length);
if(0==i.indexOf(r))return i.substring(r.length,i.length)}return null}
$('body').trigger('update_checkout');
console.log('Selected Area: '+location+' | Cookie: '+readCookie("cusarea"));
//$('#order_review').load(document.URL + ' #order_review');
//window.location.reload(true)
});
})(jQuery);
</script>
<?php
}
This is the code for calculating fees:
add_action( 'woocommerce_cart_calculate_fees', 'distance_shipping_fee', 30, 1 );
function distance_shipping_fee( $wc_cart ){
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) return;
if( empty($_COOKIE ['cusarea']) ) return;
else $cookie = $_COOKIE ['cusarea'];
// Encoding the customer's location for Google API
$customer_area = rawurlencode( $cookie );
// Getting billing city of vendors
foreach( $wc_cart->get_cart() as $cart_item ){
$vendor_id = get_post_field( 'post_author', $cart_item['product_id'] );
$vendors[$vendor_id] = get_user_meta($vendor_id, 'billing_city', true);
}
foreach( $vendors as $vend_loc){
// Setting Google API URL ##
$gapi_key = MY_APY; // Set HERE your google api key
$shippingurl = "https://maps.googleapis.com/maps/api/distancematrix/json?origins=$vend_loc";
$shippingurl .= "+dhaka+bangladesh&destinations=$customer_area+dhaka+bangladesh&key=$gapi_key";
// Now fetching json response from googleapis:
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $shippingurl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$response = json_decode(curl_exec($ch), true);
// If google responds with a status of OK: Extract the distance text:
if($response['status'] == "OK")
$dist = $response['rows'][0]['elements'][0]['distance']['text'];
$dist_array[] = preg_replace("/[^0-9\.]/", '', $dist);
}
// Get the bigger distance
$dist_ance = max($dist_array);
$dist_abs = abs ($dist_ance);
if ( $dist_abs < 5) {
$wc_cart->add_fee( "Delivery - Distance Rate", 10 , true);
} else {
$wc_cart->add_fee( "Delivery - Distance Rate", 20 , true);
}
}
Problem is - It's not working anymore.
If I dump the value, it returns me the value of $dist_abs (something like 5.4 which is the distance between seller's location and customer's area). But, no fee is added to the cart.
Sometimes, when server or site is slow, I see the label (Delivery - Distance Rate) appears in order review, but it disappears once page loads properly.
Can anyone tell me why it's not working anymore? Is there something I'm missing?
I have found the way to make it work using ajax instead of cookies… So try the following:
add_action( 'woocommerce_after_checkout_form', 'custom_checkout_jquery_script', 30 );
function custom_checkout_jquery_script() {
if( ! is_checkout() ) return;
?>
<script type="text/javascript">
jQuery(function($){
// wc_checkout_params is required to continue
if ( typeof wc_checkout_params === 'undefined' )
return false;
var a = '#billing_myfield5', b = a+' option:selected';
$( 'form.checkout' ).on( 'change', a, function(){
console.log('Chosen area: '+$(b).html()); // To be removed (testing)
// Ajax: send the chosen customer location to php
$.ajax({
type: 'POST',
url:.ajax_url,
data: {
'action': 'set_customer_area',
'customer_area': $(b).html(),
},
success: function (response) {
$(document.body).trigger('update_checkout');
console.log('Response: '+response); // To be removed (testing)
}
});
});
});
</script>
<?php
}
// Wordpress Ajax: Saved the selected customer location to WC_Session
add_action( 'wp_ajax_nopriv_set_customer_area', 'set_customer_area_in_wc_sessions' );
add_action( 'wp_ajax_set_customer_area', 'set_customer_area_in_wc_sessions' );
function set_customer_area_in_wc_sessions() {
if( ! isset($_POST['customer_area']) ) return;
// Encoding the customer's location for Google API
$customer_area_enc = rawurlencode( $_POST['customer_area'] );
// Set the chosen customer location in WC_Sessions
WC()->session->set('customer_area', rawurlencode($_POST['customer_area']) );
// To be removed (testing: Send back the data to jQuery)
echo json_encode( WC()->session->get('customer_area' ) );
die(); // To avoid server error 500
}
// Add a fee based on the highest distance between customer and vendors
add_action( 'woocommerce_cart_calculate_fees', 'distance_shipping_fee', 30, 1 );
function distance_shipping_fee( $cart ){
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
// Get Url encoded customer area that is saved in WC_Session by ajax
$customer_area = WC()->session->get('customer_area' );
// Only when customer area has been selected
if( empty($customer_area) )
return;
// Getting billing city of vendors
foreach( $cart->get_cart() as $cart_item ){
$vendor_id = get_post_field( 'post_author', $cart_item['product_id'] );
$vendors[$vendor_id] = get_user_meta($vendor_id, 'billing_city', true);
}
$dist_array = array();
// Loop through vendors locations
foreach( $vendors as $vend_loc){
// Setting Google API URL ##
$gapi_key = MY_APY; // Set HERE your google api key
$shippingurl = "https://maps.googleapis.com/maps/api/distancematrix/json?origins=$vend_loc";
$shippingurl .= "+dhaka+bangladesh&destinations=$customer_area+dhaka+bangladesh&key=$gapi_key";
// Now fetching json response from googleapis:
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $shippingurl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$response = json_decode(curl_exec($ch), true);
// If google responds with a status of OK: Extract the distance text:
if($response['status'] == "OK")
$dist = $response['rows'][0]['elements'][0]['distance']['text'];
$dist_array[] = preg_replace("/[^0-9\.]/", '', $dist);
}
// Get the bigger distance
$distance = max ($dist_array);
$distance = abs ($distance);
$fee = $distance < 5 ? 10 : 20;
if ( $distance && $fee > 0 )
$cart->add_fee( "Delivery - Distance Rate", $fee , true);
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
Once working, you will have to remove all related lines with // Testing ==> To be removed
Related
In WooCommerce Dokan multivendor shop I have Cash On Delivery (COD) payment for customers.
I created a code that counts the vendors in the cart and multiply them with the fee that i want per vendor. on my example is 2 € per vendor. so lets say that we have 1 product of each vendors(for now we have 2 vendors) on the cart. That should be 2 * 2 = 4€ total cost of COD.
That is working perfectly but when I received the order I see the fee only in main order and not in the suborders. it should be 2€ in one suborder and the other 2€ in the other suborder.
That has been working the whole time but since 11.02.2021 it suddenly stopped. Any ideas that could help me ?
Here is the code that I am using:
// 2 € Fee COD - Add a custom fee based on cart subtotal:
add_action( 'woocommerce_cart_calculate_fees', 'custom_fee_for_dokan', 999, 1 );
function custom_fee_for_dokan ( $cart ) {
$car_items = WC()->cart->get_cart(); // Cart items
$items_sort = array(); // Initializing
// Loop through cart items
foreach ( $car_items as $cart_item_key => $cart_item ) {
// Get the vendor_id
$vendor_id = get_post_field( 'post_author', $cart_item['product_id'] );
$store_info = dokan_get_store_info( $vendor_id ); // Get the store data
$store_name = $store_info['store_name']; // Get the store name
// Set in multidimentional array the vendor and then the cart item key
$items_sort[$store_name][$cart_item_key] = $vendor_id;
}
if ( count($car_items) > 1 ) {
ksort( $items_sort ); // Sorting by vendor name
}
$vendors = 0;
// Loop by vendor name
foreach ( $items_sort as $store_name => $values ) {
$vendor_id = reset($values); // The vendor id
$store_url = dokan_get_store_url( $vendor_id ); // Get the store URL (if needed)
$vendors++;
}
// End of Loop
$flatrate = $vendors * 2;
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( ! ( is_checkout() && ! is_wc_endpoint_url() ) )
return; // Only checkout page
$payment_method = WC()->session->get( 'chosen_payment_method' );
if ( 'cod' == $payment_method ) {
// $surcharge == $vendors;
$cart->add_fee( 'Pay on delivery', $flatrate , true );
}
}
// jQuery - Update checkout on methode payment change
add_action( 'wp_footer', 'nik_checkout' );
function nik_checkout() {
if ( ! ( is_checkout() && ! is_wc_endpoint_url() ) )
return; // Only checkout page
?>
<script type="text/javascript">
jQuery( function($){
$('form.checkout').on('change', 'input[name="payment_method"]', function(){
$(document.body).trigger('update_checkout');
});
});
</script>
<?php
}
The issue is related to Dokan plugin that does not split the fee by suborders anymore in the plugin recent versions. So you should ask Dokan support, to check if it's not a bug introduced on last updates or if it's not a new available setting.
Now your code is outdated and complicated for nothing. It can really be simplified and optimized.
The following code will set a COD fee based on dokan vendors count:
// COD Fee based on vendors count
add_action( 'woocommerce_cart_calculate_fees', 'dokan_cod_fee_vendors_based' );
function dokan_cod_fee_vendors_based ( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
// Only checkout page
if ( ! ( is_checkout() && ! is_wc_endpoint_url() ) )
return;
$fee_by_vendor = 2; // HERE set the fee by vendor
$vendors_array = array(); // Initializing
// Loop through cart items
foreach ( $cart->get_cart() as $item ) {
$vendor_id = get_post_field('post_author', $item['product_id']); // Get the vendor_id
// Set in an indexed array to get how many vendors
$vendors_array[$vendor_id] = $vendor_id;
}
$fee_total = count($vendors_array) * $fee_by_vendor; // Get fee total by vendor
if ( 'cod' === WC()->session->get( 'chosen_payment_method' ) ) {
$cart->add_fee( 'Cash On Delivery Fee', $fee_total, true ); // Apply the fee
}
}
// jQuery - Update checkout on methode payment change
add_action( 'wp_footer', 'payment_refresh_checkout_js' );
function payment_refresh_checkout_js() {
// Only checkout page
if ( ! ( is_checkout() && ! is_wc_endpoint_url() ) )
return; // Exit
?>
<script type="text/javascript">
jQuery( function($){
$('form.checkout').on('change', 'input[name="payment_method"]', function(){
$(document.body).trigger('update_checkout');
});
});
</script>
<?php
}
Code goes in functions.php file of the active child theme (or active theme). It should works.
Image Issue Example
At the WooCommerce "checkout" page:
I would like to input any number in the "Custom Fee Input"
- then it will be calculated in the "Total price table".
- After placing order the "Custom Fee" will be auto synchronized into the database.
Recommendation: using Plain JavaScript instead of jQuery is highly appreciated.
This is my codes:
html
<label>
Custom Fee
<input id="custom_fee" name="custom_fee" type="number" required>
</label>
Plain JavaScript
var $custom_fee = document.getElementById("custom_fee");
$custom_fee.addEventListener("change", function(){
var data = {
action: 'custom_fee',
security: wc_checkout_params.apply_state_nonce,
custom_fee_cost: $custom_fee.value,
};
var request = new XMLHttpRequest();
request.open('POST', wc_checkout_params.ajax_url, true);
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
request.onload = function() {
if (this.status >= 200 && this.status < 400) {
var resp = this.response;
console.log(resp);
} else {
var resp = this.response;
console.log(resp);
}
};
request.send(data);
});
function.php
add_action('wp_ajax_custom_fee', 'custom_fee_ajax', 10);
add_action('wp_ajax_nopriv_custom_fee', 'custom_fee_ajax', 10);
function custom_fee_ajax() {
if( ! is_checkout() ) return;
global $wpdb;
if( isset($_POST['custom_fee']) ){
$custom_fee = $_POST['custom_fee'];
WC()->session->set( 'custom_fee', $custom_fee );
echo json_encode( WC()->session->get('custom_fee' ) );
}
die();
}
//________________________________________________________
add_action('woocommerce_cart_calculate_fees', 'custom_fee');
function custom_fee() {
if( ! is_checkout() ) return;
session_start();
global $woocommerce;
if ( !defined( 'DOING_AJAX' ) ) return;
$fee = $_SESSION['custom_fee'];
$woocommerce->cart->add_fee( 'Custom Fee' , $fee, true, '' );
}
NOTE: The above code does not work correctly (bad request 400; php functions); so I would like to have someone help I to solve this issue.
I trying to pass a variable in woocommerce_cart_calculate_fees hook, but this code doesn't work. When I try to pass static value within a variable it works.
Here is my code:
add_action('woocommerce_cart_calculate_fees' , 'add_custom_fees', 20, 1);
function add_custom_fees( WC_Cart $cart){
$final_discount; // storing ajax value
$static_value = 5; // static value
if ($final_discount) {
/**
* custom discount for the cart in woocommerce
*/
// Hook before calculate fees
add_action('woocommerce_cart_calculate_fees', 'add_custom_fees');
/**
* Add custom fee if more than three article
* #param WC_Cart $cart
*/
function add_custom_fees(WC_Cart $cart) {
// Calculate the amount to reduce
global $final_discount;
$discount = $cart->subtotal * $final_discount / 100;
$cart->add_fee('10% discount has been added.', -$discount);
}
}
}
EDIT:
Now I am to pass a WC_Session value in woocommerce_cart_calculate_fees hook, the code works but session variable does not update before page refreshing.
The session variable is storing from onchange ajax value of billing_postcode field of woocommerce checkout page.
My jQuery code (Ajax):
jQuery(document).ready(function () {
jQuery('#billing_postcode').on('change', function (event) {
//alert( this.value );
event.preventDefault();
var billing_postcode = jQuery('#billing_postcode').val();
console.log(billing_postcode);
var data = {
action: 'woocommerce_apply_state',
security: wc_checkout_params.apply_state_nonce,
billing_postcode: billing_postcode
};
jQuery.ajax({
type: "POST",
data: data,
url: wc_checkout_params.ajax_url,
success: function (code) {
console.log(code);
if (code === '0') {
jQuery('body').trigger('update_checkout');
}
},
dataType: 'html'
});
});
});
My PHP code in functions.php file of my theme:
wp_enqueue_script('zip_code', get_template_directory_uri() . '/assets/js/zipcode.js', array('jquery'));
wp_localize_script('zip_code', 'wc_checkout_params', array('ajaxurl' => admin_url('admin-ajax.php')));
add_action('wp_ajax_woocommerce_apply_state', 'discount', 10);
add_action('wp_ajax_nopriv_woocommerce_apply_state', 'discount', 10);
function discount() {
if(isset($_POST['billing_postcode'])){
$billing_postcode = isset($_POST['billing_postcode'])?$_POST['billing_postcode']:'not yet';
global $wpdb;
$zipcodes = $wpdb->get_results(
$wpdb->prepare( "
SELECT * FROM wp_zip_codes_value
WHERE zip_code_value = %d",
$billing_postcode
)
);
$zip_for_discount = array();
foreach ( $zipcodes as $zipcode ){
$zip_for_discount = $zipcode->zip_code_id;
}
$find_discount = $wpdb->get_results(
$wpdb->prepare( "
SELECT * FROM wp_zip_codes
WHERE zip_code = %d",
$zip_for_discount
)
);
$final_discount = array();
if($find_discount){
foreach ( $find_discount as $discount ){
$final_discount[] = $discount->discount;
}
}
$final_discount[0];
WC()->session->set( 'final_discount', $final_discount[0] );
}
}
add_action('woocommerce_cart_calculate_fees' , 'add_custom_fees', 20, 1);
function add_custom_fees( WC_Cart $cart){
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) return;
$percent = WC()->session->get( 'final_discount' );
if( $percent > 0 ){
$discount = $cart->subtotal * $percent / 100;
$cart->add_fee( __('Zip Code Discount', 'woocommerce' ) . " ($percent%)", -$discount);
}
}
The best way to get that working is to set your $final_discount variable using WC_Sessions in another function before, this way:
WC()->session->set( 'final_discount', $final_discount );
Now you can get this value anywhere on front end hooks or code using:
$final_discount = WC()->session->get( 'final_discount' );
So your final discount (negative fee) code will be something like:
add_action('woocommerce_cart_calculate_fees', 'add_custom_fee', 20, 1 );
function add_custom_fee( $cart ){
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) return;
// Get the data from WC_Sessions
$percent = WC()->session->get( 'final_discount' );
if( $percent > 0 ){
$discount = $cart->subtotal * $percent / 100;
$cart->add_fee( __('Discount', 'woocommerce' ) . " ($percent%)" . $percent, -$discount);
}
}
Code goes in function.php file of your active child theme (active theme).
Tested and works.
Update regarding your updated question:
Here is a working Ajax example code similar to yours but simplified (as I don't have similar database custom tables to get the percentage for the discount).
The variable discount percentage is based on the imputed zip-code value…
The PHP code:
add_action( 'wp_enqueue_scripts', 'wc_zipcode_enqueue_scripts' );
function wc_zipcode_enqueue_scripts() {
// Only on front-end and checkout page
if( is_admin() || ! is_checkout() ) return;
// (For child themes use get_stylesheet_directory_uri() instead)
wp_enqueue_script('zip_code', get_template_directory_uri() . '/assets/js/zipcode.js', array('jquery'));
wp_localize_script('zip_code', 'wc_checkout_params', array('ajaxurl' => admin_url('admin-ajax.php')));
}
add_action('wp_ajax_woocommerce_apply_state', 'woocommerce_apply_state', 10 );
add_action('wp_ajax_nopriv_woocommerce_apply_state', 'woocommerce_apply_state', 10 );
function woocommerce_apply_state() {
global $wpdb;
if( isset($_POST['billing_postcode']) ){
$billing_postcode = $_POST['billing_postcode'];
if( empty($billing_postcode) || $billing_postcode == 0 ) die();
if( $billing_postcode < 30000 )
$final_discount = 10;
elseif( $billing_postcode >= 30000 && $billing_postcode < 60000 )
$final_discount = 15;
else
$final_discount = 20;
WC()->session->set( 'final_discount', $final_discount );
echo json_encode( WC()->session->get('final_discount' ) );
}
die(); // Alway at the end (to avoid server error 500)
}
add_action('woocommerce_cart_calculate_fees' , 'add_custom_discount', 20, 1);
function add_custom_discount( WC_Cart $cart){
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) return;
// Get the data from WC_Sessions
$percent = WC()->session->get( 'final_discount' );
// If the billing postcode is not set we exit
$billing_postcode = WC()->session->get('customer')['postcode'];
if( empty($billing_postcode) ) return;
if( $percent > 0 ){
$discount = $cart->subtotal * $percent / 100;
$cart->add_fee( __('Zip Code Discount', 'woocommerce' ) . " ($percent%)", -$discount);
}
}
Code goes in function.php file of your active child theme (active theme).
The javascript code:
jQuery(document).ready(function($) {
function postcodeAjax(){
$.ajax({
type: 'POST',
data: {
action: 'woocommerce_apply_state',
billing_postcode: $('#billing_postcode').val(),
security: wc_checkout_params.apply_state_nonce
},
url: wc_checkout_params.ajax_url,
success: function (response) {
$('body').trigger('update_checkout');
console.log('updating checkout…');
}
});
}
if( $('#billing_postcode').val() > 0 )
postcodeAjax();
$('#billing_postcode').on('change blur', function(e){
e.preventDefault();
postcodeAjax();
$('body').trigger('update_checkout');
});
});
To be saved in a file named zipcode.js located in your theme folder /assets/js/zipcode.js
Tested and perfectly works
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.
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.
I want to create custom add to cart button to add my 3 product into cart with 2 quantity for each..
For add three product into cart I have done using like this:
<a id="buy" class="single_add_to_cart_button shop-skin-btn shop-flat-btn alt" href="#">ADD MY PRODUCT</a>
$p_id = my product id eg: 45,99,152
<script>
jQuery('#buy').click(function(e) {
e.preventDefault();
var myStringArray = [<?php echo $p_id; ?>];
var arrayLength = myStringArray.length;
for (var i = 0; i < arrayLength; i++) {
addToCart(myStringArray[i]);
}
return true;
});
function addToCart(p_id) {
$.get('/glassful/?post_type=product&add-to-cart=' + p_id, function() {
$(".show_success").show();
});
}
</script>
It will add my resulted product into cart but with only 1 quantity
Please let me know how I can add quantity? I want to add 2 quantity of each product.
Mean when click on add to cart three product will added to cart with 2 quantity each.
Thanks for help in advance..
You need to pass quantity in query string like:
?post_type=product&add-to-cart=100&quantity=2
I have modify you code
<script>
jQuery('#buy').click(function(e) {
e.preventDefault();
var myStringArray = [<?php echo $p_id; ?>];
var arrayLength = myStringArray.length;
for (var i = 0; i < arrayLength; i++) {
addToCart(myStringArray[i],2);
}
return true;
//window.location.href = "http://seoexpertiser.ca/glassful/cart/";
});
function addToCart(p_id,qu) {
$.get('/glassful/?post_type=product&add-to-cart=' + p_id +'&quantity='+qu, function() {
// success
$(".show_success").show();
});
}
</script>
I think this will solve your problem.
Please check this link , This is works for me :)
WooCommerce: Allow adding multiple products to the cart via the add-to-cart query string
functions.php
function woocommerce_maybe_add_multiple_products_to_cart() {
// Make sure WC is installed, and add-to-cart qauery arg exists, and contains at least one comma.
if ( ! class_exists( 'WC_Form_Handler' ) || empty( $_REQUEST['add-to-cart'] ) || false === strpos( $_REQUEST['add-to-cart'], ',' ) ) {
return;
}
// Remove WooCommerce's hook, as it's useless (doesn't handle multiple products).
remove_action( 'wp_loaded', array( 'WC_Form_Handler', 'add_to_cart_action' ), 20 );
$product_ids = explode( ',', $_REQUEST['add-to-cart'] );
$count = count( $product_ids );
$number = 0;
foreach ( $product_ids as $product_id ) {
if ( ++$number === $count ) {
// Ok, final item, let's send it back to woocommerce's add_to_cart_action method for handling.
$_REQUEST['add-to-cart'] = $product_id;
return WC_Form_Handler::add_to_cart_action();
}
$product_id = apply_filters( 'woocommerce_add_to_cart_product_id', absint( $product_id ) );
$was_added_to_cart = false;
$adding_to_cart = wc_get_product( $product_id );
if ( ! $adding_to_cart ) {
continue;
}
$add_to_cart_handler = apply_filters( 'woocommerce_add_to_cart_handler', $adding_to_cart->product_type, $adding_to_cart );
/*
* Sorry.. if you want non-simple products, you're on your own.
*
* Related: WooCommerce has set the following methods as private:
* WC_Form_Handler::add_to_cart_handler_variable(),
* WC_Form_Handler::add_to_cart_handler_grouped(),
* WC_Form_Handler::add_to_cart_handler_simple()
*
* Why you gotta be like that WooCommerce?
*/
if ( 'simple' !== $add_to_cart_handler ) {
continue;
}
// For now, quantity applies to all products.. This could be changed easily enough, but I didn't need this feature.
$quantity = empty( $_REQUEST['quantity'] ) ? 1 : wc_stock_amount( $_REQUEST['quantity'] );
$passed_validation = apply_filters( 'woocommerce_add_to_cart_validation', true, $product_id, $quantity );
if ( $passed_validation && false !== WC()->cart->add_to_cart( $product_id, $quantity ) ) {
wc_add_to_cart_message( array( $product_id => $quantity ), true );
}
}
}
// Fire before the WC_Form_Handler::add_to_cart_action callback.
add_action( 'wp_loaded', 'woocommerce_maybe_add_multiple_products_to_cart', 15 );
and you can use for your link.
$product_ids = implode( ',', array( 1, 2, 55 ) );
$url = esc_url_raw( add_query_arg( 'add-to-cart', $product_ids, wc_get_checkout_url() ) );
Thanks !
Save products id in array and make ajax request
jQuery("#addSelected").click(function() { var arrayLength =
arrayOfAddedProducts.length; jQuery.ajax({
type: "POST",
url: ajaxurl,
data: {action : 'add_item_from_cart','getIDtoAdd' : arrayOfAddedProducts},
success: function (res) {
wc_load_all_products();
}
}); });
function.php
function add_item_from_cart() {
foreach ($_POST['getIDtoAdd'] as $productId) {
WC()->cart->add_to_cart( intval($productId), 1, 0, array(), array() );
// WC()->cart->add_to_cart( $product_id = 0, $quantity = 1, $variation_id = 0, $variation = array(), $cart_item_data = array() );
}
}
add_action('wp_ajax_add_item_from_cart', 'add_item_from_cart');
add_action('wp_ajax_nopriv_add_item_from_cart', 'add_item_from_cart');