#native_company# #native_desc#
#native_cta#

aaronUPS – XML UPS Rates and Services Class

By Aaron C. Meadows
on June 22, 2005

Version: 0.4

Type: Class

Category: Shopping Carts

License: GNU General Public License

Description: Hi. This is the first time I’ve worked with XML, but I’ve done a fair amount of shopping cart work. I was looking for a way to get UPS rates for various packages in an order placed online, and couldn’t find any that supported international orders….. So I wrote one. Hope you all like it. It is basicly very usable, (but read the disclaimer!) it just lacks a list of country codes, etc. I’ll update it later and add those, but who knows if I will get around to it, so I might as well upload version .1 now.

Enjoy!

<?
////////////////////////////////////////////////////////////////////////////////
//
// AaronUPS Version 0.4: (PHP/cURL-XML)
// [email protected]
//
// UPS Rate and Rate Shopping Script.
//
// This code is released without any warranty, even the implied warranty of 
// fitness for a particular purpose.  So, USE AT YOUR OWN RISK.
// 
// To use it, you MUST compile PHP with cURL (http://curl.haxx.se) support,
// and cURL must be compiled with SSL support.  
//
// I will try to answer any questions you have when using it, addressed to 
// [email protected].
//
//
// To use these tools, you have to register at UPS.  When I did it, it was here:
//
//   https://www.ups.com/servlet/registration?loc=en_US_EC&returnto=http://www.ec.ups.com/ecommerce/gettools/gtools_intro.html
//
// If this doesn't seem to work, just go to UPS.com and start looking for online tools, 
// and how to register for your developers key and get an XML key, etc..
//
//
// Based on the SurePay script by [email protected], version .07
// Find it at http://www.kreisler.org/surepay/index.php
//
////////////////////////////////////////////////////////////////////////////////

###########################################################################################
## Sample Code (simple) using this script
###########################################################################################
#
# This script should be pretty self explainitory.  Copy this section of code
# into another file in the same directory as aaronups.php (this file) to test
# it out.  be sure you fill in your $AccessLicenseNumber on line 204 of this
# file, as well as your UserID and Password on the following lines (205,206)
#

$SAMPLE=<<<__HTML__

<?
	// include the UPS Script.
	require_once('aaronups.php');


	// Create an opject of type ups
	$MyUPS=new ups();

	// set shipper info
	$MyUPS->SetShipper('Springfield','MO','65807','US');
	
	// uncomment this if you ship from somewhere other than the address you 
	// registered your key to.
	//$MyUPS->SetShipFrom('Springfield','MO','65807','US');  

	// Set the Ship To address.  This will probably be gleaned from the user
	$MyUPS->SetShipTo('Cassville','MO','65625','US',1);
	
	// Add a package.  Note that I saved the package number for use in the following functions
	$pkg=$MyUPS->AddPackage('02','First Package',33);
	$MyUPS->SetPackageValue($pkg,87.53);	// Adding an insured value
	$MyUPS->SetPackageSize($pkg,108,2,2);	// Adding a size to the box

	// adding another package (with an insured value)
	$pkg=$MyUPS->AddPackage('02','Second Package',113,25.50);
	
	// Request the rates this shipping setup
	$UPSError=$MyUPS->ModeRateShop();
	
	// limit the shipping services I want displayed 
	//    (these are the service codes from ups. NOTE: you can use either integers (12) or strings ('12') )
	$MyUPS->SetRateListLimit('03',12,'02');

	// get the list of rates I specified, adding 1.50 to each one for handling
	$selopt=$MyUPS->GetRateListShort(1.50);

	// set the services list back to all of them
	$MyUPS->SetRateListLimit();

	// I debuged here to see that everything was happy =)
//	$MyUPS->Debug();


	// here I'm getting the cost of service '03' (ground).  usually this would be on the next page
	// after the user selected something from the options of ModeRateShop
	$MyRate=$MyUPS->ModeGetRate('03');


// used my values I got back.
echo <<<__FOO__

<select name="foo">
	$selopt
</select>


<br><br>
Cost is $MyRate.
__FOO__;

?>

__HTML__;
#
#
###########################################################################################

