Skip to content

Instantly share code, notes, and snippets.

@jakobt
Created January 19, 2015 20:06
Show Gist options
  • Select an option

  • Save jakobt/b7400463e90e16928f57 to your computer and use it in GitHub Desktop.

Select an option

Save jakobt/b7400463e90e16928f57 to your computer and use it in GitHub Desktop.
Consuming WooCommerce REST API from C#
//C# port of the https://github.com/kloon/WooCommerce-REST-API-Client-Library
//Including handling of woocommerce insisting on uppercase UrlEncoded entities
public class WoocommerceApiClient
{
private static byte[] HashHMAC(byte[] key, byte[] message)
{
var hash = new HMACSHA256(key);
return hash.ComputeHash(message);
}
private string Hash(string input)
{
using (SHA1Managed sha1 = new SHA1Managed())
{
var hash = sha1.ComputeHash(Encoding.UTF8.GetBytes(input));
var sb = new StringBuilder(hash.Length*2);
foreach (byte b in hash)
{
// can be "x2" if you want lowercase
sb.Append(b.ToString("X2"));
}
return sb.ToString();
}
}
public const string API_ENDPOINT = "wc-api/v1/";
public string ApiUrl { get; set; }
public string ConsumerSecret { get; set; }
public string ConsumerKey { get; set; }
public bool IsSsl { get; set; }
public WoocommerceApiClient(string consumerKey, string consumerSecret, string storeUrl, bool isSsl = false)
{
if (string.IsNullOrEmpty(consumerKey) || string.IsNullOrEmpty(consumerSecret) ||
string.IsNullOrEmpty(storeUrl))
{
throw new ArgumentException("ConsumerKey, consumerSecret and storeUrl are required");
}
this.ConsumerKey = consumerKey;
this.ConsumerSecret = consumerSecret;
this.ApiUrl = storeUrl.TrimEnd('/') + "/" + API_ENDPOINT;
this.IsSsl = isSsl;
}
public string GetAllProducts()
{
return MakeApiCall("products", new Dictionary<string, string>() {{"filter[limit]", "2000"}});
}
public string GetProducts()
{
return MakeApiCall("products");
}
private string MakeApiCall(string endpoint, Dictionary<string, string> parameters = null, string method = "GET")
{
if (parameters == null)
{
parameters = new Dictionary<string, string>();
}
parameters["oauth_consumer_key"] = this.ConsumerKey;
parameters["oauth_timestamp"] =
DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds.ToString();
parameters["oauth_timestamp"] = parameters["oauth_timestamp"].Substring(0,
parameters["oauth_timestamp"].IndexOf("."));
parameters["oauth_nonce"] = Hash(parameters["oauth_timestamp"]);
parameters["oauth_signature_method"] = "HMAC-SHA256";
parameters["oauth_signature"] = GenerateSignature(parameters, method, endpoint);
WebClient wc = new WebClient();
StringBuilder sb = new StringBuilder();
foreach (var pair in parameters)
{
sb.AppendFormat("&{0}={1}", HttpUtility.UrlEncode(pair.Key), HttpUtility.UrlEncode(pair.Value));
}
var url = this.ApiUrl + endpoint + "?" + sb.ToString().Substring(1).Replace("%5b","%5B").Replace("%5d","%5D");
var result = wc.DownloadString(url);
return result;
}
private string GenerateSignature(Dictionary<string, string> parameters, string method, string endpoint)
{
var baserequesturi = Regex.Replace(HttpUtility.UrlEncode(this.ApiUrl + endpoint), "(%[0-9a-f][0-9a-f])", c => c.Value.ToUpper());
var normalized = NormalizeParameters(parameters);
var signingstring = string.Format("{0}&{1}&{2}", method, baserequesturi,
string.Join("%26", normalized.OrderBy(x => x.Key).ToList().ConvertAll(x => x.Key + "%3D" + x.Value)));
var signature =
Convert.ToBase64String(HashHMAC(Encoding.UTF8.GetBytes(this.ConsumerSecret),
Encoding.UTF8.GetBytes(signingstring)));
Console.WriteLine(signature);
return signature;
}
private Dictionary<string, string> NormalizeParameters(Dictionary<string, string> parameters)
{
var result = new Dictionary<string, string>();
foreach (var pair in parameters)
{
var key = HttpUtility.UrlEncode(HttpUtility.UrlDecode(pair.Key));
key = Regex.Replace(key, "(%[0-9a-f][0-9a-f])", c => c.Value.ToUpper()).Replace("%", "%25");
var value = HttpUtility.UrlEncode(HttpUtility.UrlDecode(pair.Value));
value = Regex.Replace(value, "(%[0-9a-f][0-9a-f])", c => c.Value.ToUpper()).Replace("%", "%25");
result.Add(key, value);
}
return result;
}
}
@spielinger

