1

In WooCommerce, I am trying to adjust the specific product prices for A/B testing purposes. The price adjustment is based on a cookie value ('A' or 'B') that I set afterward in a WC session variable. If the cookie value is 'B', I want to increase the price by 15%.

I've attempted this by applying filters to the hooks that fetch product prices (woocommerce_product_variation_get_price, woocommerce_product_variation_get_regular_price, woocommerce_product_variation_get_sale_price, etc.). On the product page and shop overview, the price is correctly adjusted.

However, in the shopping cart, the individual product price is incorrect, although the subtotal for each item (price*quantity) is correctly calculated using the adjusted price. More importantly, the incorrect (unadjusted) price is transferred to the payment provider.

To attempt to correct the cart issue, I've applied a function on the woocommerce_before_calculate_totals action, which also attempts to adjust the product price if the cookie value is 'B'. But, it doesn't seem to affect the prices shown in the cart.

I suspect that WooCommerce is somehow storing or caching the original prices and using these stored values in the cart and during checkout.

Can anyone suggest the correct approach to adjusting the prices in WooCommerce so that the adjusted prices are consistently used throughout the shopping and checkout process, including when transferring the final price to the payment provider?

What hooks or filters should I be using, or is there another technique to achieve this?

Your help is highly appreciated.

This is my latest code version:

class PriceTest {
    private array $product_ids = [153, 20597];
    private int $increase_percentage = 15;
    private string $cookieName = 'cnspt';
    private string $sessionKey = 'cnspt';

    public function __construct() {
        add_action('init', [$this, 'initialize_price_test_session']);
        add_filter('woocommerce_get_price_html', [$this, 'custom_price_display'], 10, 2);
        add_action('woocommerce_before_calculate_totals', [$this, 'custom_price_split_test']);
        add_filter('woocommerce_cart_item_subtotal', [$this, 'custom_cart_item_subtotal'], 10, 3);
    }

    public function initialize_price_test_session(): void {
        $variant = null;

        if (isset($_GET[$this->cookieName]) && in_array($_GET[$this->cookieName], ['A', 'B'], true)) {
            $variant = $_GET[$this->cookieName];
            WC()->session->set($this->sessionKey, $variant);
        } else {
            if (!WC()->session->get($this->sessionKey)) {
                if (!isset($_COOKIE[$this->cookieName])) {
                    $variant = rand(0, 1) === 0 ? 'A' : 'B';
                    setcookie($this->cookieName, $variant, time() + DAY_IN_SECONDS, "/");
                } else {
                    $variant = $_COOKIE[$this->cookieName];
                }
                WC()->session->set($this->sessionKey, $variant);
            }
        }
    }

    public function custom_price_split_test(\WC_Cart $cart_object): void {
        $variant = WC()->session->get($this->sessionKey);

        if ($variant === 'B') {
            foreach ($cart_object->get_cart() as $cart_item) {
                $product_id = $cart_item['data']->get_parent_id();
                if (in_array($product_id, $this->product_ids)) {
                    $original_price = $cart_item['data']->get_price();
                    $new_price = round($original_price * (1 + $this->increase_percentage / 100));
                    $cart_item['data']->set_price($new_price);
                }
            }
        }
    }

    public function custom_price_display(string $price_html, \WC_Product $product): string {
        $variant = WC()->session->get($this->sessionKey);

        if ($variant === 'B') {
            $product_id = $product->get_parent_id() ?: $product->get_id();
            if (in_array($product_id, $this->product_ids)) {
                if ($product->is_type('variable')) {
                    // Show price range for variable products
                    $prices = $product->get_variation_prices();
                    $min_price = min($prices['price']) * (1 + $this->increase_percentage / 100);
                    $max_price = max($prices['price']) * (1 + $this->increase_percentage / 100);
                    $price_html = wc_format_price_range(round($min_price), round($max_price));
                } else {
                    $original_price = $product->get_price();
                    $new_price = round($original_price * (1 + $this->increase_percentage / 100));
                    $price_html = wc_price($new_price);
                }
            }
        }

        return $price_html;
    }

    public function custom_cart_item_subtotal($subtotal, $cart_item, $cart_item_key) {
        $variant = WC()->session->get($this->sessionKey);
        if (is_cart() && $variant === 'B') {
            $product_id = $cart_item['data']->get_parent_id();
            if (in_array($product_id, $this->product_ids)) {
                $quantity = $cart_item['quantity'];
                $original_price = $cart_item['data']->get_price();
                $new_price = round($original_price * (1 + $this->increase_percentage / 100));
                $subtotal = wc_price($new_price * $quantity);
            }
        }
        return $subtotal;
    }
}
LoicTheAztec
  • 229,944
  • 23
  • 356
  • 399
Pascal Bajorat
  • 464
  • 8
  • 20
  • 1
    Hey, I've added the latest version of my code, it works not in most cases but feeling unstable because I have to fix the subtotal within the cart (woocommerce_cart_item_subtotal). Checkout overview is fine again. – Pascal Bajorat Jun 28 '23 at 20:31

1 Answers1

1

As I am not making a serious development (using OOP and strict typed variables), I have refactored your code in a more accessible way, as it might be used by everybody people on their theme's functions.php file.

