12

I am trying to create a WebHookHandler for Webhooks send from WordPress WooCommerce in ASP.NET C#.

I started with creating a ASP.NET C# Azure API App WebApplication Project and adding the relevant references (Microsoft.AspNet.WebHooks.Common, Microsoft.AspNet.WebHooks.Receivers, Microsoft.AspNet.WebHooks.Receivers.WordPress). Added the WebHookConfig, WordPressWebHookHandler and registered the WebHookConfig in the GlobalAsax.

I then published the application as an Azure App Service.

My WordPressWebHookHandler is still the default of the examples and looks like this:

public class WordPressWebHookHandler : WebHookHandler
{
    public override Task ExecuteAsync(string receiver, WebHookHandlerContext context)
    {
        // make sure we're only processing the intended type of hook
        if("WordPress".Equals(receiver, System.StringComparison.CurrentCultureIgnoreCase))
        {
            // todo: replace this placeholder functionality with your own code
            string action = context.Actions.First();
            JObject incoming = context.GetDataOrDefault<JObject>();
        }

        return Task.FromResult(true);
    }
}

When testing a User Creation WebHook in WooCommerce I can see the request in the log as below.

Webhook request log

But unfortunately it is never received while debugging and I see below error.

Webook request log error

I am thinking maybe I need a custom WebHook instead of the WordPress specific one as this is a WooCommerce Webhook. Or possibly it is handled wrong in the routing and ends up in another controller.

Any help is much appreciated.

Dhruvin Moradiya
  • 546
  • 2
  • 10
  • 20
Kevin Hendricks
  • 785
  • 1
  • 8
  • 36

3 Answers3

5

Your WebHookReceiver is wrong

There is a mismatch of expecting HTML Form Data, when in fact it should be expecting JSON.

WordPressWebHookHandler is still the default

This is what is causing your error. If you look at the WordPressWebHookReceiver, the ReceiveAsync() method implementation, calls out to ReadAsFormDataAsync() method, which is not what you want, as your Content-Type is json. So, you want to be doing ReadAsJsonAsync().

Solution: Don't use the WordPressWebHookReceiver and switch it to another one that will call ReadAsJsonAsync().


Looking at the code

I am thinking maybe I need a custom WebHook instead of the WordPress specific one as this is a WooCommerce Webhook.

You had the right idea, so I dug up some of the code to explain exactly why this was happening.

The code block below is the ReceiveAsync() method that is overridden in the WordPressWebHookReceiver. You can see that it is calling the ReadAsFormDataAsync() which is not what you want...

public override async Task<HttpResponseMessage> ReceiveAsync(
    string id, HttpRequestContext context, HttpRequestMessage request)
{
    ...
    if (request.Method == HttpMethod.Post)
    {
        // here is what you don't want to be called
        // you want ReadAsJsonAsync(), In short, USE A DIFFERENT RECEIVER.
        NameValueCollection data = await ReadAsFormDataAsync(request);
        ...
    }
    else
    {
       return CreateBadMethodResponse(request);
    }
}

A quick search through the repository for classes that call the ReadAsJsonAsync() method, shows that the following recievers implement it:

  1. DynamicsCrmWebHookReceiver
  2. ZendeskWebHookReceiver
  3. AzureAlertWebHookReceiver
  4. KuduWebHookReceiver
  5. MyGetWebHookReceiver
  6. VstsWebHookReceiver
  7. BitbucketWebHookReceiver
  8. CustomWebHookReceiver
  9. DropboxWebHookReceiver
  10. GitHubWebHookReceiver
  11. PaypalWebHookReceiver
  12. StripeWebHookReceiver
  13. PusherWebHookReceiver

I assumed that the CustomWebHookReceiver would fit your requirements, so can grab the NuGet here. Otherwise you can implement your own, or derive it from this class, etc.


Configuring a WebHook Recevier

(Copied from the Microsoft Documentation)

Microsoft.AspNet.WebHooks.Receivers.Custom provides support for receiving WebHooks generated by ASP.NET WebHooks

Out of the box you can find support for Dropbox, GitHub, MailChimp, PayPal, Pusher, Salesforce, Slack, Stripe, Trello, and WordPress but it is possible to support any number of other providers

Initializing a WebHook Receiver

