Australia Post shipping plugin

Here is my version of an Australia Post shipping plugin. If you use it and it works for you please leave me some love in the form of reputation points and maybe I can get some free membership. This took a LOT of work and was very frustrating. There were no properly functioning alternatives out there that I could find and you will be paying more than a few dollars for a developer to write this for you.

This currently only works for domestic postage.

<?php

/*
Marketpress Australia Post Shipping Plugin
A simplified version where the Aus Post API key is hardcoded.
Calculates domestic postage only.
*/
class MP_Shipping_AusPost extends MP_Shipping_API{

var $plugin_name = 'auspost';
var $public_name = 'Australia Post';

//set to true if you need to use the shipping_metabox() method to add per-product shipping options
public $use_metabox = true;

//set to true if you want to add per-product extra shipping cost field
public $use_extra = true;

//set to true if you want to add per-product weight shipping field
public $use_weight = true;

// Defines the available shipping Services and their display names
public $domestic_services = array();

public $sandbox_uri = 'https://test.npe.auspost.com.au';

public $production_uri = 'https://auspost.com.au';
private $apikey = 'Put your Australia Post API key here';

private $domestic_shipper_url;
private $domestic_letter_services_url;
private $domestic_parcel_services_url;

private $settings = '';

private $auspost_settings;

function on_creation(){

	$this->public_name = __('Australia Post', 'mp');

	$this->domestic_shipper_url = "https://auspost.com.au/api/postage/parcel/domestic/calculate";
	$this->domestic_letter_services_url = "https://auspost.com.au/api/postage/letter/domestic/service.json?";
	$this->domestic_parcel_services_url = "https://auspost.com.au/api/postage/parcel/domestic/service.json?";

	//Australia Post Domestic services
		$this->domestic_services = array(

		'AUS_LETTER_REGULAR_SMALL' 		=> new Aus_Post_Service('AUS_LETTER_REGULAR_SMALL',			__('Regular Small Letter', 'mp'), 	'0.25'),
		'AUS_LETTER_REGULAR_LARGE_125G'	=> new Aus_Post_Service('AUS_LETTER_REGULAR_LARGE_125G',	__('Regular Large Letter 125g', 'mp'), '0.125'),
		'AUS_LETTER_REGULAR_LARGE_250G' => new Aus_Post_Service('AUS_LETTER_REGULAR_LARGE_250G',	__('Regular Large Letter 250g', 'mp'),	'0.25'),
		'AUS_LETTER_REGULAR_LARGE_500G'	=> new Aus_Post_Service('AUS_LETTER_REGULAR_LARGE_500G',	__('Regular Large Letter 500g', 'mp'),	'0.5'),
		'AUS_LETTER_EXPRESS_SMALL_250G'	=> new Aus_Post_Service('AUS_LETTER_EXPRESS_SMALL_250G',	__('Small Express Letter 250g', 'mp'),	'0.25'),
		'AUS_LETTER_EXPRESS_MEDIUM_500G' => new Aus_Post_Service('AUS_LETTER_EXPRESS_MEDIUM_500G',	__('Medium Express Letter 500g', 'mp'),	'0.5'),
		'AUS_LETTER_EXPRESS_LARGE_500G'	=> new Aus_Post_Service('AUS_LETTER_EXPRESS_LARGE_500G',	__('Large Express Letter 500g', 'mp'), 	'0.5'),
		'AUS_PARCEL_REGULAR'    		=> new Aus_Post_Service('AUS_PARCEL_REGULAR', 				__('Regular Parcel Post', 'mp'),	'22'),
		'AUS_PARCEL_EXPRESS'  			=> new Aus_Post_Service('AUS_PARCEL_EXPRESS', 				__('Express Parcel Post', 'mp'),  	'22'),

		);

	// Get settings for convenience sake
	$this->settings = get_option('mp_settings');
	$this->auspost_settings =& $this->settings['shipping']['auspost'];
	}

  /**
   * Echo anything you want to add to the top of the shipping screen
   */
function before_shipping_form($content) {
		return $content;
	}

  /**
   * Echo anything you want to add to the bottom of the shipping screen
   */
	function after_shipping_form($content) {
		return $content;
  }

