6

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.

Daniel Widdis
  • 8,424
  • 13
  • 41
  • 63
Alexis Vandepitte
  • 2,077
  • 2
  • 12
  • 28
  • In woocommerce the shipment options are enabled in cart and checkout, only once you have added a product to cart, as they are based on the shipping zones (customer location)… So when a new customer buy something, he can add the related Location data needed by Shipping in checkout page (optionally in cart)… For each shipping method in each shipping zone, you can set different prices using shipping classes, then you can set the right shipping class to each product or product variation… To enable shipping choice in the product itself, is an advanced and complicated development. – LoicTheAztec Nov 21 '17 at 19:59
  • [...] I'm a developper, so i can add this on my custom page with php code, hooks or anything that can helps me to do this. Maybe i'll have to do tricky things to make it works. Actually, I made a custom page with a custom form for choosing between my variable subscriptions. If you want an exemple of what i want, it's almost exactly the same as : https://www.lepetitballon.com/subscription/ (sorry it's in french). – Alexis Vandepitte Nov 22 '17 at 21:25
  • I have made this free plugin that shows you shipping cost right on the product page and user can change there address to see the shipping cost as per there location https://wordpress.org/plugins/product-page-shipping-calculator-for-woocommerce/ – rajesh singh Oct 19 '20 at 04:51

1 Answers1

10

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 );
LoicTheAztec
  • 229,944
  • 23
  • 356
  • 399
  • Your answer's gonna helps me a lot ! I think this is exactly what i need. I missed those "basics" informations on how Woocommerce works. It will be easier now to find new missing informations. Thank you so much for the time you took for my problem. I hope it will helps some other people too. When i finish it, i will post my solution here. Have a nice day – Alexis Vandepitte Nov 23 '17 at 09:35
  • Great answer and top code examples. You have helped me today. – Erik Pöhler Aug 23 '19 at 14:14
  • Thank you for answer! Is it possible to rewrite `2) The shipping methods` considering current currency? – Yura Kosyak May 21 '21 at 11:49
  • 1
    @YuraKosyak you can use wc_price method. – Cihan Küsmez Nov 19 '21 at 02:19