Skip to content

Instantly share code, notes, and snippets.

@Martyr2 Martyr2/Webhook.cs
Created Nov 27, 2016

Embed
What would you like to do?
A C# .NET Web API class for handling webhooks from Chargify. Supply the class with request headers and the request body from a incoming webhook event to have it work its magic.
/// <summary>
/// Represents a webhook from Chargify.
/// </summary>
public class Webhook
{
private Dictionary<string, string> headerPayload = new Dictionary<string,string>();
private Dictionary<string, string> bodyPayload = new Dictionary<string, string>();
private string signatureHmacKey = string.Empty;
private string sharedSiteKey = <Chargify Store Site Key Here>;
public string Id { get; } = string.Empty;
public string Event { get; } = string.Empty;
public bool IsValid { get; } = false;
public Webhook(HttpRequestHeaders headers, string body)
{
SplitHeaders(headers);
SplitBody(body);
Id = GetPayloadValue("id");
Event = GetPayloadValue("event");
signatureHmacKey = GetHeaderValue("X-Chargify-Webhook-Signature-Hmac-Sha-256");
if (!string.IsNullOrEmpty(sharedSiteKey)) {
IsValid = signatureHmacKey.Equals(ComputeHmacHash(sharedSiteKey, body), StringComparison.InvariantCultureIgnoreCase);
}
}
#region Methods
/// <summary>
/// Returns a payload value for the given key if it exists
/// </summary>
/// <param name="key">Payload value to look for.</param>
/// <returns>Value for the specified key or empty string if not found.</returns>
public string GetPayloadValue(string key)
{
if (!string.IsNullOrWhiteSpace(key)) {
if (bodyPayload.ContainsKey(key.ToLower())) {
return bodyPayload[key.ToLower()];
}
}
return string.Empty;
}
/// <summary>
/// Returns the header value for the given header if it exists.
/// </summary>
/// <param name="header">Header to look up</param>
/// <returns>Value for the specified header or empty string if not found.</returns>
public string GetHeaderValue(string header)
{
if (!string.IsNullOrWhiteSpace(header)) {
if (headerPayload.ContainsKey(header)) {
return headerPayload[header];
}
}
return string.Empty;
}
#endregion
#region Helpers
/// <summary>
/// Computs the HMAC SHA-256 based on the supplied key and text.
/// </summary>
/// <param name="key">Hash key</param>
/// <param name="rawBody">Hash text</param>
/// <returns>String with the hash computation.</returns>
private string ComputeHmacHash(string key, string text)
{
System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
byte[] keyByte = encoding.GetBytes(key);
HMACSHA256 hmac256 = new HMACSHA256(keyByte);
byte[] messageByte = encoding.GetBytes(text);
byte[] hashMessage = hmac256.ComputeHash(messageByte);
return ByteToString(hashMessage);
}
/// <summary>
/// Converts a byte array into a string.
/// </summary>
/// <param name="buff">Buffer of bytes to convert into a string</param>
/// <returns>Returns the string</returns>
private string ByteToString(byte[] buff)
{
StringBuilder sbinary = new StringBuilder();
for (int i = 0; i < buff.Length; i++) {
sbinary.Append(buff[i].ToString("X2")); // hex format
}
return sbinary.ToString();
}
/// <summary>
/// Takes the raw list of headers and splits them into the webhook header dictionary.
/// </summary>
/// <param name="headers">Request headers from the webhook request</param>
private void SplitHeaders(HttpRequestHeaders headers)
{
foreach (var header in headers) {
headerPayload.Add(header.Key, string.Join(", ", header.Value.ToList()));
}
}
/// <summary>
/// Takes the raw body of the webhook request body and splits it into the webhook body dictionary.
/// </summary>
/// <param name="body">Body of the request.</param>
private void SplitBody(string body)
{
if (!string.IsNullOrWhiteSpace(body)) {
string[] payLoadEntries = body.Split(new[] { '&' }, StringSplitOptions.RemoveEmptyEntries);
bodyPayload = payLoadEntries.ToDictionary(s => s.Split('=')[0], s => s.Split('=')[1]);
}
}
#endregion
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.