Add fee to WooCommerce cart using a cookie - php

I have a scenario in which there's a number determined early in the user flow. I'm saving that as a cookie using setcookie( "mileage", $distance_surcharge, time() + 36000 );.
I'm then trying to use the value from that cookie to add a surcharge to my cart, but it seems as though the value from the cookie is not being passed through.
Here's the snippet from my functions.php file as instructed by Woocommerce docs: https://docs.woocommerce.com/document/add-a-surcharge-to-cart-and-checkout-uses-fees-api/
add_action( 'woocommerce_cart_calculate_fees','gt21_add_mileage' );
function gt21_add_mileage() {
global $woocommerce;
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if(isset($_COOKIE['mileage'])) {
$fee = $_COOKIE['mileage'];
$woocommerce->cart->add_fee( 'Extra Mileage Fee', $fee, true, 'standard' );
}
}
I can see the extra line item in the cart, but the value only gets passed through if I set $fee to an actual integer.

The issue seems to be more with the creation of the cookie
This will display on the cart page the output of the cookie (for debugging purposes), as well as the addition of the fee
// Set cookie
function action_init() {
// Settings
$path = parse_url( get_option('siteurl'), PHP_URL_PATH );
$host = parse_url( get_option('siteurl'), PHP_URL_HOST );
$expiry = time() + 36000;
// Value
$distance_surcharge = 10;
// Set cookie
setcookie( 'mileage', $distance_surcharge, $expiry, $path, $host );
}
add_action( 'init', 'action_init' );
// Cart page (before cart - DEBUG)
function action_woocommerce_before_cart() {
// Isset
if ( isset( $_COOKIE['mileage'] ) ) {
$cookie = $_COOKIE['mileage'];
// Output
echo 'OK = ' . $cookie;
} else {
// Output
echo 'NOT OK';
}
}
add_action( 'woocommerce_before_cart', 'action_woocommerce_before_cart', 15 );
// Add fee
function action_woocommerce_cart_calculate_fees( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
// Isset
if ( isset( $_COOKIE['mileage'] ) ) {
// Fee
$fee = $_COOKIE['mileage'];
// Applying fee
$cart->add_fee( __( 'Extra Mileage Fee', 'woocommerce' ), $fee, true );
}
}
add_action( 'woocommerce_cart_calculate_fees', 'action_woocommerce_cart_calculate_fees', 10, 1 );
Code goes in functions.php file of the active child theme (or active theme). Tested and works in Wordpress 5.8.1 & WooCommerce 5.8.0

Related

Apply a fee via Url variable (GET) in WooCommerce