#######################################################################################################################
## Function List
#######################################################################################################################
#
# Constructors - Look here for some default arrays, also, if UPS adds new service options you need to change one of them
#   ups()
#
# These functions probably won't be used by most people.  You will want to set the variables staticly for most cases
#   SetPostURL($url)
#   SetAccountInfo($ALN,$UID,$Pass)
#   SetPickupType($Code)
#   SetCustomerContext($context)
#   SetShipper($city,$state,$zip,$country)
#   SetShipFrom($city,$state,$zip,$country)
#   SetDefaultService($default)
#
# Set the ship to address (isres is to set wether or not you are shipping to a residential area)
#   SetShipTo($city,$state,$zip,$country,$isres=0)
#
# Modes of operation 
#   ModeRateShop()			// Process the current shipping options and get a list of rates (use GetRateListShort($handling); to get them)
#   ModeGetRate($ServiceCode)		// returns the price for the selected rate.  Usually you want to use the above function to find the method
#
# Get Data Functions
#   GetServiceName($service)		// Takes a 2 character service identifier and returns a string name for that service.
#
# Rate List Functions
#	GetRateListShort($handling=0,$sort='',$type='',$display='')
#   				// returns information for a selection of the service to ship via
#					// $handling is an dollar amount to add to all shipping costs to adjust for packing, etc	
#					// $sort is one of the following:
#						PRICE {default} - sorts the service options by the price, ascending
#						SERVICE - sorts the service options by the name of the service asc
#					// $type is one of the following:
#						OPTION {default} - returns option rows for use in a select box (you provide the select statement)
#						RADIO - returns a group of radio buttons.  They are named UPSShipService, and are encapsulated in div's of class "UPSRadio"
#					// $display is one of the following:
#						TCOST_SERVICE {default} - The services will show up as "PRICE - SERVICE NAME"
#						BASEDIFF - The first service will show up as above, the rest will show "upgrade to SERVICE NAME for PRICEDIFF"
#
#   SetRateListLimit(/*...*/)		// sets the rates that will be returned if they are available.  empty sets all avaliable (default)
#
# Package Functions
#   AddPackage($PackageType,$Description,$Weight,$Value=0.0,$WeightUnit='LBS',$CurrencyUnit='USD')
#   SetPackageSize($PackageNum,$Length,$Width,$Height,$Unit='IN') // use for odd shaped packages.  $PackageNum is returned by AddPackage
#   SetPackageValue($PackageNum,$Value,$CurrencyUnit='USD') // use for insured packages (or the options in AddPackage
#
# Error Functions
#   GetErrorSeverity() // Hard for a real problem, Transient(?) if you need to retry, Warning if there is just something weird you should know
#   GetErrorCode()	  // this is the error code as defined by UPS.  Ranges [01xxxx - XML Error],[02xxxx - Architecture error],[11xxxx -Rate & Service specific]
#   GetErrorDescription() // Description of the error
#   GetErrorRetry()    // seconds to wait before you retry.  only set if it's a 'Transient'(?) error
#
# YOU DON'T NEED TO RUN THESE AND WILL PROBABLY CAUSE YOURSELF HEADACHES IF YOU DO
#   CreateRequest()	  // Makes the XML request
#   XMLParser($simple) // parses the XML response into a useful format (array)
#   Process()				// sends the request to UPS, parses the response into a useuful format (condensed array)
#
#
# These functions show you a bunch of stuff you probably don't want to see, but feel free to look.  There is alot of data passed around that might be handy,
# espcially if you want to write your own Rate List Function.
#   Debug()
#   Debug2()
#
# This function is not really in the class, it's at the end of the file.  I wrote it to display arrays, like $HTTP_POST_VARS while debugging. 
# It recursively calls itself for nested arrays, and staticly numbers the arrays.  It is only used in the two Debug functions of UPS, and can safely
# be deleted to clear namespace, if you don't plan on running the Debug functions.
#   buildarray($array,$arraylabel)
#
#######################################################################################################################


class ups
{



//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
///// VARIABLE DATA     //////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////

##########################################################################
###### YOU MAY WISH TO SET THESE STATICLY ################################
##########################################################################
	
	// URL to send request to
	var $postURL='https://www.ups.com/ups.app/xml/Rate';

	// Access Passwords
	var $AccessLicenseNumber='';
	var $UserId='';
	var $Password='';

	// Pickup Service You have
	var $UPSPickupTypeCode='01';

	// CustomerContext can contain XML you want Posted Back
	var $CustomerContext='AaronUPS Version 0.2: (PHP/cURL-XML)';


	// Address of Shipper.  should match info givin to ups
	var $ShipperCity='Rogersville';
	var $ShipperState='MO';
	var $ShipperPostalCode='65742';
	var $ShipperCountry='US';

	// Address package is shipped from, use if different than above
	var $ShipFromCity='Springfield';
	var $ShipFromState='MO';
	var $ShipFromPostalCode='65804';
	var $ShipFromCountry='US';

	// Default Service
	var $DefaultService=3; // 3=Ground

###############################################################################################
## These variables are usually set with functions, so you probably don't want to edit them.
###############################################################################################

		
	// UPS Service Data Options
	var $UPSRequestAction;
	var $UPSRequestOption;
	var $UPSServiceCode;

	// Address that is recieving the package 
	var $ShipToCity;
	var $ShipToState;
	var $ShipToPostalCode;
	var $ShipToCountry;
	var $ShipToResidential='';

	// used if you don't use packages (NOT IMPLEMENTED - Use Packages)
	var $PackageWeight;
	var $UPSPackageType;

