It’s hard to compete in the modern market if you don’t think about your customers that use mobile devices. Based on the current Statcounter GlobalStats data, mobile users consume more than half of total web traffic. The largest search engines give a boost to mobile-friendly websites. No wonder that marketers and UX designers try to find the best ways to make mobile users buy more.
I’m pretty sure that you’ve seen the special prices for mobile users on Booking.com. You’ve probably seen similar bonuses for mobile users on AliExpress and many other e-commerce platforms and websites. Today, I want to show you how to set special prices for mobile users in WooCommerce just like the mentioned giants do. We won’t use any plugins: just PHP and in-built WordPress and WooCommerce features.
Introduction
First of all, we have to figure out how things should work. Our products will have two different prices: a regular price and a special price for mobile users. The special price will be an optional field: a product will use the regular price if it’s empty.
We’re going to create a custom field in the WooCommerce product editor similar to the one we’ve added in one of the previous posts: How to hide products for non-authorized users in WooCommerce?. This time, it will be a bit more complicated because we have to support the special price feature both for simple and variable products.
If the special price is set, it should be used in multiple places:
- On the product category archive
- On the single product page
- On the cart page
- On the checkout page
The most important requirement: the special prices should be shown if a visitor uses a mobile device or a tablet. I’m going to use an in-built WordPress function to check that: wp_is_mobile(). This function is pretty simple: it checks if a user agent’s name includes mobile-related substrings.
Also, we want to highlight special prices on the frontend to show users that an offer they see is available for mobile users only.
Are you already excited to see how to create a solution like this? Let’s get started then!
Let’s create a custom field that will store our special price
We want to support the special mobile price feature for both simple and variable products. Because of this, we’ll have to use two different hooks to display our custom field: woocommerce_product_options_general_product_data for simple products and woocommerce_variation_options_pricing for variable products. The first one triggers after the Sale price field in the General tab, and the second one triggers after the Sale price field in the variation editing form.
add_action( 'woocommerce_product_options_general_product_data', 'add_mobile_price_field_for_simple_products' );
function add_mobile_price_field_for_simple_products() {
$args = array(
'id' => 'mobile_price',
'label' => 'Special price for mobile users',
'data_type' => 'price',
'wrapper_class' => 'form-row'
);
woocommerce_wp_text_input( $args );
}
add_action( 'woocommerce_variation_options_pricing', 'add_mobile_price_field_for_variations', 10, 3 );
function add_mobile_price_field_for_variations( $i, $variation_data, $variation ) {
$args = array(
'id' => 'mobile_price[' . $i . ']',
'label' => 'Special price for mobile users',
'data_type' => 'price',
'wrapper_class' => 'form-row',
'value' => get_post_meta( $variation->ID, 'mobile_price', true )
);
woocommerce_wp_text_input( $args );
}
As you see, the hook handlers are not identical. For example, in the add_mobile_price_field_for_variations function, we have to pass the current variation’s index in the loop as the id argument. Also, we don’t need to specify the value argument if we’re working with a simple product: WooCommerce will fetch the current value for us. But this argument is mandatory for variation custom fields.
Now, if I open a simple product in the editor and go to the General tab, I’ll see my new field there:
And if I open a variable product and try to edit one of its variations, I’ll be able to see the field here too:
That was pretty easy. The fields are displayed, and that’s great. Now, we need to store their values in the product/variation meta. To achieve this, we have to use two different hooks again: woocommerce_process_product_meta for simple products and woocommerce_save_product_variation for variations.
add_action( 'woocommerce_process_product_meta', 'save_mobile_price_for_simple_products' );
function save_mobile_price_for_simple_products( $post_id ) {
$mobile_price = $_POST['mobile_price'];
if ( ! empty( $mobile_price ) ) {
update_post_meta( $post_id, 'mobile_price', sanitize_text_field( $mobile_price ) );
}
}
add_action( 'woocommerce_save_product_variation', 'save_mobile_price_for_variations', 10, 2 );
function save_mobile_price_for_variations( $variation_id, $i ) {
$mobile_price = $_POST['mobile_price'][ $i ];
if ( ! empty( $mobile_price ) ) {
update_post_meta( $variation_id, 'mobile_price', sanitize_text_field( $mobile_price ) );
}
}
Okay, we can store and update products’ special mobile prices. Now, we need to inject them into the pricing logic.
Let’s modify the price HTML
Simple products
By default, to display a simple product’s price on the frontend, WooCommerce uses the WC_Product::get_price_html() function. So, if you want to modify how simple products’ prices are displayed on the frontend, you need to modify this function’s output by using the woocommerce_get_price_html filter hook. As I said in the introduction, we’re going to use this hook to:
- Modify the default price HTML if the mobile_price meta field of a product is not empty
- Show a simple message for a user that the price they see is a special offer for mobile users
How can we achieve something like this?
add_filter( 'woocommerce_get_price_html', 'modify_simple_product_price_html', 10, 2 );
function modify_simple_product_price_html( $price, $product ) {
// If a user doesn't use a mobile device, we just return the default price HTML
if ( ! wp_is_mobile() ) {
return $price;
}
// Variable products use this filter too, but we're going to use a different hook with them
if ( $product->is_type( 'variable' ) ) {
return $price;
}
// Let's see if a product has a special price for mobile users
$mobile_price = get_post_meta( $product->get_id(), 'mobile_price', true );
if ( isset( $mobile_price ) && strlen( $mobile_price ) > 0 ) {
// This is the HTML code of the message we'll show to a user notifying them that this is a special offer
$mobile_price_message = '<div class="mobile-price-message">Special offer for mobile users</div>';
return $mobile_price_message . wc_price( $mobile_price );
} else {
return $price;
}
}
Now, I will take one of the simple products and set its mobile price to some number.
If I open the shop page from my laptop, I’ll see that this product costs $18. WooCommerce will take its sale price by default. But if I open the page from my phone, that’s what I will see:
We see our custom price HTML instead of the default one.
It seems that we’re done here; everything works as expected. Let’s try to achieve something similar with variable products.
Variable products
By default, WooCommerce displays the price of a variable product as a price range. You can see an example on the screenshot below:
So if you want to modify this price range’s HTML code, you need to use a different filter hook —woocommerce_variable_price_html. We want to use the same logic here (the price range should show the cheapest and the most expensive variations’ prices). But we need to integrate this logic with our custom fields. Here’s the code I’ll use:
add_filter( 'woocommerce_variable_price_html', 'modify_variable_product_price_range_html', 10, 2 );
function modify_variable_product_price_range_html( $price, $product ) {
// If a user doesn't use a mobile device, we just return the default price HTML
if ( ! wp_is_mobile() ) {
return $price;
}
$mobile_price_message = '<div class="mobile-price-message">Special offer for mobile users</div>';
$variations = $product->get_available_variations();
$variations_prices = array();
foreach ( $variations as $variation ) {
$variation_id = $variation['variation_id'];
$mobile_price = get_post_meta( $variation_id, 'mobile_price', true );
if ( isset( $mobile_price ) && strlen( $mobile_price ) > 0 ) {
$variations_prices[] = floatval( $mobile_price );
} else {
$variation = new WC_Product_Variation( $variation_id );
$variation_price = $variation->get_price();
$variations_prices[] = floatval( $variation_price );
}
}
return $mobile_price_message . wc_format_price_range( min( $variations_prices ), max( $variations_prices ) ) . $product->get_price_suffix();
}
Let’s check the hoodie from the previous screenshot again:
Great! Now, the special mobile prices will be considered for variable products too. Let’s work on the next step.
How to pass the special prices to the cart
At this point, you may think that we’re done: we can save and update special mobile prices of our products, and they will be automatically shown on the frontend. But if you try to add one of those products to the cart, you’ll see that it has a default price (a regular or a sale one).
The WooCommerce cart doesn’t know anything about our mobile prices yet. We have to integrate this logic into the WooCommerce cart on our own. But don’t worry: it won’t be too hard. All we need to do is to use the woocommerce_before_calculate_totals hook that allows us to modify the cart object before WooCommerce has calculated the total price.
Here’s the code I will use:
add_action( 'woocommerce_before_calculate_totals', 'use_mobile_price_in_cart', 10, 1 );
function use_mobile_price_in_cart( $cart ) {
if ( ! wp_is_mobile() ) {
return;
}
foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) {
// Let's access the product object of the cart item to make our job easier
$product = $cart_item['data'];
// The logic of getting the mobile price is different for simple and variable products
// That's why we need to check the type of the current cart item
if ( $product->is_type( 'variable' ) ) {
$product_id = $cart_item['variation_id'];
} else {
$product_id = $product->get_id();
}
$mobile_price = get_post_meta( $product_id, 'mobile_price', true );
// If a product has the mobile price, use this price instead of the default one
if ( isset( $mobile_price ) && strlen( $mobile_price ) > 0 ) {
$product->set_price( $mobile_price );
}
}
}
Now, if you add a product with a special mobile price to your cart, you’ll see that this price is used in the cart instead of the default one. Since we modified the cart (not just its HTML presentation), the mobile prices will be used in orders too. So if you create an order and then check it in the admin dashboard, you’ll see that order items use their mobile prices, and the order’s total price is calculated properly.
By the way, you can go further and make the order items table in the admin dashboard to show if a product was bought for a special mobile price. Something like this:
I decided to not make this post more complex, but I just wanted to say that’s you can achieve this too. If you’d like to read more about how to edit the order items table, just let me know in the comments.
Final code
add_action( 'woocommerce_product_options_general_product_data', 'add_mobile_price_field_for_simple_products' );
function add_mobile_price_field_for_simple_products() {
$args = array(
'id' => 'mobile_price',
'label' => 'Special price for mobile users',
'data_type' => 'price',
'wrapper_class' => 'form-row'
);
woocommerce_wp_text_input( $args );
}
add_action( 'woocommerce_variation_options_pricing', 'add_mobile_price_field_for_variations', 10, 3 );
function add_mobile_price_field_for_variations( $i, $variation_data, $variation ) {
$args = array(
'id' => 'mobile_price[' . $i . ']',
'label' => 'Special price for mobile users',
'data_type' => 'price',
'wrapper_class' => 'form-row',
'value' => get_post_meta( $variation->ID, 'mobile_price', true )
);
woocommerce_wp_text_input( $args );
}
add_action( 'woocommerce_process_product_meta', 'save_mobile_price_for_simple_products' );
function save_mobile_price_for_simple_products( $post_id ) {
$mobile_price = $_POST['mobile_price'];
if ( ! empty( $mobile_price ) ) {
update_post_meta( $post_id, 'mobile_price', sanitize_text_field( $mobile_price ) );
}
}
add_action( 'woocommerce_save_product_variation', 'save_mobile_price_for_variations', 10, 2 );
function save_mobile_price_for_variations( $variation_id, $i ) {
$mobile_price = $_POST['mobile_price'][ $i ];
if ( ! empty( $mobile_price ) ) {
update_post_meta( $variation_id, 'mobile_price', sanitize_text_field( $mobile_price ) );
}
}
add_filter( 'woocommerce_get_price_html', 'modify_simple_product_price_html', 10, 2 );
function modify_simple_product_price_html( $price, $product ) {
// If a user doesn't use a mobile device, we just return the default price HTML
if ( ! wp_is_mobile() ) {
return $price;
}
// Variable products use this filter too, but we're going to use a different hook with them
if ( $product->is_type( 'variable' ) ) {
return $price;
}
// Let's see if a product has a special price for mobile users
$mobile_price = get_post_meta( $product->get_id(), 'mobile_price', true );
if ( isset( $mobile_price ) && strlen( $mobile_price ) > 0 ) {
// This is the HTML code of the message we'll show to a user notifying them that this is a special offer
$mobile_price_message = '<div class="mobile-price-message">Special offer for mobile users</div>';
return $mobile_price_message . wc_price( $mobile_price );
} else {
return $price;
}
}
add_filter( 'woocommerce_variable_price_html', 'modify_variable_product_price_range_html', 10, 2 );
function modify_variable_product_price_range_html( $price, $product ) {
// If a user doesn't use a mobile device, we just return the default price HTML
if ( ! wp_is_mobile() ) {
return $price;
}
$mobile_price_message = '<div class="mobile-price-message">Special offer for mobile users</div>';
$variations = $product->get_available_variations();
$variations_prices = array();
foreach ( $variations as $variation ) {
$variation_id = $variation['variation_id'];
$mobile_price = get_post_meta( $variation_id, 'mobile_price', true );
if ( isset( $mobile_price ) && strlen( $mobile_price ) > 0 ) {
$variations_prices[] = floatval( $mobile_price );
} else {
$variation = new WC_Product_Variation( $variation_id );
$variation_price = $variation->get_price();
$variations_prices[] = floatval( $variation_price );
}
}
return $mobile_price_message . wc_format_price_range( min( $variations_prices ), max( $variations_prices ) ) . $product->get_price_suffix();
}
add_action( 'woocommerce_before_calculate_totals', 'use_mobile_price_in_cart', 10, 1 );
function use_mobile_price_in_cart( $cart ) {
if ( ! wp_is_mobile() ) {
return;
}
foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) {
// Let's access the product object of the cart item to make our job easier
$product = $cart_item['data'];
// The logic of getting the mobile price is different for simple and variable products
// That's why we need to check the type of the current cart item
if ( $product->is_type( 'variable' ) ) {
$product_id = $cart_item['variation_id'];
} else {
$product_id = $product->get_id();
}
$mobile_price = get_post_meta( $product_id, 'mobile_price', true );
// If a product has the mobile price, use this price instead of the default one
if ( isset( $mobile_price ) && strlen( $mobile_price ) > 0 ) {
$product->set_price( $mobile_price );
}
}
}
As you see, it’s not that complicated to extend WooCommerce with your own features. All you need is to dig into the source code and show some creativity 😉
Thank you very much for reading this post. Feel free to share your opinion about its content in the comments sections below. See you in the next one!
Nice tutorial, thank you
Thank you! Happy to help 🙂