Skip to content
7 changes: 5 additions & 2 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,15 @@ _default:
# base test command that other, more specific commands use
[no-quiet]
[no-exit-message]
_test no_build framework config:
dotnet test {{no_build}} {{framework}} src/StripeTests/StripeTests.csproj -c {{config}}
_test no_build framework config filter="":
dotnet test {{no_build}} {{framework}} src/StripeTests/StripeTests.csproj -c {{config}} {{ if filter == "" {""} else {"--filter " + filter} }}

# ⭐ run tests in debug mode
test: (_test "" "-f net8.0" "Debug")

# run a test matching a filter
test-one name: (_test "" "-f net8.0" "Debug" name)

# skip build and don't specify the dotnet framework
ci-test: (_test "--no-build" "" "Release")

Expand Down
77 changes: 77 additions & 0 deletions src/Examples/V2/EventNotificationWebhookHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
namespace Examples.V2
{
#pragma warning disable SA1101 // Prefix local calls with this

using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Stripe;
using Stripe.Events;
using Stripe.V2;

/// <summary>
/// Receive and process EventNotifications like the v1.billing.meter.error_report_triggered event.
///
/// In this example, we:
/// - use ParseEventNotification to parse the received event notification webhook body
/// - call StripeClient.v2.core.events.retrieve to retrieve the full event object
/// - if it is a V1BillingMeterErrorReportTriggeredEvent event type, call fetchRelatedObject
/// to retrieve the Billing Meter object associated with the event.
/// </summary>
[Route("api/[controller]")]
[ApiController]
public class EventNotificationWebhookHandler : ControllerBase
{
private readonly StripeClient client;
private readonly string webhookSecret;

public EventNotificationWebhookHandler()

Check warning on line 29 in src/Examples/V2/EventNotificationWebhookHandler.cs

View workflow job for this annotation

GitHub Actions / Build and test

Non-nullable field 'webhookSecret' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.
{
var apiKey = Environment.GetEnvironmentVariable("STRIPE_API_KEY");
client = new StripeClient(apiKey);

webhookSecret = Environment.GetEnvironmentVariable("WEBHOOK_SECRET");

Check warning on line 34 in src/Examples/V2/EventNotificationWebhookHandler.cs

View workflow job for this annotation

GitHub Actions / Build and test

Possible null reference assignment.
}

[HttpPost]
public async Task<IActionResult> Index()
{
var json = await new StreamReader(HttpContext.Request.Body).ReadToEndAsync();
try
{
var eventNotification = client.ParseEventNotification(json, Request.Headers["Stripe-Signature"], webhookSecret);

// match on the type of the class to determine what event you have
if (eventNotification is V1BillingMeterErrorReportTriggeredEventNotification evt)
{
// the related object is correctly typed
var meter = evt.FetchRelatedObject();
Console.WriteLine($"Default aggregation: {meter.DefaultAggregation}");

// can also fetch the full event to get the event data
var eventObj = evt.FetchEvent();
var eventData = eventObj.Data;
Console.WriteLine($"Err summary: {eventData.DeveloperMessageSummary}");
}

// check other types the SDK knows about
// for event types that were released after this SDK was generated, check type
else if (eventNotification is UnknownEventNotification unknownEvt)
{
// fall back to checking type
if (unknownEvt.Type == "some.other.event")
{
// handle the event
}
}

return Ok();
}
catch (StripeException e)
{
return BadRequest(e.Message);
}
}
}
}
63 changes: 0 additions & 63 deletions src/Examples/V2/ThinEventWebhookHandler.cs

This file was deleted.

4 changes: 2 additions & 2 deletions src/Stripe.net/Entities/V2/Events/Event.partial.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace Stripe.V2
using Stripe.Infrastructure;

