chargebee / chargebee-dotnet

.NET library for the Chargebee API.
https://apidocs.chargebee.com/docs/api?lang=dotnet
MIT License
20 stars 24 forks source link

Serializing a parent object with a Chargebee Subscription fails #71

Open dcreeronbemo opened 3 months ago

dcreeronbemo commented 3 months ago

Description of the Bug

We have a class that has a property for ChargeBee.Models.Subscription. When we try and serialize this parent class we get an exception with the following details:

Error getting value from 'SubscriptionId' on 'ChargeBee.Models.Subscription+SubscriptionContractTerm'. The property subscription_id is not present!

If I look in the debug visualizer and drill down to the ChargeBeeSubscription.ContractTerm.SubscriptionId I see a circular red-X for the property, and the value is:

"'(new System.Collections.Generic.ICollectionDebugView(value.LineItems).Items[0]).BemoSub.ChargeBeeSubscription.ContractTerm.SubscriptionId' threw an exception of type 'System.ArgumentException'"

If I clone the latest chargebee-dotnet repository, include the project in my solution, and then comment out the SubscriptionContractTerm.SubscriptionId property in Subscription.cs (lines 6540-6542) then the serialization works as expected. ETA: I can also change that property so the call the GetValue() sets the "required" parameter to false.

Steps to reproduce

  1. Create a parent class with a ChargeBee.Models.Subscription
  2. Populate the parent class, including the CB Subscription property
  3. Try and Serialize the parent class with Newtonsoft
  4. Error/exception as noted in description

Expected Behavior

Serialization should not throw this exception.

Code Snippets (if applicable)

No response

Operating System

Windows 10/11

Language version

.Net 8.0

Library version

v3.20.0

Additional context

No response

cb-alish commented 3 months ago

Hi @dcreeronbemo, subscription_id is a required field and it looks like this is missing while populating ContractTerm object inside Subscription property. Can you please help with more details on how you're populating the Chargebee Subscription property in your parent class? Are you directly using the API response from Chargebee or are you constructing this yourself?

dcreeronbemo commented 3 months ago

Hello @cb-alish. Thanks for the quick response.

I see from the code that the CB SubscriptionContractTerm.SubscriptionId is supposed to be required.

Our code is calling:

