Skip to content

Commit 57e8092

Browse files
authored
Merge pull request #75 from chargebee/release/sdk-dotnet/v3.34.0
Release dotnet SDK v3.34.0
2 parents 55ef2cb + 0396d2a commit 57e8092

28 files changed

+461
-96
lines changed

CHANGELOG.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,30 @@
1+
### v3.34.0 (2025-07-18)
2+
* * *
3+
4+
### New Resources:
5+
* BillingConfiguration has been added.
6+
* Brand has been added.
7+
8+
### New Attributes:
9+
* has_children has been added to Hierarchy
10+
* coupon_applicability_mappings has been added to QuotedRamp.
11+
12+
### New Endpoint:
13+
* listHierarchyDetail has been added to Customer.
14+
15+
### New Input parameters:
16+
* change_reason children has been added to Entitlement#CreateRequest.
17+
* entitlements[apply_grandfathering] has been added to Entitlement#CreateRequest.
18+
* replace_primary_payment_source has been added to Purchase#CreateRequest.
19+
* omnichannel_subscription has been added to RecordedPurchase#CreateRequest.
20+
* contract_term has been added to Subscription#RemoveScheduledCancellationRequest.
21+
* contract_term_billing_cycle_on_renewal has been added to Subscription#RemoveScheduledCancellationRequest.
22+
23+
### New Enums:
24+
* payconiq_by_bancontact has been added to PaymentMethodType.
25+
* solidgate has been added to Gateway.
26+
* solidgate has been added to PaymentMethod.
27+
128
### v3.33.0 (2025-06-23)
229
* * *
330

ChargeBee/Api/ApiConfig.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public sealed class ApiConfig
2323
{
2424
public static string DomainSuffix = "chargebee.com";
2525
public static string Proto = "https";
26-
public static string Version = "3.33.0";
26+
public static string Version = "3.34.0";
2727
public static readonly string API_VERSION = "v2";
2828
public static int TimeTravelMillis { get; set; }
2929
public static int ExportSleepMillis { get; set;}

ChargeBee/Api/ApiUtil.cs

Lines changed: 65 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
using System;
22
using System.Text;
33
using System.Collections.Generic;
4+
using System.Linq;
45
using System.Runtime.InteropServices;
56

67
using Newtonsoft.Json;
78

89
using ChargeBee.Exceptions;
910
using System.Net.Http;
1011
using System.Threading.Tasks;
12+
using ChargeBee.Internal;
1113

1214
namespace ChargeBee.Api
1315
{
@@ -94,11 +96,13 @@ private static HttpRequestMessage BuildContentTypeJsonRequest(string uri, HttpMe
9496
}
9597
return request;
9698
}
97-
private static HttpRequestMessage GetRequestMessage(string url, HttpMethod method, Params parameters, Dictionary<string, string> headers, ApiConfig env, bool supportsFilter=false, string subDomain = null, bool isJsonRequest=false)
99+
private static HttpRequestMessage GetRequestMessage(string url, HttpMethod method, Params parameters,
100+
Dictionary<string, string> headers, ApiConfig env, bool supportsFilter = false, string subDomain = null,
101+
bool isJsonRequest = false, Dictionary<string, dynamic> options = null)
98102
{
99103
HttpRequestMessage request = isJsonRequest ? BuildContentTypeJsonRequest(url, method, parameters, env, supportsFilter, subDomain) : BuildRequest(url, method, parameters, env, supportsFilter, subDomain);
100104
AddHeaders(request, env);
101-
AddCustomHeaders(request, headers);
105+
AddCustomHeaders(request, headers,env, options);
102106
return request;
103107
}
104108
private static void AddHeaders(HttpRequestMessage request, ApiConfig env)
@@ -118,12 +122,32 @@ private static void AddHeaders(HttpRequestMessage request, ApiConfig env)
118122
#endif
119123
}
120124