/// <summary>
/// Manually-maintained convenience methods added to ThinEvent.
/// Manually-maintained convenience methods added to V2 Events.
/// </summary>
[JsonConverter(typeof(V2EventConverter))]
#if NET6_0_OR_GREATER
Expand Down Expand Up @@ -66,7 +66,7 @@ protected virtual Task<T> FetchRelatedObjectAsync<T>(EventRelatedObject relatedO
RequestOptions opts = null;
if (this.Context != null)
{
opts = new RequestOptions { StripeAccount = this.Context };
Comment thread
xavdid-stripe marked this conversation as resolved.
opts = new RequestOptions { StripeContext = this.Context };
}

return this.Requestor.RequestAsync<T>(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// File generated from our OpenAPI spec
namespace Stripe.Events
{
using System.Threading.Tasks;
using Newtonsoft.Json;
using Stripe.V2;
#if NET6_0_OR_GREATER
using STJS = System.Text.Json.Serialization;
#endif

/// <summary>
/// Occurs when a Meter has invalid async usage events.
/// </summary>
public class V1BillingMeterErrorReportTriggeredEventNotification : V2.EventNotification
{
/// <summary>
/// Object containing the reference to API resource relevant to the event.
/// </summary>
[JsonProperty("related_object")]
#if NET6_0_OR_GREATER
[STJS.JsonPropertyName("related_object")]
#endif

public V2.EventNotificationRelatedObject RelatedObject { get; set; }

/// <summary>
/// Asynchronously retrieves the related object from the API. Make an API request on every
/// call.
/// </summary>
public Task<Billing.Meter> FetchRelatedObjectAsync()
{
return this.FetchRelatedObjectAsync<Billing.Meter>(this.RelatedObject);
}

/// <summary>
/// Retrieves the related object from the API. Make an API request on every call.
/// </summary>
public Billing.Meter FetchRelatedObject()
{
return this.FetchRelatedObject<Billing.Meter>(this.RelatedObject);
}

public V1BillingMeterErrorReportTriggeredEvent FetchEvent()
{
return this.FetchEvent<V1BillingMeterErrorReportTriggeredEvent>();
}

public Task<V1BillingMeterErrorReportTriggeredEvent> FetchEventAsync()
{
return this.FetchEventAsync<V1BillingMeterErrorReportTriggeredEvent>();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// File generated from our OpenAPI spec
namespace Stripe.Events
{
using System.Threading.Tasks;
using Stripe.V2;

/// <summary>
/// Occurs when a Meter's id is missing or invalid in async usage events.
/// </summary>
public class V1BillingMeterNoMeterFoundEventNotification : V2.EventNotification
{
public V1BillingMeterNoMeterFoundEvent FetchEvent()
{
return this.FetchEvent<V1BillingMeterNoMeterFoundEvent>();
}

public Task<V1BillingMeterNoMeterFoundEvent> FetchEventAsync()
{
return this.FetchEventAsync<V1BillingMeterNoMeterFoundEvent>();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// File generated from our OpenAPI spec
namespace Stripe.Events
{
using System.Threading.Tasks;
using Newtonsoft.Json;
using Stripe.V2;
#if NET6_0_OR_GREATER
using STJS = System.Text.Json.Serialization;
#endif

/// <summary>
/// A ping event used to test the connection to an EventDestination.
/// </summary>
public class V2CoreEventDestinationPingEventNotification : V2.EventNotification
{
/// <summary>
/// Object containing the reference to API resource relevant to the event.
/// </summary>
[JsonProperty("related_object")]
#if NET6_0_OR_GREATER
[STJS.JsonPropertyName("related_object")]
#endif

public V2.EventNotificationRelatedObject RelatedObject { get; set; }

/// <summary>
/// Asynchronously retrieves the related object from the API. Make an API request on every
/// call.
/// </summary>
public Task<V2.EventDestination> FetchRelatedObjectAsync()
{
return this.FetchRelatedObjectAsync<V2.EventDestination>(this.RelatedObject);
}

/// <summary>
/// Retrieves the related object from the API. Make an API request on every call.
/// </summary>
public V2.EventDestination FetchRelatedObject()
{
return this.FetchRelatedObject<V2.EventDestination>(this.RelatedObject);
}

public V2CoreEventDestinationPingEvent FetchEvent()
{
return this.FetchEvent<V2CoreEventDestinationPingEvent>();
}

public Task<V2CoreEventDestinationPingEvent> FetchEventAsync()
{
return this.FetchEventAsync<V2CoreEventDestinationPingEvent>();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#if NET6_0_OR_GREATER
namespace Stripe.Infrastructure
Comment thread
jar-stripe marked this conversation as resolved.
{
using System;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
using static Stripe.Infrastructure.SerializablePropertyCache;

/// <summary>
/// Converts a <see cref="V2.Event"/> to JSON, including any fields
/// in derived classes.
/// </summary>
internal class STJV2EventNotificationConverter : STJDefaultConverter<V2.EventNotification>
{
/// <summary>
/// Reads the JSON representation of the object.
/// </summary>
/// <param name="reader">The <see cref="Utf8JsonReader"/> to read from.</param>
/// <param name="typeToConvert">Type of the object.</param>
/// <param name="options">The calling serializer's options.</param>
/// <returns>The object value.</returns>
public override V2.EventNotification Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
throw new NotSupportedException("STJV2EventConverter should only be used while serializing.");
}

/// <summary>
/// Determines whether this instance can convert the specified object type.
/// </summary>
/// <param name="objectType">Type of the object.</param>
/// <returns>
/// <c>true</c> if this instance can convert the specified object type; otherwise, <c>false</c>.
/// </returns>
public override bool CanConvert(Type objectType)
{
return typeof(Stripe.V2.EventNotification).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo());
}
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace Stripe.Infrastructure
using Newtonsoft.Json.Linq;

/// <summary>
/// This converter deserializes Stripe thin events, which are polymorphic and discriminated by the value
/// This converter deserializes Stripe's V2 Events, which are polymorphic and discriminated by the value
/// of a property named "type".
/// </summary>
internal class V2EventConverter : JsonConverter
Expand All @@ -31,7 +31,7 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist
// class to deserialize into.
var typeValue = (string)jsonObject["type"];

Type concreteType = StripeTypeRegistry.GetConcreteThinEventType(typeValue);
Type concreteType = StripeTypeRegistry.GetConcreteV2EventType(typeValue);
if (concreteType == null)
{
// If "type" is unknown by this SDK, default to the generic ThinEvent type.
Expand Down
Loading
Loading