Copy link
Copy Markdown

Hi Jakob.
When trying to request other endpoints, for example
/products/count
/products/categories/{id}
the calls could not be oauth authenticated because of a wrong signature. I did not yet fully understand what you are doing in the GenerateSignature method, but the problems seem to be there. Could you try the above endpoints and maybe find or explain the problem...
Thanks

@LilianDyer

Copy link
Copy Markdown

The GenerateSignature is generating a good signature. In my case, the problem was after it comes back, the HttpUtility.UrlEncode will encode the signature to lower case hex. So if it contains a '+' it will encode it to '%2b' but Woo Commerce is expecting '%2B'.

@shahshyam

Copy link
Copy Markdown

Hi ALL,
I completed GET,POST,DELETE and PUT operation in Woocommerce using C#. Let me know if anyone need help
Bsic Req: https enable

@itzRamkumar

Copy link
Copy Markdown

I am trying to update the status of an order. Here is the code i tried...

string orders = client.updateOrderStatus("180");

public string updateOrderStatus(string orderNo)
{
return updateStatus("orders/" + orderNo);
}

private string updateStatus(string endpoint, Dictionary<string, string> parameters = null, string method = "PUT")
{
if (parameters == null)
{
parameters = new Dictionary<string, string>();
}
parameters["oauth_consumer_key"] = this.ConsumerKey;
parameters["oauth_timestamp"] =
DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds.ToString();
parameters["oauth_timestamp"] = parameters["oauth_timestamp"].Substring(0,
parameters["oauth_timestamp"].IndexOf("."));
parameters["oauth_nonce"] = Hash(parameters["oauth_timestamp"]);
parameters["oauth_signature_method"] = "HMAC-SHA256";
parameters["oauth_signature"] = GenerateSignature(parameters, method, endpoint);
WebClient wc = new WebClient();
StringBuilder sb = new StringBuilder();
foreach (var pair in parameters)
{
sb.AppendFormat("&{0}={1}", HttpUtility.UrlEncode(pair.Key), HttpUtility.UrlEncode(pair.Value));
}
var url = this.ApiUrl + endpoint + "?" + sb.ToString().Substring(1).Replace("%5b", "%5B").Replace("%5d", "%5D");
//var result = wc.DownloadString(url);
wc.UploadString(url, "{"order":{"status":"completed"}}");
return "";
}

It will throw the Exception