WebHook Receivers are initialized by registering them, typically in the WebApiConfig static class, for example:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        ...

        // Load receivers
        config.InitializeReceiveGitHubWebHooks();
    }
}
Svek
  • 12,350
  • 6
  • 38
  • 69
  • I dont understand one thing in there. I'm implementing this in my web api project for zendesk? do i have to create a controller with route api/webhooks/incoming. and how do i call WebHookHandler class? – coder771 Oct 16 '18 at 08:20
  • @coder771 You might want to consider searching online a bit, as there are a lot of different ways you can implement WebHooks in your project. I would start with understanding how the request pipeline mechanics work in asp net core before making the conclusion you need a controller. – Svek Oct 16 '18 at 09:59
1

There is a problem with the data format that you send in your request. You must use format of HTML Form as your error message said.

Proper POST data format is described here: How are parameters sent in an HTTP POST request?

Don't forget to set Content-Length header and correct Content-Type if your library doesn't do it. Usually the content type is application/x-www-form-urlencoded.

Community
  • 1
  • 1
Alexander Ushakov
  • 5,139
  • 3
  • 27
  • 50
  • Hello Alexander, Thank you for your reply. Unfortunately Woocommerce / Wordpress sends the content in Json out-of-the-box which is also my preferred standard. I see many posts about receiving Json with ASP.NET Webhooks in general, however it does not seem to work for me. – Kevin Hendricks Feb 20 '17 at 08:43
1

I would like to make some additions to Svek's answer as I now got my Proof-of-concept completed and understand a bit more about the receivers.

His answer pointed me in the right direction, but needs a little addition.

WordpressWebHookReceiver Can take in Wordpress Webhooks of type HttpPost. This does not work with Woocommerce as Woocommerce sends Json Webhook messages and will fail the HttpPost validation which is build into the WordpressWebHookReceiver class.

CustomWebHookReceiver Can take in custom ASP.NET Webhooks. The custom ASP.NET webhooks have a specific partner for validation which includes but is not limited to the 'ms-signature'. Even adding the header will not suffice as the signature is also used in a different way from out of the box Woocommerce to encrypt the message. Basically coming to a point that you can't integrate Woocommerce with the CustomWebHookReceiver without changing the Webhook classes of Woocommerce.

GenericWebHookReceiver This is the receiver you want, which accepts basically a generic set of Json data and will be able to use the "code" query parameter to verify the secret which you can add in the web.config of your asp.net api application. I used this receiver to finish the Proof-of-concept and got both the signature validation as well as the deciphering of the message working right of the bat.

My basic class which I will start to build into a real solution can be viewed below and changes the JObject into a dynamic object in the methods I call from the class. As you can see I have two methods currently added, one for the customer create and one for the order create to call the respective methods which do an insert into Dynamics 365 (former CRM).

public class GenericJsonWebHookHandler : WebHookHandler
{
    public GenericJsonWebHookHandler()
    {
        this.Receiver = "genericjson";
    }

    public override Task ExecuteAsync(string generator, WebHookHandlerContext context)
    {
        var result = false;

        try
        {
            // Get JSON from WebHook
            var data = context.GetDataOrDefault<JObject>();

            if(context.Id != "crcu" && context.Id != "cror")
                return Task.FromResult(true);

            if (context.Id == "crcu")
            {
                result = WoocommerceCRMIntegrations.Entities.Contact.CreateContactInCRM(data);
            }
            else if (context.Id == "cror")
            {
                result = WoocommerceCRMIntegrations.Entities.Order.CreateOrderInCRM(data);
            }
        }
        catch (Exception ex)
        {
            result = false;
        }


        return Task.FromResult(result);
    }
}
Kevin Hendricks
  • 785
  • 1
  • 8
  • 36
  • No problem I figured you deserve the credits for the extensive answer which pointed me to the right direction. THank you again – Kevin Hendricks Feb 28 '17 at 15:14
  • Hello. I'm using WooComm 4.0.1 and I have tried our approach but it does not work for me at all. "Error: Delivery URL returned response code: 415" If I go and trigger the hook anyway I see it went thru in the wordpress logs. But on the .net side says in log: w3wp.exe Information: 0 : Processing incoming WebHook request with receiver 'genericjson' and id 'crcpn'. w3wp.exe Information: 0 : The WebHook request must contain an entity body formatted as JSON. Not sure where I'm wrong. Help Please! – Mudasser Mian May 12 '20 at 15:45