  /**
   * Echo a table row with any extra shipping fields you need to add to the shipping checkout form
   */
	function extra_shipping_field($content) {
		return $content;
  }

  /**
   * Use this to process any additional field you may add. Use the $_POST global,
   *  and be sure to save it to both the cookie and usermeta if logged in.
   */
	function process_shipping_form() {

  }

	/**
   * Echo a settings meta box with whatever settings you need for you shipping module.
   *  Form field names should be prefixed with mp[shipping][plugin_name], like "mp[shipping][plugin_name][mysetting]".
   *  You can access saved settings via $settings array.
   */
	function shipping_settings_box($settings) {
		global $mp;

		$this->settings = $settings;
		$this->auspost_settings = $this->settings['shipping']['auspost'];
		$system = $this->settings['shipping']['system']; //Current Unit settings english | metric
		?>

		<div id="mp_ups_rate" class="postbox">
			<h3 class='hndle'><span><?php _e('Australia Post Settings', 'mp'); ?></span></h3>
			<div class="inside">
				<table class="form-table">
					<tbody>
						<tr>
							<th scop="row"><?php _e('Attention', 'mp') ?></th>
							<td>
								<p><?php _e('Australia Post uses your products weight and dimensions (length, width, height or thickness) to calculate shipping.  Please ensure you have your product details entered correctly.  ', 'mp') ?></p>
                                <p><?php _e('For best results enter your largest dimensions as length.', 'mp') ?></p>
								<p><strong><?php _e('If you have not set the weight of a product, then it will be calculated as FREE shipping for your buyer and you, as the seller, will absorb the cost of postage. ', 'mp') ?></strong></p>
								<p><?php _e('Domestic shipping also uses dimensions to calculate shipping.  If you do not enter the dimensions correctly you may get a surprise at the post office!   ', 'mp') ?></p>
								<p><?php _e('Remember to take into account your packing materials when calculating the package weight and dimensions.  Weight is measured in kilograms and dimensions are measured in cm.  ', 'mp') ?></p>
								<p><?php _e('If dimension are not entered a default of 10cm x 10cm x 10cm will be used.', 'mp') ?></p>
							</td>
						</tr>
                        <tr>
							<th scope="row"><?php _e('Australia Post Offered Domestic Services', 'mp') ?></th>
							<td>
								<?php foreach($this->domestic_services as $code => $service) : ?>
								<label>
                                <input type="checkbox" name="mp[shipping][auspost][domestic_services][<?php echo $code; ?>]" value="1" <?php checked($this->auspost_settings['domestic_services'][$code], 1); ?> />&nbsp;<?php echo $service->name; ?>
                                </label><br />
								<?php endforeach; ?>
							</td>
						</tr>
                        <tr>
							<th scope="row"><?php _e('Handling Charge per Domestic Package Shipped', 'mp') ?></th>
							<td>
								<input type="text" name="mp[shipping][auspost][domestic_handling]" value="<?php echo (empty($this->auspost_settings['domestic_handling']) ) ? '0.00' : esc_attr($this->auspost_settings['domestic_handling']); ?>" size="20" maxlength="20" />
							</td>
						</tr>
                     </tbody>
				</table>
			</div>
		</div>
		<?php

  }

  /**
   * Filters posted data from your form. Do anything you need to the $settings['shipping']['plugin_name']
   *  array. Don't forget to return!
   */
	function process_shipping_settings($settings) {
	//handle domestic services checkboxes
	foreach ( $this->domestic_services as $service => $code ) {
			if ( isset($_POST['mp']['shipping']['auspost']['domestic_services'][$service]) ) {
				$settings['shipping']['auspost']['domestic_services'][$service] = 1;
			} else {
				$settings['shipping']['auspost']['domestic_services'][$service] = 0;
			}
		}
    return $settings;
  }

