I’ve just spent 3…no…4 hours messing about with this, until I finally got it to work.
The new Amazon Associates (or as it’s to be renamed, Amazon Product Advertising API) requires that by August all requests include a Signature.
Documentation here:
http://docs.amazonwebservices.com/AWSECommerceService/latest/DG/rest-signature.html
Here’s how I implemented it in PHP (I had to make some changes to support multiple keywords, such as ‘dan aykroyd’. The space was causing huge problems after being urlencoded and decoded, parsed, hashed, etc).
define('AMAZON_ID','YOUR AWS DEVELOPER CODE');
define('SECRET_KEY','YOUR AWS SECRET KEY');
$keyword = "steve martin"; // The keywords you're searching for
$aws = array();
$keyword = urlencode($keyword);
$url = "http://webservices.amazon.com/onca/xml?Service=AWSECommerceService";
$url .= "&AWSAccessKeyId=".AMAZON_ID;
$url .= "&Operation=ItemSearch";
$url .= "&SearchIndex=DVD";
$url .= "&Keywords=".$keyword."";
$url .= "&ResponseGroup=Small,OfferFull,Images,Reviews,ItemAttributes,SalesRank";
$url .= "&Timestamp=".gmdate('Y-m-d\TH:i:s\Z');
$url = str_replace("+",urlencode("+"),$url);
$url_a = parse_url($url);
$url_a['query'] = str_replace(',',urlencode(','),$url_a['query']);
$url_a['query'] = str_replace(';',urlencode(':'),$url_a['query']);
parse_str($url_a['query'],$params);
uksort($params, 'strnatcmp');
$qstr = '';
foreach ($params as $key => $val) {
$qstr .= "=".rawurlencode($val);
}
$qstr = substr($qstr, 1);
$qstr = str_replace('%20',urlencode('+'),$qstr);
$qstr = str_replace(',',urlencode(','),$qstr);
$qstr = str_replace(';',urlencode(':'),$qstr);
$sig = "GET\n"
. "webservices.amazon.com\n"
. "/onca/xml\n"
. $qstr;
$sig = base64_encode(hash_hmac('sha256', $sig, SECRET_KEY, true));
$sig = str_replace('+','%2B',$sig);
$sig = str_replace('=','%3D',$sig);
$params['Signature'] = $sig;
$p = array();
foreach($params as $k=>$v) {
$p[]=$k."=".$v;
}
$qstr = implode("&",$p);
$rebuilt_url = $url_a['scheme']."://".$url_a['host'].$url_a['path']."?".$qstr;
$aws['url'] = $url."&Signature=".$sig;
$xml = file_get_contents($aws['url']);
echo '';
var_dump($xml);
echo '';
I know what you’re going to say, why didn’t I do it ‘this way’ or ‘that way’?
The problem is that you don’t just encrypt the query string and tack it onto the end of the URL as a Signature. You need to split the query string up, re-sort them, and then join them back together again first. And if the query doesn’t come out looking exactly the same as the original, then the Signature won’t validate. I tried dozens of other ways and in the end I had to go for the most longwinded and seemingly illogical method.
I *could* probably waste time finding a way to condense this and make it simpler, but I’ve already lost my entire night because of this. And at least I know it works.
Update: In the end I used a modified version of Ulrich Mierendorff’s code from here:
http://mierendo.com/software/aws_signed_query/
It works with all the queries I can throw at it, including searches with single quotes.
View it in action here:
http://www.bluesbrotherscentral.com/profiles/willie-big-eyes-smith/
(The Amazon box at the bottom of the page)
It looks like I was on the right track, if I had the time I would have fixed the bugs myself (but why reinvent the wheel?)
Hi,
why are you using uksort($params, ’strnatcmp’);? Afaik ksort($params) would be better.
That part of the code came from elsewhere, you could be right but I’m not changing my code now. It works fine for me.
What am I missing here ? When do u use $rebuilt_url ? Also why are u using an array to store $aws['url'] ?
I probably didn’t need to include that line, I reuse it later on in my script for another reason. Same thing with the $aws array, it’s used later (I wrote my own custom class for all AWS requests).
I’m tweaking my original code right now, there’s a few types of queries that don’t work with it. I’ll try to update it sometime soon.
Thanks for the quick responsive. I am also trying to write a class to handle the new signature requirement where I was planning on including ur build signature method but have not got it too work yet. Thanks for posting this example to get me on the right path
.
So if u can post the revisions I would really like to see how u handle rebuilding the query string for Operation= ItemLookup.
I noticed after trying to use your code sample that ur doing a str_replace after already generating the $sig which could cause the signature not to match.
$sig = base64_encode(hash_hmac(’sha256′, $sig, SECRET_KEY, true));
$sig = str_replace(‘+’,'%2B’,$sig);
$sig = str_replace(‘=’,'%3D’,$sig);
Thanks again for posting. It really helped me get my class working.
So what exactly is the output of this? Is it the same API output (XML format) from before August? Also, what if I wanted to use other parameters than you have listed? What is the order, or would I have to play with it?
Everything is identical to the way it worked before August, you shouldn’t have to change anything else. The order shouldn’t matter, nor should adding other parameters. It’s basically lines 5-12 that you can change safely (don’t remove the timestamp parameter or else the code will break).
Hope that helps!