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

Invalid response object from API #1213

Closed
apflieger opened this issue May 28, 2021 · 12 comments
Closed

Invalid response object from API #1213

apflieger opened this issue May 28, 2021 · 12 comments
Assignees

Comments

@apflieger
Copy link

Version of stripe-java: 20.50.0
Version of the API: 2020-08-27
java -version

openjdk version "15" 2020-09-15
OpenJDK Runtime Environment (build 15+36-1562)
OpenJDK 64-Bit Server VM (build 15+36-1562, mixed mode, sharing)

I experience these errors since 30th of April.

I can't figure out what is causing the error since gson doesn't say much about the error. It seems that the json schema changed somewhere and the library became incompatible.
Note that this concerns only a few payments.

(I anonymized personnal data in the json)

com.stripe.exception.ApiException: Invalid response object from API: {
  "error": {
    "charge": "ch_1Ilv0tHwOwMy49kYnq6bnax5",
    "code": "card_declined",
    "decline_code": "lost_card",
    "doc_url": "https://stripe.com/docs/error-codes/card-declined",
    "message": "Your card was declined.",
    "payment_intent": {
      "id": "pi_1Ilv0tHwOwMy49kYMD531auw",
      "object": "payment_intent",
      "amount": 3000,
      "amount_capturable": 0,
      "amount_received": 0,
      "application": null,
      "application_fee_amount": null,
      "canceled_at": null,
      "cancellation_reason": null,
      "capture_method": "automatic",
      "charges": {
        "object": "list",
        "data": [
          {
            "id": "ch_1Ilv0tHwOwMy49kYnq6bnax5",
            "object": "charge",
            "amount": 3000,
            "amount_captured": 0,
            "amount_refunded": 0,
            "application": null,
            "application_fee": null,
            "application_fee_amount": null,
            "balance_transaction": null,
            "billing_details": {
              "address": {
                "city": "trappes",
                "country": "FR",
                "line1": "55 ave paul vaillant couturier ",
                "line2": null,
                "postal_code": "78190",
                "state": null
              },
              "email": null,
              "name": "xxxxx xxxxx",
              "phone": null
            },
            "calculated_statement_descriptor": "xxxxxxx",
            "captured": false,
            "created": 1619784007,
            "currency": "eur",
            "customer": "cus_IeiwsMWdZ5xKN7",
            "description": "xxxxxxxxxxxxxx",
            "destination": "acct_1CHvTHD8dOesO2oo",
            "dispute": null,
            "disputed": false,
            "failure_code": "card_declined",
            "failure_message": "Your card was declined.",
            "fraud_details": {
            },
            "invoice": null,
            "livemode": true,
            "metadata": {
              "attendance_id": "39b41396851548cebceb8a7d07b23813"
            },
            "on_behalf_of": null,
            "order": null,
            "outcome": {
              "network_status": "declined_by_network",
              "reason": "lost_card",
              "risk_level": "normal",
              "seller_message": "The bank returned the decline code `lost_card`.",
              "type": "issuer_declined"
            },
            "paid": false,
            "payment_intent": "pi_1Ilv0tHwOwMy49kYMD531auw",
            "payment_method": "pm_1IBQitHwOwMy49kYtB3QPFYY",
            "payment_method_details": {
              "card": {
                "brand": "mastercard",
                "checks": {
                  "address_line1_check": null,
                  "address_postal_code_check": null,
                  "cvc_check": null
                },
                "country": "FR",
                "exp_month": 12,
                "exp_year": 2021,
                "fingerprint": "XNwC0UFUHvSovyvt",
                "funding": "credit",
                "installments": null,
                "last4": "2010",
                "network": "cartes_bancaires",
                "three_d_secure": null,
                "wallet": null
              },
              "type": "card"
            },
            "receipt_email": "xxxxxxxxxx",
            "receipt_number": null,
            "receipt_url": null,
            "refunded": false,
            "refunds": {
              "object": "list",
              "data": [

              ],
              "has_more": false,
              "total_count": 0,
              "url": "/v1/charges/ch_1Ilv0tHwOwMy49kYnq6bnax5/refunds"
            },
            "review": null,
            "shipping": null,
            "source": null,
            "source_transfer": null,
            "statement_descriptor": null,
            "statement_descriptor_suffix": null,
            "status": "failed",
            "transfer_data": {
              "amount": 2400,
              "destination": "acct_1CHvTHD8dOesO2oo"
            },
            "transfer_group": "group_pi_1Ilv0tHwOwMy49kYMD531auw"
          }
        ],
        "has_more": false,
        "total_count": 1,
        "url": "/v1/charges?payment_intent=pi_1Ilv0tHwOwMy49kYMD531auw"
      },
      "client_secret": "pi_1Ilv0tHwOwMy49kYMD531auw_secret_yysQFCX9tlfovmOEYtCWPRQGA",
      "confirmation_method": "automatic",
      "created": 1619784007,
      "currency": "eur",
      "customer": "cus_IeiwsMWdZ5xKN7",
      "description": "xxxxxxxxxxxxx",
      "invoice": null,
      "last_payment_error": {
        "charge": "ch_1Ilv0tHwOwMy49kYnq6bnax5",
        "code": "card_declined",
        "decline_code": "lost_card",
        "doc_url": "https://stripe.com/docs/error-codes/card-declined",
        "message": "Your card was declined.",
        "payment_method": {
          "id": "pm_1IBQitHwOwMy49kYtB3QPFYY",
          "object": "payment_method",
          "billing_details": {
            "address": {
              "city": "trappes",
              "country": "FR",
              "line1": "xxxxxxx",
              "line2": null,
              "postal_code": "xxxxxx",
              "state": null
            },
            "email": null,
            "name": "xxxxxxx",
            "phone": null
          },
          "card": {
            "brand": "mastercard",
            "checks": {
              "address_line1_check": "unavailable",
              "address_postal_code_check": "unavailable",
              "cvc_check": "pass"
            },
            "country": "FR",
            "exp_month": 12,
            "exp_year": 2021,
            "fingerprint": "XNwC0UFUHvSovyvt",
            "funding": "credit",
            "generated_from": null,
            "last4": "2010",
            "networks": {
              "available": [
                "cartes_bancaires",
                "mastercard"
              ],
              "preferred": null
            },
            "three_d_secure_usage": {
              "supported": true
            },
            "wallet": null
          },
          "created": 1611087763,
          "customer": "cus_IeiwsMWdZ5xKN7",
          "livemode": true,
          "metadata": {
          },
          "type": "card"
        },
        "type": "card_error"
      },
      "livemode": true,
      "metadata": {
        "attendance_id": "39b41396851548cebceb8a7d07b23813"
      },
      "next_action": null,
      "on_behalf_of": null,
      "payment_method": null,
      "payment_method_options": {
        "card": {
          "installments": null,
          "network": null,
          "request_three_d_secure": "automatic"
        }
      },
      "payment_method_types": [
        "card"
      ],
      "receipt_email": "xxxxxxxxxx",
      "review": null,
      "setup_future_usage": null,
      "shipping": null,
      "source": null,
      "statement_descriptor": null,
      "statement_descriptor_suffix": null,
      "status": "requires_payment_method",
      "transfer_data": {
        "amount": 2400,
        "destination": "acct_1CHvTHD8dOesO2oo"
      },
      "transfer_group": null
    },
    "payment_method": {
      "id": "pm_1IBQitHwOwMy49kYtB3QPFYY",
      "object": "payment_method",
      "billing_details": {
        "address": {
          "city": "xxxxx",
          "country": "FR",
          "line1": "xxxxxx",
          "line2": null,
          "postal_code": "xxxxxx",
          "state": null
        },
        "email": null,
        "name": "xxxxxxx",
        "phone": null
      },
      "card": {
        "brand": "mastercard",
        "checks": {
          "address_line1_check": "unavailable",
          "address_postal_code_check": "unavailable",
          "cvc_check": "pass"
        },
        "country": "FR",
        "exp_month": 12,
        "exp_year": 2021,
        "fingerprint": "XNwC0UFUHvSovyvt",
        "funding": "credit",
        "generated_from": null,
        "last4": "2010",
        "networks": {
          "available": [
            "cartes_bancaires",
            "mastercard"
          ],
          "preferred": null
        },
        "three_d_secure_usage": {
          "supported": true
        },
        "wallet": null
      },
      "created": 1611087763,
      "customer": "cus_IeiwsMWdZ5xKN7",
      "livemode": true,
      "metadata": {
      },
      "type": "card"
    },
    "type": "card_error"
  }
}
. (HTTP response code was 402). Additional details: java.lang.IllegalStateException.; request-id: req_bvq5wO20IlED4y
        at com.stripe.net.LiveStripeResponseGetter.raiseMalformedJsonError(LiveStripeResponseGetter.java:123)
        at com.stripe.net.LiveStripeResponseGetter.handleApiError(LiveStripeResponseGetter.java:136)
        at com.stripe.net.LiveStripeResponseGetter.request(LiveStripeResponseGetter.java:61)
        at com.stripe.net.ApiResource.request(ApiResource.java:179)
        at com.stripe.net.ApiResource.request(ApiResource.java:169)
        at com.stripe.model.PaymentIntent.create(PaymentIntent.java:497)
        at com.stripe.model.PaymentIntent.create(PaymentIntent.java:478)
        at com.meetinclass.services.StripeService.customerDestinationCharge(StripeService.java:189)
        at com.meetinclass.services.BillingService.lambda$perform$11(BillingService.java:173)
        at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1800)
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)
        at java.base/java.lang.Thread.run(Thread.java:832)
