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.
Related
I want to add percentage value to cart item prices based on the selected payment gateway.
The problem I am facing is variation product price is not updating for the product price. Initially selected price is showing all the time.
How can I get the changed price accordingly?
My code so far:
// Set custom cart item price
function add_custom_price( $cart ) {
// This is necessary for WC 3.0+
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
// Avoiding hook repetition (when using price calculations for example | optional)
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// Loop through cart items
foreach ( $cart->get_cart() as $cart_item ) {
$chosen_payment_method = WC()->session->get('chosen_payment_method');
if($chosen_payment_method == 'cod') {
$increaseby = 3;
} elseif($chosen_payment_method == 'paypal') {
$increaseby = 8;
} else {
$increaseby = 10;
}
$price = get_post_meta($cart_item['product_id'] , '_price', true);
$price = $price + (($price * $increaseby)/100);
$cart_item['data']->set_price( $price );
}
}
add_action( 'woocommerce_before_calculate_totals', 'add_custom_price', 1000, 1);
Any help highly appreciated.
There are some mistakes in your code
Use WC()->session->get( 'chosen_payment_method' ); outside the foreach loop
get_post_meta() is not needed to get the price, you can use get_price()
You will also need jQuery that is triggered when changing the payment method.
So you get:
function action_woocommerce_before_calculate_totals( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// Get payment method
$chosen_payment_method = WC()->session->get( 'chosen_payment_method' );
// Compare
if ( $chosen_payment_method == 'cod' ) {
$increaseby = 3;
} elseif ( $chosen_payment_method == 'paypal' ) {
$increaseby = 8;
} else {
$increaseby = 10;
}
// Loop through cart items
foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) {
// Get price
$price = $cart_item['data']->get_price();
// Set price
$cart_item['data']->set_price( $price + ( $price * $increaseby ) / 100 );
}
}
add_action( 'woocommerce_before_calculate_totals', 'action_woocommerce_before_calculate_totals', 10, 1 );
function action_wp_footer() {
if ( is_checkout() && ! is_wc_endpoint_url() ) :
?>
<script type="text/javascript">
jQuery(function($){
$( 'form.checkout' ).on( 'change', 'input[name="payment_method"]', function() {
$(document.body).trigger( 'update_checkout' );
});
});
</script>
<?php
endif;
}
add_action( 'wp_footer', 'action_wp_footer' );
I need to apply an additional fee when a customer can place an order with free shipping, but wants to select COD payment.
So, Free Shipping + COD payment => fee.
I tried unsuccessfully the following piece of code. Where am I wrong?
add_action( 'woocommerce_cart_calculate_fees','cod_fee' );
function cod_fee() {
global $woocommerce;
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
$chosen_gateway = WC()->session->chosen_payment_method;
$chosen_methods = WC()->session->get( 'chosen_shipping_methods' );
$chosen_shipping = $chosen_methods[0];
$fee = 19;
if ( $chosen_shipping == 'free_shipping' && $chosen_gateway == 'cod' ) {
WC()->cart->add_fee( 'Spese per pagamento alla consegna', $fee, false, '' );
}
}
There is a mistake in your code and some additional code is needed. Try the following code that will add a specific fee when chosen payment method is Cash on delivery (cod) and when chosen shipping methods is "Free shipping":
// Add a conditional fee
add_action( 'woocommerce_cart_calculate_fees', 'add_cod_fee', 20, 1 );
function add_cod_fee( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
## ------ Your Settings (below) ------ ##
$your_payment_id = 'cod'; // The payment method
$your_shipping_method = 'free_shipping'; // The shipping method
$fee_amount = 19; // The fee amount
## ----------------------------------- ##
$chosen_payment_method_id = WC()->session->get( 'chosen_payment_method' );
$chosen_shipping_method_id = WC()->session->get( 'chosen_shipping_methods' )[0];
$chosen_shipping_method = explode( ':', $chosen_shipping_method_id )[0];
if ( $chosen_shipping_method == $your_shipping_method
&& $chosen_payment_method_id == $your_payment_id ) {
$fee_text = __( "Spese per pagamento alla consegna", "woocommerce" );
$cart->add_fee( $fee_text, $fee_amount, false );
}
}
// Refresh checkout on payment method change
add_action( 'wp_footer', 'refresh_checkout_script' );
function refresh_checkout_script() {
// Only on checkout page
if( is_checkout() && ! is_wc_endpoint_url('order-received') ) :
?>
<script type="text/javascript">
jQuery(function($){
// On payment method change
$('form.woocommerce-checkout').on( 'change', 'input[name="payment_method"]', function(){
// Refresh checkout
$('body').trigger('update_checkout');
});
})
</script>
<?php
endif;
}
Code goes in functions.php file of your active child theme (or active theme). tested and works.
Recently I tryied to use 2 sniped codes in one for this Woocommerce project where I need to set a fee for a specific product category (term ID: 19) and and a specific country state (Florida 'FL')
Additionally I should need to multiply this fee rate by the items from this product category (19).
This is my actual code:
add_action('woocommerce_cart_calculate_fees','woocommerce_custom_surcharge');
function woocommerce_custom_surcharge() {
$category_ID = '19';
global $woocommerce;
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
$state = array('FL');
foreach ($woocommerce->cart->cart_contents as $key => $values ) {
// Get the terms, i.e. category list using the ID of the product
$terms = get_the_terms( $values['product_id'], 'product_cat' );
// Because a product can have multiple categories, we need to iterate through the list of the products category for a match
foreach ($terms as $term)
{
// 19 is the ID of the category for which we want to remove the payment gateway
if($term->term_id == $category_ID){
$surcharge = 1;
if ( in_array( WC()->customer->shipping_state, $state && ) ) {
$woocommerce->cart->add_fee( 'State Tire Fee', $surcharge, true, '' );
}
}
How can I set a progressive fee based on items from a specific category and customers from a specific state?
Any help will be appreciated.
There are some mistakes and your code is a bit outdated...
The code below will set a progressive fee based on cart item quantity from a specific product category and for a specific state only.
add_action( 'woocommerce_cart_calculate_fees', 'wc_custom_surcharge', 20, 1 );
function wc_custom_surcharge( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return; // Exit
## Your Settings (below) ##
$categories = array(19);
$targeted_states = array('FL');
$base_rate = 1;
$user_state = WC()->customer->get_shipping_state();
$user_state = empty($user_state) ? WC()->customer->get_billing_state() : $user_state;
$surcharge = 0; // Initializing
// If user is not from florida we exit
if ( ! in_array( $user_state, $targeted_states ) )
return; // Exit
// Loop through cart items
foreach ( $cart->get_cart() as $cart_item ) {
if ( has_term( $categories, 'product_cat', $cart_item['product_id'] ) ){
// calculating fee based on the defined rate and on item quatinty
$surcharge += $cart_item['quantity'] * $base_rate;
}
}
// Applying the surcharge
if ( $surcharge > 0 ) {
$cart->add_fee( __("State Tire Fee", "woocommerce"), $surcharge, true );
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
In WooCommerce, I am using the code below to add a $12 surcharge to sales for Canadian Customers in the functions.php file of the child theme for one of my client.
But I need to remove the charge for all pdf downloads.
Is this possible altering the code I used?
Here is my code:
add_action( 'woocommerce_cart_calculate_fees','xa_add_surcharge' );
function xa_add_surcharge() {
global $woocommerce;
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
$county = array('CA');
$fee = 12.00;
if ( in_array( $woocommerce->customer->get_shipping_country(), $county ) ) :
$surcharge = + $fee;
$woocommerce->cart->add_fee( 'Surcharge for International Orders', $surcharge, true, '' );
endif;
}
It's possible checking in cart items for non downloadable product (or depending ion your PDF products settings for non virtual products).
Additionally I have revisited your code a bit:
add_action( 'woocommerce_cart_calculate_fees','add_custom_surcharge', 10, 1 );
function add_custom_surcharge( $wc_cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) return;
$countries = array('CA'); // Defined countries
// Continue only for defined countries
if( ! in_array( WC()->customer->get_shipping_country(), $countries ) ) return;
$fee_cost = 12; // The Defined fee cost
$downloadable_only = true;
// Checking cart items for NON downloadable products
foreach ( $wc_cart->get_cart() as $cart_item_key => $cart_item ) {
// Checks if a product is not downloadable.
if( ! $cart_item['data']->is_downloadable( ) ){ // or cart_item['data']->is_virtual()
$downloadable_only = false;
break;
}
}
// If one product is not downloadable and if customer shipping country is Canada we add the fee
if ( ! $downloadable_only )
$wc_cart->add_fee( "Surcharge for International Orders", number_format( $fee_cost, 2 ), true );
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
All code is tested on Woocommerce 3+ and works.
In WooCommerce I am using some code from this answer:
Set WooCommerce cart item price to zero if the product has already been bought
It works great with one product, but I would like to make it work with several products.
So the code below, for a specific product, change the cart item price to $0.00 if customer has already purchased and if not the price is set to $100 (for the first purchase):
add_action( 'woocommerce_before_calculate_totals', 'conditionally_change_cart_items_price', 10, 1 );
function conditionally_change_cart_items_price( $cart_object ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
$targeted_product_id = 1107;
// Set Here your custom price (1st purshase)
$custom_price = 100; // First purshase for product ID 1092
// Detecting if customer has already bought The targeted product (1092)
if( is_user_logged_in() ){
$customer = wp_get_current_user();
$customer_id = $customer->ID; // customer ID
$customer_email = $customer->email; // customer email
if( wc_customer_bought_product( $customer_email, $customer_id, $targeted_product_id) )
$custom_price = 0; // Set to 0 for other purchases (product ID 1092)
}
foreach ( $cart_object->get_cart() as $cart_item ) {
// When targeted product is in cart we change the price
if ( $cart_item['product_id'] == $targeted_product_id ) {
// Woocommerce 3+ compatibility
if ( version_compare( WC_VERSION, '3.0', '<' ) )
$cart_item['data']->price = $custom_price;
else
$cart_item['data']->set_price( $custom_price );
}
}
}
How could I make it work for multiple defined products (instead of one actually)?
You have to create an array with all target product ids and then you should check if a customer bought this product already in the foreach loop:
add_action( 'woocommerce_before_calculate_totals', 'conditionally_change_cart_items_price', 10, 1 );
function conditionally_change_cart_items_price( $cart_object ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
// add as many ids as you need
$targeted_product_ids = array( 1107, 1108, 1109 );
// Set Here your custom price (1st purshase)
$custom_price = 100; // First purshase for target product
if ( is_user_logged_in() ) {
$customer = wp_get_current_user();
$customer_id = $customer->ID; // customer ID
$customer_email = $customer->email; // customer email
foreach ( $cart_object->get_cart() as $cart_item ) {
// When targeted product is in cart we change the price
if ( in_array( $cart_item['product_id'], $targeted_product_ids ) ) {
// Detecting if customer has already bought The targeted product
if ( wc_customer_bought_product( $customer_email, $customer_id, $targeted_product_id ) )
// Set to 0 for other purchases
$custom_price = 0;
// Woocommerce 3+ compatibility
if ( version_compare( WC_VERSION, '3.0', '<' ) )
$cart_item['data']->price = $custom_price;
else
$cart_item['data']->set_price( $custom_price );
}
}
}
}
Based on: Set Woocommerce cart item price to zero if the product has already been bought
You will be able to make it work for multiple defined products IDs (in an array) this way:
// For WooCommerce version 3 and above (only)
add_action( 'woocommerce_before_calculate_totals', 'conditionally_change_cart_items_prices', 10, 1 );
function conditionally_change_cart_items_prices( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// Set HERE the Products to check in this array
$targeted_product_ids = array( 37, 50, 89, 124, 327 );
$products_bought = array();
// Set Here your custom price (1st purshase)
$custom_price1 = 100; // First purshase
// Set Here your custom price (more purshases than first)
$custom_price2 = 0; // Next purshases
// Detecting if customer has already bought our targeted products
if( is_user_logged_in() ){
$customer = wp_get_current_user();
$customer_id = $customer->ID; // customer ID
$customer_email = $customer->email; // customer email
// Checking each product in the array
foreach( $targeted_product_ids as $product_id ){
if( wc_customer_bought_product( $customer_email, $customer_id, $product_id) ){
// We set all (targeted) purchased products in an array
$products_purchased[] = $product_id;
}
}
}
// Checking cart items and changing item prices if needed
foreach ( $cart->get_cart() as $cart_item ) {
// When a targeted products already purchased is in cart we set price 2
if ( in_array( $cart_item['product_id'], $products_purchased ) ) {
$cart_item['data']->set_price( $custom_price2 );
}
// When a targeted products is in cart and has not been purchased we set price 1
elseif ( in_array( $cart_item['product_id'], $targeted_product_ids ) ) {
$cart_item['data']->set_price( $custom_price1 );
}
}
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
This code is tested and works in WooCommerce 3+