/* PHP & MySQL Journal */
In this post we will see how to access the Amazon Product Advertising API from PHP. Amazon has recently changed (from 15th Aug ‘09) the authentication mechanism for accessing their API which must now be signed with your Amazon keys. Unsigned requests will be rejected by Amazon. Note that the code uses the hash_hmac() hash function which is only available for PHP versions 5.1.2 and above, so the code will not work for versions below that.
Below is an example to access the Amazon Product Advertising API using the provided class.
<?php /* Example usage of the Amazon Product Advertising API */ include("amazon_api_class.php"); $obj = new AmazonProductAPI(); try { /* Returns a SimpleXML object */ $result = $obj->searchProducts("X-Men Origins", AmazonProductAPI::DVD, "TITLE"); } catch(Exception $e) { echo $e->getMessage(); } print_r($result); ?> |
Given below is the implementation of the class to access the Amazon Product Advertising API. Comments have been removed for brevity, but are included in the source download. Note that only a few access operations are implemented in the class: ItemLookup, ItemSearch; there are more available in the API, a complete list can be found here. It’s just a simple matter of changing some parameters to implement others.
<?php require_once 'aws_signed_request.php'; class AmazonProductAPI { private $public_key = "YOUR AMAZON ACCESS KEY ID"; private $private_key = "YOUR AMAZON SECRET KEY"; const MUSIC = "Music"; const DVD = "DVD"; const GAMES = "VideoGames"; private function verifyXmlResponse($response) { if ($response === False) { throw new Exception("Could not connect to Amazon"); } else { if (isset($response->Items->Item->ItemAttributes->Title)) { return ($response); } else { throw new Exception("Invalid xml response."); } } } private function queryAmazon($parameters) { return aws_signed_request("com", $parameters, $this->public_key, $this->private_key); } public function searchProducts($search,$category,$searchType="UPC") { $allowedTypes = array("UPC", "TITLE", "ARTIST", "KEYWORD"); $allowedCategories = array("Music", "DVD", "VideoGames"); switch($searchType) { case "UPC" : $parameters = array("Operation" => "ItemLookup", "ItemId" => $search, "SearchIndex" => $category, "IdType" => "UPC", "ResponseGroup" => "Medium"); break; case "TITLE" : $parameters = array("Operation" => "ItemSearch", "Title" => $search, "SearchIndex" => $category, "ResponseGroup" => "Medium"); break; } $xml_response = $this->queryAmazon($parameters); return $this->verifyXmlResponse($xml_response); } public function getItemByUpc($upc_code, $product_type) { $parameters = array("Operation" => "ItemLookup", "ItemId" => $upc_code, "SearchIndex" => $product_type, "IdType" => "UPC", "ResponseGroup" => "Medium"); $xml_response = $this->queryAmazon($parameters); return $this->verifyXmlResponse($xml_response); } public function getItemByAsin($asin_code) { $parameters = array("Operation" => "ItemLookup", "ItemId" => $asin_code, "ResponseGroup" => "Medium"); $xml_response = $this->queryAmazon($parameters); return $this->verifyXmlResponse($xml_response); } public function getItemByKeyword($keyword, $product_type) { $parameters = array("Operation" => "ItemSearch", "Keywords" => $keyword, "SearchIndex" => $product_type); $xml_response = $this->queryAmazon($parameters); return $this->verifyXmlResponse($xml_response); } } ?> |
The above class uses the ‘aws_signed_request’ function to generate the new request signature. Original code is by Ulrich Mierendorff, modified here to use cURL.
<?php function aws_signed_request($region,$params,$public_key,$private_key) { $method = "GET"; $host = "ecs.amazonaws.".$region; $uri = "/onca/xml"; $params["Service"] = "AWSECommerceService"; $params["AWSAccessKeyId"] = $public_key; $params["Timestamp"] = gmdate("Y-m-d\TH:i:s\Z"); $params["Version"] = "2009-03-31"; /* The params need to be sorted by the key, as Amazon does this at their end and then generates the hash of the same. If the params are not in order then the generated hash will be different from Amazon thus failing the authentication process. */ ksort($params); $canonicalized_query = array(); foreach ($params as $param=>$value) { $param = str_replace("%7E", "~", rawurlencode($param)); $value = str_replace("%7E", "~", rawurlencode($value)); $canonicalized_query[] = $param."=".$value; } $canonicalized_query = implode("&", $canonicalized_query); $string_to_sign = $method."\n".$host."\n".$uri."\n". $canonicalized_query; /* calculate the signature using HMAC, SHA256 and base64-encoding */ $signature = base64_encode(hash_hmac("sha256", $string_to_sign, $private_key, True)); /* encode the signature for the request */ $signature = str_replace("%7E", "~", rawurlencode($signature)); /* create request */ $request = "http://".$host.$uri."?".$canonicalized_query." &Signature=".$signature; /* I prefer using CURL */ $ch = curl_init(); curl_setopt($ch, CURLOPT_URL,$request); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_TIMEOUT, 15); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); $xml_response = curl_exec($ch); if ($xml_response === False) { return False; } else { /* parse XML and return a SimpleXML object, if you would rather like raw xml then just return the $xml_response. */ $parsed_xml = @simplexml_load_string($xml_response); return ($parsed_xml === False) ? False : $parsed_xml; } } ?> |
|
|
This site is a digital habitat of Sameer, a freelance web developer working from Pune.More
27 Responses
1
Steven Klotz
August 22nd, 2009 at 3:59 pm
Thanks, this was just what I needed to get started.
2
Sammie
August 28th, 2009 at 5:13 pm
Thanks for sharing your source. Very helpfull!
3
phplifestyle
September 4th, 2009 at 7:47 am
Thank for share. Great source.
4
Soediarto
September 20th, 2009 at 11:56 pm
Thanks for sharing..it helps me a lot..
5
Leo
September 21st, 2009 at 1:22 pm
Very helpful code! Thanks!
6
cd smith
September 30th, 2009 at 7:00 pm
How do you change the code to show reviews? I been trying to modify to do that, I am missing something.
sameer
September 30th, 2009 at 9:08 pm
You need to change the response group to ‘Large’ or ‘Medium’ in the following lines:
.
.
“ResponseGroup” => “Medium”);
To find out what elements are returned by the various groups, you can go here:
http://docs.amazonwebservices.com/AWSECommerceService/latest/DG/CHAP_ResponseGroupsList.html
8
cd smith
September 30th, 2009 at 9:58 pm
I tried that earlier unsuccessfully.
Here is what I did:
amazon_api_class.php
case “TITLE” : $parameters = array(”Operation” => “ItemSearch”,
“Title” => $search,
“SearchIndex” => $category,
“ResponseGroup” => “Large”);
break;
Example.php
echo “Sales Rank : {$result->Items->Item->SalesRank}”;
echo “ASIN : {$result->Items->Item->ASIN}”;
echo “Sales Detail : {$result->Items->Item->EditorialReviews->EditorialReview}”;
echo “Items->Item->LargeImage->URL . “\” />”;
Where did go wrong? Thanks.
sameer
October 1st, 2009 at 12:42 am
You need to do the following using the ‘Large’ response group:
echo $result->Items->Item->CustomerReviews->Review[0]->Content;
echo $result->Items->Item->EditorialReviews->EditorialReview[0]->Content;
10
cd smith
October 1st, 2009 at 5:10 am
That worked! But I have one last question:
How did you know the proper way to format the echo string to display the data?
I cannot find that information in the documentation.
For instance, there is a “Source” tag in the XML but if I add that in the string, nothing displays.
Ex: $result->Items->Item->EditorialReviews->EditorialReview[0]->Source->Content;
sameer
October 1st, 2009 at 5:32 am
Just print the $result using
print_r($result)
and view the source, this will display the serialized data from which you can get the node names; or in the ‘aws_signed_request.php’ file, after the line
.
.
$xml_response = curl_exec($ch);
add the following lines:
print_r($xml_response);
exit();
This will display the actual XML returned by Amazon.
12
cd smith
October 1st, 2009 at 8:11 am
Thanks again..The print_r statement browser display was too jumbled to read it clearly but when I viewed the “source” of the webpage, everything became very understandable.
[CustomerReviews] => SimpleXMLElement Object
(
[AverageRating] => 4.5
[TotalReviews] => 31
[TotalReviewPages] => 7
[Review] => Array
(
[0] => SimpleXMLElement Object
(
[ASIN] => B000000ZML
[Rating] => 5
[HelpfulVotes] => 0
[CustomerId] => A12GGA1DTETD2H
[Reviewer] => SimpleXMLElement Object
All I need to do now is figure out how to display the “Add To Cart” button.
Thanks again..
13
Helen Hunt
October 7th, 2009 at 4:45 am
Thanks for the awesome piece of code that has made my affiliate programme much better.
I was struggling with the new signature and timestamp requirements until I saw your solution.
Keep it up and thanks for sharing. Lovely blog BTW
14
Manuel88
October 12th, 2009 at 1:58 am
Finally a script that work!! Thnx man!!
15
Jim Keller
October 24th, 2009 at 12:32 pm
I was using a foreach loop to generate the query string rather than implode() as above, and noticed that the request was failing with an invalid signature because I had forgotten to rtrim() the last ampersand from the query string.
Thanks for the code - this was a huge help, and things seem to be working now.
16
Jonathan Montgomery
October 26th, 2009 at 3:32 pm
This returns an error message “The request must contain the parameter Signature” every time. Any idea why?
17
Anup Khandelwal
October 26th, 2009 at 10:53 pm
Thanks for sharing your script. I need small help from you. I am working on magazine price comparison site, wherein on comparison page i need to display magazine of amazon. Can i cache amazon records on my db?
sameer
October 26th, 2009 at 10:59 pm
Anup, you are not quite clear on your question, but you can cache any Amazon results in your db for latter access, but I’m not sure how long the cache should remain valid. If Amazon updates their db then your cache can go stale.
19
Vishy
October 29th, 2009 at 12:29 pm
Hay man thanks a lot, very useful code…. thanks again
20
Anup Khandelwal
November 24th, 2009 at 11:37 pm
Hi Sameer,
I am successfully able to retrieve magazines from the class you developed. Thank you so much.
But i facing one problem which is:
I want to retrieve magazine name ‘TIME’. But i am getting output of all magazine result which match Magazine Title ‘TIME’. I need exact match result for Magazine Title. Can you please suggest me solution for same.
Code Snippet:
$obj = new AmazonProductAPI();
$result = $obj->searchProducts(”TIME”, AmazonProductAPI::MAGAZINE, “TITLE”);
Thanks,
Anup
sameer
November 25th, 2009 at 12:12 am
Try adding an extra parameter, like the ‘Publisher’ name in the query. The publisher for ‘Time’ magazine is ‘Time Direct Ventures’, so you can add that to the ‘Title’ parameter. So the code in the class would look like this:
.
.
case "TITLE" :
$parameters = array("Operation" => "ItemSearch",
"Title" => $search,
"Publisher" => "Time Direct Ventures",
"SearchIndex" => $category,
"ResponseGroup" => "Large");
.
.
Parameter which you can use are given here:
http://docs.amazonwebservices.com/AWSECommerceService/latest/DG/USSearchIndexParamForItemsearch.html
#USSearchIndexParamForItemsearch_magazines
22
Rucha
December 15th, 2009 at 5:40 am
Hello Sameer,
I am trying to get categories of Books to show on my page using amazon api. Code that you provide is good. i m getting perticular book search result but i want to show books sub categories. can u please tell me what changes i have to do in same code?
sameer
December 15th, 2009 at 6:35 am
You need to search with the ‘BrowseNode’ parameter. For example in the ‘amazon_api_class.php’ file you can change the following code:
.
.
case "TITLE" : $parameters = array("Operation" => "ItemSearch",
"Title" => $search,
"SearchIndex" => $category,
"ResponseGroup" => "Large");
.
.
to
.
.
case "TITLE" : $parameters = array("Operation" => "ItemSearch",
"BrowseNode" => 283155,
"SearchIndex" => "Books",
"ResponseGroup" => "BrowseNodes");
.
.
This will return all the subcategories of ‘Books’, with the names and their BrowseNodes.
The BrowseNode ‘283155′ is that of Books.
Note: When using BrowseNodes, replace the following line:
return $this->verifyXmlResponse($xml_response);
with
return $xml_response;
Now you can search for a particular category by using its BrowseNode and some other search criteria, like say keyword.
.
.
case "TITLE" : $parameters = array("Operation" => "ItemSearch",
"BrowseNode" => 75,
"Keywords" => $search,
"SearchIndex" => "Books",
"ResponseGroup" => "Large");
.
.
The BrowseNode given above (75) is for the ‘Science’ category.
You can find a list of BrowseNodes here:
http://www.browsenodes.com
24
Chandra Mouli
December 16th, 2009 at 8:51 pm
Hi,
This is Chandra Mouli. Tanikonda.Please let me know how to connect to the Amazon product api from our applicatin.Thanks in advance..
Regards
Chandra Mouli Tanikonda
25
Rucha
December 19th, 2009 at 3:39 am
Thanks a lot Sameer. Now it is working fine
26
XT@z
January 4th, 2010 at 12:50 pm
Thanks a lot for this script !! Finally something understandable and WORKING !!
Do you also have something for creating and managing their distant basket ?
27
Rucha
January 5th, 2010 at 3:40 am
Hello Sameer,
Can you please tell me how i can integrate remote shopping cart provided by Amazon API? I am trying CartCreate operation but it is not working. Can you please tell me details?
Thanks…….