[WebException: The remote server returned an error: (401) Unauthorized.]
System.Net.WebClient.UploadDataInternal(Uri address, String method, Byte[] data, WebRequest& request) +309
System.Net.WebClient.UploadString(Uri address, String method, String data) +172
System.Net.WebClient.UploadString(String address, String data) +34
API.WoocommerceApiClient.updateStatus(String endpoint, Dictionary`2 parameters, String method) in E:\C# Mini Projects\API\API\WoocommerceApiClient.cs:126
API.WoocommerceApiClient.updateStatus1(String orderNo) in E:\C# Mini Projects\API\API\WoocommerceApiClient.cs:76
API.WebForm1.Page_Load(Object sender, EventArgs e) in E:\C# Mini Projects\API\API\WebForm1.aspx.cs:23
System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr fp, Object o, Object t, EventArgs e) +14
System.Web.Util.CalliEventHandlerDelegateProxy.Callback(Object sender, EventArgs e) +35
System.Web.UI.Control.OnLoad(EventArgs e) +91
System.Web.UI.Control.LoadRecursive() +74
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +2207

Help to solve this.. I am using v2 Endpoint

@manisha0510

Copy link
Copy Markdown

@shahshyam I am trying to do GET, PUT, POST and DELETE operations on WooCommerce using C#. It would be great if you could help me.

@itzRamkumar

Copy link
Copy Markdown

@manisha0510 the above code will work for GET Method u can check that.. In PUT Method it returns an error.

@manisha0510

Copy link
Copy Markdown

@itzRamkumar I am getting 404NotFound even for GET method

@shahshyam

Copy link
Copy Markdown

yes, Woocommerce performs PUT Operation in https enable site.

@shahshyam

Copy link
Copy Markdown

I have wrapper for CRUD operation. If you are here let me contact for small donation

@itzRamkumar

Copy link
Copy Markdown

@shahshyam how to run PUT Method in localhost.. I just learned let me how it works..

@manisha0510 If u finish the PUT method please share the code it may help to others..

@TOuhrouche

Copy link
Copy Markdown

I am really having hard time even to get the GET request working :(
I am receiving an HTTP/1.1 302 Found all the time. Anyone faced this issue before?

@Groot90

Groot90 commented Nov 29, 2015

Copy link
Copy Markdown

Recently, I have tried quite a few things to solve numerous issues connecting to my WC API. In the end, this library was my ultimate choice. However, it seems that it lacks compatibility with v3 version of WC API. All you need to do to be able to connect to v3 API is this: Just add a "&" sign to consumer key at line 89! I have made a VB.NET v3 port of this nice library, with an additional try / catch block to catch the full exception code. You can check it here: http://www.softwarehorizont.com/2015/11/woocommerce-api-v3-vbnet-c-library.html . Hope it helps someone!

@Rey68

Rey68 commented Dec 3, 2015

Copy link
Copy Markdown

Hi:
I have this error in the code:
Excepción no controlada del tipo 'System.ArgumentOutOfRangeException' en mscorlib.dll

Additional information: Length can not be less than zero. Line 71

Why?

@diegoaraujolima

Copy link
Copy Markdown

i have problems with PUT. Anyone can post example?

@Groot90

Groot90 commented Dec 4, 2015

Copy link
Copy Markdown

@diegoaraujolima:

You can use WebClient's UploadData method (instead of DownloadString).

Something like this (VB.NET):

'uploadData is for the body of http PUT request (for example, JSON data )

Dim dataArr() As Byte = Encoding.UTF8.GetBytes(uploadData)
wc.UploadData(url, "PUT", dataArr)

@Rey68:

Are you sure the error is exactly at line 71? It looks like your parameters[] array is empty.

@diegoaraujolima

Copy link
Copy Markdown

@Groot90, I did what you suggested, but occours the error 403. Can you help me?

@Groot90

Groot90 commented Dec 4, 2015

Copy link
Copy Markdown

@diegoaraujolima
That error is probably related to your server's configuration. Maybe this thread could help: woocommerce/woocommerce#6529

@diegoaraujolima

Copy link
Copy Markdown

Resolved. Add Header wc.Headers.Add("Content-Type", "application/json");. Works! Thankz!

@Groot90

Groot90 commented Dec 4, 2015

Copy link
Copy Markdown

@diegoaraujolima
Great! Glad to hear you've fixed it.

To everyone:
Be sure that line #65 is using the proper time delimiter for your time zone. It can be a dot, but it also can be a comma character.

@putuyoga

putuyoga commented Jan 5, 2016

Copy link
Copy Markdown

For anyone who want to simplify the things, just use WoocommerceSharp

@danielgamajpa

Copy link
Copy Markdown

To use the PUT / POST I created a new method (named for MakeApiCallPUT ()) and function equivalent to line 77

var result = wc.DownloadString (url);
I put:
var result = wc.UploadString (url, "PUT", dadosJSON);

where dadosJSON is JSON content that will be sent to update.

Full function was as follows:

 private string MakeApiCallPUT(string endpoint, string dadosJSON = null, string method = "PUT")
        {

            Dictionary<string, string> parameters = null;

            if (parameters == null)
            {
                parameters = new Dictionary<string, string>();
            }
            parameters["oauth_consumer_key"] = this.ConsumerKey;

            //parameters["oauth_timestamp"] = DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds.ToString();
            //parameters["oauth_timestamp"] = parameters["oauth_timestamp"].Substring(0, parameters["oauth_timestamp"].IndexOf(","));

            parameters["oauth_timestamp"] = ((DateTime.UtcNow.Ticks - new DateTime(1970, 1, 1).Ticks) / (1000 * 10000)).ToString();

            parameters["oauth_nonce"] = Hash(parameters["oauth_timestamp"]);
            parameters["oauth_signature_method"] = "HMAC-SHA256";
            parameters["oauth_signature"] = GenerateSignature(parameters, method, endpoint);
            WebClient wc = new WebClient();
            StringBuilder sb = new StringBuilder();

            foreach (var pair in parameters)
            {
                sb.AppendFormat("&{0}={1}", HttpUtility.UrlEncode(pair.Key), HttpUtility.UrlEncode(pair.Value));
            }
            var url = this.ApiUrl + endpoint + "?" + sb.ToString().Substring(1).Replace("%5b", "%5B").Replace("%5d", "%5D").Replace("%7b", "%7B").Replace("%7d", "%7D").Replace("%3a", "%3A").Replace("%2c", "%2C");
            //var result = wc.DownloadString(url);
            var result = wc.UploadString(url, "PUT", dadosJSON);
            return result;
        }

@shahshyam

Copy link
Copy Markdown

Hi
I have Create Product , Customer and many other item in woocommerce . Please contact me https://www.upwork.com/users/~01b266b20bfa60411d

@kapildesai

Copy link
Copy Markdown

Can any body provide a solution for PUT or POST errors....Not able to solve the same......I can Fetch the products but cannot Update a product.

@shahshyam

Copy link
Copy Markdown

@kapildesai I will help you

@mbrn01

mbrn01 commented Sep 7, 2017

Copy link
Copy Markdown

@shahshyam can you help?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment