From ffe133d95e095c2dad9c3e463535248a4a6ab8a5 Mon Sep 17 00:00:00 2001 From: Michael Broshi Date: Wed, 27 Aug 2025 17:10:46 -0400 Subject: [PATCH 1/7] Add private-preview branch to CI workflow triggers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c57d66e452..519bca2d17 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,6 +14,7 @@ on: branches: - master - beta + - private-preview - sdk-release/** - feature/** From 8ae00439e65a847a5ab48297e887f708ac562d4d Mon Sep 17 00:00:00 2001 From: David Brownman <109395161+xavdid-stripe@users.noreply.github.com> Date: Mon, 8 Sep 2025 18:04:21 -0700 Subject: [PATCH 2/7] =?UTF-8?q?=E2=9A=A0=EF=B8=8F=20Build=20SDK=20w/=20V2?= =?UTF-8?q?=20OpenAPI=20spec=20(#3167)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * generate w/ v2 openapi spec * move deleted object path --- .../V2/DeletedObject/DeletedObject.cs | 30 +++++++++++++++++++ .../EventDestinationService.cs | 8 ++--- .../Services/GeneratedExamplesTest.cs | 22 +++++++------- 3 files changed, 45 insertions(+), 15 deletions(-) create mode 100644 src/Stripe.net/Entities/V2/DeletedObject/DeletedObject.cs diff --git a/src/Stripe.net/Entities/V2/DeletedObject/DeletedObject.cs b/src/Stripe.net/Entities/V2/DeletedObject/DeletedObject.cs new file mode 100644 index 0000000000..3fca381bbc --- /dev/null +++ b/src/Stripe.net/Entities/V2/DeletedObject/DeletedObject.cs @@ -0,0 +1,30 @@ +// File generated from our OpenAPI spec +namespace Stripe.V2 +{ + using Newtonsoft.Json; +#if NET6_0_OR_GREATER + using STJS = System.Text.Json.Serialization; +#endif + + public class DeletedObject : StripeEntity, IHasId, IHasObject + { + /// + /// The ID of the object that's being deleted. + /// + [JsonProperty("id")] +#if NET6_0_OR_GREATER + [STJS.JsonPropertyName("id")] +#endif + public string Id { get; set; } + + /// + /// String representing the type of the object that has been deleted. Objects of the same + /// type share the same value of the object field. + /// + [JsonProperty("object")] +#if NET6_0_OR_GREATER + [STJS.JsonPropertyName("object")] +#endif + public string Object { get; set; } + } +} diff --git a/src/Stripe.net/Services/V2/Core/EventDestinations/EventDestinationService.cs b/src/Stripe.net/Services/V2/Core/EventDestinations/EventDestinationService.cs index 560d7f1793..299fe81932 100644 --- a/src/Stripe.net/Services/V2/Core/EventDestinations/EventDestinationService.cs +++ b/src/Stripe.net/Services/V2/Core/EventDestinations/EventDestinationService.cs @@ -39,17 +39,17 @@ public virtual V2.EventDestination Create(EventDestinationCreateOptions options, /// /// Delete an event destination. /// - public virtual V2.EventDestination Delete(string id, EventDestinationDeleteOptions options = null, RequestOptions requestOptions = null) + public virtual V2.DeletedObject Delete(string id, EventDestinationDeleteOptions options = null, RequestOptions requestOptions = null) { - return this.Request(BaseAddress.Api, HttpMethod.Delete, $"/v2/core/event_destinations/{WebUtility.UrlEncode(id)}", options, requestOptions); + return this.Request(BaseAddress.Api, HttpMethod.Delete, $"/v2/core/event_destinations/{WebUtility.UrlEncode(id)}", options, requestOptions); } /// /// Delete an event destination. /// - public virtual Task DeleteAsync(string id, EventDestinationDeleteOptions options = null, RequestOptions requestOptions = null, CancellationToken cancellationToken = default) + public virtual Task DeleteAsync(string id, EventDestinationDeleteOptions options = null, RequestOptions requestOptions = null, CancellationToken cancellationToken = default) { - return this.RequestAsync(BaseAddress.Api, HttpMethod.Delete, $"/v2/core/event_destinations/{WebUtility.UrlEncode(id)}", options, requestOptions, cancellationToken); + return this.RequestAsync(BaseAddress.Api, HttpMethod.Delete, $"/v2/core/event_destinations/{WebUtility.UrlEncode(id)}", options, requestOptions, cancellationToken); } /// diff --git a/src/StripeTests/Services/GeneratedExamplesTest.cs b/src/StripeTests/Services/GeneratedExamplesTest.cs index bf420b335c..a0a41f9759 100644 --- a/src/StripeTests/Services/GeneratedExamplesTest.cs +++ b/src/StripeTests/Services/GeneratedExamplesTest.cs @@ -6256,7 +6256,7 @@ public void TestV2CoreEventGet() HttpMethod.Get, "/v2/core/events", (HttpStatusCode)200, - "{\"data\":[{\"id\":\"obj_123\",\"object\":\"v2.core.event\",\"context\":null,\"created\":\"1970-01-12T21:42:34.472Z\",\"livemode\":true,\"reason\":null,\"type\":\"type\"}],\"next_page_url\":null,\"previous_page_url\":null}", + "{\"data\":[{\"id\":\"obj_123\",\"object\":\"v2.core.event\",\"created\":\"1970-01-12T21:42:34.472Z\",\"livemode\":true,\"type\":\"type\"}],\"next_page_url\":null,\"previous_page_url\":null}", "object_id=object_id"); var options = new Stripe.V2.Core.EventListOptions { @@ -6279,7 +6279,7 @@ public void TestV2CoreEventGet2() HttpMethod.Get, "/v2/core/events/id_123", (HttpStatusCode)200, - "{\"id\":\"obj_123\",\"object\":\"v2.core.event\",\"context\":null,\"created\":\"1970-01-12T21:42:34.472Z\",\"livemode\":true,\"reason\":null,\"type\":\"type\"}"); + "{\"id\":\"obj_123\",\"object\":\"v2.core.event\",\"created\":\"1970-01-12T21:42:34.472Z\",\"livemode\":true,\"type\":\"type\"}"); var client = new StripeClient(this.Requestor); var service = client.V2.Core.Events; Stripe.V2.Event result = service.Get("id_123"); @@ -6293,7 +6293,7 @@ public void TestV2CoreEventDestinationGet() HttpMethod.Get, "/v2/core/event_destinations", (HttpStatusCode)200, - "{\"data\":[{\"id\":\"obj_123\",\"object\":\"v2.core.event_destination\",\"amazon_eventbridge\":null,\"created\":\"1970-01-12T21:42:34.472Z\",\"description\":\"description\",\"enabled_events\":[\"enabled_events\"],\"event_payload\":\"thin\",\"events_from\":null,\"livemode\":true,\"metadata\":null,\"name\":\"name\",\"snapshot_api_version\":null,\"status\":\"disabled\",\"status_details\":null,\"type\":\"amazon_eventbridge\",\"updated\":\"1970-01-03T17:07:10.277Z\",\"webhook_endpoint\":null}],\"next_page_url\":null,\"previous_page_url\":null}"); + "{\"data\":[{\"id\":\"obj_123\",\"object\":\"v2.core.event_destination\",\"created\":\"1970-01-12T21:42:34.472Z\",\"description\":\"description\",\"enabled_events\":[\"enabled_events\"],\"event_payload\":\"thin\",\"livemode\":true,\"name\":\"name\",\"status\":\"disabled\",\"type\":\"amazon_eventbridge\",\"updated\":\"1970-01-03T17:07:10.277Z\"}],\"next_page_url\":null,\"previous_page_url\":null}"); var client = new StripeClient(this.Requestor); var service = client.V2.Core.EventDestinations; Stripe.V2.StripeList eventDestinations = service @@ -6308,7 +6308,7 @@ public void TestV2CoreEventDestinationPost() HttpMethod.Post, "/v2/core/event_destinations", (HttpStatusCode)200, - "{\"id\":\"obj_123\",\"object\":\"v2.core.event_destination\",\"amazon_eventbridge\":null,\"created\":\"1970-01-12T21:42:34.472Z\",\"description\":\"description\",\"enabled_events\":[\"enabled_events\"],\"event_payload\":\"thin\",\"events_from\":null,\"livemode\":true,\"metadata\":null,\"name\":\"name\",\"snapshot_api_version\":null,\"status\":\"disabled\",\"status_details\":null,\"type\":\"amazon_eventbridge\",\"updated\":\"1970-01-03T17:07:10.277Z\",\"webhook_endpoint\":null}"); + "{\"id\":\"obj_123\",\"object\":\"v2.core.event_destination\",\"created\":\"1970-01-12T21:42:34.472Z\",\"description\":\"description\",\"enabled_events\":[\"enabled_events\"],\"event_payload\":\"thin\",\"livemode\":true,\"name\":\"name\",\"status\":\"disabled\",\"type\":\"amazon_eventbridge\",\"updated\":\"1970-01-03T17:07:10.277Z\"}"); var options = new Stripe.V2.Core.EventDestinationCreateOptions { EnabledEvents = new List { "enabled_events" }, @@ -6330,10 +6330,10 @@ public void TestV2CoreEventDestinationDelete() HttpMethod.Delete, "/v2/core/event_destinations/id_123", (HttpStatusCode)200, - "{\"id\":\"obj_123\",\"object\":\"v2.core.event_destination\",\"amazon_eventbridge\":null,\"created\":\"1970-01-12T21:42:34.472Z\",\"description\":\"description\",\"enabled_events\":[\"enabled_events\"],\"event_payload\":\"thin\",\"events_from\":null,\"livemode\":true,\"metadata\":null,\"name\":\"name\",\"snapshot_api_version\":null,\"status\":\"disabled\",\"status_details\":null,\"type\":\"amazon_eventbridge\",\"updated\":\"1970-01-03T17:07:10.277Z\",\"webhook_endpoint\":null}"); + "{\"id\":\"abc_123\",\"object\":\"some.object.tag\"}"); var client = new StripeClient(this.Requestor); var service = client.V2.Core.EventDestinations; - Stripe.V2.EventDestination deleted = service.Delete("id_123"); + Stripe.V2.DeletedObject deleted = service.Delete("id_123"); this.AssertRequest( HttpMethod.Delete, "/v2/core/event_destinations/id_123"); @@ -6346,7 +6346,7 @@ public void TestV2CoreEventDestinationGet2() HttpMethod.Get, "/v2/core/event_destinations/id_123", (HttpStatusCode)200, - "{\"id\":\"obj_123\",\"object\":\"v2.core.event_destination\",\"amazon_eventbridge\":null,\"created\":\"1970-01-12T21:42:34.472Z\",\"description\":\"description\",\"enabled_events\":[\"enabled_events\"],\"event_payload\":\"thin\",\"events_from\":null,\"livemode\":true,\"metadata\":null,\"name\":\"name\",\"snapshot_api_version\":null,\"status\":\"disabled\",\"status_details\":null,\"type\":\"amazon_eventbridge\",\"updated\":\"1970-01-03T17:07:10.277Z\",\"webhook_endpoint\":null}"); + "{\"id\":\"obj_123\",\"object\":\"v2.core.event_destination\",\"created\":\"1970-01-12T21:42:34.472Z\",\"description\":\"description\",\"enabled_events\":[\"enabled_events\"],\"event_payload\":\"thin\",\"livemode\":true,\"name\":\"name\",\"status\":\"disabled\",\"type\":\"amazon_eventbridge\",\"updated\":\"1970-01-03T17:07:10.277Z\"}"); var client = new StripeClient(this.Requestor); var service = client.V2.Core.EventDestinations; Stripe.V2.EventDestination eventDestination = service.Get("id_123"); @@ -6362,7 +6362,7 @@ public void TestV2CoreEventDestinationPost2() HttpMethod.Post, "/v2/core/event_destinations/id_123", (HttpStatusCode)200, - "{\"id\":\"obj_123\",\"object\":\"v2.core.event_destination\",\"amazon_eventbridge\":null,\"created\":\"1970-01-12T21:42:34.472Z\",\"description\":\"description\",\"enabled_events\":[\"enabled_events\"],\"event_payload\":\"thin\",\"events_from\":null,\"livemode\":true,\"metadata\":null,\"name\":\"name\",\"snapshot_api_version\":null,\"status\":\"disabled\",\"status_details\":null,\"type\":\"amazon_eventbridge\",\"updated\":\"1970-01-03T17:07:10.277Z\",\"webhook_endpoint\":null}"); + "{\"id\":\"obj_123\",\"object\":\"v2.core.event_destination\",\"created\":\"1970-01-12T21:42:34.472Z\",\"description\":\"description\",\"enabled_events\":[\"enabled_events\"],\"event_payload\":\"thin\",\"livemode\":true,\"name\":\"name\",\"status\":\"disabled\",\"type\":\"amazon_eventbridge\",\"updated\":\"1970-01-03T17:07:10.277Z\"}"); var options = new Stripe.V2.Core.EventDestinationUpdateOptions(); var client = new StripeClient(this.Requestor); var service = client.V2.Core.EventDestinations; @@ -6381,7 +6381,7 @@ public void TestV2CoreEventDestinationPost3() HttpMethod.Post, "/v2/core/event_destinations/id_123/disable", (HttpStatusCode)200, - "{\"id\":\"obj_123\",\"object\":\"v2.core.event_destination\",\"amazon_eventbridge\":null,\"created\":\"1970-01-12T21:42:34.472Z\",\"description\":\"description\",\"enabled_events\":[\"enabled_events\"],\"event_payload\":\"thin\",\"events_from\":null,\"livemode\":true,\"metadata\":null,\"name\":\"name\",\"snapshot_api_version\":null,\"status\":\"disabled\",\"status_details\":null,\"type\":\"amazon_eventbridge\",\"updated\":\"1970-01-03T17:07:10.277Z\",\"webhook_endpoint\":null}"); + "{\"id\":\"obj_123\",\"object\":\"v2.core.event_destination\",\"created\":\"1970-01-12T21:42:34.472Z\",\"description\":\"description\",\"enabled_events\":[\"enabled_events\"],\"event_payload\":\"thin\",\"livemode\":true,\"name\":\"name\",\"status\":\"disabled\",\"type\":\"amazon_eventbridge\",\"updated\":\"1970-01-03T17:07:10.277Z\"}"); var client = new StripeClient(this.Requestor); var service = client.V2.Core.EventDestinations; Stripe.V2.EventDestination eventDestination = service.Disable( @@ -6398,7 +6398,7 @@ public void TestV2CoreEventDestinationPost4() HttpMethod.Post, "/v2/core/event_destinations/id_123/enable", (HttpStatusCode)200, - "{\"id\":\"obj_123\",\"object\":\"v2.core.event_destination\",\"amazon_eventbridge\":null,\"created\":\"1970-01-12T21:42:34.472Z\",\"description\":\"description\",\"enabled_events\":[\"enabled_events\"],\"event_payload\":\"thin\",\"events_from\":null,\"livemode\":true,\"metadata\":null,\"name\":\"name\",\"snapshot_api_version\":null,\"status\":\"disabled\",\"status_details\":null,\"type\":\"amazon_eventbridge\",\"updated\":\"1970-01-03T17:07:10.277Z\",\"webhook_endpoint\":null}"); + "{\"id\":\"obj_123\",\"object\":\"v2.core.event_destination\",\"created\":\"1970-01-12T21:42:34.472Z\",\"description\":\"description\",\"enabled_events\":[\"enabled_events\"],\"event_payload\":\"thin\",\"livemode\":true,\"name\":\"name\",\"status\":\"disabled\",\"type\":\"amazon_eventbridge\",\"updated\":\"1970-01-03T17:07:10.277Z\"}"); var client = new StripeClient(this.Requestor); var service = client.V2.Core.EventDestinations; Stripe.V2.EventDestination eventDestination = service.Enable( @@ -6415,7 +6415,7 @@ public void TestV2CoreEventDestinationPost5() HttpMethod.Post, "/v2/core/event_destinations/id_123/ping", (HttpStatusCode)200, - "{\"id\":\"obj_123\",\"object\":\"v2.core.event\",\"context\":null,\"created\":\"1970-01-12T21:42:34.472Z\",\"livemode\":true,\"reason\":null,\"type\":\"type\"}"); + "{\"id\":\"obj_123\",\"object\":\"v2.core.event\",\"created\":\"1970-01-12T21:42:34.472Z\",\"livemode\":true,\"type\":\"type\"}"); var client = new StripeClient(this.Requestor); var service = client.V2.Core.EventDestinations; Stripe.V2.Event result = service.Ping("id_123"); From e84e43c335109a1a618718100d75b29e4b2ca764 Mon Sep 17 00:00:00 2001 From: jar-stripe Date: Thu, 11 Sep 2025 08:48:46 -0700 Subject: [PATCH 3/7] Adds public BaseUrl to RawRequestOptions (#3170) renames internal BaseUrl in RequestOptions to InternalBaseUrl adds public BaseUrl to RawRequestOptions - it delegates to InternalBaseUrl for safety --- .../Public/ApiRequestorAdapter.cs | 2 +- .../Infrastructure/Public/LiveApiRequestor.cs | 2 +- .../Services/_common/RawRequestOptions.cs | 7 ++++ .../Services/_common/RequestOptions.cs | 4 +-- .../Infrastructure/Public/StripeClientTest.cs | 36 +++++++++++++++++++ 5 files changed, 47 insertions(+), 4 deletions(-) diff --git a/src/Stripe.net/Infrastructure/Public/ApiRequestorAdapter.cs b/src/Stripe.net/Infrastructure/Public/ApiRequestorAdapter.cs index c2a366da5d..95751ea666 100644 --- a/src/Stripe.net/Infrastructure/Public/ApiRequestorAdapter.cs +++ b/src/Stripe.net/Infrastructure/Public/ApiRequestorAdapter.cs @@ -54,7 +54,7 @@ public override Task RequestAsync( if (baseAddress != BaseAddress.Api) { requestOptions ??= new RequestOptions(); - requestOptions.BaseUrl = this.GetBaseUrl(baseAddress); + requestOptions.InternalBaseUrl = this.GetBaseUrl(baseAddress); } return this.client.RequestAsync(method, path, options, requestOptions, cancellationToken); diff --git a/src/Stripe.net/Infrastructure/Public/LiveApiRequestor.cs b/src/Stripe.net/Infrastructure/Public/LiveApiRequestor.cs index 652b563171..e478935b8b 100644 --- a/src/Stripe.net/Infrastructure/Public/LiveApiRequestor.cs +++ b/src/Stripe.net/Infrastructure/Public/LiveApiRequestor.cs @@ -231,7 +231,7 @@ private StripeRequest MakeStripeRequest( } var uri = StripeRequest.BuildUri( - requestOptions?.BaseUrl ?? this.GetBaseUrl(baseAddress), + requestOptions?.InternalBaseUrl ?? this.GetBaseUrl(baseAddress), method, path, options, diff --git a/src/Stripe.net/Services/_common/RawRequestOptions.cs b/src/Stripe.net/Services/_common/RawRequestOptions.cs index 56d62af257..e10e88377b 100644 --- a/src/Stripe.net/Services/_common/RawRequestOptions.cs +++ b/src/Stripe.net/Services/_common/RawRequestOptions.cs @@ -4,6 +4,13 @@ namespace Stripe public class RawRequestOptions : RequestOptions { + /// Gets or sets the base URL for the raw request. + /// + /// Use this to send API calls to e.g. files.stripe.com or + /// a proxy address. + /// + public string BaseUrl { get => this.InternalBaseUrl; set => this.InternalBaseUrl = value; } + /// Gets or sets additional headers for the request. public Dictionary AdditionalHeaders { get; set; } = new Dictionary(); diff --git a/src/Stripe.net/Services/_common/RequestOptions.cs b/src/Stripe.net/Services/_common/RequestOptions.cs index dd8409cf54..6dbeb3f34e 100644 --- a/src/Stripe.net/Services/_common/RequestOptions.cs +++ b/src/Stripe.net/Services/_common/RequestOptions.cs @@ -27,13 +27,13 @@ public class RequestOptions /// Gets or sets the value or Stripe-Context request header. public string StripeContext { get; set; } - /// Gets or sets the base URL for the request. + /// Gets the base URL for the request. /// /// This is an internal property. It is set by services or individual request methods when /// they need to send a request to a non-standard destination, e.g. files.stripe.com /// for file creation requests or connect.stripe.com for OAuth requests. /// - internal string BaseUrl { get; set; } + internal string InternalBaseUrl { get; set; } /// Gets or sets the API version for the request. /// diff --git a/src/StripeTests/Infrastructure/Public/StripeClientTest.cs b/src/StripeTests/Infrastructure/Public/StripeClientTest.cs index a02d3c64e4..dd167d6462 100644 --- a/src/StripeTests/Infrastructure/Public/StripeClientTest.cs +++ b/src/StripeTests/Infrastructure/Public/StripeClientTest.cs @@ -164,6 +164,42 @@ public async Task RawRequestAsync_Json() Assert.Equal("mes_123", obj.Id); } + [Fact] + public async Task RawRequest_BaseUrl() + { + // Stub a request as stripe-mock does not support v2 + this.MockHttpClientFixture.StubRequest( + HttpMethod.Post, + "/v2/billing/meter_event_session", + System.Net.HttpStatusCode.OK, + "{\"id\": \"mes_123\",\"object\":\"v2.billing.meter_event_session\"}"); + + var expectedBaseUrl = "https://test.stripetest.com"; + var rawResponse = await this.stripeClient.RawRequestAsync( + HttpMethod.Post, + "/v2/billing/meter_event_session", + "{}", + new RawRequestOptions + { + BaseUrl = expectedBaseUrl, + AdditionalHeaders = + { + { "foo", "bar" }, + }, + }); + + this.MockHttpClientFixture.MockHandler.Protected() + .Verify( + "SendAsync", + Times.Once(), + ItExpr.Is(m => + new Uri(expectedBaseUrl).Host == (string)m.Properties["OriginalHost"]), + ItExpr.IsAny()); + + var obj = this.stripeClient.Deserialize(rawResponse.Content); + Assert.Equal("mes_123", obj.Id); + } + [Fact] public async Task RawRequestAsyncIncludesCorrectUsage() { From 7f48eeccd1eeb8a7b78015fb695d92e0b3dd9adc Mon Sep 17 00:00:00 2001 From: "stripe-openapi[bot]" <105521251+stripe-openapi[bot]@users.noreply.github.com> Date: Fri, 19 Sep 2025 12:19:31 -0700 Subject: [PATCH 4/7] Update generated code (#3172) * Update generated code for v1955 and 2025-09-30.clover * Update generated code for v1968 and 2025-09-30.clover --------- Co-authored-by: Stripe OpenAPI <105521251+stripe-openapi[bot]@users.noreply.github.com> --- API_VERSION | 2 +- OPENAPI_VERSION | 2 +- .../Entities/PaymentIntents/PaymentIntentNextAction.cs | 3 ++- .../PaymentIntents/PaymentIntentNextActionUseStripeSdk.cs | 7 ------- .../Entities/SetupIntents/SetupIntentNextAction.cs | 3 ++- .../SetupIntents/SetupIntentNextActionUseStripeSdk.cs | 7 ------- src/StripeTests/Services/GeneratedExamplesTest.cs | 1 - 7 files changed, 6 insertions(+), 19 deletions(-) delete mode 100644 src/Stripe.net/Entities/PaymentIntents/PaymentIntentNextActionUseStripeSdk.cs delete mode 100644 src/Stripe.net/Entities/SetupIntents/SetupIntentNextActionUseStripeSdk.cs diff --git a/API_VERSION b/API_VERSION index 0336d6a3a5..fb72506858 100644 --- a/API_VERSION +++ b/API_VERSION @@ -1 +1 @@ -2025-08-27.basil \ No newline at end of file +2025-09-30.clover \ No newline at end of file diff --git a/OPENAPI_VERSION b/OPENAPI_VERSION index 6598803dc3..580d3b4079 100644 --- a/OPENAPI_VERSION +++ b/OPENAPI_VERSION @@ -1 +1 @@ -v1932 \ No newline at end of file +v1968 \ No newline at end of file diff --git a/src/Stripe.net/Entities/PaymentIntents/PaymentIntentNextAction.cs b/src/Stripe.net/Entities/PaymentIntents/PaymentIntentNextAction.cs index a645089ed7..5478c4dd6c 100644 --- a/src/Stripe.net/Entities/PaymentIntents/PaymentIntentNextAction.cs +++ b/src/Stripe.net/Entities/PaymentIntents/PaymentIntentNextAction.cs @@ -1,6 +1,7 @@ // File generated from our OpenAPI spec namespace Stripe { + using System.Collections.Generic; using Newtonsoft.Json; #if NET6_0_OR_GREATER using STJS = System.Text.Json.Serialization; @@ -107,7 +108,7 @@ public class PaymentIntentNextAction : StripeEntity #if NET6_0_OR_GREATER [STJS.JsonPropertyName("use_stripe_sdk")] #endif - public PaymentIntentNextActionUseStripeSdk UseStripeSdk { get; set; } + public Dictionary UseStripeSdk { get; set; } [JsonProperty("verify_with_microdeposits")] #if NET6_0_OR_GREATER diff --git a/src/Stripe.net/Entities/PaymentIntents/PaymentIntentNextActionUseStripeSdk.cs b/src/Stripe.net/Entities/PaymentIntents/PaymentIntentNextActionUseStripeSdk.cs deleted file mode 100644 index cdf002d2ad..0000000000 --- a/src/Stripe.net/Entities/PaymentIntents/PaymentIntentNextActionUseStripeSdk.cs +++ /dev/null @@ -1,7 +0,0 @@ -// File generated from our OpenAPI spec -namespace Stripe -{ - public class PaymentIntentNextActionUseStripeSdk : StripeEntity - { - } -} diff --git a/src/Stripe.net/Entities/SetupIntents/SetupIntentNextAction.cs b/src/Stripe.net/Entities/SetupIntents/SetupIntentNextAction.cs index 400b1efd26..b0e7d7a522 100644 --- a/src/Stripe.net/Entities/SetupIntents/SetupIntentNextAction.cs +++ b/src/Stripe.net/Entities/SetupIntents/SetupIntentNextAction.cs @@ -1,6 +1,7 @@ // File generated from our OpenAPI spec namespace Stripe { + using System.Collections.Generic; using Newtonsoft.Json; #if NET6_0_OR_GREATER using STJS = System.Text.Json.Serialization; @@ -41,7 +42,7 @@ public class SetupIntentNextAction : StripeEntity #if NET6_0_OR_GREATER [STJS.JsonPropertyName("use_stripe_sdk")] #endif - public SetupIntentNextActionUseStripeSdk UseStripeSdk { get; set; } + public Dictionary UseStripeSdk { get; set; } [JsonProperty("verify_with_microdeposits")] #if NET6_0_OR_GREATER diff --git a/src/Stripe.net/Entities/SetupIntents/SetupIntentNextActionUseStripeSdk.cs b/src/Stripe.net/Entities/SetupIntents/SetupIntentNextActionUseStripeSdk.cs deleted file mode 100644 index 71794eb783..0000000000 --- a/src/Stripe.net/Entities/SetupIntents/SetupIntentNextActionUseStripeSdk.cs +++ /dev/null @@ -1,7 +0,0 @@ -// File generated from our OpenAPI spec -namespace Stripe -{ - public class SetupIntentNextActionUseStripeSdk : StripeEntity - { - } -} diff --git a/src/StripeTests/Services/GeneratedExamplesTest.cs b/src/StripeTests/Services/GeneratedExamplesTest.cs index a0a41f9759..125a83e341 100644 --- a/src/StripeTests/Services/GeneratedExamplesTest.cs +++ b/src/StripeTests/Services/GeneratedExamplesTest.cs @@ -3911,7 +3911,6 @@ public void TestSubscriptionSchedulesPost() Quantity = 1, }, }, - Iterations = 12, }, }, }; From 6fb0d458c1e92465a7c41c49da7ad93102bbafbb Mon Sep 17 00:00:00 2001 From: jar-stripe Date: Fri, 19 Sep 2025 13:30:53 -0700 Subject: [PATCH 5/7] `just format` formats entire solution (#3174) --- OPENAPI_VERSION | 2 +- justfile | 2 +- src/Examples/Program.cs | 2 +- src/Examples/V2/ThinEventWebhookHandler.cs | 12 +- .../Entities/DeserializationTest.cs | 406 +++++++++--------- .../Services/GeneratedExamplesTest.cs | 14 +- 6 files changed, 219 insertions(+), 219 deletions(-) diff --git a/OPENAPI_VERSION b/OPENAPI_VERSION index 580d3b4079..62a6e0b6e4 100644 --- a/OPENAPI_VERSION +++ b/OPENAPI_VERSION @@ -1 +1 @@ -v1968 \ No newline at end of file +v1968 diff --git a/justfile b/justfile index 871aa51e5a..b891bed6de 100644 --- a/justfile +++ b/justfile @@ -20,7 +20,7 @@ ci-test: (_test "--no-build" "" "Release") # ⭐ format all files format *args: # This sets TargetFramework because of a race condition in dotnet format when it tries to format to multiple targets at a time, which could lead to code with compiler errors after it completes - TargetFramework=net5.0 dotnet format src/Stripe.net/Stripe.net.csproj --severity warn {{args}} + TargetFramework=net5.0 dotnet format src/Stripe.net.sln --severity warn {{args}} # verify, but don't modify, the project's formatting format-check: (format "--verify-no-changes") diff --git a/src/Examples/Program.cs b/src/Examples/Program.cs index 4d0ac5f52d..74514af254 100644 --- a/src/Examples/Program.cs +++ b/src/Examples/Program.cs @@ -27,7 +27,7 @@ public Program() /// are set before running the example. /// /// - /// command line args + /// command line args. /// public static async Task Main(string[] args) { diff --git a/src/Examples/V2/ThinEventWebhookHandler.cs b/src/Examples/V2/ThinEventWebhookHandler.cs index dd4e53f889..dbec0d14dc 100644 --- a/src/Examples/V2/ThinEventWebhookHandler.cs +++ b/src/Examples/V2/ThinEventWebhookHandler.cs @@ -22,15 +22,15 @@ namespace Examples.V2 [ApiController] public class ThinEventWebhookHandler : ControllerBase { - private readonly StripeClient _client; - private readonly string _webhookSecret; + private readonly StripeClient client; + private readonly string webhookSecret; public ThinEventWebhookHandler() { var apiKey = Environment.GetEnvironmentVariable("STRIPE_API_KEY"); - _client = new StripeClient(apiKey); + client = new StripeClient(apiKey); - _webhookSecret = Environment.GetEnvironmentVariable("WEBHOOK_SECRET"); + webhookSecret = Environment.GetEnvironmentVariable("WEBHOOK_SECRET"); } [HttpPost] @@ -39,10 +39,10 @@ public async Task Index() var json = await new StreamReader(HttpContext.Request.Body).ReadToEndAsync(); try { - var thinEvent = _client.ParseThinEvent(json, Request.Headers["Stripe-Signature"], _webhookSecret); + var thinEvent = client.ParseThinEvent(json, Request.Headers["Stripe-Signature"], webhookSecret); // Fetch the event data to understand the failure - var baseEvent = await _client.V2.Core.Events.GetAsync(thinEvent.Id); + var baseEvent = await client.V2.Core.Events.GetAsync(thinEvent.Id); if (baseEvent is V1BillingMeterErrorReportTriggeredEvent fullEvent) { var meter = await fullEvent.FetchRelatedObjectAsync(); diff --git a/src/StripeTests/Entities/DeserializationTest.cs b/src/StripeTests/Entities/DeserializationTest.cs index 5a6f715a47..62d4dc381b 100644 --- a/src/StripeTests/Entities/DeserializationTest.cs +++ b/src/StripeTests/Entities/DeserializationTest.cs @@ -1,211 +1,211 @@ namespace StripeTests { - using System; - using System.Collections.Generic; - using System.Linq; - using Newtonsoft.Json; - using Stripe; - using Stripe.Infrastructure; - using Xunit; - - public class DeserializationTest : BaseStripeTest - { - [Fact] - public void TestStubWithIdDirect() + using System; + using System.Collections.Generic; + using System.Linq; + using Newtonsoft.Json; + using Stripe; + using Stripe.Infrastructure; + using Xunit; + + public class DeserializationTest : BaseStripeTest { - string json = "{\"some_ref\":{\"id\":\"xyz\"}}"; - var entity = StripeEntity.FromJson(json); - Assert.NotNull(entity); - Assert.IsType(entity); - var id = entity.SomeRef.Id; - Assert.Equal("xyz", id); - } - - [Fact] - public void TestStubWithIdExpanded() - { - string json = "{\"some_expandable\":{\"id\":\"xyz\"}}"; - var entity = StripeEntity.FromJson(json); - Assert.NotNull(entity); - Assert.IsType(entity); - var id = entity.SomeExpandable.Id; - Assert.Equal("xyz", id); - } - - [Fact] - public void TestStubWithIdArrayExpanded() - { - string json = "{\"some_expanded_array\":[{\"id\":\"xyz\"}]}"; - var entity = StripeEntity.FromJson(json); - Assert.NotNull(entity); - Assert.IsType(entity); - var id = entity.SomeExpandedArray[0].Id; - Assert.Equal("xyz", id); - } - - [Fact] - public void TestStubWithIdInArray() - { - string json = "{\"some_ref_array\":[{\"id\":\"xyz\"}]}"; - var entity = StripeEntity.FromJson(json); - Assert.NotNull(entity); - Assert.IsType(entity); - var id = entity.SomeRefArray[0].Id; - Assert.Equal("xyz", id); - } - - [Fact] - public void TestStubWithIdInListObject() - { - string json = "{\"some_list_object\":{\"data\":[{\"id\":\"xyz\"}]}}"; - var entity = StripeEntity.FromJson(json); - Assert.NotNull(entity); - Assert.IsType(entity); - var enumerator = entity.SomeListObject.GetEnumerator(); - enumerator.MoveNext(); - var id = enumerator.Current.Id; - Assert.Equal("xyz", id); - } - - [Fact] - public void TestEmptyObjectDirect() - { - string json = "{\"some_ref\":{}}"; - var entity = JsonConvert.DeserializeObject(json); - var id = entity.SomeRef.Id; - Assert.Null(id); - } - - [Fact] - public void TestEmptyObjectArray() - { - string json = "{\"some_ref_array\":[{}]}"; - var entity = JsonConvert.DeserializeObject(json); - var id = entity.SomeRefArray[0].Id; - Assert.Null(id); - } - - [Fact] - public void TestEmptyObjectExpanded() - { - string json = "{\"some_expandable\":{}}"; - var entity = JsonConvert.DeserializeObject(json); - var id = entity.SomeExpandable.Id; - Assert.Null(id); - } - - [Fact] - public void TestEmptyObjectArrayExpanded() - { - string json = "{\"some_expanded_array\":[{}]}"; - var entity = JsonConvert.DeserializeObject(json); - var id = entity.SomeExpandedArray[0].Id; - Assert.Null(id); - } - - public class MyEntity : StripeEntity, IHasId - { - [JsonProperty("id")] - public string Id { get; set; } - - [JsonProperty("some_integer")] - public long SomeInteger { get; set; } - - [JsonProperty("some_longinteger")] - public long SomeLonginteger { get; set; } - - [JsonProperty("some_boolean")] - public bool SomeBoolean { get; set; } - - [JsonProperty("some_number")] - public decimal SomeNumber { get; set; } - - [JsonProperty("some_decimal_string")] - public decimal SomeDecimalString { get; set; } - - [JsonProperty("some_string")] - public string SomeString { get; set; } - - [JsonProperty("some_datetime")] - [JsonConverter(typeof(UnixDateTimeConverter))] - public DateTime SomeDatetime { get; set; } = Stripe.Infrastructure.DateTimeUtils.UnixEpoch; - - [JsonProperty("some_ref")] - public MyEntity SomeRef { get; set; } - - [JsonProperty("some_literal")] - public string SomeLiteral { get; set; } - - [JsonProperty("some_nullable")] - public string SomeNullable { get; set; } - - [JsonProperty("some_string_array")] - public List SomeStringArray { get; set; } - - [JsonProperty("some_ref_array")] - public List SomeRefArray { get; set; } + [Fact] + public void TestStubWithIdDirect() + { + string json = "{\"some_ref\":{\"id\":\"xyz\"}}"; + var entity = StripeEntity.FromJson(json); + Assert.NotNull(entity); + Assert.IsType(entity); + var id = entity.SomeRef.Id; + Assert.Equal("xyz", id); + } + + [Fact] + public void TestStubWithIdExpanded() + { + string json = "{\"some_expandable\":{\"id\":\"xyz\"}}"; + var entity = StripeEntity.FromJson(json); + Assert.NotNull(entity); + Assert.IsType(entity); + var id = entity.SomeExpandable.Id; + Assert.Equal("xyz", id); + } + + [Fact] + public void TestStubWithIdArrayExpanded() + { + string json = "{\"some_expanded_array\":[{\"id\":\"xyz\"}]}"; + var entity = StripeEntity.FromJson(json); + Assert.NotNull(entity); + Assert.IsType(entity); + var id = entity.SomeExpandedArray[0].Id; + Assert.Equal("xyz", id); + } + + [Fact] + public void TestStubWithIdInArray() + { + string json = "{\"some_ref_array\":[{\"id\":\"xyz\"}]}"; + var entity = StripeEntity.FromJson(json); + Assert.NotNull(entity); + Assert.IsType(entity); + var id = entity.SomeRefArray[0].Id; + Assert.Equal("xyz", id); + } + + [Fact] + public void TestStubWithIdInListObject() + { + string json = "{\"some_list_object\":{\"data\":[{\"id\":\"xyz\"}]}}"; + var entity = StripeEntity.FromJson(json); + Assert.NotNull(entity); + Assert.IsType(entity); + var enumerator = entity.SomeListObject.GetEnumerator(); + enumerator.MoveNext(); + var id = enumerator.Current.Id; + Assert.Equal("xyz", id); + } + + [Fact] + public void TestEmptyObjectDirect() + { + string json = "{\"some_ref\":{}}"; + var entity = JsonConvert.DeserializeObject(json); + var id = entity.SomeRef.Id; + Assert.Null(id); + } + + [Fact] + public void TestEmptyObjectArray() + { + string json = "{\"some_ref_array\":[{}]}"; + var entity = JsonConvert.DeserializeObject(json); + var id = entity.SomeRefArray[0].Id; + Assert.Null(id); + } + + [Fact] + public void TestEmptyObjectExpanded() + { + string json = "{\"some_expandable\":{}}"; + var entity = JsonConvert.DeserializeObject(json); + var id = entity.SomeExpandable.Id; + Assert.Null(id); + } + + [Fact] + public void TestEmptyObjectArrayExpanded() + { + string json = "{\"some_expanded_array\":[{}]}"; + var entity = JsonConvert.DeserializeObject(json); + var id = entity.SomeExpandedArray[0].Id; + Assert.Null(id); + } + + public class MyEntity : StripeEntity, IHasId + { + [JsonProperty("id")] + public string Id { get; set; } + + [JsonProperty("some_integer")] + public long SomeInteger { get; set; } + + [JsonProperty("some_longinteger")] + public long SomeLonginteger { get; set; } + + [JsonProperty("some_boolean")] + public bool SomeBoolean { get; set; } + + [JsonProperty("some_number")] + public decimal SomeNumber { get; set; } + + [JsonProperty("some_decimal_string")] + public decimal SomeDecimalString { get; set; } + + [JsonProperty("some_string")] + public string SomeString { get; set; } + + [JsonProperty("some_datetime")] + [JsonConverter(typeof(UnixDateTimeConverter))] + public DateTime SomeDatetime { get; set; } = Stripe.Infrastructure.DateTimeUtils.UnixEpoch; + + [JsonProperty("some_ref")] + public MyEntity SomeRef { get; set; } + + [JsonProperty("some_literal")] + public string SomeLiteral { get; set; } + + [JsonProperty("some_nullable")] + public string SomeNullable { get; set; } + + [JsonProperty("some_string_array")] + public List SomeStringArray { get; set; } + + [JsonProperty("some_ref_array")] + public List SomeRefArray { get; set; } + + #region Expandable SomeExpandedArray + + [JsonIgnore] + public List SomeExpandedArrayIds + { + get => this.InternalSomeExpandedArray?.Select((x) => x.Id).ToList(); + set => this.InternalSomeExpandedArray = SetExpandableArrayIds(value); + } + + [JsonIgnore] + public List SomeExpandedArray + { + get => this.InternalSomeExpandedArray?.Select((x) => x.ExpandedObject).ToList(); + set => this.InternalSomeExpandedArray = SetExpandableArrayObjects(value); + } + + [JsonProperty("some_expanded_array", ItemConverterType = typeof(ExpandableFieldConverter))] + internal List> InternalSomeExpandedArray { get; set; } + #endregion + + [JsonProperty("some_enum")] + public string SomeEnum { get; set; } + + [JsonProperty("some_map")] + public Dictionary SomeMap { get; set; } + + [JsonProperty("some_object")] + public MyEntitySomeObject SomeObject { get; set; } + + [JsonProperty("some_list_object")] + public StripeList SomeListObject { get; set; } + + #region Expandable SomeExpandable + + [JsonIgnore] + public string SomeExpandableId + { + get => this.InternalSomeExpandable?.Id; + set => this.InternalSomeExpandable = SetExpandableFieldId(value, this.InternalSomeExpandable); + } + + [JsonIgnore] + public MyEntity SomeExpandable + { + get => this.InternalSomeExpandable?.ExpandedObject; + set => this.InternalSomeExpandable = SetExpandableFieldObject(value, this.InternalSomeExpandable); + } - #region Expandable SomeExpandedArray + [JsonProperty("some_expandable")] + [JsonConverter(typeof(ExpandableFieldConverter))] + internal ExpandableField InternalSomeExpandable { get; set; } + #endregion - [JsonIgnore] - public List SomeExpandedArrayIds - { - get => this.InternalSomeExpandedArray?.Select((x) => x.Id).ToList(); - set => this.InternalSomeExpandedArray = SetExpandableArrayIds(value); - } + [JsonProperty("some_polymorphic_group")] + public MyEntity SomePolymorphicGroup { get; set; } + } - [JsonIgnore] - public List SomeExpandedArray - { - get => this.InternalSomeExpandedArray?.Select((x) => x.ExpandedObject).ToList(); - set => this.InternalSomeExpandedArray = SetExpandableArrayObjects(value); - } - - [JsonProperty("some_expanded_array", ItemConverterType = typeof(ExpandableFieldConverter))] - internal List> InternalSomeExpandedArray { get; set; } - #endregion - - [JsonProperty("some_enum")] - public string SomeEnum { get; set; } - - [JsonProperty("some_map")] - public Dictionary SomeMap { get; set; } - - [JsonProperty("some_object")] - public MyEntitySomeObject SomeObject { get; set; } - - [JsonProperty("some_list_object")] - public StripeList SomeListObject { get; set; } - - #region Expandable SomeExpandable - - [JsonIgnore] - public string SomeExpandableId - { - get => this.InternalSomeExpandable?.Id; - set => this.InternalSomeExpandable = SetExpandableFieldId(value, this.InternalSomeExpandable); - } - - [JsonIgnore] - public MyEntity SomeExpandable - { - get => this.InternalSomeExpandable?.ExpandedObject; - set => this.InternalSomeExpandable = SetExpandableFieldObject(value, this.InternalSomeExpandable); - } - - [JsonProperty("some_expandable")] - [JsonConverter(typeof(ExpandableFieldConverter))] - internal ExpandableField InternalSomeExpandable { get; set; } - #endregion - - [JsonProperty("some_polymorphic_group")] - public MyEntity SomePolymorphicGroup { get; set; } - } - - public class MyEntitySomeObject : StripeEntity - { - [JsonProperty("some_string")] - public string SomeString { get; set; } + public class MyEntitySomeObject : StripeEntity + { + [JsonProperty("some_string")] + public string SomeString { get; set; } + } } - } } diff --git a/src/StripeTests/Services/GeneratedExamplesTest.cs b/src/StripeTests/Services/GeneratedExamplesTest.cs index 125a83e341..a91e363ab4 100644 --- a/src/StripeTests/Services/GeneratedExamplesTest.cs +++ b/src/StripeTests/Services/GeneratedExamplesTest.cs @@ -6434,9 +6434,9 @@ public void TestTemporarySessionExpiredError() var exception = Assert.Throws( () => { - var options = new Stripe.V2.Billing.MeterEventStreamCreateOptions - { - Events = new List + var options = new Stripe.V2.Billing.MeterEventStreamCreateOptions + { + Events = new List { new Stripe.V2.Billing.MeterEventStreamCreateEventOptions { @@ -6447,10 +6447,10 @@ public void TestTemporarySessionExpiredError() }, }, }, - }; - var client = new StripeClient(this.Requestor); - var service = client.V2.Billing.MeterEventStream; - service.Create(options); + }; + var client = new StripeClient(this.Requestor); + var service = client.V2.Billing.MeterEventStream; + service.Create(options); }); this.AssertRequest( HttpMethod.Post, From ccf0c39d79167b1930e3561280656eadf9cc056b Mon Sep 17 00:00:00 2001 From: jar-stripe Date: Fri, 19 Sep 2025 14:25:31 -0700 Subject: [PATCH 6/7] Adds ability to specify file name and type when calling FileService.Create (#3171) --- .../FormEncoding/ContentEncoder.cs | 4 + .../FormEncoding/FormEncoder.cs | 4 + .../FormEncoding/MultipartFormDataContent.cs | 25 +++- .../Services/Files/FileCreateOptions.cs | 3 +- .../Services/_common/MultipartFileContent.cs | 31 +++++ .../MultipartFormDataContentTest.cs | 127 ++++++++++++++++++ .../Services/Files/FileServiceTest.cs | 11 +- .../Services/GeneratedExamplesTest.cs | 7 +- 8 files changed, 199 insertions(+), 13 deletions(-) create mode 100644 src/Stripe.net/Services/_common/MultipartFileContent.cs diff --git a/src/Stripe.net/Infrastructure/FormEncoding/ContentEncoder.cs b/src/Stripe.net/Infrastructure/FormEncoding/ContentEncoder.cs index aeb2453cc8..32733e638c 100644 --- a/src/Stripe.net/Infrastructure/FormEncoding/ContentEncoder.cs +++ b/src/Stripe.net/Infrastructure/FormEncoding/ContentEncoder.cs @@ -145,6 +145,10 @@ private static List> FlattenParamsValue(object valu flatParams = SingleParam(keyPrefix, s); break; + case MultipartFileContent f: + flatParams = SingleParam(keyPrefix, f); + break; + case Stream s: flatParams = SingleParam(keyPrefix, s); break; diff --git a/src/Stripe.net/Infrastructure/FormEncoding/FormEncoder.cs b/src/Stripe.net/Infrastructure/FormEncoding/FormEncoder.cs index 53b2441bae..2c9bca954d 100644 --- a/src/Stripe.net/Infrastructure/FormEncoding/FormEncoder.cs +++ b/src/Stripe.net/Infrastructure/FormEncoding/FormEncoder.cs @@ -116,6 +116,10 @@ private static List> FlattenParamsValue(object valu flatParams = SingleParam(keyPrefix, s); break; + case MultipartFileContent f: + flatParams = SingleParam(keyPrefix, f); + break; + case Stream s: flatParams = SingleParam(keyPrefix, s); break; diff --git a/src/Stripe.net/Infrastructure/FormEncoding/MultipartFormDataContent.cs b/src/Stripe.net/Infrastructure/FormEncoding/MultipartFormDataContent.cs index d7ff9b3427..4c2c46d290 100644 --- a/src/Stripe.net/Infrastructure/FormEncoding/MultipartFormDataContent.cs +++ b/src/Stripe.net/Infrastructure/FormEncoding/MultipartFormDataContent.cs @@ -41,26 +41,29 @@ public MultipartFormDataContent( private static StringContent CreateStringContent(string value) => new StringContent(value, System.Text.Encoding.UTF8); - private static StreamContent CreateStreamContent(Stream value, string name) + private static StreamContent CreateStreamContent(MultipartFileContent value, string name) { - var fileName = "blob"; - var extension = string.Empty; + var fileName = value.Name ?? "blob"; + var extension = Path.GetExtension(fileName); + var stream = value.Data; - FileStream fileStream = value as FileStream; + FileStream fileStream = stream as FileStream; if ((fileStream != null) && (!string.IsNullOrEmpty(fileStream.Name))) { fileName = fileStream.Name; extension = Path.GetExtension(fileName); } - var content = new StreamContent(value); + var type = value.Type ?? MimeTypes.GetMimeType(extension); + + var content = new StreamContent(stream); content.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data") { Name = name, FileName = fileName, FileNameStar = fileName, }; - content.Headers.ContentType = new MediaTypeHeaderValue(MimeTypes.GetMimeType(extension)); + content.Headers.ContentType = new MediaTypeHeaderValue(type); return content; } @@ -79,8 +82,16 @@ private void ProcessParameters(IEnumerable> nameVal this.Add(CreateStringContent(s), QuoteString(kvp.Key)); break; + case MultipartFileContent f: + this.Add(CreateStreamContent(f, QuoteString(kvp.Key))); + break; + case Stream s: - this.Add(CreateStreamContent(s, QuoteString(kvp.Key))); + var fileData = new MultipartFileContent + { + Data = s, + }; + this.Add(CreateStreamContent(fileData, QuoteString(kvp.Key))); break; default: diff --git a/src/Stripe.net/Services/Files/FileCreateOptions.cs b/src/Stripe.net/Services/Files/FileCreateOptions.cs index a252e950b2..093dcefc53 100644 --- a/src/Stripe.net/Services/Files/FileCreateOptions.cs +++ b/src/Stripe.net/Services/Files/FileCreateOptions.cs @@ -1,7 +1,6 @@ // File generated from our OpenAPI spec namespace Stripe { - using System.IO; using Newtonsoft.Json; #if NET6_0_OR_GREATER using STJS = System.Text.Json.Serialization; @@ -17,7 +16,7 @@ public class FileCreateOptions : BaseOptions #if NET6_0_OR_GREATER [STJS.JsonPropertyName("file")] #endif - public Stream File { get; set; } + public MultipartFileContent File { get; set; } /// /// Optional parameters that automatically create a + /// Represents Data and optional Name and Type that will be encoded as multipart form + /// data. Used in e.g. FileService.Create. + /// + /// + public class MultipartFileContent + { + /// + /// The file data to send. If this is a FileStream, the SDK will infer + /// the name and type from the file name and extension. If this is not + /// a FileStream set Name and Type to configure the file upload. + /// + public Stream Data { get; set; } + + /// + /// The optional name to send with this file data. Uses the file name if omitted + /// and Data is a FileStream. + /// + public string Name { get; set; } + + /// + /// The optional mime type to use when sending file data. Uses the type that + /// matches the file extension from Name (or the file name from Data) if omitted. + /// + public string Type { get; set; } + } +} diff --git a/src/StripeTests/Infrastructure/FormEncoding/MultipartFormDataContentTest.cs b/src/StripeTests/Infrastructure/FormEncoding/MultipartFormDataContentTest.cs index 30f2bf6c40..fcaa77faad 100644 --- a/src/StripeTests/Infrastructure/FormEncoding/MultipartFormDataContentTest.cs +++ b/src/StripeTests/Infrastructure/FormEncoding/MultipartFormDataContentTest.cs @@ -5,6 +5,7 @@ namespace StripeTests using System.IO; using System.Text; using System.Threading.Tasks; + using Stripe; using Stripe.Infrastructure.FormEncoding; using Xunit; @@ -71,6 +72,132 @@ public async Task Ctor_OneStreamEntry_Success() result); } + [Fact] + public async Task Ctor_OneMultipartFileContentEntry_Success() + { + var source = new Dictionary + { + { "key", new MultipartFileContent { Data = new MemoryStream(Encoding.UTF8.GetBytes("Hello World!")) } }, + }; + var content = new MultipartFormDataContent(source, "test-boundary"); + + var stream = await content.ReadAsStreamAsync(); + Assert.Equal(174, stream.Length); + var result = new StreamReader(stream).ReadToEnd(); + Assert.Equal( + "--test-boundary\r\n" + + "Content-Disposition: form-data; name=\"key\"; filename=blob; filename*=utf-8''blob\r\n" + + "Content-Type: application/octet-stream\r\n\r\nHello World!\r\n" + + "--test-boundary--\r\n", + result); + } + + [Fact] + public async Task Ctor_OneMultipartFileContentWithNameEntry_Success() + { + var source = new Dictionary + { + { + "key", new MultipartFileContent + { + Data = new MemoryStream(Encoding.UTF8.GetBytes("Hello World!")), + Name = "file", + } + }, + }; + var content = new MultipartFormDataContent(source, "test-boundary"); + + var stream = await content.ReadAsStreamAsync(); + Assert.Equal(174, stream.Length); + var result = new StreamReader(stream).ReadToEnd(); + Assert.Equal( + "--test-boundary\r\n" + + "Content-Disposition: form-data; name=\"key\"; filename=file; filename*=utf-8''file\r\n" + + "Content-Type: application/octet-stream\r\n\r\nHello World!\r\n" + + "--test-boundary--\r\n", + result); + } + + [Fact] + public async Task Ctor_OneMultipartFileContentWithNameAndExtEntry_Success() + { + var source = new Dictionary + { + { + "key", new MultipartFileContent + { + Data = new MemoryStream(Encoding.UTF8.GetBytes("Hello World!")), + Name = "file.csv", + } + }, + }; + var content = new MultipartFormDataContent(source, "test-boundary"); + + var stream = await content.ReadAsStreamAsync(); + Assert.Equal(166, stream.Length); + var result = new StreamReader(stream).ReadToEnd(); + Assert.Equal( + "--test-boundary\r\n" + + "Content-Disposition: form-data; name=\"key\"; filename=file.csv; filename*=utf-8''file.csv\r\n" + + "Content-Type: text/csv\r\n\r\nHello World!\r\n" + + "--test-boundary--\r\n", + result); + } + + [Fact] + public async Task Ctor_OneMultipartFileContentWithNameAndTypeEntry_Success() + { + var source = new Dictionary + { + { + "key", new MultipartFileContent + { + Data = new MemoryStream(Encoding.UTF8.GetBytes("Hello World!")), + Name = "file", + Type = "application/json", + } + }, + }; + var content = new MultipartFormDataContent(source, "test-boundary"); + + var stream = await content.ReadAsStreamAsync(); + Assert.Equal(166, stream.Length); + var result = new StreamReader(stream).ReadToEnd(); + Assert.Equal( + "--test-boundary\r\n" + + "Content-Disposition: form-data; name=\"key\"; filename=file; filename*=utf-8''file\r\n" + + "Content-Type: application/json\r\n\r\nHello World!\r\n" + + "--test-boundary--\r\n", + result); + } + + [Fact] + public async Task Ctor_OneMultipartFileContentWithNameAndExtAndTypeEntry_Success() + { + var source = new Dictionary + { + { + "key", new MultipartFileContent + { + Data = new MemoryStream(Encoding.UTF8.GetBytes("Hello World!")), + Name = "file.json", + Type = "application/octet-stream", + } + }, + }; + var content = new MultipartFormDataContent(source, "test-boundary"); + + var stream = await content.ReadAsStreamAsync(); + Assert.Equal(184, stream.Length); + var result = new StreamReader(stream).ReadToEnd(); + Assert.Equal( + "--test-boundary\r\n" + + "Content-Disposition: form-data; name=\"key\"; filename=file.json; filename*=utf-8''file.json\r\n" + + "Content-Type: application/octet-stream\r\n\r\nHello World!\r\n" + + "--test-boundary--\r\n", + result); + } + [Fact] public async Task Ctor_TwoEntries_Success() { diff --git a/src/StripeTests/Services/Files/FileServiceTest.cs b/src/StripeTests/Services/Files/FileServiceTest.cs index 9a0bd84032..bdf88084cd 100644 --- a/src/StripeTests/Services/Files/FileServiceTest.cs +++ b/src/StripeTests/Services/Files/FileServiceTest.cs @@ -28,9 +28,13 @@ public FileServiceTest( { this.service = new FileService(this.StripeClient); + var resourceStream = typeof(FileServiceTest).GetTypeInfo().Assembly.GetManifestResourceStream(FileName); this.createOptions = new FileCreateOptions { - File = typeof(FileServiceTest).GetTypeInfo().Assembly.GetManifestResourceStream(FileName), + File = new MultipartFileContent + { + Data = resourceStream, + }, FileLinkData = new FileFileLinkDataOptions { Create = true, @@ -44,7 +48,10 @@ public FileServiceTest( this.base64Options = new FileCreateOptions { - File = new MemoryStream(Convert.FromBase64String("c3RyaXBlLWRvdG5ldA==")), + File = new MultipartFileContent + { + Data = new MemoryStream(Convert.FromBase64String("c3RyaXBlLWRvdG5ldA==")), + }, Purpose = FilePurpose.BusinessLogo, }; diff --git a/src/StripeTests/Services/GeneratedExamplesTest.cs b/src/StripeTests/Services/GeneratedExamplesTest.cs index a91e363ab4..ee1712c6c8 100644 --- a/src/StripeTests/Services/GeneratedExamplesTest.cs +++ b/src/StripeTests/Services/GeneratedExamplesTest.cs @@ -1330,8 +1330,11 @@ public void TestFilesPost() var options = new FileCreateOptions { Purpose = "account_requirement", - File = new System.IO.MemoryStream( - System.Text.Encoding.UTF8.GetBytes("File contents")), + File = new Stripe.MultipartFileContent + { + Data = new System.IO.MemoryStream( + System.Text.Encoding.UTF8.GetBytes("File contents")), + }, }; var service = new FileService(this.StripeClient); File file = service.Create(options); From 74b7189f4db8633e92d8ce3854368b1342296426 Mon Sep 17 00:00:00 2001 From: Stripe OpenAPI <105521251+stripe-openapi[bot]@users.noreply.github.com> Date: Fri, 19 Sep 2025 23:32:08 +0000 Subject: [PATCH 7/7] Update generated code for 9de7288a5c444f47d15545549303c3de4c226c71 and 2025-09-30.clover --- OPENAPI_VERSION | 2 +- src/Stripe.net/Services/Files/FileCreateOptions.cs | 3 +-- src/StripeTests/Services/GeneratedExamplesTest.cs | 7 +++++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/OPENAPI_VERSION b/OPENAPI_VERSION index 798c986426..328de8c992 100644 --- a/OPENAPI_VERSION +++ b/OPENAPI_VERSION @@ -1 +1 @@ -1651e4fe240f7affbb971ce7ed3e425feb004bb3 \ No newline at end of file +9de7288a5c444f47d15545549303c3de4c226c71 \ No newline at end of file diff --git a/src/Stripe.net/Services/Files/FileCreateOptions.cs b/src/Stripe.net/Services/Files/FileCreateOptions.cs index a252e950b2..093dcefc53 100644 --- a/src/Stripe.net/Services/Files/FileCreateOptions.cs +++ b/src/Stripe.net/Services/Files/FileCreateOptions.cs @@ -1,7 +1,6 @@ // File generated from our OpenAPI spec namespace Stripe { - using System.IO; using Newtonsoft.Json; #if NET6_0_OR_GREATER using STJS = System.Text.Json.Serialization; @@ -17,7 +16,7 @@ public class FileCreateOptions : BaseOptions #if NET6_0_OR_GREATER [STJS.JsonPropertyName("file")] #endif - public Stream File { get; set; } + public MultipartFileContent File { get; set; } /// /// Optional parameters that automatically create a