问题
Im trying to implement a stripe webhook using the the c# library Stripe.net by Jayme Davis. i have set up the test endpoint in the stripe dashboard and generated the secret. The endpoint is being hit fine, and will generate the StripeEvent using the StripeEventUtility.ParseEvent. The problem is with using the ConstructEvent function i cant get the signatures to match. Any help or suggestions would be much appreciated.
isSignaturePresent is returning false
//call to create event
stripeEvent = ConstructEvent(json, Request.Headers["Stripe-Signature"],
SecretFromStripeDashBoard);
private StripeEvent ConstructEvent(string json, string
stripeSignatureHeader, string secret, int tolerance = 300)
{
var signatureItems = parseStripeSignature(stripeSignatureHeader);
var signature = computeSignature(secret, signatureItems["t"].FirstOrDefault(), json);
if (!isSignaturePresent(signature, signatureItems["v1"]))
throw new Exception("The signature for the webhook is not present in the Stripe-Signature header.");
//var utcNow = EpochUtcNowOverride ?? DateTime.UtcNow.ConvertDateTimeToEpoch();
//var webhookUtc = Convert.ToInt32(signatureItems["t"].FirstOrDefault());
//if (utcNow - webhookUtc > tolerance)
// throw new Exception("The webhook cannot be processed because the current timestamp is above the allowed tolerance.");
return Mapper<StripeEvent>.MapFromJson(json);
}
private ILookup<string, string> parseStripeSignature(string stripeSignatureHeader)
{
return stripeSignatureHeader.Trim()
.Split(',')
.Select(item => item.Trim().Split('='))
.ToLookup(item => item[0], item => item[1]);
}
private bool isSignaturePresent(string signature, IEnumerable<string> signatures)
{
return signatures.Any(key => secureCompare(key, signature));
}
private string computeSignature(string secret, string timestamp, string payload)
{
var secretBytes = Encoding.UTF8.GetBytes(secret);
var payloadBytes = Encoding.UTF8.GetBytes($"{timestamp}.{payload}");
var cryptographer = new HMACSHA256(secretBytes);
var hash = cryptographer.ComputeHash(payloadBytes);
return BitConverter.ToString(hash).Replace("-", "").ToLower();
}
private bool secureCompare(string a, string b)
{
if (a.Length != b.Length) return false;
var result = 0;
for (var i = 0; i < a.Length; i++)
{
result |= a[i] ^ b[i];
}
return result == 0;
}
}
回答1:
I answered in comments above, but to recap, the issue was that the json
string provided to the ConstructEvent
method did not contain the exact payload sent by Stripe.
Rather, you initialized the payload with:
var json = JsonSerializer.SerializeToString(request);
i.e. you reserialized the deserialized request's body. However, it's very unlikely that you would get the same string as the original payload sent by Stripe. E.g. Stripe could send this payload:
{
"a_key": "a_value",
"another_key": "another_value"
}
but after deserializing+reserializing, your JSON string could contain this value:
{"another_key": "another_value","a_key": "a_value"}
as the key order is not guaranteed to be maintained, and other formatting options (newlines, indents) come into play.
Webhook signatures are generated using the exact payload (as a raw string), so when verifying signatures, you must also provide that exact payload, as passed by your web server or framework.
来源:https://stackoverflow.com/questions/43888618/stripe-webhook-signature-failed-stripe-net