I'm trying to carry an "add_fee" value over to review order page but it's not working.
I need to enable my check out page to wait for the url parameter "getfee".
If the getfee equals 2, then add the fee.
If it doesn't, then don't add anything.
Here's my code:
add_action( 'woocommerce_cart_calculate_fees','woocommerce_custom_surcharge' );
function woocommerce_custom_surcharge() {
global $woocommerce;
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
$fee = $_GET["getfee"];
if( $fee == "2") {
$percentage = 0.01;
$surcharge = ( $woocommerce->cart->cart_contents_total + $woocommerce->cart->shipping_total ) * $percentage;
$woocommerce->cart->add_fee( 'Surcharge', $surcharge, true, '' );
}
}
So far, it adds the fee in the checkout page but when it comes to reviewing the order, it doesn't appear.
I think it could be because the checkout page doesn't have that parameter but not sure.
Any help would be greatly appreciated.
You need to set your Url fee in a WC Session variable to avoid this problem:
// Get URL variable and set it to a WC Session variable
add_action( 'template_redirect', 'getfee_to_wc_session' );
function getfee_to_wc_session() {
if ( isset($_GET['getfee']) ) {
WC()->session->set('getfee', esc_attr($_GET['getfee']));
}
}
// Add a percentage fee
add_action( 'woocommerce_cart_calculate_fees','woocommerce_custom_surcharge' );
function woocommerce_custom_surcharge( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
$fee = WC()->session->get('getfee'); // Get WC session variable value
if( $fee == "2") {
$percentage = 0.01;
$surcharge = ( $cart->cart_contents_total + $cart->shipping_total ) * $percentage;
$cart->add_fee( 'Surcharge', $surcharge, true, '' );
}
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.

Global surcharge WooCommerce exclude VAT

I made a global surcharge in PHP for WooCommerce, because all of our customers have to pay a fee for special delivery.
Is there a way to make the fee ignore VAT?
When I write 15 it adds VAT to the number. I'd just like for the number I write to be the fee.
TL:DR - MAKE SURCHARGE INCLUDED VAT
// Packing fee
add_action( 'woocommerce_cart_calculate_fees','wc_add_surcharge' );
function wc_add_surcharge() {
global $woocommerce;
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
$county = array('DK');
// change the $fee to set the surcharge to a value to suit
$fee = 15;
if ( in_array( WC()->customer->get_shipping_country(), $county ) ) :
$woocommerce->cart->add_fee( 'Emballage pant', $fee, true, 'standard' );
endif;
}
To make the fee ignore Vat you need to remove the 2 last arguments as they are related to Taxes (the third argument is false (not taxable) by default ):
Also global $woocommerce; and $woocommerce->cart as there is a missing $cart argument in this hooked function. So try this instead:
// Packing fee
add_action( 'woocommerce_cart_calculate_fees','wc_add_surcharge' );
function wc_add_surcharge( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
$county = array('DK');
// change the $fee to set the surcharge to a value to suit
$fee = 15;
if ( in_array( WC()->customer->get_shipping_country(), $county ) ) :
$cart->add_fee( 'Emballage pant', $fee );
endif;
}
Code goes in function.php file of your active child theme (or active theme). Tested and work.

Add a calculated fee based on total after discounts in WooCommerce

I'm trying to add a fee to my woocommerce cart based on the subtotal after discounts have been applied:
add_action( 'woocommerce_cart_calculate_fees','woocommerce_custom_surcharge' );
function woocommerce_custom_surcharge() {
global $woocommerce;
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
$percentage = 0.01;
$surcharge = $woocommerce->cart->subtotal - $woocommerce->cart->get_cart_discount_total();
$woocommerce->cart->add_fee( 'Surcharge', $surcharge, true, '' );
}
I don't believe calls like $woocommerce->cart->get_cart_discount_total() can be used in an action hook, which is why I keep getting 0.00 for the fee.
I also read around some WC values are deprecated and will always show zero, but it doesn't explain why those amounts appear in filters and not actions.
What else can I use in an action to get the same number and add a percent fee to?
The WC_Cart object argument is included in woocommerce_cart_calculate_fees action hook. I also use the percentage amount calculation, as I suppose you just forgot it in your code.
So you should try this instead:
add_action( 'woocommerce_cart_calculate_fees','wc_custom_surcharge', 10, 1 );
function wc_custom_surcharge( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
// HERE set your percent rate
$percent = 1; // 1%
// Fee calculation
$fee = ( $cart->subtotal - $cart->get_cart_discount_total() ) * $percent / 100;
// Add the fee if it is bigger than O
if( $fee > 0 )
$cart->add_fee( __('Surcharge', 'woocommerce'), $fee, true );
}
Code goes in function.php file of your active child theme (or theme).
Tested and works perfectly.
Note: Also global $woocommerce; with $woocommerce->cart has been replaced by WC()->cart since a long time. The WC() woocommerce object already include itself global $woocommerce;…
Specific update:
add_action( 'woocommerce_cart_calculate_fees','wc_custom_surcharge', 10, 1 );
function wc_custom_surcharge( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
// HERE set your percent rate and your state
$percent = 6;
$state = array('MI');
$coupon_total = $cart->get_discount_total();
// FEE calculation
$fee = ( $cart->subtotal - $coupon_total ) * $percent / 100;
if ( $fee > 0 && WC()->customer->get_shipping_state() == $state )
$cart->add_fee( __('Tax', 'woocommerce'), $fee, false);
}
Code goes in function.php file of your active child theme (or theme).
Tested and works.
the plugin i was using to create coupons was hooking into after_calculate_totals and adjusting the amount afterwards. anything that fires before the plugin wasn't counted into that adjusted total. i was able to call specific amounts using variables in the plugin to create the fee amount i needed
for anyone else who is interested: i am using the ignitewoo gift certificates pro plugin and wanted to create a fee based on the remaining balance after coupons. this is Loic's code with some modifications:
add_action( 'woocommerce_cart_calculate_fees','wc_custom_surcharge', 10, 1 );
function wc_custom_surcharge( $cart) {
global $woocommerce;
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
$state = array('MI');
// HERE set your percent rate
$percent = 6;
$coupTotal = 0;
foreach ( $woocommerce->cart->applied_coupons as $cc ) {
$coupon = new WC_Coupon( $cc );
$amount = ign_get_coupon_amount( $coupon );
$coupTotal += $amount;
}
// Fee calculation
$fee = ($cart->subtotal - $coupTotal) * $percent/100;
if (( $fee > 0 ) AND (in_array( WC()->customer->shipping_state, $state ) ))
$cart->add_fee( __('Tax', 'woocommerce'), $fee, false);
}

WooCommerce Price overriding not working

I have set up a hidden input item using woocommerce_before_add_to_cart_button hook
function add_gift_wrap_field() {
?>`<input type="hidden" id="price_val" name="added_price" value="100.34">`<?php
}
add_action( 'woocommerce_before_add_to_cart_button', 'add_gift_wrap_field' );
Saving fields against the product:
function save_gift_wrap_fee( $cart_item_data, $product_id ) {
if( isset( $_POST['added_price'] ) ) {
$cart_item_data = array();
$cart_item_data[ "gift_wrap_fee" ] = "YES";
$cart_item_data[ "gift_wrap_price" ] = 100;
}
return $cart_item_data;
}
add_filter( 'woocommerce_add_cart_item_data', 'save_gift_wrap_fee', 99, 2 );
Now I can echo out the $_POST['added_price'] inside this woocommerce_before_calculate_totals hook.
The code I written is shown below:
function calculate_gift_wrap_fee( $cart_object ) {
if( !WC()->session->__isset( "reload_checkout" )) {
/* Gift wrap price */
$additionalPrice = number_format($_POST['added_price'], 2);
$additionalPrice = floatval($additionalPrice);
//echo $additionalPrice; exit(); shows the value
foreach ( $cart_object->cart_contents as $key => $value ) {
if( isset( $value["gift_wrap_fee"] ) ) {
/* Woocommerce 3.0 + */
$orgPrice = floatval( $value['data']->get_price() );
$value['data']->set_price( $orgPrice + $additionalPrice ); //not adding the $additionalPrice here.
}
}
}
}
add_action( 'woocommerce_before_calculate_totals', 'calculate_gift_wrap_fee', 99 );
What am I doing wrong here?
Here is the complete clean, tested and working solution based on your code question.
Here I have included your custom field key/value in the cart object for the related cart item, instead of getting the Post value in your calculate_gift_wrap_fee() function.
This way it's not possible to loose the custom field value and the new price calculation is reliable.
I have commented 'gift_wrap_fee' and 'gift_wrap_price' as they are not really needed now (but you can uncomment them if you like).
Here is this code:
// The hidden product custom field
add_action( 'woocommerce_before_add_to_cart_button', 'add_gift_wrap_field' );
function add_gift_wrap_field() {
?>
<input type="hidden" id="price_val" name="added_price" value="100.34">
<?php
}
// Adding the custom field to as custom data for this cart item in the cart object
add_filter( 'woocommerce_add_cart_item_data', 'save_custom_fields_data_to_cart', 10, 2 );
function save_custom_fields_data_to_cart( $cart_item_data, $product_id ) {
$bool = false;
$data = array();
if( ! empty( $_REQUEST['added_price'] ) ) {
$cart_item_data['custom_data']['added_price'] = $_REQUEST['added_price'];
// Below this 2 values are not really needed (I think)
// (You can uncomment them if needed)
## $cart_item_data['custom_data']['gift_wrap_fee'] = 'YES';
## $cart_item_data['custom_data']['gift_wrap_price'] = 100;
// below statement make sure every add to cart action as unique line item
$cart_item_data['custom_data']['unique_key'] = md5( microtime().rand() );
WC()->session->set( 'custom_data', $data );
}
return $cart_item_data;
}
// Changing the cart item price based on custom field calculation
add_action( 'woocommerce_before_calculate_totals', 'calculate_gift_wrap_fee', 10, 1 );
function calculate_gift_wrap_fee( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// Iterating though cart items
foreach ( $cart->get_cart() as $cart_item ) {
// Continue if we get the custom data for the current cart item
if( ! empty( $cart_item['custom_data'] ) ){
// Get the custom field "added price" value
$added_price = number_format( $cart_item['custom_data']['added_price'], 2 );
// The WC_Product object
$product = $cart_item['data'];
// Get the price (WooCommerce versions 2.5.x to 3+)
$product_price = method_exists( $product, 'get_price' ) ? floatval(product->get_price()) : floatval($product->price);
// New price calculation
$new_price = $product_price + $added_price;
// Set the calculeted price (WooCommerce versions 2.5.x to 3+)
method_exists( $product, 'set_price' ) ? $product->set_price( $new_price ) : $product->price = $new_price;
}
}
}
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 on WooCommerce versions from 2.5.x to 3+.

Add multiple products to cart if they are not already in cart

In WooCommerce I've implemented #jtsternberg's WooCommerce: Allow adding multiple products to the cart via the add-to-cart query string to allow adding multiple products at once, but I've received many complaints from customers who actually try to use one of the links containing multiple products.
For starters, if the customer clicks checkout and then clicks the browser "back" button, all the item quantities increment. I solved this by redirecting the user to the cart URL stripped of any additional parameters after the add-to-cart behavior completes, but it's not ideal.
What I really want is to check if the item is in the cart first and only add to cart if it isn't there already. Has anyone done something similar?
Working Update:
I ended up modifying the code from #jtsternberg to use a completely separate param name in order to avoid conflict with the default add-to-cart behavior. Then I was able to use #LoicTheAztec's suggested code below by wrapping the behavior in a check to see if that new param exists. Here's the full section:
function custom_product_link() {
if (empty( $_REQUEST['multi-product-add'])) {
return;
}
$product_ids = explode( ',', $_REQUEST['multi-product-add'] );
foreach ( $product_ids as $product_id ) {
$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 );
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 );
}
}
if ( wc_notice_count( 'error' ) === 0 ) {
// If has custom URL redirect there
if ( $url = apply_filters( 'woocommerce_add_to_cart_redirect', false ) ) {
wp_safe_redirect( $url );
exit;
} elseif ( get_option( 'woocommerce_cart_redirect_after_add' ) === 'yes' ) {
wp_safe_redirect( wc_get_cart_url() );
exit;
}
}
}
function check_product_added_to_cart( $passed, $product_id, $quantity) {
if (!empty( $_REQUEST['multi-product-add'])) {
foreach (WC()->cart->get_cart() as $cart_key => $cart_item ){
// if products are already in cart:
if( $cart_item['product_id'] == $product_id ) {
// Set the verification variable to "not passed" (false)
$passed = false;
// (Optionally) Displays a notice if product(s) are already in cart
// wc_add_notice( '<strong>' . $btn['label'] . '</strong> ' . __( 'This product is already in your cart.', 'woocommerce' ), 'error' );
// Stop the function returning "false", so the products will not be added again
return $passed;
}
}
}
return $passed;
}
add_action( 'wp_loaded', 'custom_product_link', 15 );
add_action( 'woocommerce_add_to_cart_validation', 'check_product_added_to_cart', 10, 3 );
As in the code you are using you have woocommerce_add_to_cart_validation filter hook inside it, you can use it in a custom hooked function to check if products rare already in cart with something like:
add_action( 'woocommerce_add_to_cart_validation', 'check_product_added_to_cart', 10, 3 );
function check_product_added_to_cart( $passed, $product_id, $quantity) {
foreach (WC()->cart->get_cart() as $cart_key => $cart_item ){
// if products are already in cart:
if( $cart_item['data']->get_id() == $product_id ) {
// Set the verification variable to "not passed" (false)
$passed = false;
// (Optionally) Displays a notice if product(s) are already in cart
wc_add_notice( '<strong>' . $btn['label'] . '</strong> ' . __( 'This product is already in your cart.', 'woocommerce' ), 'error' );
// Stop the function returning "false", so the products will not be added again
return $passed;
}
}
return $passed;
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
Code is tested and this normally should works with your customization…
For product quantities, you can use the $quantity argument with $cart_item['quantity'] in some conditions…

Categories