Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/Stripe.net/Infrastructure/Public/StripeClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ namespace Stripe
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Stripe.V2.Core;

/// <summary>
Expand Down Expand Up @@ -228,6 +229,15 @@ public EventNotification ParseEventNotification(
{
EventUtility.ValidateSignature(json, stripeSignatureHeader, secret, tolerance, DateTimeOffset.UtcNow.ToUnixTimeSeconds());

var parsed = JObject.Parse(json);
Comment thread
xavdid-stripe marked this conversation as resolved.
var objectValue = (string)parsed["object"];
if (objectValue == "event")
{
throw new ArgumentException(
"You passed a webhook payload to ParseEventNotification, which expects "
+ "a thin event notification. Use EventUtility.ConstructEvent instead.");
}

return EventNotification.FromJson(json, this);
}

Expand Down
11 changes: 11 additions & 0 deletions src/Stripe.net/Services/Events/EventUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,17 @@ public static bool IsCompatibleApiVersion(string eventApiVersion)
/// </remarks>
public static Event ParseEvent(string json, bool throwOnApiVersionMismatch = true)
{
using (var doc = System.Text.Json.JsonDocument.Parse(json))
{
if (doc.RootElement.TryGetProperty("object", out var objectProp) &&
objectProp.GetString() == "v2.core.event")
{
throw new ArgumentException(
"You passed a thin event notification to ConstructEvent, which expects "
+ "a webhook payload. Use StripeClient.ParseEventNotification instead.");
}
}

var stripeEvent = System.Text.Json.JsonSerializer.Deserialize<Event>(
json,
StripeConfiguration.SerializerOptions);
Expand Down
35 changes: 28 additions & 7 deletions src/StripeTests/Events/V2/EventTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public class EventTest : BaseStripeTest
private static string v2UnknownEventPayload =
@"{
""id"": ""evt_234"",
""object"": ""event"",
""object"": ""v2.core.event"",
""type"": ""this.event.doesnt.exist"",
""created"": ""2022-02-15T00:27:45.330Z"",
""livemode"": true,
Expand All @@ -45,7 +45,7 @@ public class EventTest : BaseStripeTest
private static string v2KnownEventNoRelatedObjectPayload =
@"{
""id"": ""evt_234"",
""object"": ""event"",
""object"": ""v2.core.event"",
""type"": ""v1.billing.meter.no_meter_found"",
""created"": ""2022-02-15T00:27:45.330Z"",
""livemode"": true,
Expand All @@ -54,7 +54,7 @@ public class EventTest : BaseStripeTest
private static string v2KnownEventPayload =
@"{
""id"": ""evt_234"",
""object"": ""event"",
""object"": ""v2.core.event"",
""type"": ""v1.billing.meter.error_report_triggered"",
""created"": ""2022-02-15T00:27:45.330Z"",
""context"": ""context 123"",
Expand All @@ -70,7 +70,7 @@ public class EventTest : BaseStripeTest
private static string v2KnownEventWithDataPayload =
@"{
""id"": ""evt_234"",
""object"": ""event"",
""object"": ""v2.core.event"",
""type"": ""v1.billing.meter.error_report_triggered"",
""created"": ""2022-02-15T00:27:45.330Z"",
""context"": ""context 123"",
Expand Down Expand Up @@ -106,7 +106,7 @@ public class EventTest : BaseStripeTest
private static string v2KnownEventLivemodeFalsePayload =
@"{
""id"": ""evt_234"",
""object"": ""event"",
""object"": ""v2.core.event"",
""type"": ""v1.billing.meter.no_meter_found"",
""created"": ""2022-02-15T00:27:45.330Z"",
""livemode"": false,
Expand All @@ -115,7 +115,7 @@ public class EventTest : BaseStripeTest
private static string v2KnownEventWithReasonPayload =
@"{
""id"": ""evt_234"",
""object"": ""event"",
""object"": ""v2.core.event"",
""type"": ""v1.billing.meter.no_meter_found"",
""created"": ""2022-02-15T00:27:45.330Z"",
""livemode"": true,
Expand Down Expand Up @@ -290,13 +290,34 @@ public void ParseEventNotificationWithInvalidSignature()
Assert.Matches("header format is unexpected", exception.Message);
}

[Fact]
public void RejectV1PayloadInParseEventNotification()
{
var v1Payload = @"{
""id"": ""evt_123"",
""object"": ""event"",
""type"": ""customer.created"",
""api_version"": ""2017-05-25"",
""created"": 1533204620,
""livemode"": false
}";

var exception = Assert.Throws<ArgumentException>(() =>
this.stripeClient.ParseEventNotification(
v1Payload,
GenerateSigHeader(v1Payload),
WebhookSecret));

Assert.Contains("EventUtility.ConstructEvent", exception.Message);
}

[Fact]
public void ParseUnknownEventDirectly()
{
var stripeEvent = JsonUtils.DeserializeObject<Stripe.V2.Core.Event>(v2UnknownEventPayload);
Assert.NotNull(stripeEvent);
Assert.Equal("evt_234", stripeEvent.Id);
Assert.Equal("event", stripeEvent.Object);
Assert.Equal("v2.core.event", stripeEvent.Object);
Assert.Equal("this.event.doesnt.exist", stripeEvent.Type);
Assert.Equal(new DateTime(2022, 2, 15, 0, 27, 45, 330, DateTimeKind.Utc), stripeEvent.Created);
Assert.Null(stripeEvent.Requestor);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ await this.stripeClient.RawRequestAsync(
public void ConstructEventNotification()
{
string payload = @"{
""object"": ""event"",
""object"": ""v2.core.event"",
""type"": ""unknown"",
""data"": {},
""relatedObject"": {
Expand Down
39 changes: 39 additions & 0 deletions src/StripeTests/Services/Events/EventUtilityTest.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
namespace StripeTests
{
using System;
using Stripe;
using Xunit;

Expand Down Expand Up @@ -152,6 +153,44 @@ public void ValidateSignatureHandlesIncorrectHeaderValues(string headerValue)
EventUtility.ValidateSignature("{}", headerValue, string.Empty));
}

[Fact]
public void RejectV2PayloadInParseEvent()
{
var v2Payload = @"{
""id"": ""evt_234"",
""object"": ""v2.core.event"",
""type"": ""v1.billing.meter.error_report_triggered"",
""created"": ""2022-02-15T00:27:45.330Z"",
""livemode"": true
}";

var exception = Assert.Throws<ArgumentException>(() =>
EventUtility.ParseEvent(v2Payload, throwOnApiVersionMismatch: false));

Assert.Contains("StripeClient.ParseEventNotification", exception.Message);
}

[Fact]
public void RejectV2PayloadInConstructEvent()
{
var v2Payload = @"{
""id"": ""evt_234"",
""object"": ""v2.core.event"",
""type"": ""v1.billing.meter.error_report_triggered"",
""created"": ""2022-02-15T00:27:45.330Z"",
""livemode"": true
}";

var timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
var signature = EventUtility.ComputeSignature(this.secret, timestamp.ToString(), v2Payload);
var sigHeader = $"t={timestamp},v1={signature}";

var exception = Assert.Throws<ArgumentException>(() =>
EventUtility.ConstructEvent(v2Payload, sigHeader, this.secret, throwOnApiVersionMismatch: false));

Assert.Contains("StripeClient.ParseEventNotification", exception.Message);
}

[Theory]
[InlineData("2024-2-31.acacia", "1999-03-31", false)]
[InlineData("2024-2-31.acacia", "2025-03-31.basil", false)]
Expand Down
Loading