	// Arrays to hold various default data
	var $ARRAY_PickupTypes;
	var $ARRAY_ServiceCodes;
	var $ARRAY_PackageTypes;
	var $ARRAY_Packages;

	// cURL return info.  Interesting for debug.
	var $curl_array;

	// Variables to hold the communication with UPS.  Saved for debug.
	var $request;
	var $response;

###############################################################################################






#################################################################################################
## Constructor Function
#################################################################################################

	function ups()
	{
		
		if(empty($this->AccessLicenseNumber))
		{
			echo "<h1>AccessLicenseNumber is empty.  You need to put your access license number from UPS in this variable.</h1>";
		}
		if(empty($this->UserId))
		{
			echo "<h1>UserId is empty.  You need to put your user id from UPS in this variable.</h1>";
		}
		if(empty($this->Password))
		{
			echo "<h1>Password is empty.  You need to put your password from UPS in this variable.</h1>";
		}

		// These are the pickup types from UPS at the time of this writing.  Most businesses will
		// be '01', most individuals will be '03'.  
		$this->ARRAY_PickupTypes=array(
						 1 => array( '01', 'Daily Pickup'	),
						 3 => array( '03', 'Customer Counter'	),
						 6 => array( '06', 'One Time Pickup'	),
						 7 => array( '07', 'On Call Air'	),
						19 => array( '19', 'Letter Center'	),
						20 => array( '20', 'Air Service Center'	)
					);


		// This is the service Type that you are shipping with.  
		// If UPS starts offering other services (and you get empty drop boxes) fill them in here
		// Make sure the array index matches the service code
		// the true value at the end tells the Rate List Functions to return this service if they
		// get it in a rate shop list.  these values can be adjusted by using the function
		// SetRateListLimit(/*...*/)
		$this->ARRAY_ServiceCodes=array(
						 1 => array( '01', 'Next Day Air'		,true),
						 2 => array( '02', '2nd Day Air'		,true),
						 3 => array( '03', 'Ground'			,true),
						 7 => array( '07', 'Worldwide Express'		,true),
						 8 => array( '08', 'Worldwide Expendited'	,true),
						11 => array( '11', 'Standard'			,true),
						12 => array( '12', '3-Day Select'		,true),
						13 => array( '13', 'Next Day Air Saver'		,true),
						14 => array( '14', 'Next Day Air Early AM'	,true),
						54 => array( '54', 'Worldwide Express Plus'	,true),
						59 => array( '59', '2nd Day Air AM'		,true),
						65 => array( '65', 'Express Saver'		,true)
					);


					


		// Array of Package Types.  Usually '02' Package is used, but the rest are useful
		$this->ARRAY_PackageTypes=array(
						 0 => array( '00', 'Unknown'		),
						 1 => array( '01', 'UPS letter' 	),
						 2 => array( '02', 'Package'		),
						 3 => array( '03', 'UPS Tube'		),
						 4 => array( '04', 'UPS Pak'		),
						21 => array( '21', 'UPS Express Box'	),
						24 => array( '24', 'UPS 25KG Box'	),
						25 => array( '25', 'UPS 10KG Box'	)
					);

	} // end ups()
	
##############################################################################################################





##############################################################################################################
## Set Functions you probably won't need.  Usually these things are set staticly
##############################################################################################################

	// URL to send request to
	function SetPostURL($url)
	{
		$this->postURL=$url;
	}

	function SetAccountInfo($ALN,$UID,$Pass)
	{
		$this->AccessLicenseNumber=$ALN;
		$this->UserId=$UID;
		$this->Password=$Pass;
	}

	function SetPickupType($Code)
	{
		$this->UPSPickupTypeCode=$Code;	
	}

	// CustomerContext can contain XML you want Posted Back
	function SetCustomerContext($context)
	{
		$this->CustomerContext=$context;
	}
	
##############################################################################################################





##############################################################################################################
## Address Functions
##############################################################################################################

	function SetShipper($city,$state,$zip,$country)
	{

		$this->ShipperCity=$city;
		$this->ShipperState=$state;
		$this->ShipperPostalCode=$zip;
		$this->ShipperCountry=$country;
	}


	function SetShipFrom($city,$state,$zip,$country)
	{

		$this->ShipFromCity=$city;
		$this->ShipFromState=$state;
		$this->ShipFromPostalCode=$zip;
		$this->ShipFromCountry=$country;
	}


	function SetShipTo($city,$state,$zip,$country,$isres=0)
	{

		$this->ShipToCity=$city;
		$this->ShipToState=$state;
		$this->ShipToPostalCode=$zip;
		$this->ShipToCountry=$country;
		if($isres)
		{
			$this->ShipToResidential='<ResidentialAddress/>';
		}
	}

	function SetDefaultService($default)
	{
		$this->DefaultService=$default;	// set the $DefaultSerice Variable
		return 0;			// return no errors
	}


##############################################################################################################




##############################################################################################################
## Mode Functions
##############################################################################################################

	// get an <option> block list of all the rates for the set packages.
	function ModeRateShop()
	{
			$this->UPSRequestAction='Rate';
			$this->UPSRequestOption='shop';

			// Create the request and then send and process it.
			$this->CreateRequest();
			$this->Process();

			return $this->GetErrorCode();
	}



	// get the cost of the selected rate
	function ModeGetRate($ServiceCode)
	{
			// set the action from ups to Rate and rate.
			// this will get us one rate, the one we chose
			$this->UPSRequestAction='Rate';	
			$this->UPSRequestOption='rate';	
			$this->UPSServiceCode=$ServiceCode;
			
			// Create the request and then send and process it.
			$this->CreateRequest();
			$this->Process();

			// turn the details into a total cost for shipping and return it (float)
			// if the request was not successful, return 0;  (shipping is never free, hopefully)
			if($this->ResponseDistilled['Success'])
			{
						$retval=$this->ResponseDistilled["RateOption_0"]['TotalCost'];
			}
			else
			{
				$retval=0;
			}

			return $retval;
	}
	
##############################################################################################################


##############################################################################################################
## Get Data Functions
##############################################################################################################

	function GetServiceName($service)
	{
		return $this->ARRAY_ServiceCodes[intval($service)][1];
	}

##############################################################################################################


##############################################################################################################
## Rate List Functions 
##############################################################################################################

	function GetRateListShort($handling=0,$sort='',$type='',$display='')
	{
			// turn the details into <option> blocks and return them.
			// if the request was not successful, return 0;
			if($this->ResponseDistilled['Success'])
			{
				for($i=0;$i<$this->ResponseDistilled['RateOptions'];$i++)
				{
					$service=$this->ResponseDistilled["RateOption_$i"]['Service'];
					$serviceType=$this->ResponseDistilled["RateOption_$i"]['ServiceType'];
					$totalCost=$this->ResponseDistilled["RateOption_$i"]['TotalCost']+$handling;
						
					if($this->ARRAY_ServiceCodes[intval($service)][2])
					{
						switch($display)
						{
							default:
							case 'TCOST_SERVICE':
								$tc='$'.number_format($totalCost,2);
								$disp="{$tc} - {$serviceType}";
								break;

							case 'BASEDIFF':
								
								if(!empty($base))
								{
									$cost=($totalCost=$this->ResponseDistilled["RateOption_$i"]['TotalCost']+$handling)-$base;
									$tc='$'.number_format($cost,2);
									$disp="upgrade to {$serviceType} for {$tc}";
								}
								else
								{

									$base=(empty($base))?($totalCost):($base);
									$tc='$'.number_format($totalCost,2);
									$disp="{$tc} - {$serviceType}";
								}
								break;
						}
						switch($type)
						{
							default:
							case 'OPTION':
								$sel=(intval($service)==$this->DefaultService)?('SELECTED'):('');
								switch($sort)
								{
									default:
									case 'PRICE':
										$retval[($totalCost*100)]=<<<__HTML__
<option value='$service' $sel>$disp</option>

__HTML__;
										break;

									case 'SERVICE':
										$retval["$service"]=<<<__HTML__
<option value='$service' $sel>$disp</option>

__HTML__;
										break;
								}
								break;
								
							case 'RADIO':
								$sel=(intval($service)==$this->DefaultService)?('CHECKED'):('');
								switch($sort)
								{
									default:
									case 'PRICE':
										$retval[($totalCost*100)]=<<<__HTML__
<div class="UPSRadio"><input type="radio" name="UPSShipService" value='$service' $sel>$disp</div>

__HTML__;
										break;

									case 'SERVICE':
										$retval["$service"]=<<<__HTML__
<div class="UPSRadio"><input type="radio" name="UPSShipService" value='$service' $sel>$disp</div>

__HTML__;
										break;
								}
								break;
						}
					}
				}
						ksort($retval);
						$retval=@join(' ',$retval);
			}
			else
			{
				$retval=0;
			}

			return $retval;
	}


	function SetRateListLimit(/*...*/)
	{
		// If arguments were passed, it means we are limiting the options
		if(func_num_args())
		{
			$args=func_get_args();			// get service codes to turn on
			$args=array_map("intval",$args);	// make sure they are all integers
			reset($args);				// reset the array (just to be sure)
			reset($this->ARRAY_ServiceCodes);	// reset the array (just to be sure)
			
			// Turn all services off
			while(list($key,$val) = each($this->ARRAY_ServiceCodes))
			{
				$this->ARRAY_ServiceCodes[$key][2]=false;	// turn each service off
			}
			
			reset($this->ARRAY_ServiceCodes);	// reset the array (just to be sure)
			// turn on select servcies
			while(list($key,$val)=each($args))
			{
				$this->ARRAY_ServiceCodes[$val][2]=true;	// turn select services on
			}
		}
		else	// otherwise, make all services available
		{
			reset($this->ARRAY_ServiceCodes);	// reset the array (just to be sure)
			while(list($key,$val) = each($this->ARRAY_ServiceCodes))
			{
				$this->ARRAY_ServiceCodes[$key][2]=true;	// turn each service on
			}
			
			reset($this->ARRAY_ServiceCodes);	// reset the array (just to be kind)
		}
		return func_num_args();  // just seems the right thing to do...
	}
	
##############################################################################################################

	
##############################################################################################################
## Package Handling Functions
##############################################################################################################

