diff --git a/justfile b/justfile index 50a0dcbfe5..871aa51e5a 100644 --- a/justfile +++ b/justfile @@ -31,3 +31,7 @@ update-version version: echo "{{ version }}" > VERSION perl -pi -e 's|[.\-\d\w]+|{{ version }}|' src/Stripe.net/Stripe.net.csproj perl -pi -e 's|Current = "[.\-\d\w]+";|Current = "{{ version }}";|' src/Stripe.net/Constants/Version.cs + +[working-directory("src/Examples/")] +run-example example: + dotnet run --project Examples.csproj {{ example }} diff --git a/src/Examples/ExampleTemplate.cs b/src/Examples/ExampleTemplate.cs index aa38a0e08e..dd03bde9c1 100644 --- a/src/Examples/ExampleTemplate.cs +++ b/src/Examples/ExampleTemplate.cs @@ -2,6 +2,7 @@ namespace Examples { using System; using System.Threading.Tasks; + using Stripe; /// /// @@ -21,14 +22,12 @@ public class ExampleTemplate { public static async Task Run() { - var apiKey = "{{API_KEY}}"; + var apiKey = Environment.GetEnvironmentVariable("STRIPE_API_KEY"); try { - Console.WriteLine("Hello World"); - - // var client = new StripeClient(apiKey); - // client.V1... + var client = new StripeClient(apiKey); + Console.WriteLine(await client.V1.Customers.ListAsync()); } catch (Exception ex) { diff --git a/src/Examples/Program.cs b/src/Examples/Program.cs index 31579e23ce..4d0ac5f52d 100644 --- a/src/Examples/Program.cs +++ b/src/Examples/Program.cs @@ -1,6 +1,7 @@ namespace Examples { using System; + using System.Reflection; using System.Threading.Tasks; public class Program @@ -9,14 +10,62 @@ public Program() { } + /// + /// To create an example, clone ExampleTemplate.cs, implement the example + /// copy this line and replace the class name with your new class. + /// + /// To run an example from this folder, execute: + /// dotnet run --project Examples.csproj NameOfExample + /// + /// The name of the example should include any namespace parts other than "Examples" + /// which is prepended when looking up the type, for example" + /// dotnet run --project Examples.csproj + /// or + /// dotnet run --project Examples.csproj . + /// + /// Examples accept configuration via environment variables, so ensure all environment vars + /// are set before running the example. + /// + /// + /// command line args + /// public static async Task Main(string[] args) { - // To create an example, clone NewExample.cs, implement the example - // copy this line and replace the class name with your new class. - // await NewExample.Run(); - // e.g. - // await MeterEventExample.Run(); - // then build the project and run bin/Debug/net8.0/Examples + if (args.Length != 1) + { + Console.WriteLine("Usage: dotnet run --project Examples "); + Environment.Exit(1); + return; + } + + var type = Assembly.GetExecutingAssembly().GetType($"Examples.{args[0]}"); + + if (type == null) + { + Console.WriteLine($"Unable to find example class {args[0]}"); + Environment.Exit(1); + return; + } + + var runMethod = type.GetMethod( + "Run", + BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy); + if (runMethod == null) + { + Console.WriteLine($"Example class {args[0]} is missing Run method"); + Environment.Exit(1); + return; + } + + Task? result = (Task?)runMethod.Invoke(null, null); + if (result == null) + { + Console.WriteLine($"Unable to invoke Run method on {args[0]}"); + Environment.Exit(1); + return; + } + + await result; } } } diff --git a/src/Examples/V2/MeterEventStream.cs b/src/Examples/V2/MeterEventStream.cs index 4949e92671..85154b8ed8 100644 --- a/src/Examples/V2/MeterEventStream.cs +++ b/src/Examples/V2/MeterEventStream.cs @@ -1,68 +1,70 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Stripe; -using Stripe.V2.Billing; - -/// -/// Use the high-throughput MeterEventStream to report create billing meter events. -/// -/// In this example, we: -/// - create a meter event session and store the session's authentication token -/// - define an event with a payload -/// - use the meterEventStream service to create an event stream that reports this event -/// -/// This example expects a billing meter with an event_name of 'alpaca_ai_tokens'. If you have -/// a different meter event name, you can change it before running this example. -/// -public class MeterEventStream +namespace Examples.V2 { - private static MeterEventSession? meterEventSession; + using System; + using System.Collections.Generic; + using System.Threading.Tasks; + using Stripe; + using Stripe.V2.Billing; - public static async Task Run() + /// + /// Use the high-throughput MeterEventStream to report create billing meter events. + /// + /// In this example, we: + /// - create a meter event session and store the session's authentication token + /// - define an event with a payload + /// - use the meterEventStream service to create an event stream that reports this event + /// + /// This example expects a billing meter with an event_name of 'alpaca_ai_tokens'. If you have + /// a different meter event name, you can change it before running this example. + /// + public class MeterEventStream { - var apiKey = "{{API_KEY}}"; - var customerId = "{{CUSTOMER_ID}}"; // Replace with actual customer ID + private static MeterEventSession? meterEventSession; - try - { - await SendMeterEvent(apiKey, "alpaca_ai_tokens", customerId, "25"); - Console.WriteLine("Meter event sent successfully!"); - } - catch (Exception ex) + public static async Task Run() { - Console.WriteLine($"Error sending meter event: {ex.Message}"); - } - } + var apiKey = Environment.GetEnvironmentVariable("STRIPE_API_KEY"); + var customerId = Environment.GetEnvironmentVariable("CUSTOMER_ID"); - private static async Task RefreshMeterEventSession(string apiKey) - { - // Check if session is null or expired - if (meterEventSession == null || meterEventSession.ExpiresAt <= DateTime.UtcNow) - { - // Create a new meter event session in case the existing session expired - var client = new StripeClient(apiKey); - meterEventSession = await client.V2.Billing.MeterEventSession.CreateAsync(new MeterEventSessionCreateOptions()); + try + { + await SendMeterEvent(apiKey, "alpaca_ai_tokens", customerId, "25"); + Console.WriteLine("Meter event sent successfully!"); + } + catch (Exception ex) + { + Console.WriteLine($"Error sending meter event: {ex.Message}"); + } } - } - - private static async Task SendMeterEvent(string apiKey, string eventName, string stripeCustomerId, string value) - { - // Refresh the meter event session if necessary - await RefreshMeterEventSession(apiKey); - if (meterEventSession == null) + private static async Task RefreshMeterEventSession(string apiKey) { - throw new Exception("Unable to refresh meter event session"); + // Check if session is null or expired + if (meterEventSession == null || meterEventSession.ExpiresAt <= DateTime.UtcNow) + { + // Create a new meter event session in case the existing session expired + var client = new StripeClient(apiKey); + meterEventSession = await client.V2.Billing.MeterEventSession.CreateAsync(new MeterEventSessionCreateOptions()); + } } - // Create a meter event - var client = new StripeClient(meterEventSession.AuthenticationToken); - var options = new MeterEventStreamCreateOptions + private static async Task SendMeterEvent(string apiKey, string eventName, string stripeCustomerId, string value) { - Events = - [ - new MeterEventStreamCreateEventOptions + // Refresh the meter event session if necessary + await RefreshMeterEventSession(apiKey); + + if (meterEventSession == null) + { + throw new Exception("Unable to refresh meter event session"); + } + + // Create a meter event + var client = new StripeClient(meterEventSession.AuthenticationToken); + var options = new MeterEventStreamCreateOptions + { + Events = + [ + new MeterEventStreamCreateEventOptions { EventName = eventName, Payload = new Dictionary @@ -72,7 +74,8 @@ private static async Task SendMeterEvent(string apiKey, string eventName, string }, }, ], - }; - client.V2.Billing.MeterEventStream.Create(options); + }; + client.V2.Billing.MeterEventStream.Create(options); + } } } diff --git a/src/Examples/V2/ThinEventWebhookHandler.cs b/src/Examples/V2/ThinEventWebhookHandler.cs index bf717d287a..dd4e53f889 100644 --- a/src/Examples/V2/ThinEventWebhookHandler.cs +++ b/src/Examples/V2/ThinEventWebhookHandler.cs @@ -1,60 +1,63 @@ -#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; - -/// -/// Receive and process thin events like the v1.billing.meter.error_report_triggered event. -/// -/// In this example, we: -/// - use parseThinEvent to parse the received thin event 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. -/// -[Route("api/[controller]")] -[ApiController] -public class ThinEventWebhookHandler : ControllerBase +namespace Examples.V2 { - private readonly StripeClient _client; - private readonly string _webhookSecret; +#pragma warning disable SA1101 // Prefix local calls with this - public ThinEventWebhookHandler() + using System; + using System.IO; + using System.Threading.Tasks; + using Microsoft.AspNetCore.Mvc; + using Stripe; + using Stripe.Events; + + /// + /// Receive and process thin events like the v1.billing.meter.error_report_triggered event. + /// + /// In this example, we: + /// - use parseThinEvent to parse the received thin event 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. + /// + [Route("api/[controller]")] + [ApiController] + public class ThinEventWebhookHandler : ControllerBase { - var apiKey = Environment.GetEnvironmentVariable("STRIPE_API_KEY"); - _client = new StripeClient(apiKey); - - _webhookSecret = Environment.GetEnvironmentVariable("WEBHOOK_SECRET"); - } + private readonly StripeClient _client; + private readonly string _webhookSecret; - [HttpPost] - public async Task Index() - { - var json = await new StreamReader(HttpContext.Request.Body).ReadToEndAsync(); - try + public ThinEventWebhookHandler() { - var thinEvent = _client.ParseThinEvent(json, Request.Headers["Stripe-Signature"], _webhookSecret); + var apiKey = Environment.GetEnvironmentVariable("STRIPE_API_KEY"); + _client = new StripeClient(apiKey); + + _webhookSecret = Environment.GetEnvironmentVariable("WEBHOOK_SECRET"); + } - // Fetch the event data to understand the failure - var baseEvent = await _client.V2.Core.Events.GetAsync(thinEvent.Id); - if (baseEvent is V1BillingMeterErrorReportTriggeredEvent fullEvent) + [HttpPost] + public async Task Index() + { + var json = await new StreamReader(HttpContext.Request.Body).ReadToEndAsync(); + try { - var meter = await fullEvent.FetchRelatedObjectAsync(); - var meterId = meter.Id; + var thinEvent = _client.ParseThinEvent(json, Request.Headers["Stripe-Signature"], _webhookSecret); - // Record the failures and alert your team - // Add your logic here - } + // Fetch the event data to understand the failure + var baseEvent = await _client.V2.Core.Events.GetAsync(thinEvent.Id); + if (baseEvent is V1BillingMeterErrorReportTriggeredEvent fullEvent) + { + var meter = await fullEvent.FetchRelatedObjectAsync(); + var meterId = meter.Id; - return Ok(); - } - catch (StripeException e) - { - return BadRequest(e.Message); + // Record the failures and alert your team + // Add your logic here + } + + return Ok(); + } + catch (StripeException e) + { + return BadRequest(e.Message); + } } } }