EntityResult chargebeeResult = Subscription.CreateForCustomer("CUSTOMERIDHERE) .PlanId("PLANIDHERE") .PlanQuantity(1) .InvoiceImmediately(true) .StartDate(SubscriptionStartDate(true)) .ContractTermActionAtTermEnd(Subscription.SubscriptionContractTerm.ActionAtTermEndEnum.Renew) .BillingCycles(12) .ContractTermBillingCycleOnRenewal(12) .Request()

With the 3.20.0 NUGET package (or the project directly in our code) with the default "required == true" then the ContractTerm.SubscriptionId isn't populated and throws the error. This causes the serialization further downstream to fail.

If I set required == false, then the ContractTerm.SubscriptionId value is set to null, and the serialization works.

It seems to me that the data being returned from the API call isn't populating the ContractTerm.SubscriptionId for some reason.

For what it's worth, the rest of the ContractTerm looks like it's populated correctly (see attached screenshot).

image

dcreeronbemo commented 3 months ago

Additional info @cb-alish. A co-worker performed an API get subscription call directly and got the following JSON. Notice how the "contract_term" doesn't contain a "subscription_id" property. It does have an "id" property, which matches the parent subscription id.

To me the "id" of the contract_term seems like it should be a unique id for the contract_term, and not the id of the parent subscription. Thus, the need/thought for having a SubscriptionId ("subscription_id") property.

I'm guessing someone set Id instead of SubscriptionId, thus overwriting the ContractTerm unique Id, and not setting the Contract Term subscription Id.

"subscription": { "id" : "AzqC89UK7EV8y1DIh", "currency_code" : "USD", "plan_id" : "6C1E56E1-E195-4D6A-8252-CE9C7A1E10EC", "plan_quantity" : "11", "plan_unit_price" : "3900", "billing_period" : "1", "billing_period_unit" : "month", "remaining_billing_cycles" : "11", "customer_id" : "1dde681a-6a9b-4d0a-b00c-0e2366355d6e", "plan_amount" : "42900", "plan_free_quantity" : "0", "status" : "active", "current_term_start" : "31-Jul-2024 00:00:00", "current_term_end" : "30-Aug-2024 23:59:59", "next_billing_at" : "31-Aug-2024 00:00:00", "created_at" : "31-Jul-2024 14:36:54", "started_at" : "31-Jul-2024 00:00:00", "activated_at" : "31-Jul-2024 00:00:00", "contract_term_billing_cycle_on_renewal" : "12", "resource_version" : "1722436616424", "updated_at" : "31-Jul-2024 14:36:56", "has_scheduled_advance_invoices" : "false", "has_scheduled_changes" : "false", "channel" : "web", "due_invoices_count" : "0", "mrr" : "0", "deleted" : "false", "contract_term" : { "id" : "AzqC89UK7EV951DIi", "status" : "active", "contract_start" : "31-Jul-2024 00:00:00", "contract_end" : "30-Jul-2025 23:59:59", "billing_cycle" : "12", "action_at_term_end" : "renew", "total_contract_value" : "514800", "total_contract_value_before_tax" : "514800", "cancellation_cutoff_period" : "31", "created_at" : "31-Jul-2024 14:36:56", "remaining_billing_cycles" : "11", } }

dcreeronbemo commented 1 month ago

@cb-alish Hello. Any updates or suggestions? We're having a similar issue with SubscriptionAddress as well.

Thanks.

cb-alish commented 1 month ago

Hi @dcreeronbemo, we're working on the fix and will update you here once it's ready. Could you also provide more details on SubscriptionAddress so we can address it fully? Thanks for your patience!

dcreeronbemo commented 1 month ago

Thanks for the response @cb-alish. The issue is the same as the earlier issue, just a different object. In this case Subscription.

We try to get the Subscriptions for a specific user by:

            var subscriptionList = (await Subscription.List()
                .CustomerId().Is(customerId)
                .Status().IsNot(Subscription.StatusEnum.Cancelled)
                .RequestAsync()).List.Select(x => x.Subscription).ToList();

We save this list into a parent object and then try to serial the parent object. The serialization throws an exception:

"Exception caught: ArgumentException Message: The property subscription_id is not present! StackTrace: at ChargeBee.Internal.Resource.ThrowIfKeyMissed(String key) at SubscriptionIdGetter(Object)"

image

The problem appears to be that it's trying to serialize the Subscription.ShippingAddress and throwing this error.

at ChargeBee.Internal.Resource.ThrowIfKeyMissed(String key) at System.Text.Json.Serialization.Metadata.JsonPropertyInfo1.GetMemberAndWriteJson(Object obj, WriteStack& state, Utf8JsonWriter writer) at System.Text.Json.Serialization.Converters.ObjectDefaultConverter1.OnTryWrite(Utf8JsonWriter writer, T value, JsonSerializerOptions options, WriteStack& state) at System.Text.Json.Serialization.JsonConverter1.TryWrite(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state) at System.Text.Json.Serialization.Metadata.JsonPropertyInfo1.GetMemberAndWriteJson(Object obj, WriteStack& state, Utf8JsonWriter writer) at System.Text.Json.Serialization.Converters.ObjectDefaultConverter1.OnTryWrite(Utf8JsonWriter writer, T value, JsonSerializerOptions options, WriteStack& state) at System.Text.Json.Serialization.JsonConverter1.TryWrite(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state) at System.Text.Json.Serialization.JsonConverter1.WriteCore(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state) at System.Text.Json.Serialization.Metadata.JsonTypeInfo1.Serialize(Utf8JsonWriter writer, T& rootValue, Object rootValueBoxed) at System.Text.Json.JsonSerializer.WriteString[TValue](TValue& value, JsonTypeInfo`1 jsonTypeInfo) at System.Text.Json.JsonSerializer.Serialize[TValue](TValue value, JsonSerializerOptions options) at Prismo.Services.Chargebee.Services.ChargeBeeService.d__6.MoveNext() in C:\Users\DanielCreeron\source\repos\PCvsCB_BemoReconciliationReport\Prismo.Services.Chargebee\Services\ChargebeeService.cs:line 151

Th

cb-alish commented 1 month ago

Hi @dcreeronbemo,

The issue with SubscriptionContractTerm has been resolved. Please give it a try and let us know if it still doesn't work for you.

We're working on the SubscriptionAddress issue and will update you once it's fixed. Apologies for the inconvenience, and thank you for your patience.

dcreeronbemo commented 1 month ago

Thanks @cb-alish. I updated the project to go back to the Chargebee dotnet 3.24.0 NUGET package, and initial testing is working as expected. Was there a change to the NUGET package source (I didn't see anything related, but may have missed it) or was it a backend issue?

cb-alish commented 1 month ago

Hi @dcreeronbemo, it was a backend issue.