	function AddPackage($PackageType,$Description,$Weight,$Value=0.0,$WeightUnit='LBS',$CurrencyUnit='USD')
	{
		$this->ARRAY_Packages[]=array($PackageType,$Description,'',$Weight,$WeightUnit,$Value,$CurrencyUnit);
		return (count($this->ARRAY_Packages));
	}

	function SetPackageSize($PackageNum,$Length,$Width,$Height,$Unit='IN')
	{
		$this->ARRAY_Packages[$PackageNum-1][2]=array($Unit,$Length,$Width,$Height);
		return $PackageNum;
	}
	
	function SetPackageValue($PackageNum,$Value,$CurrencyUnit='USD')
	{
		$this->ARRAY_Packages[$PackageNum-1][5]=$Value;
		$this->ARRAY_Packages[$PackageNum-1][6]=$CurrencyUnit;
		return $PackageNum;
	}

##############################################################################################################




######################################################################################
## Error Functions
######################################################################################

	function GetErrorSeverity()
	{
		$retval=0;
		if(!($this->ResponseDistilled['Success']))
		{
			$retval=$this->ResponseDistilled['Error']['Description'];
		}
		return $retval;
	}

	function GetErrorCode()
	{
		$retval=0;
		if(!($this->ResponseDistilled['Success']))
		{
			$retval=$this->ResponseDistilled['Error']['Code'];
		}
		return $retval;
	}
	
	function GetErrorDescription()
	{
		$retval=0;
		if(!($this->ResponseDistilled['Success']))
		{
			$retval=$this->ResponseDistilled['Error']['Description'];
		}
		return $retval;
	}

	function GetErrorRetry()
	{
		$retval=0;
		if(!($this->ResponseDistilled['Success']))
		{
			$retval=$this->ResponseDistilled['Error']['MinimumRetrySeconds'];
		}
		return $retval;
	}

######################################################################################



#################################################################################################
## CreateRequest - assemples the XML request
#################################################################################################

	function CreateRequest()
	{
		$this->Request='';

		// CREATE SHIPFROM ADDRESS BLOCK
		$ShipFromAddress='';
		if(!empty($this->ShipFromCity) || !empty($this->ShipFromState) || !empty($this->ShipFromPostalCode) || !empty($this->ShipFromCountry))
		{
			$ShipFromAddress=<<<__SHIPFROMADDRESS__

    <ShipFrom>
      <Address>
        <City>$this->ShipFromCity</City>
        <StateProvinceCode>$this->ShipFromState</StateProvinceCode>
        <PostalCode>$this->ShipFromPostalCode</PostalCode>
        <CountryCode>$this->ShipFromCountry</CountryCode>
      </Address>
    </ShipFrom>
			 
__SHIPFROMADDRESS__;

		}

		// CREATE PACKAGELIST BLOCK
		$PackageList='';
		reset($this->ARRAY_Packages);
		while(list($key,$val)=each($this->ARRAY_Packages))
		{
			$Dimensions='';
			$InSure='';
			$pcode=$val[0];
			
			if(!empty($val[2]))
			{
				list($c,$l,$w,$h)=$val[2];

				$Dimensions=<<<__DIM__
    <Dimensions>
      <UnitOfMeasurement>
        <Code>$c</Code>
      </UnitOfMeasurement>
      <Length>$l</Length>
      <Width>$w</Width>
      <Height>$h</Height>
    </Dimensions>
__DIM__;
			}

			if(!empty($val[5]))
			{
				$InSure=<<<__INS__
    <PackageServiceOptions>
      <InsuredValue>
        <CurrencyCode>$val[6]</CurrencyCode>
        <MonetaryValue>$val[5]</MonetaryValue>
      </InsuredValue>
    </PackageServiceOptions>
__INS__;
				
			}

			$pdesc=$this->ARRAY_PackageTypes[intval($val[0])][1];

			$PackageList.=<<<__PACKAGE__
  <Package>
    <PackagingType>
      <Code>$val[0]</Code>
      <Description>$pdesc</Description>
    </PackagingType>
    <Description>$val[1]</Description>
$Dimensions
    <PackageWeight>
      <Weight>$val[3]</Weight>
      <UnitOfMeasurement>
        <Code>$val[4]</Code>
      </UnitOfMeasurement>
    </PackageWeight>
$InSure
  </Package>

__PACKAGE__;

		}


		// write code for this to be filled in..
		$ShipmentWeight='';



		// CREATE REQUEST BLOCK
		$this->request=<<<__REQUEST__

<?xml version="1.0"?>
<AccessRequest xml:lang="en-US">
  <AccessLicenseNumber>$this->AccessLicenseNumber</AccessLicenseNumber>
  <UserId>$this->UserId</UserId>
  <Password>$this->Password</Password>
</AccessRequest>

<?xml version="1.0"?>
<RatingServiceSelectionRequest xml:lang="en-US">
  <Request>
   <TransactionReference>
    <CustomerContext>$this->CustomerContext</CustomerContext>
    <XpciVersion>1.0001</XpciVersion>
   </TransactionReference>
   <RequestAction>$this->UPSRequestAction</RequestAction>
   <RequestOption>$this->UPSRequestOption</RequestOption>
  </Request>

  <PickupType>
   <Code>$this->UPSPickupTypeCode</Code>
  </PickupType>

  <Shipment>
   <Shipper>
    <Address>
      <City>$this->ShipperCity</City>
      <StateProvinceCode>$this->ShipperState</StateProvinceCode>
      <PostalCode>$this->ShipperPostalCode</PostalCode>
      <CountryCode>$this->ShipperCountry</CountryCode>
    </Address>
   </Shipper>

   <ShipTo>
    <Address>
      <City>$this->ShipToCity</City>
      <StateProvinceCode>$this->ShipToState</StateProvinceCode>
      <PostalCode>$this->ShipToPostalCode</PostalCode>
      <CountryCode>$this->ShipToCountry</CountryCode>
      $this->ShipToResidential
    </Address>
   </ShipTo>
  
   $ShipFromAddress

   <Service>
    <Code>$this->UPSServiceCode</Code>
   </Service>

  $ShipmentWeight

  $PackageList

  </Shipment>
</RatingServiceSelectionRequest>


__REQUEST__;


	} // END CreateRequest()




###############################################################################################
## XMLParser to put all the values from response into an array
###############################################################################################

