diff --git a/CHANGELOG.md b/CHANGELOG.md index 9fa19f765a..ab7f65611d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -102,7 +102,13 @@ See [SaaS platform payments with subscription billing using Accounts v2](https:/ * [#3069](https://github.com/stripe/stripe-dotnet/pull/3069) Enhance beta version handling in ApiVersion * `StripeConfiguation.AddBetaVersion` will use the highest version number used for a beta feature instead of throwing an `Exception` on a conflict as it had done previously. - + +## 48.0.2 - 2025-04-15 +* [#3101](https://github.com/stripe/stripe-dotnet/pull/3101) Replace Dictionary with ConcurrentDictionary in SerializablePropertyCache to fix a concurrency related error reported in [#3100](https://github.com/stripe/stripe-dotnet/issues/3100) + +## 48.0.1 - 2025-04-14 +* [#3090](https://github.com/stripe/stripe-dotnet/pull/3090) Disable Json.NET metadata special handling. Fixes issue [#3068](https://github.com/stripe/stripe-dotnet/issues/3068) + ## 48.0.0 - 2025-04-01 * [#3074](https://github.com/stripe/stripe-dotnet/pull/3074) System.Text.Json Serialization Support release to GA * Add System.Text.Json support for serializing Stripe.net entities and objects for applications running on .NET 6 and above. Now you can pass a Stripe.net object or collection of objects to the System.Text.Json serializer and it will produce the correct JSON string. diff --git a/src/Stripe.net/Infrastructure/FormEncoding/ContentEncoder.cs b/src/Stripe.net/Infrastructure/FormEncoding/ContentEncoder.cs index 3bc7d8aa22..aeb2453cc8 100644 --- a/src/Stripe.net/Infrastructure/FormEncoding/ContentEncoder.cs +++ b/src/Stripe.net/Infrastructure/FormEncoding/ContentEncoder.cs @@ -163,6 +163,12 @@ private static List> FlattenParamsValue(object valu flatParams = SingleParam(keyPrefix, JsonUtils.SerializeObject(e).Trim('"')); break; + case bool b: + flatParams = SingleParam( + keyPrefix, + b ? "true" : "false"); + break; + default: flatParams = SingleParam( keyPrefix, diff --git a/src/Stripe.net/Infrastructure/JsonConverters/SerializablePropertyCache.cs b/src/Stripe.net/Infrastructure/JsonConverters/SerializablePropertyCache.cs index f7bbece3a7..bacfe7b40c 100644 --- a/src/Stripe.net/Infrastructure/JsonConverters/SerializablePropertyCache.cs +++ b/src/Stripe.net/Infrastructure/JsonConverters/SerializablePropertyCache.cs @@ -2,6 +2,7 @@ namespace Stripe.Infrastructure { using System; + using System.Collections.Concurrent; using System.Collections.Generic; using System.Reflection; using System.Text.Json; @@ -13,17 +14,17 @@ namespace Stripe.Infrastructure /// internal class SerializablePropertyCache { - private static Dictionary converterCache = new Dictionary(); - private static Dictionary> propertyCache = new Dictionary>(); + private static ConcurrentDictionary converterCache = new ConcurrentDictionary(); + private static ConcurrentDictionary> propertyCache = new ConcurrentDictionary>(); internal static List GetPropertiesForType(Type type) { - if (!propertyCache.TryGetValue(type, out var propsToSerialize)) + return propertyCache.GetOrAdd(type, (key) => { // Gets the all properties including nonpublic properties var rawProps = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); - propsToSerialize = new List(); + var propsToSerialize = new List(); foreach (var prop in rawProps) { var propertyNameAttribute = prop.GetCustomAttribute(typeof(JsonPropertyNameAttribute), false) as JsonPropertyNameAttribute; @@ -55,10 +56,8 @@ internal static List GetPropertiesForType(Type type) }); } - propertyCache[type] = propsToSerialize; - } - - return propsToSerialize; + return propsToSerialize; + }); } // Create the various methods stored in SerializablePropertyInfo @@ -169,11 +168,10 @@ private static Action CreateSetDelegate(MethodInfo m) private static JsonConverter GetConverterForType(Type ct) where T : JsonConverter { - if (!converterCache.TryGetValue(ct, out var conv)) + var conv = converterCache.GetOrAdd(ct, (key) => { - conv = (JsonConverter)Activator.CreateInstance(ct); - converterCache[ct] = conv; - } + return (JsonConverter)Activator.CreateInstance(ct); + }); return new JsonConverterAdapter((T)conv); } diff --git a/src/StripeTests/Infrastructure/FormEncoding/ContentEncoderTest.cs b/src/StripeTests/Infrastructure/FormEncoding/ContentEncoderTest.cs index 09602f3268..2953e0eba0 100644 --- a/src/StripeTests/Infrastructure/FormEncoding/ContentEncoderTest.cs +++ b/src/StripeTests/Infrastructure/FormEncoding/ContentEncoderTest.cs @@ -205,7 +205,7 @@ public void CreateQueryString() { Bool = false, }, - Want = "bool=False", + Want = "bool=false", }, new QueryStringTestCase { @@ -213,7 +213,7 @@ public void CreateQueryString() { Bool = true, }, - Want = "bool=True", + Want = "bool=true", }, // DateRangeOptions diff --git a/src/StripeTests/Services/PaymentIntents/PaymentIntentConfirmOptionsTest.cs b/src/StripeTests/Services/PaymentIntents/PaymentIntentConfirmOptionsTest.cs index 9e9b6f01c8..2a049aefcb 100644 --- a/src/StripeTests/Services/PaymentIntents/PaymentIntentConfirmOptionsTest.cs +++ b/src/StripeTests/Services/PaymentIntents/PaymentIntentConfirmOptionsTest.cs @@ -14,7 +14,7 @@ public void SerializeObjectProperly() OffSession = true, }; - Assert.Equal("off_session=True", ContentEncoder.CreateQueryString(options_bool)); + Assert.Equal("off_session=true", ContentEncoder.CreateQueryString(options_bool)); } } } diff --git a/src/StripeTests/Services/PaymentIntents/PaymentIntentCreateOptionsTest.cs b/src/StripeTests/Services/PaymentIntents/PaymentIntentCreateOptionsTest.cs index 011ca022a0..ea099904b7 100644 --- a/src/StripeTests/Services/PaymentIntents/PaymentIntentCreateOptionsTest.cs +++ b/src/StripeTests/Services/PaymentIntents/PaymentIntentCreateOptionsTest.cs @@ -14,7 +14,7 @@ public void SerializeObjectProperly() OffSession = true, }; - Assert.Equal("off_session=True", ContentEncoder.CreateQueryString(options_bool)); + Assert.Equal("off_session=true", ContentEncoder.CreateQueryString(options_bool)); } } }