  /**
   * Echo any per-product shipping fields you need to add to the product edit screen shipping metabox
   *
   * @param array $shipping_meta, the contents of the post meta. Use to retrieve any previously saved product meta
   * @param array $settings, access saved settings via $settings array.
   */
	function shipping_metabox($shipping_meta, $settings) {
	global $mp;

		// if weight of product is greater than 22kg then can't use Australia Post
		if ($shipping_meta['weight'] > 22){
			// need warning that Australia Post cannot be used.
			?><div class="mp_checkout_error">Products over 22kg cannot be shipped by Australia Post.  Please choose another shipping option such as pickup or set weight to zero which will calculate free postage and add handling to cover delivery costs.</div>
		<?php }
		echo '<p>';
		?>
		<label><?php _e('Length (cms)', 'mp'); ?>:<br />
		<input type="text" size="6" name="mp_shipping_length" value="<?php echo isset($shipping_meta['length']) ? $shipping_meta['length'] : '0'; ?>" />
		</label>
		</p><p>
		<label><?php _e('Width (cms)', 'mp'); ?>:<br />
		<input type="text" size="6" name="mp_shipping_width" value="<?php echo isset($shipping_meta['width']) ? $shipping_meta['width'] : '0'; ?>" />
		</label>
		</p><p>
		<label><?php _e('Height (cms)', 'mp'); ?>:<br />
		<input type="text" size="6" name="mp_shipping_height" value="<?php echo isset($shipping_meta['height']) ? $shipping_meta['height'] : '0'; ?>" />
		</label>
		<?php
		echo '</p>';

  }

  /**
   * Save any per-product shipping fields from the shipping metabox using update_post_meta
   *
   * @param array $shipping_meta, save anything from the $_POST global
   * return array $shipping_meta
   */
	function save_shipping_metabox($shipping_meta) {
	global $mp;
		$shipping_meta['length'] = (!empty($_POST['mp_shipping_length'])) ? round($_POST['mp_shipping_length'], 2) : 0;
		$shipping_meta['width'] = (!empty($_POST['mp_shipping_width'])) ? round($_POST['mp_shipping_width'], 2) : 0;
		$shipping_meta['height'] = (!empty($_POST['mp_shipping_height'])) ? round($_POST['mp_shipping_height'], 2) : 0;

    return $shipping_meta;
  }

	/**
	*Function to return whether the package is domestic letter sized
	*If the package is a letter the dimensions of the packages length, width and height are put in mid, max, min order to fit the dimensions needed to calculate letter postage rates.
	*return boolean $letter
	*
	**/
	private function is_domestic_letter() {

		$max = '';
		$mid = '';
		$min = '';
		$letter = false;

		//find smallest dimension
		$max_dim = max($this->length, $this->width, $this->height);
		if ($this->weight <= 0.5 ){
			if ($max_dim <= 36){
				if ($max_dim == $this->height){
					$max = $this->height;
					if ($this->width >= $this->length){
						$mid = $this->width;
						$min = $this->length;
					}
					else {
						$mid = $this->length;
						$min = $this->width;
					}
				}
				elseif ($max_dim == $this->width){
					$max = $this->width;
					if ($this->height >= $this->length){
						$mid = $this->height;
						$min = $this->length;
					}
					else {
						$mid = $this->length;
						$min = $this->height;
					}
				}
				else {
					if ($this->height >= $this->width){
						$max = $this->length;
						$mid = $this->height;
						$min = $this->width;
					}
					else {
						$max = $this->length;
						$mid = $this->width;
						$min = $this->height;
					}
				}
				if (($mid <= 26) && ($min <= 2)){
					$letter = true;
					$this->length = $mid;
					$this->width = $max;
					$this->height = $min;
				}
				else {
					$letter = false;
				}
			}
			else {
				$letter = false;
			}
		}
		return $letter;
	}

  /**
		* Use this function to return your calculated price as an integer or float
		*
		* @param int $price, always 0. Modify this and return
		* @param float $total, cart total after any coupons and before tax
		* @param array $cart, the contents of the shopping cart for advanced calculations
		* @param string $address1
		* @param string $address2
		* @param string $city
		* @param string $state, state/province/region
		* @param string $zip, postal code
		* @param string $country, ISO 3166-1 alpha-2 country code
		* @param string $selected_option, if a calculated shipping module, passes the currently selected sub shipping option if set
		*
		* return float $price
		*/
	function calculate_shipping($price, $total, $cart, $address1, $address2, $city, $state, $zip, $country, $selected_option) {
		global $mp;

		if(! $this->crc_ok())
		{
			//Price added to this object
			$this->shipping_options($cart, $address1, $address2, $city, $state, $zip, $country);
		}

		$price = floatval($_SESSION['mp_shipping_info']['shipping_cost']);
		return $price;

	}

