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?)