	function XMLParser($simple) 
	{
		$p = xml_parser_create();
		xml_parser_set_option($p,XML_OPTION_CASE_FOLDING,0);
		xml_parser_set_option($p,XML_OPTION_SKIP_WHITE,1);
		xml_parse_into_struct($p,$simple,$vals,$index);
		xml_parser_free($p);

		return $vals;
	}


###############################################################################################
## Process the current settings
###############################################################################################


	function Process()
	{
		// clear out our variables
		unset($this->ResponseDistilled);
		$this->Response='';

		//******************************************************************************
		// INITIALIZE cURL SESSION AND SET OPTIONS.  SEE PHP MANUAL FOR MORE DETAILS.
		// http://www.php.net/manual/en/ref.curl.php
		//******************************************************************************

		// INITIALIZE 

		$ch = curl_init ();

		// TELL cURL WHERE TO POST THE REQUEST.  UNCOMMENT THE SECOND URL TO SEND A LIVE POST.

		curl_setopt ($ch, CURLOPT_URL, $this->postURL);
		//curl_setopt ($ch, CURLOPT_URL, "https://xml.surepay.com");

		// TELL cURL TO DO A REGULAR HTTP POST.

		curl_setopt ($ch, CURLOPT_POST, 1);

		// PASS THE REQUEST STRING THAT WE BUILD ABOVE

		curl_setopt ($ch, CURLOPT_POSTFIELDS, $this->request);

		// TELL cURL TO USE strlen()  TO GET THE DATA SIZE.

		curl_setopt ($ch, CURLOPT_POSTFIELDSIZE, 0);

		// TELL cURL WHEN TO TIME OUT 
		//IF YOU'RE TIMING OUT BEFORE GETTING A RESPONSE FROM SUREPAY, INCREASE THIS NUMBER 

		curl_setopt ($ch, CURLOPT_TIMEOUT, 360); 
						  
		// TELL cURL TO INCLUDE THE HEADER IN THE OUTPUT 

		curl_setopt ($ch, CURLOPT_HEADER, 0);

		// TELL cURL TO USE SSL VERSION 3.

		curl_setopt ($ch, CURLOPT_SSLVERSION, 3);
		 
		// TRANSFER THE SUREPAY RESPONSE INTO A VARIABLE.

		curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1);


		//******************************************************************************
		// EXECUTE THE REQUEST
		//******************************************************************************

		$this->result = curl_exec ($ch);

		// get stats for later use in debug, if nessesary.
		$this->curl_array = curl_getinfo($ch);

		// close curl connection
		curl_close ($ch);
		//******************************************************************************
		// PARSE RESPONSE
		//******************************************************************************



		// CALL THE XML PARSING FUNCTION TO CREATE ARRAY OF RESULT

		$attributes = $this->XMLParser($this->result);


		$ShipRate=0;
		$ShipPackage=0;
		$MaxPackage=0;