	/**
		* For calculated shipping modules, use this method to return an associative array of the sub-options. The key will be what's saved as selected
		*  in the session. Note the shipping parameters won't always be set. If they are, add the prices to the labels for each option.
		*
		* @param array $cart, the contents of the shopping cart for advanced calculations
		* @param string $address1
		* @param string $address2
		* @param string $city
		* @param string $state, state/province/region
		* @param string $zip, postal code
		* @param string $country, ISO 3166-1 alpha-2 country code
		*
		* return array $shipping_options
		*/
	function shipping_options($cart, $address1, $address2, $city, $state, $zip, $country) {
		$shipping_options = array();

		$this->address1 = $address1;
		$this->address2 = $address2;
		$this->city = $city;
		$this->state = $state;
		$this->destination_zip = $zip;
		$this->country = $country;
		$too_heavy = false;

		//set weight, length, width and thickness to zero if not set in the shipping meta
		$shipping_meta['weight'] = (is_numeric($shipping_meta['weight']) ) ? $shipping_meta['weight'] : 0;
		$shipping_meta['length'] = (is_numeric($shipping_meta['length']) ) ? $shipping_meta['length'] : 0;
		$shipping_meta['height'] = (is_numeric($shipping_meta['height']) ) ? $shipping_meta['height'] : 0;
		$shipping_meta['width'] = (is_numeric($shipping_meta['width']) ) ? $shipping_meta['width'] : 0;

		// for each item in the cart using the product_id as the key
		foreach ($cart as $product_id => $variations) {
			// get the shipping meta for that product
			$shipping_meta = get_post_meta($product_id, 'mp_shipping', true);
			// for each product variation using as the key
			foreach($variations as $variation => $product) {
				$qty = $product['quantity'];
				$weight = $shipping_meta['weight'];
				// need to check here whether item is too heavy?
				// add the weight of the product * the qty to the shopping cart weight
				$this->weight += floatval($weight) * $qty;
				$length = $shipping_meta['length'];
				$this->length = max($length, $this->length);
				$height = $shipping_meta['height'];
				$this->height = max($height, $this->height);
				$width = $shipping_meta['width'];
				$this->width += $width * $qty;
			  }
		  }

		//If whole shipment is zero weight then there's nothing to ship. Return Free Shipping
		if($this->weight == 0){ //Nothing to ship
				$_SESSION['mp_shipping_info']['shipping_sub_option'] = __('Free Shipping', 'mp');
				$_SESSION['mp_shipping_info']['shipping_cost'] =  0;
				return array(__('Free Shipping', 'mp') => __('Free Shipping - 0.00', 'mp') );
		}

		// if a single item too heavy for Australia Post.  Sale will not go through.
		if(($this->weight > 22) && (count($cart)==1)){
			return array('error' => '<div class="mp_checkout_error">Too heavy for Australia Post.  Choose pickup if available or contact seller.</div>');

		}
		//Multiple item cart too heavy for Australia Post.  Sale will not go through.
		if(($this->weight > 22) && (count($cart)>1)){
			return array('error' => '<div class="mp_checkout_error">Too heavy for Australia Post.  Please divide your purchase into multiple carts.</div>');
		}

		if (!$country){
			$country = "AU";
			}
		if ($country == "AU"){
		 	//call get domestic list
			$shipping_options = $this->rate_request();
		}

		return $shipping_options;
	}

	/**For uasort below
	*/
	function compare_rates($a, $b){
		if($a['rate'] == $b['rate']) return 0;
		return ($a['rate'] < $b['rate']) ? -1 : 1;
	}

	/**
	* Domestic rate_request - Makes the actual call to Australia Post
	*/
	function rate_request() {
		global $mp;

		$letter = false;
		$shipping_options = array();

		//figure out whether letter or parcel
		$letter = $this->is_domestic_letter();

		if ($letter){
			// change measurements to mm and grams from cm and kg and Australia Post letters are measured by length, width and thickness
			$letter_length = $this->length * 10;
			$letter_width = $this->width* 10;
			$letter_thickness = $this->height* 10;
			$letter_weight = $this->weight * 1000;
			$shipping_options = $this->get_domestic_letter_rates($letter_length, $letter_thickness, $letter_width, $letter_weight);
		}

		else{
			$shipping_options = $this->get_domestic_parcel_rates($this->settings['base_zip'], $this->destination_zip, $this->length, $this->height, $this->width, $this->weight);
		}

		return $shipping_options;

	}

