0

Has anyone been able to successfully implement the Amazon Products API in C#?

This is a conversion of the PHP code sample on the scratchpad website.

I get this response:

{"Output":{"__type":"com.amazon.coral.service#InternalFailure","message":null},"Version":"1.0"}

Preferably there should be an SDK to make this easier, however as of 2023 Amazon doesn't seem to offer an SDK for the products API.

public class Program
{
    static async Task Main(string[] args)
    {
        string serviceName = "ProductAdvertisingAPI";
        string region = "us-east-1";
        string accessKey = "**************";
        string secretKey = "**************";
        string payload = "{"
            + "\"Keywords\": \"notebook\","
            + "\"PartnerTag\": \"x0d-20\","
            + "\"PartnerType\": \"Associates\","
            + "\"Marketplace\": \"www.amazon.com\""
            + "}";
        string host = "webservices.amazon.com";
        string uriPath = "/paapi5/searchitems";
        AwsV4 awsv4 = new AwsV4(accessKey, secretKey);
        awsv4.SetRegionName(region);
        awsv4.SetServiceName(serviceName);
        awsv4.SetPath(uriPath);
        awsv4.SetPayload(payload);
        awsv4.SetRequestMethod("POST");
        awsv4.AddHeader("content-encoding", "amz-1.0");
        awsv4.AddHeader("content-type", "application/json; charset=utf-8");
        awsv4.AddHeader("host", host);
        awsv4.AddHeader("x-amz-target", "com.amazon.paapi5.v1.ProductAdvertisingAPIv1.SearchItems");
        Dictionary<string, string> headers = awsv4.GetHeaders();

        using (HttpClient client = new HttpClient())
        {
            HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, $"https://{host}{uriPath}");
            foreach (var header in headers)
            {
                request.Headers.TryAddWithoutValidation(header.Key, header.Value);
            }
            request.Content = new StringContent(payload, Encoding.UTF8, "application/json");

            HttpResponseMessage response = await client.SendAsync(request);

            string responseContent = await response.Content.ReadAsStringAsync();
            Console.WriteLine(responseContent);
        }
        Console.ReadLine();
    }

    public class AwsV4
    {
        private string accessKey = null;
        private string secretKey = null;
        private string path = null;
        private string regionName = null;
        private string serviceName = null;
        private string httpMethodName = null;
        private Dictionary<string, string> queryParameters = new Dictionary<string, string>();
        private Dictionary<string, string> awsHeaders = new Dictionary<string, string>();
        private string payload = "";

        private string HMACAlgorithm = "AWS4-HMAC-SHA256";
        private string aws4Request = "aws4_request";
        private string strSignedHeader = null;
        private string xAmzDate = null;
        private string currentDate = null;

        public AwsV4(string accessKey, string secretKey)
        {
            this.accessKey = accessKey;
            this.secretKey = secretKey;
            this.xAmzDate = GetTimeStamp();
            this.currentDate = GetDate();
        }

        public void SetPath(string path)
        {
            this.path = path;
        }

        public void SetServiceName(string serviceName)
        {
            this.serviceName = serviceName;
        }

        public void SetRegionName(string regionName)
        {
            this.regionName = regionName;
        }

        public void SetPayload(string payload)
        {
            this.payload = payload;
        }

        public void SetRequestMethod(string method)
        {
            this.httpMethodName = method;
        }

        public void AddHeader(string headerName, string headerValue)
        {
            this.awsHeaders[headerName] = headerValue;
        }

        private string PrepareCanonicalRequest()
        {
            StringBuilder canonicalURL = new StringBuilder();
            canonicalURL.Append(httpMethodName).Append("\n");
            canonicalURL.Append(path).Append("\n").Append("\n");
            string signedHeaders = "";
            foreach (var header in awsHeaders)
            {
                signedHeaders += header.Key + ";";
                canonicalURL.Append(header.Key).Append(":").Append(header.Value).Append("\n");
            }
            canonicalURL.Append("\n");
            strSignedHeader = signedHeaders.TrimEnd(';');
            canonicalURL.Append(strSignedHeader).Append("\n");
            canonicalURL.Append(GenerateHex(payload));
            return canonicalURL.ToString();
        }

        private string PrepareStringToSign(string canonicalURL)
        {
            StringBuilder stringToSign = new StringBuilder();
            stringToSign.Append(HMACAlgorithm).Append("\n");
            stringToSign.Append(xAmzDate).Append("\n");
            stringToSign.Append(currentDate).Append("/").Append(regionName).Append("/").Append(serviceName).Append("/").Append(aws4Request).Append("\n");
            stringToSign.Append(GenerateHex(canonicalURL));
            return stringToSign.ToString();
        }

        private string CalculateSignature(string stringToSign)
        {
            byte[] signatureKey = GetSignatureKey(secretKey, currentDate, regionName, serviceName);
            byte[] signature = HashHMAC(Encoding.UTF8.GetBytes(stringToSign), signatureKey);
            string strHexSignature = BitConverter.ToString(signature).Replace("-", "").ToLower();
            return strHexSignature;
        }

        public Dictionary<string, string> GetHeaders()
        {
            awsHeaders["x-amz-date"] = xAmzDate;
            SortedDictionary<string, string> sortedHeaders = new SortedDictionary<string, string>(awsHeaders);
            StringBuilder canonicalURL = new StringBuilder(PrepareCanonicalRequest());
            string stringToSign = PrepareStringToSign(canonicalURL.ToString());
            string signature = CalculateSignature(stringToSign);

            awsHeaders["Authorization"] = BuildAuthorizationString(signature);

            return awsHeaders;
        }

        private string BuildAuthorizationString(string strSignature)
        {
            return HMACAlgorithm + " " + "Credential=" + accessKey + "/" + GetDate() + "/" + regionName + "/" + serviceName + "/" + aws4Request + "," + "SignedHeaders=" + strSignedHeader + "," + "Signature=" + strSignature;
        }

        private string GenerateHex(string data)
        {
            using (SHA256 sha256Hash = SHA256.Create())
            {
                byte[] bytes = sha256Hash.ComputeHash(Encoding.UTF8.GetBytes(data));
                StringBuilder builder = new StringBuilder();
                for (int i = 0; i < bytes.Length; i++)
                {
                    builder.Append(bytes[i].ToString("x2"));
                }
                return builder.ToString();
            }
        }

        private byte[] GetSignatureKey(string key, string date, string regionName, string serviceName)
        {
            byte[] kSecret = Encoding.UTF8.GetBytes("AWS4" + key);
            byte[] kDate = HashHMAC(Encoding.UTF8.GetBytes(date), kSecret);
            byte[] kRegion = HashHMAC(Encoding.UTF8.GetBytes(regionName), kDate);
            byte[] kService = HashHMAC(Encoding.UTF8.GetBytes(serviceName), kRegion);
            byte[] kSigning = HashHMAC(Encoding.UTF8.GetBytes(aws4Request), kService);

            return kSigning;
        }

        private byte[] HashHMAC(byte[] data, byte[] key)
        {
            using (HMACSHA256 hmac = new HMACSHA256(key))
            {
                return hmac.ComputeHash(data);
            }
        }

        private string GetTimeStamp()
        {
            return DateTime.UtcNow.ToString("yyyyMMdd\\THHmmss\\Z");
        }

        private string GetDate()
        {
            return DateTime.UtcNow.ToString("yyyyMMdd");
        }
    }
}
  • maybe this helps ? https://techiesalman.medium.com/how-to-use-amazon-product-advertising-api-in-c-net-core-507435d8851d – jeb Jun 16 '23 at 15:13
  • No, that uses the Nager SDK pack – QuietWorld Jun 16 '23 at 16:24
  • I modified the Nager SDK and was able to get it to work a year or so ago. Someone should really fork that repo and bring it back to life. – jon.r Jul 10 '23 at 23:26

0 Answers0