		// Setup Some defaults
		$this->ResponseDistilled['Success']=0;
		$this->ResponseDistilled['Error']['Code']=0;


		reset($attributes);
		while (list ($key, $val) = each ($attributes)) 
		{

			switch($val['tag'])
			{
				case 'ResponseStatusCode':
					$this->ResponseDistilled['Success']=$val['value'];
					break;

				case 'Error':
					while((list($key,$val)=each($attributes)) && ($val['tag']!='Error'))
					{
						if($val['tag']=='ErrorSeverity')
						{
							$this->ResponseDistilled['Error']['Severity']=$val['value'];
						}

						if($val['tag']=='ErrorCode')
						{
							$this->ResponseDistilled['Error']['Code']=$val['value'];
						}
						
						if($val['tag']=='ErrorDescription')
						{
							$this->ResponseDistilled['Error']['Description']=$val['value'];
						}
						
						if($val['tag']=='MinimumRetrySeconds')
						{
							$this->ResponseDistilled['Error']['MinimumRetrySeconds']=$val['value'];
						}
					
					}
					break;
					

				case 'RatedShipment':
					while((list($key,$val)=each($attributes)) && ($val['tag']!='RatedShipment'))
					{
						switch($val['tag'])
						{
							case 'Service':
								while((list($key,$val)=each($attributes)) && ($val['tag']!='Service'))
								{
									if($val['tag']=='Code')
									{
										$this->ResponseDistilled["RateOption_$ShipRate"]['Service']=$val['value'];
										$this->ResponseDistilled["RateOption_$ShipRate"]['ServiceType']=$this->ARRAY_ServiceCodes[intval($val['value'])][1];
									}
								}
								break;

							case 'BillingWeight':
								while((list($key,$val)=each($attributes)) && ($val['tag']!='BillingWeight'))
								{
									if($val['tag']=='Code')
									{
										$this->ResponseDistilled["RateOption_$ShipRate"]['Unit']=$val['value'];
									}
									if($val['tag']=='Weight')
									{
										$this->ResponseDistilled["RateOption_$ShipRate"]['Weight']=$val['value'];
									}
								}
								break;

							case 'TransportationCharges':
								while((list($key,$val)=each($attributes)) && ($val['tag']!='TransportationCharges'))
								{
									if($val['tag']=='CurrencyCode')
									{
										$this->ResponseDistilled["RateOption_$ShipRate"]['Currency']=$val['value'];
									}
									if($val['tag']=='MonetaryValue')
									{
										$this->ResponseDistilled["RateOption_$ShipRate"]['TransportCost']=$val['value'];
									}
								}
								break;

							case 'ServiceOptionsCharges':
								while((list($key,$val)=each($attributes)) && ($val['tag']!='ServiceOptionsCharges'))
								{
									if($val['tag']=='CurrencyCode')
									{
										$this->ResponseDistilled["RateOption_$ShipRate"]['Currency']=$val['value'];
									}
									if($val['tag']=='MonetaryValue')
									{
										$this->ResponseDistilled["RateOption_$ShipRate"]['ServiceCost']=$val['value'];
									}
								}
								break;

							case 'TotalCharges':
								while((list($key,$val)=each($attributes)) && ($val['tag']!='TotalCharges'))
								{
									if($val['tag']=='CurrencyCode')
									{
										$this->ResponseDistilled["RateOption_$ShipRate"]['Currency']=$val['value'];
									}
									if($val['tag']=='MonetaryValue')
									{
										$this->ResponseDistilled["RateOption_$ShipRate"]['TotalCost']=$val['value'];
									}
								}
								break;
								
							case 'GuaranteedDaysToDelivery':
								$this->ResponseDistilled["RateOption_$ShipRate"]['Days']=$val['value'];
								break;
								
							case 'ScheduledDeliveryTime':
								$this->ResponseDistilled["RateOption_$ShipRate"]['DeliveryTime']=$val['value'];
								break;

							case 'RatedPackage':
								while((list($key,$val)=each($attributes)) && ($val['tag']!='RatedPackage'))
								{
									switch($val['tag'])
									{

										case 'BillingWeight':
											while((list($key,$val)=each($attributes)) && ($val['tag']!='BillingWeight'))
											{
												if($val['tag']=='Code')
												{
													$this->ResponseDistilled["RateOption_$ShipRate"]["Package_$ShipPackage"]['Unit']=$val['value'];
												}
												if($val['tag']=='Weight')
												{
													$this->ResponseDistilled["RateOption_$ShipRate"]["Package_$ShipPackage"]['Weight']=$val['value'];
												}
											}
											break;

										case 'TransportationCharges':
											while((list($key,$val)=each($attributes)) && ($val['tag']!='TransportationCharges'))
											{
												if($val['tag']=='CurrencyCode')
												{
													$this->ResponseDistilled["RateOption_$ShipRate"]["Package_$ShipPackage"]['Currency']=$val['value'];
												}
												if($val['tag']=='MonetaryValue')
												{
													$this->ResponseDistilled["RateOption_$ShipRate"]["Package_$ShipPackage"]['TransportCost']=$val['value'];
												}
											}
											break;

										case 'ServiceOptionsCharges':
											while((list($key,$val)=each($attributes)) && ($val['tag']!='ServiceOptionsCharges'))
											{
												if($val['tag']=='CurrencyCode')
												{
													$this->ResponseDistilled["RateOption_$ShipRate"]["Package_$ShipPackage"]['Currency']=$val['value'];
												}
												if($val['tag']=='MonetaryValue')
												{
													$this->ResponseDistilled["RateOption_$ShipRate"]["Package_$ShipPackage"]['ServiceCost']=$val['value'];
												}
											}
											break;

										case 'TotalCharges':
											while((list($key,$val)=each($attributes)) && ($val['tag']!='TotalCharges'))
											{
												if($val['tag']=='CurrencyCode')
												{
													$this->ResponseDistilled["RateOption_$ShipRate"]["Package_$ShipPackage"]['Currency']=$val['value'];
												}
												if($val['tag']=='MonetaryValue')
												{
													$this->ResponseDistilled["RateOption_$ShipRate"]["Package_$ShipPackage"]['TotalCost']=$val['value'];
												}
											}
											break;
									}
								}
								$ShipPackage++;
								break;
								
						}
					}
					$ShipRate++;
					if($ShipPackage>$MaxPackage)
					{
						$MaxPackage=$ShipPackage;
					}
					$ShipPackage=0;
					break;


			}
			
		}