	/* Function to get Australia Post domestic letter rates.  The Australia Post API requires length, width, thickness and weight for letters.
		Values for length, height and thickness should be in the order mid, max, min.  Maximum dimensions allowable are 360mm x 260mm x 20mm.
		Maximum weight is 500g.  Measurements are in mm and grams.
	*/
	  private function get_domestic_letter_rates( $length, $thickness, $width, $weight ){
		global $mp;
		$no_postcode_set = false;

		//gets the shipping options that are set as available?
		$shipping_options = array_filter($this->auspost_settings['domestic_services'], create_function('$val', 'return ($val == 1);'));

		$ch = curl_init();	

		// if $to_postcode is NULL set it to 4000 but return the list of service options without prices?
		if($to_postcode == NULL){
			$to_postcode = 4000;  // you might want to change this to your shop's postcode as a default
			$no_postcode_set = true;
		}
		// set the query params
		$queryParams = array(
			"length" => $length,
			"thickness" => $thickness,
			"width" => $width,
			"weight" => $weight
			);

		// Set the URL for the Domestic Letter Size service
		$postageTypesURL = $this->domestic_letter_services_url . http_build_query($queryParams);

		// Lookup available domestic letter delivery service types
		curl_setopt($ch, CURLOPT_URL, $postageTypesURL);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
		curl_setopt($ch, CURLOPT_HTTPHEADER, array('AUTH-KEY: ' . $this->apikey));
		$rawBody = curl_exec($ch);

		// Check the response; if the body is empty then an error has occurred
		if(!$rawBody){
			//echo "!rawbody";
			die('<div class="mp_checkout_error">Australia Post: ' . curl_error(ch) . '" - Code: ' . curl_errno($ch));
			//die('Error: "' . curl_error($ch) . '" - Code: ' . curl_errno($ch));
		}

		// All good, lets parse the response into a JSON object
		$servicesJSON = json_decode($rawBody, true);

		if (!$servicesJSON){
			//get data
			return array('error' => '<div class="mp_checkout_error">Australia Post: ' . $servicesJSON['error']['errorMessage'] . '</div>');
        }

		//Clear any old price
		unset($_SESSION['mp_shipping_info']['shipping_cost']);

		//Good to go
		//Make SESSION copy with just prices and delivery

		if(!is_array($shipping_options)) $shipping_options = array();
		$mp_shipping_options = $shipping_options;

		foreach($mp_shipping_options as $key => $value){

			// test whether option returned from Australia Post is one of the options allowed by store
			foreach ($servicesJSON[services][service] as $k => $v){
				if ($servicesJSON[services][service][$k][code] == $key){

					//if(array_search($mp_shipping_options[$key], $servicesJSON)){

					//get price
					$price = $servicesJSON[services][service][$k][price];
					$handling = floatval($this->auspost_settings['domestic_handling']) * $this->pkg_count; // Add handling times number of packages.
					$mp_shipping_options[$key] = array('price' => $price, 'handling' => $handling);

					//match it up if there is already a selection
					if (! empty($_SESSION['mp_shipping_info']['shipping_sub_option'])){
						if ($_SESSION['mp_shipping_info']['shipping_sub_option'] == $key){
							$_SESSION['mp_shipping_info']['shipping_cost'] =  $price + $handling;
						}
					}
				break;
				}
				else {
					//remove from options
					unset($mp_shipping_options[$key]);
				}
			}
		}			

		// sort shipping options by cost
		uasort($mp_shipping_options, array($this,'compare_rates') );

		// copy sorted array back into shipping options array
		$shipping_options = array();
		if ($no_postcode_set){
			foreach($mp_shipping_options as $service => $options){
				$shipping_options[$service] = $this->format_shipping_choices($service);
			}
		}
		else {
			foreach($mp_shipping_options as $service => $options){
				$shipping_options[$service] = $this->format_shipping_option($service, $options['price'], $options['handling']);
			}
		}

		//Update the session. Save the currently calculated CRCs
		$_SESSION['mp_shipping_options'] = $mp_shipping_options;
		$_SESSION['mp_cart_crc'] = $this->crc($mp->get_cart_cookie());
		$_SESSION['mp_shipping_crc'] = $this->crc($_SESSION['mp_shipping_info']);

		return $shipping_options;

	  }

