How to sign Walmart API request headers

Currently I am in the process of integrating Walmart API into our Home Resource Planner portal. We are building functionality to search products in Walmart online store and integrate them directly with shopping lists generated from the application. As part of the process, I built a library that sends API request to Walmart.

Walmart has very specific requirements about the headers that need to be included in the request. The critical part of that process is signing of the values and included correct signature in the header. I wrote some POC code as console application to share with our development community.

namespace Am.Console
{
    class Program
    {
        private static IConfigurationRoot _configuration;
        static void Main(string[] args)
        {
            _configuration = new ConfigurationBuilder().AddJsonFile("appsettings.json",true, true).Build();

            var httpClient = new HttpClient();
            AddRequestHeaders(httpClient);
            var requestUri = 
               new Uri("https://developer.api.walmart.com/api-proxy/service/affil/product/v2/items?upc=035000521019");

            var getTask = Task.Run(async () => await httpClient.GetAsync(requestUri));
            var response = getTask.Result;
            var content = Task.Run(async () => await response.Content.ReadAsStringAsync());
            switch (response.StatusCode)
            {
                case HttpStatusCode.OK:
                    System.Console.WriteLine($"Result: {content.Result}");
                    break;
                default:
                    System.Console.WriteLine($"Error: {response.StatusCode}: {content.Result}");
                    break;
            }
        }

        static void AddRequestHeaders(HttpClient client)
        {
            var parameterValueBuffer = new StringBuilder();
            var parameterNamesBuffer = new StringBuilder();
            var headerKeyMap = GenerateHeaderKeyMap();
            var keySet = headerKeyMap.Keys.ToImmutableSortedSet();
            foreach (var key in keySet)
            {
                var headerValue = headerKeyMap[key];
                System.Console.WriteLine($"{key}:{headerValue}");
                client.DefaultRequestHeaders.Add(key, headerValue);
                parameterNamesBuffer.Append($"{key.Trim()};");
                parameterValueBuffer.Append($"{headerValue.Trim()}\n");
            }
            var canonicalHeaders = new string[] { parameterNamesBuffer.ToString(), parameterValueBuffer.ToString() };
            AddSignatureHeader(client, canonicalHeaders);
        }

        static Dictionary GenerateHeaderKeyMap()
        {
            var dict = new Dictionary
            {
                ["WM_CONSUMER.ID"] = $"{ _configuration["consumerKey"] }",
                ["WM_SEC.KEY_VERSION"] = "1",
                ["WM_CONSUMER.INTIMESTAMP"] = $"{DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}"
            };
            return dict;
        }
        private static string[] CanonicalizeHeaders(Dictionary headersToSign)
        {
            var parameterValueBuffer = new StringBuilder();
            var parameterNamesBuffer = new StringBuilder();
            var keySet = headersToSign.Keys.ToImmutableSortedSet();
            foreach (var key in keySet)
            {
                var headerValue = headersToSign[key];
                parameterNamesBuffer.Append($"{key.Trim()};");
                parameterValueBuffer.Append($"{headerValue.Trim()}\n");
            }

            return new string[] { parameterNamesBuffer.ToString(), parameterValueBuffer.ToString() };
        }

        private static void AddSignatureHeader(HttpClient client, string[] keysToSign)
        {
            var signature = GenerateKeySignature(keysToSign[1]);
            System.Console.WriteLine($"WM_SEC.AUTH_SIGNATURE:{signature}");
            client.DefaultRequestHeaders.Add("WM_SEC.AUTH_SIGNATURE", signature);
        }

        private static string GenerateKeySignature(string headerValuesToSign)
        {
            var key = File.ReadAllText("PrivateKey.txt");
            var encodedKey = Convert.FromBase64String(key);
            var asymmetricKeyParameter = PrivateKeyFactory.CreateKey(encodedKey);
            var rsaKey = (RsaKeyParameters)asymmetricKeyParameter;
            var dataSigner = SignerUtilities.GetSigner("SHA256withRSA");
            dataSigner.Init(true, rsaKey);
            var dataStream = Encoding.UTF8.GetBytes(headerValuesToSign);
            dataSigner.BlockUpdate(dataStream, 0, dataStream.Length);
            var signature = dataSigner.GenerateSignature();
            var encodedSignature = Convert.ToBase64String(signature);
            return encodedSignature;
        }
    }
}

I used BouncyCastle library to sign request headers. Make sure that you have included that NuGet package in your project.

Search

Social

Weather

22.6 °C / 72.7 °F

weather conditions Clouds

Monthly Posts

Blog Tags