There are many ways to increase conditionally some product prices, like in This answer using a different way than yours.

Regarding your code, I have been able to get rid of cookies, using just WooCommerce session variable, and I have no more issues with cart totals calculations when the product has an increased price (+ 15%). I have solved a critical issue in admin interacting badly with WC()->session.

Note that you may have to rework yourself the displayed product prices that don't handle tax display settings and on sale prices (woocommerce_get_price_html).

I have added 2 things:

  • Early enabling WC session variables for guest users (so it works for guest too),
  • Added some code for MiniCart displayed increase prices.

All your main variables are now located here in the first function as an array.

The code:

// Your settings (variables)
function loading_my_variables() {
    $key = 'cnspt';
    
    if( isset(WC()->session) && WC()->session->has_session()) {
        // We get from here our session variable value
        $variant = WC()->session->get($key);
    } else {
        $variant = '';
    }

    return array(
        'product_ids' => [153, 20597],
        'price_rate'  => 1.15, // + 15%
        'cookie_name' => $key,
        'session_key' => $key,
        'variant'     => $variant,
    );
}

// Early enable WC session variable for guest users 
add_action('woocommerce_init', 'early_enable_wc_session_for_guest_user', 5);
function early_enable_wc_session_for_guest_user() {
    if (is_user_logged_in() || is_admin()) {
        return;
    }
    if (isset(WC()->session) && !WC()->session->has_session()) {
        WC()->session->set_customer_session_cookie(true);
    }
}

// Set the "cnspt" WC session variable (random "A" or "B" value)
add_action('template_redirect', 'set_cnspt_wc_session', 10);
function set_cnspt_wc_session() {
    if(is_admin()) return; // !important: to avoid critical errors on admin side

    extract(loading_my_variables());

    if ( empty($variant) ) {
        WC()->session->set($session_key, (rand(0, 1) === 0 ? 'A' : 'B'));
        // WC()->session->__unset('cnspt'); // <== Reset session variable (for testing)
    }
}

// Displaying altered prices for specific products
add_filter('woocommerce_get_price_html', 'product_altered_prices_display', 10, 2);
function product_altered_prices_display($price_html, $product) {
    if(is_admin()) return $price_html;  // !important: to avoid critical errors on admin side

    extract(loading_my_variables());
    
    if ($variant === 'B') {
        $product_id = $product->get_parent_id() ?: $product->get_id();

        if (in_array( $product_id, $product_ids )) {
            if ($product->is_type('variable')) {
                $prices = $product->get_variation_prices(); // Variable Prod. price range
                $min_price = min($prices['price']) * $price_rate;
                $max_price = max($prices['price']) * $price_rate;
                $price_html = wc_format_price_range(round($min_price), round($max_price));
            } else {
                $price = $product->get_price();
                $price_html = wc_price(round($price * $price_rate));
            }
        }
    }
    return $price_html;
}

// Display In minicart items altered prices
add_action( 'woocommerce_cart_item_price', 'minicart_altered_price_display', 10, 2 );
function minicart_altered_price_display( $price, $cart_item ) {
    extract(loading_my_variables());
    
    if (!is_cart() && $variant === 'B' && in_array( $cart_item['product_id'], $product_ids )) {
        $args = array( 'price' => round($cart_item['data']->get_price() * $price_rate)  );

        if ( 'incl' === get_option('woocommerce_tax_display_cart') ) {
            $product_price = wc_get_price_including_tax( $cart_item['data'], $args );
        } else {
            $product_price = wc_get_price_excluding_tax( $cart_item['data'], $args );
        }
        return wc_price( $product_price );
    }
    return $price;
}

// Set the the cart item altered price, before calculations
add_action('woocommerce_before_calculate_totals', 'set_cart_items_altered_price');
function set_cart_items_altered_price( $cart ) {
    extract(loading_my_variables());

    if ($variant === 'B') {
        foreach ($cart->get_cart() as $cart_item) {
            $product_id = $cart_item['data']->get_parent_id();
            if (in_array($product_id, $product_ids)) {
                $price = $cart_item['data']->get_price();
                $cart_item['data']->set_price(round($price * $price_rate));
            }
        }
    }
}

Code goes in functions.php file of your active child theme (or active theme) or in a plugin too. Tested and works.

LoicTheAztec
  • 229,944
  • 23
  • 356
  • 399
  • Thanks, I will test it with your code and improvements. FYI - here is a Syntax error: 'price_rate' => 1,15 should be 'price_rate' => 1.15, – Pascal Bajorat Jun 29 '23 at 08:16
  • I've tested everything, but it seems we have the same problem here. I need to fix the subtotal in the cart with an additional function for woocommerce_cart_item_subtotal. Maybe this problem is related to a plugin conflict or possibly a bug with Divi as this is the theme used. – Pascal Bajorat Jun 29 '23 at 08:47
  • oups! just changed the value too quockly, before publishing :) I don't have any issue with subtotal… I have tested that in all ways. So There is something else that is making troubles in your case. The code really works without needing any subtotal fix. – LoicTheAztec Jun 29 '23 at 12:14
  • 1
    I think this is really a problem with plugins or themes in my specific system. I will try to debug this, thank you very much for your help and input, I really appreciate it. – Pascal Bajorat Jun 30 '23 at 13:23