	/* Function to get Australia Post domestic parcel rates.  The Australia Post API requires to and from postcodes, length, height, width and weight.
		Maximum weight is 22kg.  Maximum length is 105cm.  Maximum dimensions is 0.25m3.  Australia Post Domestic parcels are charged according to actual weight or cubic weight, whichever is greater.
		See http://auspost.com.au/parcels-mail/size-and-weight-guidelines.html for details.
		Measurements are in cm and kgs.
	*/
	private function get_domestic_parcel_rates($from_postcode, $to_postcode, $length, $height, $width, $weight){
		global $mp;
		$no_postcode_set = false;

		//gets the shipping options that are set as available?
		$shipping_options = array_filter($this->auspost_settings['domestic_services'], create_function('$val', 'return ($val == 1);'));

		$ch = curl_init();	

		// if $to_postcode is NULL set it to 4000 but return the list of service options without prices
		if($to_postcode == NULL){
			$to_postcode = 4000;  // you might want to change this to your shop's postcode as a default
			$no_postcode_set = true;
		}

		// Set the query params
		$queryParams = array(
		  "from_postcode" => $from_postcode,
		  "to_postcode" => $to_postcode,
		  "length" => $length,
		  "width" => $width,
		  "height" => $height,
		  "weight" => $weight
		);

		// Set the URL for the Domestic Parcel Size service
		$postageTypesURL = $this->domestic_parcel_services_url . http_build_query($queryParams);

		// Lookup available domestic parcel delivery service types
		curl_setopt($ch, CURLOPT_URL, $postageTypesURL);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
		curl_setopt($ch, CURLOPT_HTTPHEADER, array('AUTH-KEY: ' . $this->apikey));
		$rawBody = curl_exec($ch);

		// Check the response; if the body is empty then an error has occurred
		if(!$rawBody){
			//echo "!rawbody";
			die('<div class="mp_checkout_error">Australia Post: ' . curl_error(ch) . '" - Code: ' . curl_errno($ch));
			//die('Error: "' . curl_error($ch) . '" - Code: ' . curl_errno($ch));
		}

		// All good, lets parse the response into a JSON object
		$servicesJSON = json_decode($rawBody, true);

		if (!$servicesJSON){
			//get data
			return array('error' => '<div class="mp_checkout_error">Australia Post: ' . $servicesJSON['error']['errorMessage'] . '</div>');
        }

		//Clear any old price
		unset($_SESSION['mp_shipping_info']['shipping_cost']);

		//Good to go
		//Make SESSION copy with just prices and delivery

		if(!is_array($shipping_options)) $shipping_options = array();
		$mp_shipping_options = $shipping_options;

		foreach($mp_shipping_options as $key => $value){

			// test whether option returned from Australia Post is one of the options allowed by store
			foreach ($servicesJSON[services][service] as $k => $v){
				if ($servicesJSON[services][service][$k][code] == $key){

					//if(array_search($mp_shipping_options[$key], $servicesJSON)){

					//get price
					$price = $servicesJSON[services][service][$k][price];
					$handling = floatval($this->auspost_settings['domestic_handling']) * $this->pkg_count; // Add handling times number of packages.
					$mp_shipping_options[$key] = array('price' => $price, 'handling' => $handling);

					//match it up if there is already a selection
					if (! empty($_SESSION['mp_shipping_info']['shipping_sub_option'])){
						if ($_SESSION['mp_shipping_info']['shipping_sub_option'] == $key){
							$_SESSION['mp_shipping_info']['shipping_cost'] =  $price + $handling;
						}
					}
				break;
				}
				else {
					//remove from options
					unset($mp_shipping_options[$key]);
				}
			}
		}			

		// sort shipping options by cost
		uasort($mp_shipping_options, array($this,'compare_rates') );

		// copy sorted array back into shipping options array
		$shipping_options = array();
		if ($no_postcode_set){
			foreach($mp_shipping_options as $service => $options){
				//echo"foreach";
				$shipping_options[$service] = $this->format_shipping_choices($service);
			}
		}
		else {
			foreach($mp_shipping_options as $service => $options){
				//echo"foreach";
				$shipping_options[$service] = $this->format_shipping_option($service, $options['price'], $options['handling']);
			}
		}

		//Update the session. Save the currently calculated CRCs
		$_SESSION['mp_shipping_options'] = $mp_shipping_options;
		$_SESSION['mp_cart_crc'] = $this->crc($mp->get_cart_cookie());
		$_SESSION['mp_shipping_crc'] = $this->crc($_SESSION['mp_shipping_info']);

		//echo"<p>end of parcel rates function</p>";

		return $shipping_options;
	}

