Skip to content

Commit 3e3987b

Browse files
committed
initial implementation
1 parent fb2ff78 commit 3e3987b

File tree

3 files changed

+157
-0
lines changed

3 files changed

+157
-0
lines changed

src/Stripe.net/Infrastructure/Public/StripeClient.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,8 @@ public EventNotification ParseEventNotification(
229229
return EventNotification.FromJson(json, this);
230230
}
231231

232+
// public TKTK
233+
232234
internal JsonSerializerSettings GetJsonSerializationSettings()
233235
{
234236
return this.jsonSerializerSettings;
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
namespace Stripe
2+
{
3+
using System;
4+
5+
/// <summary>
6+
/// EventArgs for Stripe webhook event notifications.
7+
/// Contains the strongly-typed EventNotification and the StripeClient instance.
8+
/// </summary>
9+
/// <typeparam name="TEventNotification">The type of EventNotification.</typeparam>
10+
public class StripeEventNotificationEventArgs<TEventNotification> : EventArgs
11+
where TEventNotification : V2.Core.EventNotification
12+
{
13+
/// <summary>
14+
/// Initializes a new instance of the <see cref="StripeEventNotificationEventArgs{TEventNotification}"/> class.
15+
/// </summary>
16+
/// <param name="eventNotification">The event notification instance.</param>
17+
/// <param name="client">The StripeClient instance.</param>
18+
public StripeEventNotificationEventArgs(TEventNotification eventNotification, StripeClient client)
19+
{
20+
this.EventNotification = eventNotification ?? throw new ArgumentNullException(nameof(eventNotification));
21+
this.Client = client ?? throw new ArgumentNullException(nameof(client));
22+
}
23+
24+
/// <summary>
25+
/// Gets the event notification instance.
26+
/// </summary>
27+
public TEventNotification EventNotification { get; }
28+
29+
/// <summary>
30+
/// Gets the StripeClient instance that can be used to make API requests.
31+
/// </summary>
32+
public StripeClient Client { get; }
33+
}
34+
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
namespace Stripe
2+
{
3+
using System;
4+
using System.Collections.Generic;
5+
6+
/// <summary>
7+
/// A handler for Stripe webhook events that uses the .NET event handler pattern.
8+
/// Allows registration of strongly-typed event handlers for specific EventNotification types.
9+
/// </summary>
10+
public class StripeEventRouter
11+
{
12+
private readonly StripeClient client;
13+
private readonly string webhookSecret;
14+
15+
// TODO: override `add` and check this
16+
// private bool hasHandledEvent = false;
17+
18+
/// <summary>
19+
/// Initializes a new instance of the <see cref="StripeEventRouter"/> class.
20+
/// </summary>
21+
/// <param name="client">The StripeClient instance to use for parsing and API requests.</param>
22+
/// <param name="webhookSecret">The webhook secret used for signature verification.</param>
23+
public StripeEventRouter(StripeClient client, string webhookSecret)
24+
{
25+
this.client = client ?? throw new ArgumentNullException(nameof(client));
26+
this.webhookSecret = webhookSecret ?? throw new ArgumentNullException(nameof(webhookSecret));
27+
}
28+
29+
public event EventHandler<StripeEventNotificationEventArgs<Stripe.Events.UnknownEventNotification>> UnknownEventNotification;
30+
31+
// event-handler-declarations: The beginning of the section generated from our OpenAPI spec
32+
public event EventHandler<StripeEventNotificationEventArgs<Stripe.Events.V1BillingMeterErrorReportTriggeredEventNotification>> V1BillingMeterErrorReportTriggeredEvent;
33+
34+
public event EventHandler<StripeEventNotificationEventArgs<Stripe.Events.V1BillingMeterNoMeterFoundEventNotification>> V1BillingMeterNoMeterFoundEvent;
35+
36+
public event EventHandler<StripeEventNotificationEventArgs<Stripe.Events.V2CoreEventDestinationPingEventNotification>> V2CoreEventDestinationPingEvent;
37+
38+
// event-handler-declarations: The end of the section generated from our OpenAPI spec
39+
40+
/// <summary>
41+
/// Handles an incoming webhook by parsing the payload, validating the signature,
42+
/// and dispatching to the registered handler if one exists.
43+
/// </summary>
44+
/// <param name="json">The JSON payload from the webhook request body.</param>
45+
/// <param name="stripeSignatureHeader">The Stripe-Signature header value.</param>
46+
/// <exception cref="ArgumentNullException">Thrown if any required parameter is null.</exception>
47+
/// <exception cref="StripeException">Thrown if signature validation fails or parsing fails.</exception>
48+
public void Handle(
49+
string json,
50+
string stripeSignatureHeader)
51+
{
52+
// this.hasHandledEvent = true;
53+
if (json == null)
54+
{
55+
throw new ArgumentNullException(nameof(json));
56+
}
57+
58+
if (stripeSignatureHeader == null)
59+
{
60+
throw new ArgumentNullException(nameof(stripeSignatureHeader));
61+
}
62+
63+
// Parse and validate the event notification
64+
var eventNotification = this.client.ParseEventNotification(json, stripeSignatureHeader, this.webhookSecret);
65+
66+
// Dispatch to the registered handler
67+
this.DispatchEvent(eventNotification);
68+
}
69+
70+
private void DispatchEvent(V2.Core.EventNotification eventNotification)
71+
{
72+
if (eventNotification is Stripe.Events.UnknownEventNotification e)
73+
{
74+
if (this.UnknownEventNotification == null)
75+
{
76+
throw new Exception("No handler registered for \"UknownEventNotification\"");
77+
}
78+
79+
this.UnknownEventNotification.Invoke(
80+
this,
81+
new StripeEventNotificationEventArgs<Stripe.Events.UnknownEventNotification>(
82+
(Stripe.Events.UnknownEventNotification)eventNotification, this.client));
83+
}
84+
85+
// event-handler-dispatch: The beginning of the section generated from our OpenAPI spec
86+
else if (eventNotification is Stripe.Events.V1BillingMeterErrorReportTriggeredEventNotification)
87+
{
88+
if (this.V1BillingMeterErrorReportTriggeredEvent == null)
89+
{
90+
throw new Exception("No handler registered for \"V1BillingMeterErrorReportTriggeredEvent\"");
91+
}
92+
93+
this.V1BillingMeterErrorReportTriggeredEvent.Invoke(this, new StripeEventNotificationEventArgs<Stripe.Events.V1BillingMeterErrorReportTriggeredEventNotification>((Stripe.Events.V1BillingMeterErrorReportTriggeredEventNotification)eventNotification, this.client));
94+
}
95+
else if (eventNotification is Stripe.Events.V1BillingMeterNoMeterFoundEventNotification)
96+
{
97+
if (this.V1BillingMeterNoMeterFoundEvent == null)
98+
{
99+
throw new Exception("No handler registered for \"V1BillingMeterNoMeterFoundEvent\"");
100+
}
101+
102+
this.V1BillingMeterNoMeterFoundEvent.Invoke(this, new StripeEventNotificationEventArgs<Stripe.Events.V1BillingMeterNoMeterFoundEventNotification>((Stripe.Events.V1BillingMeterNoMeterFoundEventNotification)eventNotification, this.client));
103+
}
104+
else if (eventNotification is Stripe.Events.V2CoreEventDestinationPingEventNotification)
105+
{
106+
if (this.V2CoreEventDestinationPingEvent == null)
107+
{
108+
throw new Exception("No handler registered for \"V2CoreEventDestinationPingEvent\"");
109+
}
110+
111+
this.V2CoreEventDestinationPingEvent.Invoke(this, new StripeEventNotificationEventArgs<Stripe.Events.V2CoreEventDestinationPingEventNotification>((Stripe.Events.V2CoreEventDestinationPingEventNotification)eventNotification, this.client));
112+
}
113+
114+
// event-handler-dispatch: The end of the section generated from our OpenAPI spec
115+
else
116+
{
117+
throw new Exception("No handler registered!");
118+
}
119+
}
120+
}
121+
}

0 commit comments

Comments
 (0)