121-
private static void AddCustomHeaders(HttpRequestMessage request, Dictionary<string, string> headers)
125+
private static void AddCustomHeaders(HttpRequestMessage request, Dictionary<string, string> headers,
126+
ApiConfig env,
127+
Dictionary<string, dynamic> options)
122128
{
123129
foreach (KeyValuePair<string, string> entry in headers)
124130
{
125131
AddHeader(request, entry.Key, entry.Value);
126132
}
133+
134+
if (request.Method != System.Net.Http.HttpMethod.Post ||
135+
headers.ContainsKey(IdempotencyConstants.IDEMPOTENCY_HEADER))
136+
{
137+
return;
138+
}
139+
var shouldAddIdempotencyKey = true;
140+
if (options != null && options.TryGetValue(EntityRequestConstants.IdempotencyOption, out var option))
141+
{
142+
if (option is bool optBool)
143+
{
144+
shouldAddIdempotencyKey = optBool;
145+
}
146+
}
147+
if (shouldAddIdempotencyKey && env.RetryConfig.Enabled)
148+
{
149+
AddHeader(request, IdempotencyConstants.IDEMPOTENCY_HEADER, Guid.NewGuid().ToString());
150+
}
127151
}
128152

129153
private static void AddHeader(HttpRequestMessage request, String headerName, String value)
@@ -169,22 +193,40 @@ private static void HandleException(HttpResponseMessage response)
169193
}
170194

