stripe-dotnet: Error parsing JSON returned from webhook

Hi, I’ve recently moved to version 21.4.1 from 19.x, and now I’m receiving errors when parsing some webhook events.

Below is some JSON that caused an error in the parser. It looks like it falls over trying to generate a request object - from what it looks like to be an Id only,

I’ve tried both the ParseEvent and ConstructEvent methods - both fail identically.

{ "id": "evt_1DZUr64WLhpec2eZ8YxDi4sm", "object": "event", "api_version": "2014-06-17", "created": 1542941840, "data": { "object": { "id": "card_1DZUqq4WLhpec2eZr2GUSqJW", "object": "card", "address_city": null, "address_country": null, "address_line1": null, "address_line1_check": null, "address_line2": null, "address_state": null, "address_zip": null, "address_zip_check": null, "brand": "Visa", "country": "US", "customer": "cus_4iIYzLmgQznYlK", "cvc_check": "pass", "dynamic_last4": null, "exp_month": 9, "exp_year": 2019, "fingerprint": "tXS6DZYDEDNFUeFL", "funding": "credit", "last4": "4242", "metadata": { }, "name": "Gabe Newell", "tokenization_method": null } }, "livemode": false, "pending_webhooks": 1, "request": "req_7hFEZuZn4RwSm6", "type": "customer.card.deleted" }

Newtonsoft.Json.JsonSerializationException Message = Error converting value “req_7hFEZuZn4RwSm6” to type ‘Stripe.EventRequest’. Path ‘request’, line 36, position 33.

Hack “fix” until this has been sorted out:

var jObject = Newtonsoft.Json.JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JObject>(json);`
if (jObject["request"] != null)`
{
    jObject["request"] = null;
    json = jObject.ToString();
}

Cheers, Steve

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 19 (8 by maintainers)

Most upvoted comments

Hi @steve-tapley. The Stripe.net library is pinned to a specific API version, and expects the objects it decodes to be formatted according to that version.

In this case, Stripe.net v21.4.1 is pinned to API version 2018-11-08 (cf. https://github.com/stripe/stripe-dotnet/blob/v21.4.1/src/Stripe.net/Infrastructure/Public/StripeConfiguration.cs#L10). According to the api_version key in the JSON object you shared, the event object you’re trying to decode was formatted with API version 2014-06-17. As such, it’s not unexpected that trying to decode that object would fail. The exact cause is the change in the request property introduced in API version 2017-05-25.

When sending requests to Stripe’s API, the Stripe.net library uses the versioning header to force a specific API version. However, event objects are always formatted according to your Stripe account’s default API version. So when using webhooks with the Stripe.net library, it’s important that your account’s default API version matches the API version expected by the Stripe.net library.

You can upgrade to the latest API version (which is currently 2018-11-08, the version expected by Stripe.net v21.4.1) from your dashboard at https://dashboard.stripe.com/developers.

In short, this is expected behavior. I think we can improve the developer experience with a clearer error message though, so I’ll leave this issue open for now.

Not sure if this is working with the new version of stripe or that I am missing something. I have a . net 4.6 environment upgraded to the latest version of Stripe.net for .net and also updated my version to 2019-03-14 on the dashboard, I have: Stream req = Request.InputStream; req.Seek(0, System.IO.SeekOrigin.Begin); string json = new StreamReader(req).ReadToEnd(); (So far its working and if I would write the Json string to for example a database its as the event that was send from the dasboard) The next step always returns an error and stops there, with or without the , true/false: Event stripeEvent = EventUtility.ParseEvent(json);

Any idea?

Hi @dpellizza, thanks for the detailed information. I don’t have much experience with ASP.NET, but I believe this issue is similar to #1361. Basically, ASP.NET won’t automatically fill Data.Object because doing so requires using the StripeObjectConverter custom JSON converter, which ASP.NET doesn’t know about.

This issue should be fixed in the upcoming v22 version (cf. #1396). In the meantime, you can work around the issue by deserializing the event manually using the Stripe.EventUtility.ParseEvent method. Something like this should work:

public async Task<HttpResponseMessage> Post()
{
    string rawBody = response.Content.ReadAsStringAsync().Result;
    var stripeEvent = Stripe.EventUtility.ParseEvent(rawBody);
    ...
}

(Not directly related to your issue, but ideally you should use the Stripe.EventUtility.ConstructEvent method so that you verify the webhook signature while constructing the event.)

Let me know if that helps!

Hi @steve-tapley.

It would be nice to know what API versions a particular Stripe.net version is compatible with (e.g 2018-08-23 thru to 2018-11-08 for example).

The only API version a particular Stripe.net version is 100% compatible with is the one it’s pinned to. Trying to use a different API version may work for some resources, but not for others. Depending on the exact changes, it may fail silently (e.g. deserialization would appear to work, but some fields would not be filled because the API didn’t return them and other fields would be missing because the API did return them but they’re not present in the library).

At the moment, its difficult to test upgrades of Stripe.Net, because I cant set my test environment to be a different version of API to the live version.

You can create a test webhook endpoint with the latest API version, without having to upgrade the API version for the entire account. Once the endpoint is created, you can create test events “organically” (i.e. by sending requests that would result in the events you want, not by using the “Send test webhook” button) and those events will be formatted with the latest API version.

The docs at https://stripe.com/docs/upgrades recommend updating the webhook code to handle both old and new version of each objects - are they really implying having two versions of the Stripe.net library? Sure, I could do that by having multiple assemblies, but I really think thats something better done by Stripe.net + Stripe API.

Right, I think the advice in those docs is mostly applicable to our client libraries that are not pinned to a specific API version, like Ruby or PHP. Things are a lot easier there, because those libraries don’t care about specific resource formats.

For Stripe.net, my recommendation would be to do something like this:

  • set up a separate test server with the latest Stripe.net version
  • create a test webhook endpoint with the latest API version, pointing to your test server
  • test your integration until you’re confident everything works correctly
  • at this point, upgrade the API version for your entire Stripe account and upgrade your production server with the latest Stripe.net and whatever changes you made to your integration

That last step is difficult to do in one go, so you may want to temporarily force your webhook handler to return a non-2xx status code temporarily so that Stripe will retry those events ~one hour later.

I realize all this is not ideal and we can definitely do better to improve the experience. I’ll share your feedback internally.

@phil118 As of v20.0.0, the Data.Object property of Event objects is directly instantiated to the correct type. You can either use pattern matching or use the Event object’s Type property to cast to the correct type. You can find more information here: https://github.com/stripe/stripe-dotnet/wiki/Migration-guide-for-v20#interfaces-for-polymorphic-resources.