Caused by: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:226)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:131)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:222)
        at com.google.gson.Gson.fromJson(Gson.java:932)
        at com.google.gson.Gson.fromJson(Gson.java:1003)
        at com.google.gson.Gson.fromJson(Gson.java:975)
        at com.stripe.net.LiveStripeResponseGetter.handleApiError(LiveStripeResponseGetter.java:134)
        ... 11 common frames omitted
Caused by: java.lang.IllegalStateException: null
        at com.google.gson.Gson$FutureTypeAdapter.read(Gson.java:1018)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:131)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:222)
        ... 17 common frames omitted
@remi-stripe
Copy link
Contributor

@apflieger Thanks for reaching out! Would you be able to dump a full/complete stacktrace without the frames omitted? I tried to repro on my end but I can de-serialize this exact raw JSON with the latest version of stripe-java so I'm not yet sure what could be causing this.

@remi-stripe remi-stripe self-assigned this Jun 2, 2021
@apflieger
Copy link
Author

Apparently, "frames omitted" means that these frames were already printed in the stack trace, there's no more to print.
https://stackoverflow.com/questions/12226375/how-can-i-see-a-full-log-of-exceptions-in-java

It might be a thread safety issue because we get these errors since we started processing payment in parallel

@remi-stripe
Copy link
Contributor

@apflieger Sorry for the delay, we haven't been able to reproduce this yet unfortunately. We do think it's a thread safety issue with GSON that is likely related to this issue: google/gson#764 (comment)

We're going to keep this one in mind and see if we can make improvements to the library internally to protect from this further in the future.

@apflieger
Copy link
Author

It hasn't crossed my mind that this could happen on successful payment too so I did a bit more investigations and yes, it actually does happen on successful payments which is a bit problematic since our payment system do retries... We end up billing multiple times our customers.

The issue on Gson is still opened google/gson#764 (comment)

A simple way to fix it would be to not share Gson instance between threads. I ran some performance tests on that (I guess performance is the only concern)

  @Test
  public void gsonCreationPerformance() {
    var durations = new ArrayList<>();
    for (int i = 0; i < 1000; i++) {
      var gson = ApiResource.createGson();
      Instant at1 = Instant.now();
      gson.fromJson(JSON, PaymentIntent.class);
      durations.add(Duration.between(at1, Instant.now()).toMillis());
    }
    System.out.println(durations.stream().mapToLong(d -> (long) d).average());
    durations.clear();
    var gson = ApiResource.createGson();
    for (int i = 0; i < 1000; i++) {
      Instant at2 = Instant.now();
      gson.fromJson(JSON, PaymentIntent.class);
      durations.add(Duration.between(at2, Instant.now()).toMillis());
    }
    System.out.println(durations.stream().mapToLong(d -> (long) d).average());
  }

Where JSON is a hardcoded response. I made ApiResource.createGson public