171195
}
172-
private static EntityResult GetEntityResult(String url, Params parameters, Dictionary<string, string> headers, ApiConfig env, HttpMethod meth, bool supportsFilter, string subDomain = null, bool isJsonRequest=false)
196+
private static EntityResult GetEntityResult(string url, Params parameters, Dictionary<string, string> headers,
197+
ApiConfig env, HttpMethod meth, bool supportsFilter, string subDomain = null, bool isJsonRequest = false,
198+
Dictionary<string, dynamic> options = null)
173199
{
174200

175-
return GetEntityResultAsync(url, parameters, headers, env, meth, supportsFilter, subDomain, isJsonRequest).ConfigureAwait(false).GetAwaiter().GetResult();
201+
return GetEntityResultAsync(url, parameters, headers, env, meth, supportsFilter, subDomain, isJsonRequest, options).ConfigureAwait(false).GetAwaiter().GetResult();
176202
}
177-
private static async Task<EntityResult> GetEntityResultAsync(String url, Params parameters, Dictionary<string, string> headers, ApiConfig env, HttpMethod meth, bool supportsFilter, string subDomain = null, bool isJsonRequest=false)
203+
private static async Task<EntityResult> GetEntityResultAsync(string url, Params parameters,
204+
Dictionary<string, string> headers, ApiConfig env, HttpMethod meth, bool supportsFilter,
205+
string subDomain = null, bool isJsonRequest = false, Dictionary<string, dynamic> options = null)
178206
{
179207
int attempt = 0;
208+
string idempotentKey = null;
180209
int lastRetryAfterDelay = 0;
181210
Random rng = new Random();
182211
while (true)
183212
{
184-
HttpRequestMessage request = GetRequestMessage(url, meth, parameters, headers, env, supportsFilter, subDomain, isJsonRequest);
213+
if (idempotentKey != null && !headers.ContainsKey(IdempotencyConstants.IDEMPOTENCY_HEADER))
214+
{
215+
headers[IdempotencyConstants.IDEMPOTENCY_HEADER] = idempotentKey;
216+
}
217+
HttpRequestMessage request = GetRequestMessage(url, meth, parameters, headers, env, supportsFilter, subDomain, isJsonRequest,options);
185218
HttpResponseMessage response = null;
186219
try
187220
{
221+
if (idempotentKey == null && request.Headers.TryGetValues(IdempotencyConstants.IDEMPOTENCY_HEADER, out var values))
222+
{
223+
idempotentKey = values.First();
224+
}
225+
if (attempt > 0)
226+
{
227+
request.Headers.Remove("X-CB-Retry-Attempt"); // Ensure no duplicates
228+
request.Headers.Add("X-CB-Retry-Attempt", attempt.ToString());
229+
}
188230
response = await httpClient.SendAsync(request).ConfigureAwait(false);
189231
var json = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
190232
if (response.IsSuccessStatusCode)
@@ -217,38 +259,41 @@ private static async Task<EntityResult> GetEntityResultAsync(String url, Params
217259
await Task.Delay(delay).ConfigureAwait(false);
218260
attempt++;
219261
}
220-
catch (Exception e)
221-
{
222-
if (env.RetryConfig == null || !env.RetryConfig.ShouldRetry(0, attempt)) throw;
223-
224-
}
225262
finally
226263
{
227264
response?.Dispose();
228265
request.Dispose();
229266
}
230267
}
231268
}
232-
public static EntityResult Post(string url, Params parameters, Dictionary<string, string> headers, ApiConfig env, bool supportsFilter=false, string subDomain = null, bool isJsonRequest=false)
269+
public static EntityResult Post(string url, Params parameters, Dictionary<string, string> headers,
270+
ApiConfig env, bool supportsFilter = false, string subDomain = null, bool isJsonRequest = false,
271+
Dictionary<string, dynamic> options= null)
233272
{
234-
return GetEntityResult(url, parameters, headers, env, HttpMethod.POST, supportsFilter, subDomain, isJsonRequest);
273+
return GetEntityResult(url, parameters, headers, env, HttpMethod.POST, supportsFilter, subDomain, isJsonRequest,options);
235274
}
236275

237-
public static Task<EntityResult> PostAsync(string url, Params parameters, Dictionary<string, string> headers, ApiConfig env, bool supportsFilter=false, string subDomain = null, bool isJsonRequest=false)
276+
public static Task<EntityResult> PostAsync(string url, Params parameters, Dictionary<string, string> headers,
277+
ApiConfig env, bool supportsFilter = false, string subDomain = null, bool isJsonRequest = false,
278+
Dictionary<string, dynamic> options= null)
238279
{
239-
return GetEntityResultAsync(url, parameters, headers, env, HttpMethod.POST, supportsFilter, subDomain, isJsonRequest);
280+
return GetEntityResultAsync(url, parameters, headers, env, HttpMethod.POST, supportsFilter, subDomain, isJsonRequest,options);
240281
}
241282

242-
public static EntityResult Get(string url, Params parameters, Dictionary<string, string> headers, ApiConfig env, bool supportsFilter=false, string subDomain = null, bool isJsonRequest=false)
283+
public static EntityResult Get(string url, Params parameters, Dictionary<string, string> headers, ApiConfig env,
284+
bool supportsFilter = false, string subDomain = null, bool isJsonRequest = false,
285+
Dictionary<string, dynamic> options = null)
243286
{
244287
url = String.Format("{0}?{1}", url, parameters.GetQuery(false));
245-
return GetEntityResult(url, parameters, headers, env, HttpMethod.GET, supportsFilter, subDomain, isJsonRequest);
288+
return GetEntityResult(url, parameters, headers, env, HttpMethod.GET, supportsFilter, subDomain, isJsonRequest,options);
246289
}
247290

248-
public static Task<EntityResult> GetAsync(string url, Params parameters, Dictionary<string, string> headers, ApiConfig env, bool supportsFilter=false, string subDomain = null, bool isJsonRequest=false)
291+
public static Task<EntityResult> GetAsync(string url, Params parameters, Dictionary<string, string> headers,
292+
ApiConfig env, bool supportsFilter = false, string subDomain = null, bool isJsonRequest = false,
293+
Dictionary<string, dynamic> options= null)
249294
{
250295
url = String.Format("{0}?{1}", url, parameters.GetQuery(supportsFilter));
251-
return GetEntityResultAsync(url, parameters, headers, env, HttpMethod.GET, supportsFilter, subDomain, isJsonRequest);
296+
return GetEntityResultAsync(url, parameters, headers, env, HttpMethod.GET, supportsFilter, subDomain, isJsonRequest,options);
252297
}
253298

254299
public static ListResult GetList(string url, Params parameters, Dictionary<string, string> headers, ApiConfig env, string subDomain = null)

ChargeBee/Api/EntityRequest.cs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ public class EntityRequest<T>
1414
protected bool m_supportsFilter;
1515
protected bool is_json_request = false;
1616
protected string sub_domain;
17+
private readonly Dictionary<string, dynamic> _options = new Dictionary<string, dynamic>();
1718

1819
public EntityRequest(string url, HttpMethod method, bool supportsFilter = false)
1920
{
@@ -33,6 +34,13 @@ public T IsJsonRequest(bool isJsonRequest)
3334
is_json_request = true;
3435
return (T)Convert.ChangeType (this, typeof(T));
3536
}
37+
38+
public T SetIdempotent(bool isIdempotent)
39+
{
40+
_options.Add(EntityRequestConstants.IdempotencyOption, isIdempotent);
41+
return (T)Convert.ChangeType (this, typeof(T));
42+
}
43+
3644
public T SetIdempotencyKey(string idempotencyKey){
3745
headers.Add (IdempotencyConstants.IDEMPOTENCY_HEADER, idempotencyKey);
3846
return (T)Convert.ChangeType (this, typeof(T));
@@ -51,6 +59,7 @@ public T Header(string headerName, string headerValue){
5159
headers.Add (headerName, headerValue);
5260
return (T)Convert.ChangeType (this, typeof(T));
5361
}
62+
5463

5564
public EntityResult Request()
5665
{
@@ -67,9 +76,9 @@ public EntityResult Request(ApiConfig env)
6776
switch (m_method)
6877
{
6978
case HttpMethod.GET:
70-
return ApiUtil.Get(m_url, m_params, headers, env, m_supportsFilter, sub_domain, is_json_request);
79+
return ApiUtil.Get(m_url, m_params, headers, env, m_supportsFilter, sub_domain, is_json_request, _options);
7180
case HttpMethod.POST:
72-
return ApiUtil.Post(m_url, m_params, headers, env, m_supportsFilter, sub_domain, is_json_request);
81+
return ApiUtil.Post(m_url, m_params, headers, env, m_supportsFilter, sub_domain, is_json_request, _options);
7382
default:
7483
throw new NotImplementedException(String.Format(
7584
"HTTP method {0} is not implemented",
@@ -83,9 +92,9 @@ public Task<EntityResult> RequestAsync(ApiConfig env)
8392
switch (m_method)
8493
{
8594
case HttpMethod.GET:
86-
return ApiUtil.GetAsync(m_url, m_params, headers, env, m_supportsFilter, sub_domain, is_json_request);
95+
return ApiUtil.GetAsync(m_url, m_params, headers, env, m_supportsFilter, sub_domain, is_json_request, _options);
8796
case HttpMethod.POST:
88-
return ApiUtil.PostAsync(m_url, m_params, headers, env, m_supportsFilter, sub_domain, is_json_request);
97+
return ApiUtil.PostAsync(m_url, m_params, headers, env, m_supportsFilter, sub_domain, is_json_request, _options);
8998
default:
9099
throw new NotImplementedException(String.Format(
91100
"HTTP method {0} is not implemented",

ChargeBee/ChargeBee.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
33
<TargetFrameworks>netstandard1.2;netstandard2.0;net45</TargetFrameworks>
4-
<Version>3.33.0</Version>
4+
<Version>3.34.0</Version>
55
<PackageId>chargebee</PackageId>
66
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
77
<PackageProjectUrl>https://github.com/chargebee/chargebee-dotnet</PackageProjectUrl>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace ChargeBee.Internal
2+
{
3+
public abstract class EntityRequestConstants
4+
{
5+
public const string IdempotencyOption = "IS_IDEMPOTENT";
6+
}
7+
}

ChargeBee/Internal/ResultBase.cs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,12 @@ internal ResultBase(JToken jobj)
3232
{
3333
m_jobj = jobj;
3434
}
35-
3635
public string ToJson()
3736
{
38-
37+
3938
return m_jobj.ToString(Formatting.None) ;
4039
}
41-
40+
4241

4342
public Subscription Subscription
4443
{
@@ -156,6 +155,10 @@ public QuotedRamp QuotedRamp
156155
{
157156
get { return GetResource<QuotedRamp>("quoted_ramp"); }
158157
}
158+
public BillingConfiguration BillingConfiguration
159+
{
160+
get { return GetResource<BillingConfiguration>("billing_configuration"); }
161+
}
159162
public QuoteLineGroup QuoteLineGroup
160163
{
161164
get { return GetResource<QuoteLineGroup>("quote_line_group"); }
@@ -360,6 +363,10 @@ public OmnichannelSubscriptionItemScheduledChange OmnichannelSubscriptionItemSch
360363
{
361364
get { return GetResource<OmnichannelSubscriptionItemScheduledChange>("omnichannel_subscription_item_scheduled_change"); }
362365
}
366+
public Brand Brand
367+
{
368+
get { return GetResource<Brand>("brand"); }
369+
}
363370

364371
public List<AdvanceInvoiceSchedule> AdvanceInvoiceSchedules
365372
{
@@ -405,10 +412,7 @@ public List<InAppSubscription> InAppSubscriptions
405412
{
406413
get { return (List<InAppSubscription>)GetResourceList<InAppSubscription>("in_app_subscriptions", "in_app_subscription"); }
407414
}
408-
public List<DifferentialPrice> DifferentialPrices
409-
{
410-
get { return (List<DifferentialPrice>)GetResourceList<DifferentialPrice>("differential_prices", "differential_price"); }
411-
}
415+
412416

413417
private List<T> GetResourceList<T>(string property, string propertySingularName) where T : Resource, new()
414418
{

0 commit comments

Comments
 (0)