		$this->ResponseDistilled['RateOptions']=$ShipRate;
		$this->ResponseDistilled['Packages']=$MaxPackage;



	} // end process();






###################################################################################################
## Debuging output of interest
###################################################################################################
	function Debug()
	{

		// format Request for display
		$treq=htmlspecialchars($this->request);

		// format Response for display
		$tres=htmlspecialchars($this->result);
		$tres=str_replace("&lt;","r&lt;",$tres);

		// format cURL info for display
		$tcURL=buildarray($this->curl_array,'cURL Info');

		// format distilled response for display
		$tDist=buildarray($this->ResponseDistilled,'Distilled Response');

		// build temp page
		$TESTPAGE=<<<__TESTPAGE__
		<html>
		<head>
		<title>Testing cURL with UPS</title>
		</head>

		<body bgcolor="#999999">

		<table border="1" width="100%" bgcolor="#ffffff">
			<tr>
				<td width="50%" bgcolor="#9999ff">
					<b>Request to UPS:</b>
				</td>
				<td width="50%" bgcolor="#99ff99">
					<b>Response from UPS:</b><br>
				</td>
			</tr>
			<tr>
				<td valign="top">
					<font face="Verdana,Helvetica,Arial,sans-serif" size="2">
						<pre>$treq</pre>
					</font>
				</td>
				<td valign="top">
					<font face="Verdana,Helvetica,Arial,sans-serif" size="2">
						<pre>$tres</pre>
					</font>
				</td>
			</tr>
			<tr>
				<td width="50%" bgcolor="#ff9999">
					<b>cURL Info:</b>
				</td>
				<td width="50%" bgcolor="#99ffff">
					<b>Parsed Response</b><br>
				</td>
			</tr>
			<tr>
				<td valign="top">
					$tcURL
				</td>
				<td valign="top">
					$tDist
				</td>
			</tr>

		</table>

		</body>
		</html>
__TESTPAGE__;


		echo $TESTPAGE;
		
	} // end Debug();

	function Debug2()
	{
		$tDist=buildarray($this->ResponseDistilled,'Distilled Response');
		echo $tDist;
	} // end Debug2();


} // end class ups


############################################################################################
## Helper Function that changes an array into a series of nexted tables for easy display
############################################################################################

function buildarray($array,$arraylabel)
{

    // count table number
    static $array_count_preinc;
    $array_count_preinc++;

    // gather statistics
    $size=sizeof($array);
    $foo=<<<__TABLE__
    <table border="4" bgcolor="#ffffff">
      <tr>
	<td colspan="2" bgcolor="#ffaaaa">
	  Array $array_count_preinc: $arraylabel<br>
	  Entries: $size
	</td>
      </tr>
      <tr bgcolor="#aaaaff">
	<th>
	  Key
	</th>
	<th>
	  Value
	</th>
      </tr>
__TABLE__;
	
	

	if(is_array($array))
	{
		while (list ($key, $val) = each ($array))
		{
			$foo.= "<tr><td bgcolor="#aaffaa" valign="top"><b>$key</b></td><td>";
			if(is_array($val))
			{
				$foo.=buildarray($val,"");
			}
			else
			{
				$foo.= $val;
			}
			$foo.= "</td></tr>";
		}
	}
	$foo.= "</table>";

	return $foo;
}



?>