Ad

Our DNA is written in Swift
Jump

Talking to Amazon Web Services

I have several boxes of books that I want to create a list of. I have the technology down to scan the book bar codes rapidly. But how could I get the book titles for my inventory?

Amazon Web Services (AWS) is a collection of Amazon’s web APIs. Their Product Advertising API offers to search for books by ISBN and so I had to dig into their documentation to figure out how to query their API.

Product Advertising API

The search in question is a feature of the Amazon Product Advertising API which has fine documentation, but at first glance looks somewhat intimidating. To use the services – no anonymous queries – you need to sign up as a Product Advertising API Developer on one of several sub sites, pick the one that best matches your locale.

Amazon recently revamped their services console, the confirmation e-mail you will have received for the sign up still points to the old site. You can look up your access key there. The sign-up process will have created the first Access key for you to use.

AWS Security Console

The whole thing is unnecessarily complicated by Amazon using this console as the single point of configuration for all your cloud services. Even if you don’t have any services and even though you might never plan to procure any, you still have to have a login here. I also found that you need to sign up via the partner net sites mentioned above. If you try to sign up directly you will be asked for a credit card.

Access Key and Secret

To sign web requests you need an AWK Access Key (20 characters) and the matching AWS Secret Key (40 characters). On the old system you could look up both, on the new system Amazon does not let you access the secret key any more. Instead you get to download it once when you push the Create New Root Key.

This is probably to make it impossible for a hacker to find a matching pair of keys on Amazon’s servers. Instead it is now up to you to keep your secret key secret. Security is more important than making it more easy to understand for developers it seems.

In your download you receive a rootkey.csv.txt file which contains both keys and you should put them somewhere safe for later reference. Of course you can create a new pair at any time and disable a previous one. But deleting one also disables the associated access. The system will reject all requests signed with a thus disabled secret key.

rootkey.csv.txt

Amazon explains the meaning of these keys such:

The Access Key ID is not a secret, and anyone could use your Access Key ID in requests to AWS. To provide proof that you truly are the sender of the request, you must also include a digital signature. For all requests except those using SOAP with WS-Security, you calculate the signature using your Secret Access Key. AWS uses the Access Key ID in the request to look up your Secret Access Key and then calculates a digital signature with the key. If the signature AWS calculates matches the signature you sent, the request is considered authentic. Otherwise, the request fails authentication and is not processed.

To do summarize: the access key will be sent with the web request and a signature is sent as well generated from the secret key.

Becoming an Associate

Amazon provides the Product Advertising API not out of the goodness of their hearts, but to enable us to help them sell more products. And for that to be worthwhile for us they pay certain commissions based on these sales. To be able to do that you also need to sign up to be an Amazon Associatiate. This provides you with a partner tag which you also need to pass as a parameter for product searches.

Amazon explains:

Use the Product Advertising API to advertise Amazon products in conjunction with the Amazon Associates Program and earn referral fees when the users you refer to Amazon sites buy qualifying products.

You need to get an Amazon Associates account and ensure that you include your associate tag in API requests. That way the URLs returned by the API contain your Associate tag. When a user visits an Amazon site through a tagged link and buys a product, you earn referral fees. For more information about getting an Amazon Associates account, see Becoming an Associate.

Becoming an associate is not optional as the AssociateTag parameter is a mandatory parameter in the product search API. I found – after painstakingly constructing a signed request that Amazon responded telling me this.

Signing a Request

Amazon seems to be terribly afraid that some anonymous person might abuse their data. For maximum security you need to:

  • add a time stamp to insure that the request is not an old one
  • add your associate tag and access key to identify you
  • sign the entire URL including parameters. To get consistent results you have to sort the parameters and URL encode them in a specific way.

Below is a basic implementation of the algorithm, note that several methods from DTFoundation are used for URL-encoding and Base64-encoding.

NSString *accessKey = @"XXXXXXXXXXXXXXXXXXXX";
NSString *secretKey = @"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
 
NSString *verb = @"GET";
NSString *hostName = @"webservices.amazon.com";
NSString *path = @"/onca/xml";
 
