Loading ...

Walmart Signer

Hello everybody,

today I want to preserve at my blog piece of code that almost eaten my brain. Walmart signer. 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Globalization;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;

namespace Walmart
{
    public class WallmartSigner
    {
        /// <summary>
        /// Begining of time according to Java world.
        /// </summary>
        private static readonly DateTime JanuaryFirst1970 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

        /// <summary>
        /// Initializes a new instance of the <see cref="WallmartSigner"/> class. 
        /// Default Constructor for Object initializer
        /// </summary>
        public WallmartSigner()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="WallmartSigner"/> class.
        /// </summary>
        /// <param name="consumerId">
        /// The Consumer ID provided by Developer Portal.
        /// </param>
        /// <param name="privateKey">
        /// The Base64 Encoded Private Key provided by Developer Portal.
        /// </param>
        /// <param name="requestUrl">
        /// The URL of API request being made.
        /// </param>
        /// <param name="requestMethod">
        /// For signer you'll need get
        /// </param>
        public WallmartSigner(string consumerId, string privateKey, string requestUrl, string requestMethod)
        {
            this.ConsumerId = consumerId;
            this.PrivateKey = privateKey;
            this.RequestUrl = requestUrl;
            this.RequestMethod = requestMethod;
        }

        /// <summary>
        /// Consumer ID provided by Developer Portal. Looks like GUID
        /// </summary>
        public string ConsumerId { get; set; }

        /// <summary>
        /// Longest string at developer portal
        /// </summary>
        public string PrivateKey { get; set; }

        /// <summary>
        /// Something like https://marketplace.walmartapis.com/v3/orders?createdStartDate=2016-08-16
        /// </summary>
        public string RequestUrl { get; set; }

        /// <summary>
        /// Default value for wallmart signing is GET
        /// </summary>
        [DefaultValue("GET")]
        public string RequestMethod { get; set; }

        /// <summary>
        /// Gets the time stamp used for the signature.
        /// </summary>
        public string TimeStamp { get; private set; }

        /// <summary>
        /// Get the signature based on the timestamp.  If the timestamp is null, create
        /// a new timestamp.
        /// </summary>
        /// <param name="timeStamp">
        /// The time stamp.
        /// </param>
        /// <returns>
        /// The calculate signature <see cref="string"/>.
        /// </returns>
        public string GetWallmartSignature(string timeStamp)
        {
            TimeStamp = timeStamp ?? GetTimestampInJavaMillis();

            // Append values into string for signing
            var message = ConsumerId + "\n" + RequestUrl + "\n" + RequestMethod.ToUpper() + "\n" + TimeStamp + "\n";

            RsaKeyParameters rsaKeyParameter;
            try
            {
                var keyBytes = Convert.FromBase64String(this.PrivateKey);
                var asymmetricKeyParameter = PrivateKeyFactory.CreateKey(keyBytes);
                rsaKeyParameter = (RsaKeyParameters)asymmetricKeyParameter;
            }
            catch (Exception)
            {
                throw new Exception("Unable to load private key");
            }

            var signer = SignerUtilities.GetSigner("SHA256withRSA");
            signer.Init(true, rsaKeyParameter);
            var messageBytes = Encoding.UTF8.GetBytes(message);
            signer.BlockUpdate(messageBytes, 0, messageBytes.Length);
            var signed = signer.GenerateSignature();
            var hashed = Convert.ToBase64String(signed);
            return hashed;
        }

        /// <summary>
        /// Get the TimeStamp as a string equivalent to Java System.currentTimeMillis
        /// </summary>
        /// <returns>
        /// Generated sign string
        /// </returns>
        public static string GetTimestampInJavaMillis()
        {
            var millis = (DateTime.UtcNow - JanuaryFirst1970).TotalMilliseconds;
            return Convert.ToString(Math.Round(millis), CultureInfo.InvariantCulture);
        }
    }
}

With this class you'll be able to get signed string for your Walmart api.

If you found this deep dive into Acumatica development and the Walmart API integration helpful, why not take your customization game to the next level? Whether you need a tailored solution for your business or have a unique integration challenge, our team is here to help. Leave a customization request today and let’s turn your ideas into reality. Together, we can build solutions as robust and precise as the Walmart signer code above! 

6 Comments

  • Jeff Partin said 22 months ago

    Thank you so much for publishing this. You've saved me a month's worth of head-banging for my Walmart Direct implementation project. It is working perfectly. Thanks again.

  • docotor said 22 months ago

    You are warmly welcomed. Happy that was able to help you

  • Beau Claar said 22 months ago

    Thanks for posting the code. Much better than using the JAR solution and having to pipe command lines.

  • docotor said 22 months ago

    Yep. My first implementation was including jar

  • Faisal Pathan said 15 months ago

    Org.BouncyCastle.Crypto.Parameters

    its missing from where I can download.I'm new please help

  • docotor said 15 months ago

    Hi Faisal Pathan, you can download it at nuget as I've mentioned in mine email to you.

 

Be the first to rate this post

  • Currently 0.0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5