Search code examples
phpwordpresswoocommercewoocommerce-subscriptions

Display shipping cost on product page - WooCommerce


I'm trying to make a custom page on Wordpress that display 2 subscription types in a form with 3 variations each (monthly, 6 month, 12 month). Each variation gets a radio button and I have a total price that is live updated when user clicks on the radio buttons. This part is working.

But now, I want to add 3 other radio buttons to choose the shipment method. (When user select one, it'll live update the total price too).

I've been searching a long time how to get shipping cost for a product but nothing has worked.


Solution

  • This question is too broad. So I can answer partially as you should need to make some work yourself, and ask later more specific questions…

    Now the correct way to set shipping data on product page, is to use Ajax to update the data, as the action is made by the customer on client side (browser), avoiding 'post' and reload the page. But this should be your work...

    1) Customer location (for shipping Zones):

    You should need to get the customer location or shipping zone first.

    Then you will need to update the customer country in WC()->session and in WC()->customer objects. This can be done with the following:

    ## Get the geolocated customer country code *(if enabled)*:
    $country_code = WC()->customer->get_billing_country();
    // or
    // $country_code = WC()->customer->get_shipping_country();
    
    
    ## Set a new country code
    $new_country_code = 'FR';
    
    ## 1. WC_session: set customer billing and shipping country
    
    // Get the data
    $customer_session = WC()->session->get( 'customer' );
    // Change some data
    $customer_session['country'] = $new_country_code; // Billing
    $customer_session['shipping_country'] = $new_country_code; // Shipping
    // Set the changed data
    $customer_session = WC()->session->set( 'customer', $customer_session );
    
    ## 2. WC_Customer: set customer billing and shipping country
    
    WC()->customer->set_billing_country( $new_country_code );
    WC()->customer->set_shipping_country( $new_country_code );
    

    2) The shipping methods (by Shipping Zone, with costs):

    In Woocommerce the Shipping methods for a Shipping Zone are only available when customer add a product to cart…

    Based on this answer code: Display shipping methods to frontend as in the admin panel?
    we can make a custom array of the necessary data to be used to get the shipping methods by shipping Zones, with costs and everything needed.

    The code below is much more complete and include the shipping methods costs:

    // Initializing variable
    $zones = $data = $classes_keys = array();
    
    // Rest of the World zone
    $zone                                              = new \WC_Shipping_Zone(0);
    $zones[$zone->get_id()]                            = $zone->get_data();
    $zones[$zone->get_id()]['formatted_zone_location'] = $zone->get_formatted_location();
    $zones[$zone->get_id()]['shipping_methods']        = $zone->get_shipping_methods();
    
    // Merging shipping zones
    $shipping_zones = array_merge( $zones, WC_Shipping_Zones::get_zones() );
    
    // Shipping Classes
    $shipping           = new \WC_Shipping();
    $shipping_classes   = $shipping->get_shipping_classes();
    
    // The Shipping Classes for costs in "Flat rate" Shipping Method
    foreach($shipping_classes as $shipping_class) {
        //
        $key_class_cost = 'class_cost_'.$shipping_class->term_id;
    
        // The shipping classes
        $classes_keys[$shipping_class->term_id] = array(
            'term_id' => $shipping_class->term_id,
            'name' => $shipping_class->name,
            'slug' => $shipping_class->slug,
            'count' => $shipping_class->count,
            'key_cost' => $key_class_cost
        );
    }
    
    // For 'No class" cost
    $classes_keys[0] = array(
        'term_id' => '',
        'name' =>  'No shipping class',
        'slug' => 'no_class',
        'count' => '',
        'key_cost' => 'no_class_cost'
    );
    
    foreach ( $shipping_zones as $shipping_zone ) {
        $zone_id = $shipping_zone['id'];
        $zone_name = $zone_id == '0' ? __('Rest of the word', 'woocommerce') : $shipping_zone['zone_name'];
        $zone_locations = $shipping_zone['zone_locations']; // array
        $zone_location_name = $shipping_zone['formatted_zone_location'];
    
        // Set the data in an array:
        $data[$zone_id]= array(
            'zone_id'               => $zone_id,
            'zone_name'             => $zone_name,
            'zone_location_name'    => $zone_location_name,
            'zone_locations'        => $zone_locations,
            'shipping_methods'      => array()
        );
    
        foreach ( $shipping_zone['shipping_methods'] as $sm_obj ) {
            $method_id   = $sm_obj->id;
            $instance_id = $sm_obj->get_instance_id();
            $enabled = $sm_obj->is_enabled() ? true : 0;
            // Settings specific to each shipping method
            $instance_settings = $sm_obj->instance_settings;
            if( $enabled ){
                $data[$zone_id]['shipping_methods'][$instance_id] = array(
                    '$method_id'    => $sm_obj->id,
                    'instance_id'   => $instance_id,
                    'rate_id'       => $sm_obj->get_rate_id(),
                    'default_name'  => $sm_obj->get_method_title(),
                    'custom_name'   => $sm_obj->get_title(),
                );
    
                if( $method_id == 'free_shipping' ){
                    $data[$zone_id]['shipping_methods'][$instance_id]['requires'] = $instance_settings['requires'];
                    $data[$zone_id]['shipping_methods'][$instance_id]['min_amount'] = $instance_settings['min_amount'];
                }
                if( $method_id == 'flat_rate' || $method_id == 'local_pickup' ){
                    $data[$zone_id]['shipping_methods'][$instance_id]['tax_status'] = $instance_settings['tax_status'];
                    $data[$zone_id]['shipping_methods'][$instance_id]['cost'] = $sm_obj->cost;
                }
                if( $method_id == 'flat_rate' ){
                    $data[$zone_id]['shipping_methods'][$instance_id]['class_costs'] = $instance_settings['class_costs'];
                    $data[$zone_id]['shipping_methods'][$instance_id]['calculation_type'] = $instance_settings['type'];
                    $classes_keys[0]['cost'] = $instance_settings['no_class_cost'];
                    foreach( $instance_settings as $key => $setting )
                        if ( strpos( $key, 'class_cost_') !== false ){
                            $class_id = str_replace('class_cost_', '', $key );
                            $classes_keys[$class_id]['cost'] = $setting;
                        }
    
                    $data[$zone_id]['shipping_methods'][$instance_id]['classes_&_costs'] = $classes_keys;
                }
            }
        }
    }
    
    // Row output (for testing)
    echo '<pre>'; print_r($data); echo '</pre>';
    

    custom shipping methods
    Now if you are using custom shipping methods (enabled sometimes by 3rd party shipping plugins) you will need to make some changes in the code…

    Costs and taxes calculation
    You should need to make the taxes calculations, as the costs are displayed just as they are set in shipping settings…


    3) Product page

    Customer location:
    You will need first to have a location selector (to define the Shipping Zone) or to set the location based on Woocommerce geolocation.

    Shipping Methods:
    Once the Shipping Zone is defined, you can get the corresponding Shipping Methods and rates (costs), displaying on this product page your radio buttons for Shipping methods.

    To get this, you should need to alter the single product pages:

    You should need to get/set/update the "chosen_shipping_methods" with the following code (Ajax).

    Get the Chosen Shipping method:

    $chosen_shipping = WC()->session->get('chosen_shipping_methods')[0];
    

    Set/Update the Chosen Shipping method (through Javascript/Ajax and admin-ajax.php):

    // HERE the new method ID
    $method_rate_id = array('free_shipping:10');
    
    // Set/Update the Chosen Shipping method
    WC()->session->set( 'chosen_shipping_methods', $method_rate_id );