NSDictionary *params = @{@"Service": @"AWSECommerceService",
                         @"AWSAccessKeyId": accessKey,
                         @"Operation": @"ItemLookup",
                         @"ItemId": @"9783826615863",
                         @"ResponseGroup": @"Large",
                         @"SearchIndex": @"All",
                         @"IdType": @"ISBN",
                         @"AssociateTag": @"wwwdrobnikcom-21"};
 
// add time stamp
NSDateFormatter *UTCFormatter = [[NSDateFormatter alloc] init];
UTCFormatter.dateFormat = @"yyMMddHHmmss'Z'";
UTCFormatter.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"UTC"];
 
NSString *timeStamp = [UTCFormatter stringFromDate:[NSDate date]];
 
NSMutableDictionary *tmpParams = [params mutableCopy];
[tmpParams setObject:timeStamp forKey:@"Timestamp"];
 
NSMutableString *paramString = [NSMutableString string];
 
NSArray *sortedKeys = [[tmpParams allKeys] sortedArrayUsingSelector:@selector(compare:)];
 
[sortedKeys enumerateObjectsUsingBlock:^(NSString *oneKey, NSUInteger idx, BOOL *stop) {
 
   if (idx)
   {
      [paramString appendString:@"&"];
   }
 
   [paramString appendString:oneKey];
   [paramString appendString:@"="];
 
   NSString *value = [tmpParams objectForKey:oneKey];
   [paramString appendString:[value stringByURLEncoding]];
}];
 
// create canonical string for signing
 
NSMutableString *canonicalString = [NSMutableString string];
 
[canonicalString appendString:verb];
[canonicalString appendString:@"\n"];
[canonicalString appendString:hostName];
[canonicalString appendString:@"\n"];
[canonicalString appendString:path];
[canonicalString appendString:@"\n"];
 
[canonicalString appendString:paramString];
 
// create HMAC with SHA256
const char *cKey  = [secretKey cStringUsingEncoding:NSUTF8StringEncoding];
const char *cData = [canonicalString cStringUsingEncoding:NSUTF8StringEncoding];
unsigned char cHMAC[CC_SHA256_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA256, cKey, strlen(cKey), cData, strlen(cData), cHMAC);
 
NSData *hashData = [NSData dataWithBytes:cHMAC length:CC_SHA256_DIGEST_LENGTH];
NSString *signature = [[DTBase64Coding stringByEncodingData:hashData] stringByURLEncoding];
 
// create URL String
NSMutableString *urlString = [NSMutableString string];
 
[urlString appendString:@"http://"];
[urlString appendString:hostName];
[urlString appendString:path];
[urlString appendString:@"?"];
[urlString appendString:paramString];
 
[urlString appendFormat:@"&Signature=%@", signature];
 
NSLog(@"%@", urlString);

We’re using the HMAC functions from CommonCrypto to produce the signature and then we encode the resulting data bytes in base64. Then we URL-encode that as well as it also is appended to the URL.

If all worked well then you can test the resulting URL in Safari.

Successful Query

There are a variety of useful infos here, most importantly the book title, size, weight, language, thumbnails and much more. All the ItemLinks (Technical Details, Add To Baby/Wedding Registry, Add to Wishlist, etc.) contain your associate tag of course. This is where Amazon hopes that you will present those links to your users for that they might eventually purchase such an item.

I have no idea if the above code works for all cases, frankly I was ecstatic to see it working after only a few attempts.

Conclusion

There is a great deal of hoops to jump through if you want to query Amazon for product information. You need a matching pair of keys, an associate tag and you need to be able to produce a canonical URL that includes a signature.

However if you manage to see it through to the stage where AWS accepts your requests and delivers useful responses then you will have access to the world’s largest database of products. This enables many useful use cases besides sending customers to Amazon’s website.

I’ll probably end up putting the above code into DTFoundation, let me know if you have some input.


Categories: Recipes

13 Comments »

  1. This no longer seems to work. I get a unrecognized selector error when using stringByURLEncoding and the app crashes.

  2. Here is the Amazon Product Advertisement API based search tool

    http://www.alreadycoded.com/api-web-services/amazon-product-search-api-tool.html