Question

Here's what I have so far:

function sha256(stringToSign, secretKey) {
  return CryptoJS.HmacSHA256(stringToSign, secretKey);
} 

function getAmazonItemInfo(barcode) {
  
  var parameters = 
    "Service=AWSECommerceService&"
    + "AWSAccessKeyId=" + appSettings.amazon.accessKey + "&"
    + "Operation=ItemLookup&"
    + "ItemId=" + barcode
    + "&Timestamp=" + Date.now().toString();

  var stringToSign =
    "GET\n"
    + "webservices.amazon.com\n"
    + "/onca/xml\n"
    + parameters;

  var signature = "&Signature=" + encodeURIComponent(sha256(stringToSign, appSettings.amazon.secretKey));

  var amazonUrl =  
    "http://webservices.amazon.com/onca/xml?"
    + parameters
    + signature;

  // perform a GET request with amazonUrl and do other stuff

}

When executed as an HTTP GET request, the value of amazonUrl in the above code results in the following response from Amazon:

<?xml version="1.0"?> 
  <ItemLookupErrorResponse xmlns="http://ecs.amazonaws.com/doc/2005-10-05/">
    <Error>
      <Code>SignatureDoesNotMatch</Code>
      <Message>
        The request signature we calculated does not match the signature you provided. 
        Check your AWS Secret Access Key and signing method. Consult the service 
        documentation for details.
      </Message>
   </Error>
   <RequestId>[REMOVED]</RequestId>
  </ItemLookupErrorResponse>

Useful links:

Was it helpful?

Solution

I hacked around with your code and I got it working.

function sha256(stringToSign, secretKey) {
  var hex = CryptoJS.HmacSHA256(stringToSign, secretKey);
  return hex.toString(CryptoJS.enc.Base64);
} 

function timestamp() {
    var date = new Date();
    var y = date.getUTCFullYear().toString();
    var m = (date.getUTCMonth() + 1).toString();
    var d = date.getUTCDate().toString();
    var h = date.getUTCHours().toString();
    var min = date.getUTCMinutes().toString();
    var s = date.getUTCSeconds().toString();

    if(m.length < 2) { m = "0" + m; }
    if(d.length < 2) { d = "0" + d; }
    if(h.length < 2) { h = "0" + h; }
    if(min.length < 2) { min = "0" + min; }
    if(s.length < 2) { s = "0" + s}

    var date = y + "-" + m + "-" + d;
    var time = h + ":" + min + ":" + s;
    return date + "T" + time + "Z";
}

function getAmazonItemInfo(barcode) {
    var PrivateKey = "";
    var PublicKey = "";
    var AssociateTag = "";

    var parameters = [];
    parameters.push("AWSAccessKeyId=" + PublicKey);
    parameters.push("ItemId=" + barcode);
    parameters.push("Operation=ItemLookup");
    parameters.push("Service=AWSECommerceService");
    parameters.push("Timestamp=" + encodeURIComponent(timestamp()));
    parameters.push("Version=2011-08-01");
parameters.push("AssociateTag=" + AssociateTag);

    parameters.sort();
    var paramString = parameters.join('&');

    var signingKey = "GET\n" + "webservices.amazon.com\n" + "/onca/xml\n" + paramString

    var signature = sha256(signingKey,PrivateKey);
        signature = encodeURIComponent(signature);

    var amazonUrl =  "http://webservices.amazon.com/onca/xml?" + paramString + "&Signature=" + signature;
    console.log(amazonUrl);
}

The Header of the Javascript I used for some reference.

<script src="hmac-sha256.js"></script>
<script src="http://crypto-js.googlecode.com/svn/tags/3.0.2/build/components/enc-base64-min.js"></script>
<script src="amazon.js"></script>

You will need to modify parts of it because I changed some parameters around and don't reference your "app" object.

For what I did to fix it (from what I can recall).

  1. The parameters have to be alphabetical. I placed them in an array and then sort them. I follow this up by a join with the ampersand.

  2. I modified the sha256 function to return the base64 of the RAW sha256. Before it was returning the hexbits in lowercase, which isn't correct.

  3. I was going to add a base64 before encoding, but the sha256 now handles all of the signing.

  4. The date format was incorrect. It was returning a epoch timestamp instead of a string timestamp. I hacked together a simple timestamp option.

    This code requires you to include the Base64 Library for CryptoJS also.

OTHER TIPS

Use this Node.js library for AWS. It even includes an example specifically for the Product Advertising API.

Building on David's great answer, I made some tweaks. The solution below uses moment.js and crytpo-js, and can be used to search for items by keyword. I used the amazon scratch-pad to help build the target call. A couple of things I noticed:

  • The scratch-pad needs to use the same location as your associates account, ".com" ".co.uk", etc.
  • The end point you call to needs to be the same country as your associates account.
  • The time-stamp you use needs to match the local time in the country your associates account is registered.

const getAmazonItemInfo = (keywords) => {

  let date = moment().startOf().add(-9, 'hours').format("YYYY-MM-DDThh:mm:ss.000") + 'Z'
  let SecretKey = "GENERATED_IN_AFFILATES_ACCOUNT";
  let AccessKey = "GENERATED_IN_AFFILATES_ACCOUNT";
  let AssociateTag = "FOUND_IN_AFFILATES_ACCOUNT";
  let parameters = [];
  let url = 'webservices.amazon.co.uk' // UK account
  //let url = 'webservices.amazon.com'// US account

  parameters.push("AWSAccessKeyId=" + AccessKey);
  parameters.push("Keywords=" + keywords);
  parameters.push("Operation=ItemSearch");
  parameters.push("SearchIndex=All");
  parameters.push("ResponseGroup=" + encodeURIComponent('Images,ItemAttributes,Offers'));
  parameters.push("Service=AWSECommerceService");
  parameters.push("Timestamp=" + encodeURIComponent(date));
  parameters.push("AssociateTag=" + AssociateTag);
  parameters.sort();

  let paramString = parameters.join('&');
  let string_to_sign = "GET\n" + url + "\n" + "/onca/xml\n" + paramString

  let signature = CryptoJS.HmacSHA256(string_to_sign, SecretKey);
  signature = CryptoJS.enc.Base64.stringify(signature);

  let amazonUrl = "http://" + url + "/onca/xml?" + paramString + "&Signature=" + signature;
  return amazonUrl;
}

let keywords = 'iphone'
console.log(getAmazonItemInfo(keywords))

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top