Coder, Entrepreneur, Blogger, and Coffee Addict
It’s actually very easy to calculate shipping from UPS with PHP once you know how. But at first glance it seems like an impossible task to do.
Update: Although this code still works great, I have started an open source project at http://code.google.com/p/ups-php/. For easier to use code, improved documentation and a more complete feature set. The project also covers the other UPS API services.
Update 2: For a no fuss, 100% complete UPS Online Tools script with label printing support, integration support and automatic certification generation (required by UPS), I have created, RocketShipIt.
Comments have also been disabled, if you are having problems with the script please start an issue ticket at: http://code.google.com/p/ups-php
The UPS manual for XML shipping calculation is hundreds of pages long and unfortunately provides no examples written in PHP. Only ASP… Damn them! After hours of reading the UPS manual and lots of Google searching I figured out how to make my own and I thought I would share my method. So in today’s post we will look at an example ups rate calculation using PHP.
If you are looking for the United States Postal Service (USPS) function see, Calculating USPS Shipping Rates with PHP.
You can also integrate UPS Tracking features on your site once you get a handle on this.
Basically you start off by getting setup with UPS and grabbing a copy of their XML manual. Now you need to construct an XML file that will be later sent off to UPS’s server. UPS will then send a response back to you in XML format. We then use PHP to turn the XML into variables that we can use and manipulate. Even add the shipping cost into the subtotal or final checkout cost. After all, whats the use of calculating UPS shipping if you are not going to charge the user for it.
Copy the UPS PHP Function I have created into a new .txt file and name it ups.php.
Go through this function and find the parts in the XML file that are in CAPS, You will need to add your UPS shipper number, access key, username, password, departure zip, etc…
Then create another .php file and make sure to include ups.php.
It might look something like this:
require_once("ups.php")
Now all you have to do to get an accurate UPS rate for a package is call the ups function.
The basic syntax for the function is:
ups($dest_zip,$service,$weight,$length,$width,$height)
So, for example if you wanted to send a five pound package to Beverly Hills CA using UPS ground you would do this:
ups(90210,03,5,5,5,5)
Here is an already written php test file that uses the ups function.
By now you may be wondering how I know that 03 is the code for UPS ground and you also may be wondering what the code is for 2nd Day Air.
You can find all this information in the huge manual with the name dtk_RateXML_V1.zip. You can find this manual on the UPS website after you login under UPS Rates and Services Selection but to save you the time I have listed them below.
Feel free to edit the function. The XML part of the code should be altered to fit your needs. This function only has the basic elements needed to calculate a rate. There are other attributes you can specify in the XML part of the document, such as pickup type, packaging type, etc…
Unlike some UPS Rate calculators you do not need to update the fuel surcharges or anything else. These rates come straight from UPS and are as accurate as your XML file has specified.
If it’s not working and you think you may have messed up the PHP code you can test to see if it is working by sending your XML file with curl.
Log into your host via SSH, make an xml file named RequestXML.txt. Then run this command.
curl -d @RequestXML.txt https://www.ups.com/ups.app/xml/Rate
If you want to use UPS’s testing server you can use this URL:
https://wwwcie.ups.com/ups.app/xml/Rate
Godaddy requires a minor change in the ups function. After reading this article:
You need to have a proxy to use cURL on Godaddy.
So under this line:
“curl_setopt($ch,CURLOPT_TIMEOUT, 60);”
add this:
“curl_setopt($ch,CURLOPT_PROXY,’http://proxy.shr.secureserver.net:3128′);”
I have started a UPS API open source project at, http://code.google.com/p/ups-php. If you would like to contribute and add to my code or help create more modules for the different UPS tools come on over.
If you are using Dreamhost as your webhost, there is currently something wrong with the way they handle cURL. It takes almost 60 seconds to receive a response. This is not the script or UPS’s fault. As of 01-24-2008 I have a support ticket out to fix the problem. I am getting emails from them and I will update this post as soon as I get a fix or information from them.
Here are the emails I have received from Dreamhost regarding the issue:
On Thu, 24 Jan 2008, you wrote:
So I am using cURL to send a receive XML requests for the purposes of shipping products via UPS.
A year ago cURL responses were almost immediate. Today (and the last few months) they seem to take almost 60 seconds to send and receive the same XML file. Out of laziness on my part I have not complained about the issue until now. But I need to know what I can do to speed this up a bit.
Attached will be an example of the code I use.
Thanks in advance
Mark
We don’t really troubleshoot code but I would think the most likely
explanation (since cURL in itself should be very fast) is that something
changed with UPS’s site where it slowed down or the process keeps pending
for some reason until it times out. You could try setting the timeout to
a lower amount (15-20 seconds) and see if it still gets the data or not.
Another thing to test the theory if its on USP’s site would be to make a
regular static page which sends the same POST data to UPS’s site see how
long it takes to fetch the results. Please let me know if you have any
additional questions.
I guess I should have included this in my original email. I have tried this exact code on other hosting servers and it works almost instantly. I have indeed sshed and ran cURL by itself and it is extremely slow. This is definitely not a code issue or UPS slow server issue. I can send the same request via curl on my home computer and it is faster than dreamhost.
Please advise
Mark
Although normally we do not assist customers with third party software,
I’ve dealt with similar issues in the past with UPS. Could you please
send me the complete path of the PHP that’s running slowly? I can run
some debugging software on it for you to see if there is anything hanging
things up and anything I can do to fix it.
Thankyou for attempting to solve my issue. I still must insist that it is not a third party software issue. I believe this has nothing to do with UPS. As stated in my earlier email the PHP is not to blame here either. The problem lies within the response time of cURL.
To seperate php from the equation I went ahead and created an xml text file on my root directory on dreamhost. I then ran the following command:
curl -d @xml.txt https://www.ups.com/ups.app/xml/Rate
It took 56.853 seconds to receive a response from Dreamhost.
I copied the same xml.txt file to my personal home computer and ran the same command and received a response in 1.2 seconds.
I will attach a log of the commands I ran.
As you can see it takes Dreamhost way longer to send/receive responses via curl. If you want to test it yourself you can run the same command I did from my home directory. The xml file is in there.
Thanks for your help in this matter. If you have any questions about my analysis please email.
Mark
Alright, I’ve run some tests and asked some admins their opinions and
here are the results.It appears that the slowness is because cURL is trying to reverse lookup
the IP address for www.ups.com which is timing out. This is the culprit,
but, unfortunately there is no easy fix for it if you must use cURL.Here is the strace results:
connect(3, {sa_family=AF_INET, sin_port=htons(53),
sin_addr=inet_addr(“66.33.216.208″)}, 28) = 0
send(3, “\230\313\1\102500322801203153\7″…, 43,
0) = 43
gettimeofday({1201218751, 713629}, NULL) = 0
poll(This is what it’s lagging on (as the DNS lookup takes 3-5 seconds to time
out)One quick option may be to instead use fopen for the URL — of course to
do this you’ll need to install a custom PHP.ini file and customize it to
allow_url_fopen ( http://wiki.dreamhost.com/PHP.ini )Sorry about the semi-bad news. Of course if you have any other questions
Why would a reverse look up to a well known domain such as www.ups.com fail? Why can other webhosts receive cURL responses with out a hangup?
When you say that a reverse lookup is causing a 3-5 second timeout why is curl taking over 60 seconds to receive a response? I would be more than happy to have a 3-5 second response time.
Why did curl respond much faster on Dreamhost prior to 10/09/2007? (I looked back in my emails and found the date I started having slow response times)
Mark
Sorry for the delay in getting back to you…our support system has been
pretty backlogged lately. The only explanation I can come up with is a
quirky routing issue or something…I’ve seen it before, specifically
with UPS, and the only solution was to use the IPs rather than the
hostnames. I am sorry we don’t have a better solution for you. If it is
possibly something on our end, we have no idea what it is, I’m afraid. I
apologize for the hassle.
If this article helped you in any way please consider digging this article by clicking the “digg it” link below. This will help others find the article.
Mark,
Thanks for your help today. I truly appreciate it. Made a donation . . . have a merry Christmas!
Glen
Pingback: Mastering the UPS Shipping API: Rate Shopping Dropdown
Mark,
Thank you very much! I will work on this today, see if I can get it applied and post the site it is working on!
I just noticed too that I never posted my url. I have done so… just in case you want to go there.
I know I speak for MANY people when I tell you that your efforts are VERY welcome.
Have a great day, I will post results when complete.
Mark,
By the way, my wife raises and trains Australian Shepherds. We currently have a litter on the ground and she tells me because of your responses and help that if you would like a puppy, she will give you a puppy if you would like… all you would have to do is pay for shipping.
Her site is http://www.purestockaussies.com.
Just a way of saying thank you.
Mark,
Okay, everything is working very well… I still don’t seem to be getting the negotiated rates. I have looked at the code in the Google svn and don’t see anything that specifically calls for negotiated rates.
I have been in contact with the UPS rep and he assures me that all settings on there end are “turned on” for negotiated rates… am I missing something silly?
Thanks again for all your help.
Pingback: PHP: Gather ALL Post Variables - CodeCall Programming Forum
Hello Mark,
Can you please help me te get mij shipping through UPS module to work?
I have a zencart webshop. I am located in the netherlands. I do not get any quote. I have all i need from the ups site. I yhink i do not use the right code with the right ajustments.
thanks in advance
I am currently looking at creating an internal module with the rates and service tools, does anyone have any insight on whether or not you can specify your origin and destination and calculated international rates and your landed duty paid? I want to be able to specify where the shipment is going, where it is going, enter in the dimensions and the weight and have it spit out how much it will be, is this possible?
@judi, @mark I can confirm that Judi’s comment on on August 8th, 2008 also resolved my issue with similar error messages. Thank you Judi.
Notice: Undefined index: value in /Users/joeymarchy/Sites/happy0000/ee/modules/checkout/mod.checkout.php(250) : eval()’d code on line 1
Notice: Undefined index: value in /Users/joeymarchy/Sites/happy0000/ee/modules/checkout/mod.checkout.php(250) : eval()’d code on line 1
If you want to add the option for Saturday delivery (otherwise UPS will only count business days), just add:
“.
($sat?”1″:”)
.”
In place of already existing tags.
Then add $sat=0 as the last parameter for the getRate function. When you need the Saturday rate, add a 1 as the option, or just add this function inside the getRate class:
function getSaturdayRate($PostalCode,$dest_zip,$service,$length,$width,$height,$weight){
return $this->getRate($PostalCode,$dest_zip,$service,$length,$width,$height,$weight,1);
}
Comment system ate my code, should be:
$sat?”1″:”
$sat?”<ShipmentServiceOptions><SaturdayDelivery>1</SaturdayDelivery></ShipmentServiceOptions>”:”
Worked brilliantly. I don’t know much about XML, so this would have been a huge project for me. You took the headache out of it.
Hi mark thanks a lot this code works superbly tat was a great job from u. One small doubt,how can i get price for all the services(around 10 services) which UPS provide. Currently i am making separate call for each service which takes lot of time to respond back. I need to show the price for each service and let the user to choose one service from the list.
Thanks in advance…..
Anyone figure out the deal with the rate being off? I have a client that suspects the answer has something to do with “shipping to a residence” not being factored in, but the fuel surcharge being missing sounds possible…
My UPS toolkit works on server php 4, but it doesn’t work after change to php 5 server, what code do I need to change…. ?
I’m using upsRate.php with the address https://www.ups.com/ups.app/xml/Rate, I’m assuming that this is the production server, and just wanted to check on the discrepancies between the UPS website and the script, that can’t be accounted for entirely by the fuel surcharge. I’ve played around with it for a while and can’t resolve the difference. Thanks.
Hi,
when i am using above script, i am getting error
“Fatal error: Call to undefined function curl_init() in C:\Program Files\xampp\htdocs\sites_comp\USPS test\index.php on line 16″.
Cau u tell me, have i need any other file to call this function curl_init().
how i change the currency type value? (CAD, USD or other) ? thanks
I just wanted to thank you for your write up.
I converted your script to perl for the rest of us out there that still use it for the heavy lifting. I made a copy available for anyone that needs it. It took about 4 minutes to convert your perfectly crafted UPS XML.
–Anton
Just wanted to thank you for this fantastic library/function thing
If only UPS made it this easy.
the script above works to perfection you are the man!!
One question.. In a cart using the shipping calculator should I add up the weights, lengths, etc then send to ups.. I dont think that would give an accurate cost if anything to much..
Hey Mark,
Thanks for the code..It helped me a lot…But i got into trouble about the height, and wide of the code.
Because in my shopping cart, I know the weight of the product and destination. But I dont know hoe big box it is going to take to pack that product. Products with same weight can be some time big and some time small!!!
How do I solve this problem?? Is there any way I can calculate shipping charges without inserting length, width, and height??
Can somebody help me out please.
Thank you in advance..
High Mark,
First thanks for putting this code together. Don’t know where I would have found out how to get to UPS through PHP.
Kapur, you might want to pay attention to this note, it seems you are in a similar situation. Feel free to contact me if you want to discuss approaches. My email is at the end of comment.
I think a heads up on size is in order. I seem to recall in one of the notes that size doesn’t seem to matter so just use 5 inches for length, width and height.
I am a fine art photographer opening an e-commerce store. I will be shipping some pretty big stuff. It could reach 50 inches long x 50 inches high x 6 inches wide. And even bigger.
Being afraid that size could matter, especially since UPS requires it for rate calculation, I did a quick test. I simply put in a loop that kept everything constant except for length and height.
Parameters are getRate(’80133′,’99501′,’03′,$size,’5′,$size,10)
I started with a size of 5 and incremented by 5 for each rate look up.
The results:
size=>5 cost=>37.84
size=>10 cost=>37.84
size=>15 cost=>37.84
size=>20 cost=>37.84
size=>25 cost=>37.84
size=>30 cost=>37.84
size=>35 cost=>87.14
size=>40 cost=>106.31
size=>45 cost=>240.07
size=>50 cost=>240.07
Note that size does not matter until passing 30x30x5, but then it seems to jump at almost every increment.
I run the loop another time with weight set at 1 lb. Notice that rates are lower up until the 30x30x5 point but once passing that it seems that weight doesn’t matter since a 1 lb package costs the same as a 10 lb package.
Parameters are getRate(’80133′,’99501′,’03′,$size,’5′,$size,1)
The results:
size=>5 cost=>19.36
size=>10 cost=>19.36
size=>15 cost=>19.36
size=>20 cost=>19.36
size=>25 cost=>19.36
size=>30 cost=>19.36
size=>35 cost=>87.14
size=>40 cost=>106.31
size=>45 cost=>240.07
size=>50 cost=>240.07
Again thanks for your effort, it was real useful for myself.
Bill Magee (bill@bill-magee.com)
Kapur:
UPS rate quotes are based on weight only until a box exceeds certain sizes, then its kicked into dimensional weight. If you have products that are large enough they will always be billed by “dimensional weight” then its best to enter their weight as what UPS will bill you for. For example i have a tank i sell that is 24x70x7. It weights 20 lbs, however dimensional weight for this product is 80lbs. So to avoid having to have dimensions for all my products, and a script which can tell me how to pack what, i simply use 80lbs as its weight, which gets me within $5-10 of what it will actually bill for.
Mark:
I was getting that undefined index for value error and i’ve determined it to be because im not getting these 2 fields
[GUARANTEEDDAYSTODELIVERY] =>
[SCHEDULEDDELIVERYTIME] =>
both are empty in the array. I used a similar fix as to the one listed above, but any idea why im not getting this information?
You sir, are a hero to me. The immense UPS API reference guide made me want to run out in the street and scream at people! And I don’t have an extensive knowledge of cURL, so you have saved my life from angry clients
hi all, i really want to know how to get the exact shipping rates using the same ups.php page for Canada… Please help i m not able to proceed
Add me to the list of people getting lower rates from https://www.ups.com/ups.app/xml/Rate then from the UPS site. Anyone have any idea how to resolve this?
© 2011 All rights reserved
Hi Mark,
Sorry, I must not have asked my question correctly. I do receive the array, that is all working perfectly!
What I would like to do is take the response from that array, and create for elements from that response so that a customer can select the service and subsequently the price of shipping they would like to use.
I.e., if a client is checking out in the shopping cart area, they should reach a point in the checkout that allows them to select things like, UPS Ground, Overnight, Express, etc. with rates for each appearing next to the options for each service respectively.
I know it’s a simple thing, but I don’t know how to split the response into variables that I can use in this manner.
Thanks in advance for any help you can offer!
All the best,
Mark