WooCommerce – update product price programmatically
There are many reasons you might want to update a WooCommerce product price programmatically. For instance, in Bookings for WooCommerce the booking (product) price is set dynamically according to certain parameters – e.g. length of stay, etc. In WooCommerce Product Add Ons Ultimate, you can specify extra fields for your products (like checkboxes and text fields) which will add extra cost to the product when selected. In this article, I’ll share some simple code to achieve this.
If you’re looking for a way to create complex cost calculations using a formula based on user inputs, you should take a look at this article on measurement price calculators and setting WooCommerce product prices by formula.
And for detailed guidance on how to add custom cart item data in WooCommerce, take a look at this article.
How to update WooCommerce product prices programmatically
Let’s say, as an example, that you are selling computers on your WooCommerce store and you want to add an option to each product that, when selected, will update the product price in the cart.
If we take a look at an example product from the WooCommerce Product Add Ons Ultimate demo (screenshot below), we can see that if the user chooses the ‘Add extended warranty’ checkbox, another £250 will be added to the product price.
The product price is updated programmatically in the cart by using two WooCommerce hooks:
woocommerce_add_cart_item_data
woocommerce_before_calculate_totals
WooCommerce Product Add-Ons Ultimate
Personalize products with extra fields and custom options
The woocommerce_add_cart_item_data
filter
The woocommerce_add_cart_item_data
filter allows us to add custom data to products when they’re added to the cart. When the user clicks the ‘Add to cart’ button, we use the filter to set a new variable in the product’s item data that defines the updated price. Like this:
function add_cart_item_data( $cart_item_data, $product_id, $variation_id ) { | |
// Has our option been selected? | |
if( ! empty( $_POST['extended_warranty'] ) ) { | |
$product = wc_get_product( $product_id ); | |
$price = $product->get_price(); | |
// Store the overall price for the product, including the cost of the warranty | |
$cart_item_data['warranty_price'] = $price + 250; | |
} | |
return $cart_item_data; | |
} | |
add_filter( 'woocommerce_add_cart_item_data', 'add_cart_item_data', 10, 3 ); |
When the user adds the product to their cart, this code checks to see if a checkbox with the extended_warranty
name attribute has been set.
If the checkbox has been set then we used the $product_id
variable passed by the filter to get the $product
object, then use the get_price
method to get the product price:
$product = wc_get_product( $product_id ); | |
$price = $product->get_price(); |
Then we store our updated price in a new element in the $cart_item_data
object.
$cart_item_data['warranty_price'] = $price + 250; |
Note that I’ve hardcoded a value here. You will undoubtedly want to use your own value.
The woocommerce_before_calculate_totals
action
Now that we have our updated price stored as a variable in the cart item data, we can use it when the user arrives on the cart page. We do that like this:
function before_calculate_totals( $cart_obj ) { | |
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) { | |
return; | |
} | |
// Iterate through each cart item | |
foreach( $cart_obj->get_cart() as $key=>$value ) { | |
if( isset( $value['warranty_price'] ) ) { | |
$price = $value['warranty_price']; | |
$value['data']->set_price( ( $price ) ); | |
} | |
} | |
} | |
add_action( 'woocommerce_before_calculate_totals', 'before_calculate_totals', 10, 1 ); |
As you can guess from the name of the action, woocommerce_before_calculate_totals
runs just before the cart total is calculated. We pass it the cart object, then iterate through each item in the cart to check for our extended_warranty
variable. If we find it, we update the price for the product using the set_price
method.
Why not set the price variable at the cart page?
If you try to calculate the updated price directly in the cart using woocommerce_before_calculate_totals
, you might find that the extra price is added twice or even multiple times. That’s because woocommerce_before_calculate_totals
can be called more than once on the cart page.
Prevent customers purchasing products from archive pages
Thanks to Rick for pointing this out in the comments below. If you have custom fields in your product, you might want to prevent your users from purchasing those products direct from the shop or archive pages. You can use the filter below to achieve this:
<?php | |
/** | |
* Prevent products being purchased from archive | |
* @return Boolean | |
*/ | |
function pewc_filter_is_purchasable( $is_purchasable, $product ) { | |
if( is_archive() ) { | |
return false; | |
} | |
return $is_purchasable; | |
} | |
add_filter( 'woocommerce_is_purchasable', 'pewc_filter_is_purchasable', 10, 2 ); |
Further resources
woocommerce_add_cart_item_data
– https://docs.woocommerce.com/wc-apidocs/source-class-WC_Cart.html#897
woocommerce_before_calculate_totals
– https://docs.woocommerce.com/wc-apidocs/source-class-WC_Cart.html#1097
WooCommerce Product Add-Ons Ultimate
Personalize products with extra fields and custom options
How would this be used with a percentage. I have a nonprofit company that needs a checkbox to increase the price by a percentage of the donation to cover processing fees of 2.9%
Hi Steve
For percentage based pricing, you just make the calculation as a percentage of the product price rather than adding a set amount to the product price.
Version 2 of WooCommerce Product Add Ons Ultimate is due to be released shortly and will feature percentage based pricing.
Gareth
Hi there. Thanks. I have another question about this title.
Assume that we buy products with a currency (let’s say Dollars) and we have to sell them with another currency (let’s say Euro) that its rate may change occasionally. So we have to product all prices to a rate value. From one of your posts I could add a custom field that I enter the dollar price. Now, I want to set all of my products based on that price and change them all (for example 1 Euro equals 1.16 Dollars right now, so I want to product all prices to 1/1.16 to convert them to Euro for all products).
I used a code that is described in there: https://stackoverflow.com/questions/52289723/changing-displayed-price-by-get-post-meta-ruins-the-cart-price-in-wordpress
But I had the problem described there. So, do you have any suggestion?
Thanks
At quick glance, looking at the code you posted on the Stack Overflow link, you’re trying to use the
$price
variable as an array,$price[0]
, when it’s just a simple value. I think that’s why you’re getting a 0 for your result.Hello. Thank you for your time posting this valuable info.
In my case, I used this method to add a custom charge. I was so happy to have this functionality without adding yet another plugin.
The next day I received an order and it was processed without the custom charge. I tried doing test orders and always the custom charge is there. So I was wondering how the customer got away without the custom charge.
I found out that the customer ordered from the archive page and not from the product single page. The info given here works perfect in the single page, but since I have “Add to Cart” buttons in the archive page (category page), if the product is added from there, the custom charge is overridden.
Any easy way to make it work from category too? If not, I will just hide the “Add to Cart” buttons in the category page or change it somehow to “More Info” and force to order from the single page.
I had a loss in that order, but still very happy that this is working without a plugin (already have about 40 plugins running).
Kind regards
You’re absolutely right. I’ve updated the article to filter the Add to Cart buttons on archive pages.
Thank you!!!!
The code works perfectly thanks. If a customer ticks the warranty box, how would I get that to display in the basket? The new price displays correctly but I’d like to display “Extended Warranty – £250” to my item description in the basket/checkout.
So I ended up writing the code myself. This will display the warranty information inside the basket.
function display_warranty_option( $item_data, $cart_item ) {
if (!empty($cart_item[‘extended_warranty’])) {
$item_data[] = array(
‘key’ => ‘Extended Warranty’,
‘value’ => wc_clean($cart_item[‘extended_warranty’]),
‘display’ => ‘Extended warranty included.’,
);
}
return $item_data;
}
add_filter(‘woocommerce_get_item_data’, ‘display_warranty_option’, 10, 2);
I want to change the pricing on display. Can you give me any reference how to change product price on display as I have already gone through this tutorial https://www.cloudways.com/blog/change-woocommerce-price-display/ and having an error while running the code that I have mentioned below. Is there any alternative to do this?
function cw_change_product_price_display( $price ) {
$price .= ‘ At Each Item Product’;
return $price;
}
add_filter( ‘woocommerce_get_price_html’, ‘cw_change_product_price_display’ );
add_filter( ‘woocommerce_cart_item_price’, ‘cw_change_product_price_display’ );
Hi,
Is it possible for a specific product that the price does not multiply by the quantity? I just want the quantity to appear but not multiply it in just one product.
Example: https://prnt.sc/s6uumo
Thanks.
Hi – yes, using the Add-Ons Ultimate plugin, you could set the product price to 0 then assign a flat rate price to the add-on field.
Thanks
Gareth
Hi @Gareth.Is That Possible to Update Product Price And In-Stock Status In WooCommerce DataBase?
We Read This Info From A Remote DataBase And Insert Into WooCommerce DataBase.
Thanks
This method updates prices on the front end. Are you pulling in data remotely to the back end?
Hello, thanks for a good article. In my case I want to use addons to my main product that has a percentage price of both the main product + all addons added by the user. Is that possible? Trying to add percentage based price addons today will only result in showing the 10% cost of the main product, and the addons aren’t counted in the 10% prices.
Any ideas?