Search code examples
phpwordpresswoocommercegetcart

Set a custom cart item price value from a GET variable In Woocommerce 3


I have a function on my Woocommerce website that enables customers to set a custom amount to pay for a specific product, based on a value I'm passing through the URL.

I'm using the woocommerce_before_calculate_totals hook, and up until I upgraded to WC 3.3.5, it was working fine. Now, when I run the code, the checkout initially shows the custom amount.

However, once the loader has finished updating, it resets the price to '0' (i.e. displaying £0.00 the checkout page's total fields).

Here's that code:

add_action( 'woocommerce_before_calculate_totals', 'pay_custom_amount', 99);

function pay_custom_amount() {
    $payment_value = $_GET['amount'];

    foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {

        if($cart_item['data']->id == 21 ){
            $cart_item['data']->set_price($payment_value);
        }
    }
}

Well, colour me baffled. I've trawled Stack Overflow for solutions but can't see any similar problems. I see the hook runs multiple times but gather this is normal.

If anyone know what might be happening here, it would be great if you could share.


Solution

  • You can't get a price from an URL and set it in woocommerce_before_calculate_totals action hook. This needs to be done differently.

    In the below code:

    • the first hooked function will get that "amount" from the URl and will set it (register it) in cart item object as custom data.
    • the 2nd hooked function will read that amount from custom cart item data and will set it as the new price.

    Now your the target product ID in your code need to be the same ID that the added to cart product.

    The code:

    // Get the custom "amount" from URL and save it as custom data to the cart item
    add_filter( 'woocommerce_add_cart_item_data', 'add_pack_data_to_cart_item_data', 20, 2 );
    function add_pack_data_to_cart_item_data( $cart_item_data, $product_id ){
        if( ! isset($_GET['amount']) )
            return $cart_item_data;
    
        $amount = esc_attr( $_GET['amount'] );
        if( empty($amount) )
            return $cart_item_data;
    
        // Set the custom amount in cart object
        $cart_item_data['custom_price'] = (float) $amount;
        $cart_item_data['unique_key'] = md5( microtime() . rand() ); // Make each item unique
    
        return $cart_item_data;
    }
    
    // Alter conditionally cart item price based on product ID and custom registered "amount"
    add_action( 'woocommerce_before_calculate_totals', 'change_conditionally_cart_item_price', 30, 1 );
    function change_conditionally_cart_item_price( $cart ) {
        if ( is_admin() && ! defined( 'DOING_AJAX' ) )
            return;
    
        if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
            return;
    
        // HERE set your targeted product ID
        $targeted_product_id = 21;
    
        foreach ( $cart->get_cart() as $cart_item ) {
            // Checking for the targeted product ID and the registered "amount" cart item custom data to set the new price
            if($cart_item['data']->get_id() == $targeted_product_id && isset($cart_item['custom_price']) )
                $cart_item['data']->set_price($cart_item['custom_price']);
        }
    }
    

    Code goes in function.php file of your active child theme (or active theme). Tested and works.