0
votes

I am trying to integrate with the Amazon Product Affiliate REST API, I refuse to use the SOAP integration, and please don't bring up the Soap here, I want to use REST, I am using the latest API version, which is 08/01/2013 I followed the documentation, and somehow I get always back a 403 forbidden error, I have looked all over the place, and the only thing I saw was, ppl angry about the outdated samples on Amazon, so I am getting frustrated not getting my integration work, here are the specs that I use.

VS 2015 Console Application targeting .NET 4.5.2 Amazon Api version 08/01/2013

and here is my code base.

I have a class called AmazonRestService which has all properties needed for an ItemLookup,

in the constructor i set some properties which is the default for every request, then i have a method called SignAmazonRequest which takes an AmazonRestService as an argument, and i check all fields that has a value, i fill up the string that will need to be signed,

 public class AmazonRestService
    {
        public string Url { get; set; }
        public string Operation { get; set; }
        public string AWSAccessKeyId { get; set; }
        public string SecretKey { get; set; }
        public string AssociateTag { get; set; }
        public string ItemId { get; set; }
        public string IdType { get; set; }
        public string[] ResponseGroup { get; set; }
        public string Timestamp { get; set; }
        public string Signature { get; set; }
        public string Version { get; set; }

        public AmazonRestService()
        {
            Url = "http://webservices.amazon.com/onca/xml?Service=AWSECommerceService&";
            AWSAccessKeyId = "XXXXXXXXXXXXXXXXXXXXX";
            AssociateTag = "xxxx-xx";
            SecretKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
            Timestamp = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ");
            Version = "2013-08-01";
        }

        public AmazonRestService SignAmazonRequest(AmazonRestService service)
        {
            var stringToSign = "";
            stringToSign = stringToSign + "AssociateTag=" + service.AssociateTag;
            if (!string.IsNullOrEmpty(service.Operation))
            {
                stringToSign = stringToSign + "&Operation=" + service.Operation;
            }
            if (!string.IsNullOrEmpty(service.ItemId))
            {
                stringToSign = stringToSign + "&ItemId=" + service.ItemId;
            }
            if (!string.IsNullOrEmpty(service.IdType))
            {
                stringToSign = stringToSign + "&IdType=" + service.IdType;
            }
            if (service.ResponseGroup.Length > 0)
            {
                stringToSign = stringToSign + "&ResponseGroup=";

                var lastResponse = service.ResponseGroup.Last();

                foreach (var response in service.ResponseGroup)
                {
                    stringToSign = stringToSign + response;
                    if (response != lastResponse)
                    {
                        stringToSign = stringToSign + ",";
                    }
                }
            }

            stringToSign = stringToSign + "&Version=" + service.Version;
            stringToSign = stringToSign + "&AWSAccessKeyId=" + service.AWSAccessKeyId + "&Timestamp=" + service.Timestamp;
            stringToSign = stringToSign.Replace(",", "%2C");
            stringToSign = stringToSign.Replace(":", "%3A");
            service.Url = service.Url + stringToSign;
            stringToSign = stringToSign + "&Service=AWSECommerceService";
            service.Signature = HmacSha256.SignAmazonRequest(service.SecretKey, stringToSign);           
            service.Url = service.Url + "&Signature=" + service.Signature;
            return service;

        }

    }

once the string is ready to be signed, i call a static mehod on a different class called HmacSha256.SignAmazonRequest where i pass my secretKey and the string to be signed,

    public static class HmacSha256
    {
        public static string SignAmazonRequest(string secretKey, string request)
        {
            var stringToSign = PrepareSignatureEncryption(request);
            var bytesToSign = Encoding.UTF8.GetBytes(stringToSign);
            var secretKeyBytes = Encoding.UTF8.GetBytes(secretKey);
            var hmacSha256 = new HMACSHA256(secretKeyBytes);
            var hashBytes = hmacSha256.ComputeHash(bytesToSign);

            return Convert.ToBase64String(hashBytes).Replace("+", "%2B").Replace("=", "%3D");
        }

        private static string PrepareSignatureEncryption(string request)
        {           

            var header = "GET" + Environment.NewLine
                         + "webservices.amazon.com" + Environment.NewLine
                         + "/once/xml" + Environment.NewLine;

            var result = header + request;
            return result;
        }
    }

inside the SignAmazonRequest method, i first prepare the string as amazon wants it to be,

i have a private helper method called PrepareSignatureEncryption it takes the string to be signed as an argument, and returns a ready formatted string as amazon shows in their documentation, then convert the string to bytes, i convert the secret key to bytes, generating an HMACSHA256 based on the secret key bytes, with that i compute a hash for the string to sign, i return the bytes converted to base64 string,

now with all that, here is my code in the static void main.

    class Program
    {
        static void Main(string[] args)
        {
            var restService = new AmazonRestService();
            restService.Operation = "ItemLookup";
            restService.IdType = "ASIN";
            restService.ItemId = "ASINTOLOOKUP";
            restService.ResponseGroup = new string[] { "BrowseNodes", "Images", "ItemAttributes", "Offers", "Reviews", "SalesRank" };

            var signedRequest = restService.SignAmazonRequest(restService);

            Console.WriteLine(signedRequest.Url);
            Console.ReadLine();

            try
            {
                var request = WebRequest.Create(signedRequest.Url);
                var response = request.GetResponse();
                var doc = new XmlDocument();
                doc.Load(response.GetResponseStream());
                doc.Save("C:/data.xml");


            }
            catch (Exception ex)
            {

                var msg = ex.Message;
            }
        }
    }

i am following this documentation.

any idea where i go wrong?

1
i tried on webservices.amazon.com/scratchpad/index.html and everything works over there, i tried to get it in my code, with the same timestamp, and everything looks exactly the same as on the scratchpad, sorted by bytes, and still the signature is different, anyone with some taughts on this? - Sol Stein

1 Answers

0
votes

The Documentation of Amazon is bad. But you can use this library Nager.AmazonProductAdvertising is available on nuget and github

PM> Install-Package Nager.AmazonProductAdvertising

Example

var authentication = new AmazonAuthentication();
authentication.AccessKey = "accesskey";
authentication.SecretKey = "secretkey";

var wrapper = new AmazonWrapper(authentication, AmazonEndpoint.US);
var result = wrapper.Search("canon eos", AmazonSearchIndex.Electronics, AmazonResponseGroup.Large);