	/**
	* Formats a choice for the Shipping options dropdown
	* @param array $shipping_option, a $this->services key
	* @param float $price, the price to display
	*
	* @return string, Formatted string with shipping method name delivery time and price
	*
	*/
	private function format_shipping_option($shipping_option = '', $price = '', $delivery = '', $handling=''){
		global $mp;
		if ( isset($this->services[$shipping_option])){
			$option = $this->services[$shipping_option]->name;
		}
		elseif ( isset($this->intl_services[$shipping_option])){
			$option = $this->intl_services[$shipping_option]->name;
		}

		$price = is_numeric($price) ? $price : 0;
		$handling = is_numeric($handling) ? $handling : 0;
		$total = $price + $handling;

		if ( $mp->get_setting('tax->tax_inclusive') && $mp->get_setting('tax->tax_shipping') ) {
			$total = $mp->shipping_tax_price($total);
		}
		$service = $this->domestic_services[$shipping_option]->name;
		$option .=  sprintf(__(' %1$s - %2$s', 'mp'), $service, $mp->format_currency('', $total) );
		return $option;
	}

	/* function to return shipping choices with no prices if no postcode is provided
	*/
	private function format_shipping_choices($shipping_option = ''){
		global $mp;
		if (isset($this->services[$shipping_option])){
			$option = $this->services[$shipping_option]->name;
		}

		$service = $this->domestic_services[$shipping_option]->name;
		$option .= sprintf(__(' %1$s', 'mp'), $service);
		return $option;
		}	

	/**Used to detect changes in shopping cart between calculations
	* @param (mixed) $item to calculate CRC of
	*
	* @return CRC32 of the serialized item
	*/
	public function crc($item = ''){
		return crc32(serialize($item));
	}

	/**
	* Tests the $_SESSION cart cookie and mp_shipping_info to see if the data changed since last calculated
	* Returns true if the either the crc for cart or shipping info has changed
	*
	* @return boolean true | false
	*/
	private function crc_ok(){
		global $mp;

		//Assume it changed
		$result = false;

		//Check the shipping options to see if we already have a valid shipping price
		if(isset($_SESSION['mp_shipping_options'])){
			//We have a set of prices. Are they still valid?
			//Did the cart change since last calculation
			if ( is_numeric($_SESSION['mp_shipping_info']['shipping_cost'])){

				if($_SESSION['mp_cart_crc'] == $this->crc($mp->get_cart_cookie())){
					//Did the shipping info change
					if($_SESSION['mp_shipping_crc'] == $this->crc($_SESSION['mp_shipping_info'])){
						$result = true;
					}
				}
			}
		}
		return $result;
	}

}
// End MP_Shipping_AusPost

if(! class_exists('Aus_Post_Service') ):
class Aus_Post_Service
{
	public $code;
	public $name;
	public $max_weight;

	function __construct($code, $name, $max_weight = null)
	{
		$this->code = $code;
		$this->name = $name;
		$this->max_weight = $max_weight;

	}
}
endif;

//register plugin as calculated. Only in Australia
$settings = get_option('mp_settings');
if ( in_array( $settings['base_country'], array('AU') ) ) {
	mp_register_shipping_plugin( 'MP_Shipping_AusPost', 'auspost', __('Australia Post', 'mp'), true );
}

Nat.