Stripe webhook 签名失败 - Stripe.net
Stripe webhook signature failed - Stripe.net
我正在尝试使用 Jayme Davis 的 c# 库 Stripe.net 来实现条纹 webhook。我已经在条纹仪表板中设置了测试端点并生成了秘密。端点被命中,并将使用 StripeEventUtility.ParseEvent 生成 StripeEvent。问题是使用 ConstructEvent 函数我无法获得匹配的签名。任何帮助或建议将不胜感激。
isSignaturePresent 返回 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;
}
}
我在上面的评论中回答过,但回顾一下,问题是提供给 ConstructEvent
方法的 json
字符串不包含 Stripe 发送的确切负载。
相反,您使用以下方法初始化了负载:
var json = JsonSerializer.SerializeToString(request);
即您重新序列化了反序列化请求的主体。但是,您不太可能获得与 Stripe 发送的原始有效负载相同的字符串。例如。 Stripe 可以发送此负载:
{
"a_key": "a_value",
"another_key": "another_value"
}
但在反序列化+重新序列化之后,您的 JSON 字符串可能包含此值:
{"another_key": "another_value","a_key": "a_value"}
由于不能保证保持键顺序,因此其他格式选项(换行符、缩进)会起作用。
Webhook 签名是使用 exact 有效载荷(作为原始字符串)生成的,因此在验证签名时,您还必须提供由您的网络服务器传递的确切有效载荷或框架。
正如前面的回答所指出的,json 字符串应该与 stripe 发送的播放负载相同。在我的情况下,我不得不删除 \r
以使其像下面那样工作 var json = (await stripStream.ReadToEndAsync()).Replace("\r", "");
这是代码:
[HttpPost("webhook")]
public async Task<IActionResult> Index()
{
try
{
using (var stripStream = new StreamReader(HttpContext.Request.Body))
{
var json = (await stripStream.ReadToEndAsync()).Replace("\r", "");
var secretKey = _settings.Value.StripeSettings.WebhookSecretKey;
// validate webhook called by stripe only
Logger.Information($"[WEBHOOK]: Validating webhook signature {secretKey.Substring(0, 4)}**********");
var stripeEvent = EventUtility.ConstructEvent(json, Request.Headers["Stripe-Signature"], secretKey);
Logger.Information($"[WEBHOOK]: Webhook signature validated");
// handle stripe event
await this._stripeWebhookService.HandleAsync(stripeEvent);
return Ok();
}
}catch(Exception){ // handle exception}
此时我的应用程序也不断抛出异常。
var stripeEvent = EventUtility.ConstructEvent(
json,
Request.Headers["Stripe-Signature"],
secret
);
阅读异常日志,它说我在条带仪表板中使用了新版本的 NuGet 包 stripe.net 和旧版本。我将我的帐户更新到最新的 API 版本,我不再收到错误。
如果您在其他应用程序中使用旧版本,请小心更新仪表板中的版本。如果它破坏了您的其他应用程序,您有 72 小时的时间在仪表板中撤消更新。我是从零开始的,所以更新对我来说不是什么大问题。
我正在尝试使用 Jayme Davis 的 c# 库 Stripe.net 来实现条纹 webhook。我已经在条纹仪表板中设置了测试端点并生成了秘密。端点被命中,并将使用 StripeEventUtility.ParseEvent 生成 StripeEvent。问题是使用 ConstructEvent 函数我无法获得匹配的签名。任何帮助或建议将不胜感激。
isSignaturePresent 返回 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;
}
}
我在上面的评论中回答过,但回顾一下,问题是提供给 ConstructEvent
方法的 json
字符串不包含 Stripe 发送的确切负载。
相反,您使用以下方法初始化了负载:
var json = JsonSerializer.SerializeToString(request);
即您重新序列化了反序列化请求的主体。但是,您不太可能获得与 Stripe 发送的原始有效负载相同的字符串。例如。 Stripe 可以发送此负载:
{
"a_key": "a_value",
"another_key": "another_value"
}
但在反序列化+重新序列化之后,您的 JSON 字符串可能包含此值:
{"another_key": "another_value","a_key": "a_value"}
由于不能保证保持键顺序,因此其他格式选项(换行符、缩进)会起作用。
Webhook 签名是使用 exact 有效载荷(作为原始字符串)生成的,因此在验证签名时,您还必须提供由您的网络服务器传递的确切有效载荷或框架。
正如前面的回答所指出的,json 字符串应该与 stripe 发送的播放负载相同。在我的情况下,我不得不删除 \r
以使其像下面那样工作 var json = (await stripStream.ReadToEndAsync()).Replace("\r", "");
这是代码:
[HttpPost("webhook")]
public async Task<IActionResult> Index()
{
try
{
using (var stripStream = new StreamReader(HttpContext.Request.Body))
{
var json = (await stripStream.ReadToEndAsync()).Replace("\r", "");
var secretKey = _settings.Value.StripeSettings.WebhookSecretKey;
// validate webhook called by stripe only
Logger.Information($"[WEBHOOK]: Validating webhook signature {secretKey.Substring(0, 4)}**********");
var stripeEvent = EventUtility.ConstructEvent(json, Request.Headers["Stripe-Signature"], secretKey);
Logger.Information($"[WEBHOOK]: Webhook signature validated");
// handle stripe event
await this._stripeWebhookService.HandleAsync(stripeEvent);
return Ok();
}
}catch(Exception){ // handle exception}
此时我的应用程序也不断抛出异常。
var stripeEvent = EventUtility.ConstructEvent(
json,
Request.Headers["Stripe-Signature"],
secret
);
阅读异常日志,它说我在条带仪表板中使用了新版本的 NuGet 包 stripe.net 和旧版本。我将我的帐户更新到最新的 API 版本,我不再收到错误。
如果您在其他应用程序中使用旧版本,请小心更新仪表板中的版本。如果它破坏了您的其他应用程序,您有 72 小时的时间在仪表板中撤消更新。我是从零开始的,所以更新对我来说不是什么大问题。