Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cards are not being deserialized properly #1676

Closed
Excelzn opened this issue Jun 21, 2019 · 12 comments · Fixed by #1694
Closed

Cards are not being deserialized properly #1676

Excelzn opened this issue Jun 21, 2019 · 12 comments · Fixed by #1694

Comments

@Excelzn
Copy link

Excelzn commented Jun 21, 2019

Using: Stripe.net 26.0.0 on .NET Core 3 preview 5

I know this is a preview build of .NET Core, but since every other piece of the library works fine, I am not sure if the issue is related to Stripe or .NET Core 3.

When using the CardService class to retrieve cards from the API, they are not being deserialized correctly. Every value on the Card ends up being null. This happens both when using the List methods and when using the Get methods. The StripeResponse.ObjectJson property does have all the values that should be there, but the object itself does not. Current workaround is to just deserialize it myself using the ObjectJson property.

@ob-stripe
Copy link
Contributor

Hi @Excelzn. I just tried running the test suite for Stripe.net 26.0.0 with .NET Core 3 preview 6 and didn't see any issues. Can you share the exact code you're using to get the Card object, and how you're inspecting the values?

@Excelzn
Copy link
Author

Excelzn commented Jun 21, 2019

Absolutely. Here is the code:

var _service = new CardService();
var card = await _service.GetAsync("STRIPE CUSTOMER ID", "STRIPE CARD ID");

I am inspecting the values in the debugger in JetBrains Rider. I also looked in VS 2019. The specific fields I am looking at are the Brand, ExpMonth, ExpYear, and Last4 fields.

@ob-stripe
Copy link
Contributor

🤔 I've tried that exact code with Stripe.net 26.0.0 and .NET Core 3 preview 6, and the Card object is properly populated.

Here's the exact project I used: https://gist.github.com/ob-stripe/f2f90e42f0fdd5afb062e07436605489. Can you try it and tell me the results? The output should be:

{
  "id": "card_1EntZj2eZvKYlo2CV1sarcbm",
  "object": "card",
  "account": null,
  "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,
  "available_payout_methods": null,
  "brand": "Visa",
  "country": "US",
  "currency": null,
  "customer": "cus_FIUWEyqQlxmOEj",
  "cvc_check": null,
  "default_for_currency": false,
  "dynamic_last4": null,
  "exp_month": 6,
  "exp_year": 2020,
  "fingerprint": "Xt5EWLLDS7FJjR1c",
  "funding": "credit",
  "last4": "4242",
  "metadata": {},
  "name": null,
  "recipient": null,
  "three_d_secure": null,
  "tokenization_method": null,
  "description": null,
  "iin": null,
  "issuer": null
}

@Excelzn
Copy link
Author

Excelzn commented Jun 26, 2019

Apologies for the delay in responding. I tried your test project and it does work fine. One thing I noticed is that the cards I have are using IDs that begin with src rather than card. I am not sure why that is happening as I am adding the cards through the API like this:

var card = await _service.CreateAsync(userId, new CardCreateOptions
{
      SourceToken = token
});

Which according to the docs should add it as a card rather than a source.

@ob-stripe
Copy link
Contributor

ob-stripe commented Jun 30, 2019

@Excelzn The type of the payment source (card vs. source) is decided when you collect the card information in your frontend code:

(The word "source" is a bit overloaded here, so this is a bit confusing!)

You can technically use the CardService.Create/CreateAsync methods with a source object (src_...) instead of SourceService.Create/CreateAsync, but it won't turn the source into a card. This only works because under the hood, both methods map to the same API endpoint (POST /v1/customers/{customer}/sources).

Similarly, you can use CardService.Get/GetAsync and SourceService.Get/GetAsync with both card IDs (card_...) and source IDs (src_...) because they also both map to the same API endpoint (GET /v1/customers/{customer}/sources/{card|source}). However, using the wrong service will produce unexpected results because the object returned by the API will be deserialized with the wrong model class.

In short, you need to fix your integration to use the correct service class: CardService if you're dealing with tokens/cards, SourceService if you're dealing with sources. What object type you use is decided by your frontend (Stripe.js / Elements) code.

I'm leaving this issue open because I think we can improve the library to make this less confusing, for instance by raising an exception if we know we're deserializing into the wrong model class, and/or by adding a PaymentSourceService that returns a PaymentSource interface that will be either a Card or a Source at runtime.

@ackava
Copy link

ackava commented Jul 5, 2019

I confirm this, it is not working on any platform !!

Problem is with the JSON Structure,

Json retrieved from server is...

{
    "data": [
         {
            "id": "src_.....",
            "card": {
                "last4": "1233"
            }
         }
    ]
}

If you notice, all the properties of Card are inside card property, and in C# Last4 property is on class itself.

Structure of Stripe Card is wrong...

    public class Card {
        public string Id {get;set;}
        public string Last4 {get;set;}
    }

Instead it should be...

    public class Card {
        public string Id {get;set;}
        public CardDetails Card {get;set;}
    }
    public class CardDetails {
        public string Last4 {get;set;}
    }

@ob-stripe
Copy link
Contributor

ob-stripe commented Jul 5, 2019

@ackava Please read my comment above (#1676 (comment)). Source objects with type=card and card objects are two different kinds of objects. Source objects must be deserialized using the Source class, which means you must use SourceService to retrieve them.

@ackava
Copy link

ackava commented Jul 5, 2019

@ob-stripe If that is the case, CardService.List customerID should not return source when we expect card

@ob-stripe
Copy link
Contributor

@ackava That should be the case already. Using CardService.List will include object=card in the request, causing the API to only return card objects, not source objects (even if those source objects have type=card).

AFAIK the only case where an object would be deserialized to the wrong model class is if you use CardService.Get with a "src_..." ID.

Have you observed a case where CardService.List returned incorrectly deserialized objects?

@ackava
Copy link

ackava commented Jul 6, 2019

@ob-stripe here is my code,

Yes, CardService.List is returning SourceObjects

I am using StripeResponse.Content to manually parse the JSON and retrieve card information.

            var cs = new CardService(client);

            var cards = cs.List(custome.StripeCustomerID);
            
            string json = cards.StripeResponse.Content;
            var js = JObject.Parse(json);

            var data = js["data"] as JArray;

            var first = data[0];

            var card = first["card"];

            return new CardInfo {
                ExpirationMonth = card["exp_month"].Value<int>(),
                ExpirationYear = card["exp_year"].Value<int>(),
                CardNumber = card["last4"].ToString(),
                CardType = card["brand"].ToString()
            };

@ob-stripe
Copy link
Contributor

@ackava Thanks. This is indeed a bug in the library where the object=card filter is not sent if you call CardService.List without passing a CardListOptions instance. We will release a fix soon.

@ob-stripe ob-stripe mentioned this issue Aug 20, 2019
19 tasks
@ob-stripe
Copy link
Contributor

Sorry for the delay here. This should be fixed in the latest major version, 30.0.0. Calling CardService.List/ListAsync with no parameters should now properly return only a list of cards.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants