WooCommerce钩子以有条件地增加一些产品价格

huangapple go评论62阅读模式
英文:

WooCommerce hooks to increase some product prices conditionally

问题

在WooCommerce中,我正在尝试调整特定产品的价格,以进行A/B测试。价格调整基于我随后在WC会话变量中设置的Cookie值('A'或'B')。如果Cookie值为'B',我想将价格提高15%。

我尝试通过对获取产品价格的钩子应用过滤器来实现此目标(例如woocommerce_product_variation_get_price,woocommerce_product_variation_get_regular_price,woocommerce_product_variation_get_sale_price等)。在产品页面和商店概览中,价格正确调整了。

然而,在购物车中,各个产品的价格是不正确的,尽管每个项目的小计(价格*数量)使用了调整后的价格进行了正确计算。更重要的是,不正确(未调整)的价格被传递给了支付提供商。

为了尝试纠正购物车的问题,我在woocommerce_before_calculate_totals操作上应用了一个函数,该函数在Cookie值为'B'时也尝试调整产品价格。但是,似乎不影响购物车中显示的价格。

我怀疑WooCommerce以某种方式存储或缓存原始价格,并在购物车和结账过程中使用这些存储的值。

有人能否建议正确的方法来调整WooCommerce中的价格,以便在整个购物和结账过程中始终一致使用调整后的价格,包括在将最终价格传递给支付提供商时?

我应该使用哪些钩子或过滤器,或者是否有其他技术可以实现这一目标?

非常感谢您的帮助。

英文:

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;
    }
}

答案1

得分: 1

由于我不是严肃开发(使用OOP和严格类型的变量),我已经以更容易访问的方式重构了您的代码,因为它可能被每个人都用于他们的主题的functions.php文件。

有许多增加产品价格的条件方式,例如在这个答案中使用与您不同的方式。

关于您的代码,我已经能够摆脱了cookies,只使用WooCommerce会话变量,当产品价格增加(+ 15%)时,购物车总计计算不再出现问题。我已经解决了与WC()->session交互时在管理员端产生的关键问题。

请注意,您可能需要重新调整未处理税收显示设置和促销价格(woocommerce_get_price_html)的显示产品价格。

我添加了两个内容:

  • 为访客用户提前启用WC会话变量(以便它也适用于访客),
  • 为MiniCart显示的价格增加了一些代码。

所有主要变量现在都在第一个函数中作为数组。

以下是代码:

// 您的设置(变量)
function loading_my_variables() {
    $key = 'cnspt';
    
    if( isset(WC()->session) && WC()->session->has_session()) {
        // 从这里获取我们的会话变量值
        $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,
    );
}

// 提前为访客用户启用WC会话变量
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);
    }
}

// 设置“cnspt”WC会话变量(随机值“A”或“B”)
add_action('template_redirect', 'set_cnspt_wc_session', 10);
function set_cnspt_wc_session() {
    if(is_admin()) return; // 重要:避免在管理员端产生关键错误

    extract(loading_my_variables());

    if ( empty($variant) ) {
        WC()->session->set($session_key, (rand(0, 1) === 0 ? 'A' : 'B'));
        // WC()->session->__unset('cnspt'); // <== 重置会话变量(用于测试)
    }
}

// 显示特定产品的修改价格
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;  // 重要:避免在管理员端产生关键错误

    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(); // 变量产品价格范围
                $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;
}

// 显示迷你购物车中的项目修改价格
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;
}

// 在计算之前设置购物车项目修改价格
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));
            }
        }
    }
}

代码放在您的活动子主题(或活动主题)的functions.php文件中,或者也可以放在插件中。经过测试,可正常工作。

英文:

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()-&gt;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 = &#39;cnspt&#39;;
    
    if( isset(WC()-&gt;session) &amp;&amp; WC()-&gt;session-&gt;has_session()) {
        // We get from here our session variable value
        $variant = WC()-&gt;session-&gt;get($key);
    } else {
        $variant = &#39;&#39;;
    }

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

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

// Set the &quot;cnspt&quot; WC session variable (random &quot;A&quot; or &quot;B&quot; value)
add_action(&#39;template_redirect&#39;, &#39;set_cnspt_wc_session&#39;, 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()-&gt;session-&gt;set($session_key, (rand(0, 1) === 0 ? &#39;A&#39; : &#39;B&#39;));
        // WC()-&gt;session-&gt;__unset(&#39;cnspt&#39;); // &lt;== Reset session variable (for testing)
    }
}

// Displaying altered prices for specific products
add_filter(&#39;woocommerce_get_price_html&#39;, &#39;product_altered_prices_display&#39;, 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 === &#39;B&#39;) {
        $product_id = $product-&gt;get_parent_id() ?: $product-&gt;get_id();

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

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

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

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

    if ($variant === &#39;B&#39;) {
        foreach ($cart-&gt;get_cart() as $cart_item) {
            $product_id = $cart_item[&#39;data&#39;]-&gt;get_parent_id();
            if (in_array($product_id, $product_ids)) {
                $price = $cart_item[&#39;data&#39;]-&gt;get_price();
                $cart_item[&#39;data&#39;]-&gt;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.

huangapple
  • 本文由 发表于 2023年6月29日 01:44:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/76575570.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定