On my machine, it gives

ApiResourceTest > gsonCreationPerformance() STANDARD_OUT
    OptionalDouble[4.278]
    OptionalDouble[0.003]

4-5 milliseconds process time would be added to a request. This looks negligeable to me.
Note that I'm not measuring the call ApiResource.createGson(), I found out in previous tests that it costs nearly 0.

@dcr-stripe
Copy link
Contributor

Thanks for the very detailed report @apflieger - it's really appreciated! We'll be looking at that this quarter to see how we can fix this internally.

@apflieger
Copy link
Author

The bug seems to be fixed in Gson 2.9.0. I upgraded 2 months ago and we got no error since.

@dcr-stripe
Copy link
Contributor

Great - thank you for the report back @apflieger ! Gson 2.9.0 is the version we include in our Gradle file as of #1322. I'll close this issue for now but please re-open if you experience this issue again.

@apflieger
Copy link
Author

Hi,

We still get the error sometimes unfortunately. We reduced the amount of charges that we do so we see it less often but still.

This one is a successful payment that has been marked as failed in our system. As I said previously, this is kinda problematic because we retry these payments so we end up charging customers multiple times.

com.stripe.exception.ApiException: Invalid response object from API: {
  "id": "pi_3M8NzAHwOwMy49kY171nDEM3",
  "object": "payment_intent",
  "amount": 3400,
  "amount_capturable": 0,
  "amount_details": {
    "tip": {}
  },
  "amount_received": 3400,
  "application": null,
  "application_fee_amount": null,
  "automatic_payment_methods": null,
  "canceled_at": null,
  "cancellation_reason": null,
  "capture_method": "automatic",
  "charges": {
    "object": "list",
    "data": [
      {
        "id": "ch_3M8NzAHwOwMy49kY1AOJGLGp",
        "object": "charge",
        "amount": 3400,
        "amount_captured": 3400,
        "amount_refunded": 0,
        "application": null,
        "application_fee": null,
        "application_fee_amount": null,
        "balance_transaction": "txn_3M8NzAHwOwMy49kY1op64Ty7",
        "billing_details": {
          "address": {
            "city": "xxx",
            "country": "FR",
            "line1": "xxx",
            "line2": null,
            "postal_code": "xxx",
            "state": null
          },
          "email": null,
          "name": "xxx",
          "phone": null
        },
        "calculated_statement_descriptor": "MEET IN CLASS",
        "captured": true,
        "created": 1669467601,
        "currency": "eur",
        "customer": "cus_L4qr3DyvA2chvB",
        "description": "xxx",
        "destination": null,
        "dispute": null,
        "disputed": false,
        "failure_balance_transaction": null,
        "failure_code": null,
        "failure_message": null,
        "fraud_details": {},
        "invoice": null,
        "livemode": true,
        "metadata": {
          "attendance_id": "xxx"
        },
        "on_behalf_of": null,
        "order": null,
        "outcome": {
          "network_status": "approved_by_network",
          "reason": null,
          "risk_level": "normal",
          "seller_message": "Payment complete.",
          "type": "authorized"
        },
        "paid": true,
        "payment_intent": "pi_3M8NzAHwOwMy49kY171nDEM3",
        "payment_method": "pm_1KOh87HwOwMy49kYwjRYntBu",
        "payment_method_details": {
          "card": {
            "brand": "mastercard",
            "checks": {
              "address_line1_check": "unchecked",
              "address_postal_code_check": "unchecked",
              "cvc_check": null
            },
            "country": "FR",
            "exp_month": 4,
            "exp_year": 2024,
            "fingerprint": "wMaFyTGwUS4itJYe",
            "funding": "debit",
            "installments": null,
            "last4": "1158",
            "mandate": null,
            "network": "cartes_bancaires",
            "three_d_secure": null,
            "wallet": null
          },
          "type": "card"
        },
        "receipt_email": "xxx",
        "receipt_number": "1754-7606",
        "receipt_url": "https://pay.stripe.com/receipts/payment/CAcQARoXChVhY2N0XzFBYnU5dUh3T3dNeTQ5a1ko0puInAYyBrYJgaLaWjosFjHir7Jn8hWIavShB1i2fbV0IyLXMaekgig45WKepQP7GvKnPrc1gjHocGI",
        "refunded": false,
        "refunds": {
          "object": "list",
          "data": [],
          "has_more": false,
          "total_count": 0,
          "url": "/v1/charges/ch_3M8NzAHwOwMy49kY1AOJGLGp/refunds"
        },
        "review": null,
        "shipping": null,
        "source": null,
        "source_transfer": null,
        "statement_descriptor": null,
        "statement_descriptor_suffix": null,
        "status": "succeeded",
        "transfer_data": null,
        "transfer_group": null
      }
    ],
    "has_more": false,
    "total_count": 1,
    "url": "/v1/charges?payment_intent=pi_3M8NzAHwOwMy49kY171nDEM3"
  },
  "client_secret": "xxx",
  "confirmation_method": "automatic",
  "created": 1669467600,
  "currency": "eur",
  "customer": "cus_L4qr3DyvA2chvB",
  "description": "xxx",
  "invoice": null,
  "last_payment_error": null,
  "livemode": true,
  "metadata": {
    "attendance_id": "3e8c5e2645654ddead9f671e48df0850"
  },
  "next_action": null,
  "on_behalf_of": null,
  "payment_method": "pm_1KOh87HwOwMy49kYwjRYntBu",
  "payment_method_options": {
    "card": {
      "installments": null,
      "mandate_options": null,
      "network": null,
      "request_three_d_secure": "automatic"
    }
  },
  "payment_method_types": [
    "card"
  ],
  "processing": null,
  "receipt_email": "xxx",
  "review": null,
  "setup_future_usage": null,
  "shipping": null,
  "source": null,
  "statement_descriptor": null,
  "statement_descriptor_suffix": null,
  "status": "succeeded",
  "transfer_data": null,
  "transfer_group": null
}. (HTTP response code was 200). Additional details: java.lang.IllegalStateException.; request-id: req_a8aB2jmgmhr8sy
        at com.stripe.net.LiveStripeResponseGetter.raiseMalformedJsonError(LiveStripeResponseGetter.java:159)
        at com.stripe.net.LiveStripeResponseGetter.request(LiveStripeResponseGetter.java:72)
        at com.stripe.net.ApiResource.request(ApiResource.java:181)
        at com.stripe.net.ApiResource.request(ApiResource.java:171)
        at com.stripe.model.PaymentIntent.create(PaymentIntent.java:569)
        at com.stripe.model.PaymentIntent.create(PaymentIntent.java:550)
        at com.meetinclass.services.payment.StripeApi.customerDirectCharge(StripeApi.java:270)
        at com.meetinclass.services.payment.PaymentServiceImpl.executePayment(PaymentServiceImpl.java:47)
        at com.meetinclass.services.BillingService.lambda$executePayments$3(BillingService.java:140)
        at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1768)
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
        at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:270)
        at com.google.gson.Gson.fromJson(Gson.java:1058)
        at com.google.gson.Gson.fromJson(Gson.java:1016)
        at com.google.gson.Gson.fromJson(Gson.java:959)
        at com.google.gson.Gson.fromJson(Gson.java:927)
        at com.stripe.net.LiveStripeResponseGetter.request(LiveStripeResponseGetter.java:70)
        ... 11 common frames omitted
Caused by: java.lang.IllegalStateException: null
        at com.google.gson.Gson$FutureTypeAdapter.read(Gson.java:1144)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:161)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:266)
        ... 16 common frames omitted

@apflieger
Copy link
Author

Apparently, this might be fixed in the next release of Gson (2.11 probably) by this PR google/gson#1832

@apflieger
Copy link
Author

The fix has been released in gson 2.10.1 🎉
https://github.com/google/gson/releases/tag/gson-parent-2.10.1

Fix non-threadsafe creation of adapter for type with cyclic dependency by @Marcono1234 in google/gson#1832

We've just deployed the upgrade. Let's monitor 👀

@richardm-stripe
Copy link
Contributor

Thank you @apflieger!

@richardm-stripe
Copy link
Contributor

In https://github.com/stripe/stripe-java/releases/tag/v22.5.1 we bumped stripe-java's dependency to the newest version of GSON.

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

No branches or pull requests

4 participants