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

[1.51.0 -> 2.3.3 Upgrade] TypeError: quote_from_bytes() expected bytes #597

Closed
NicolaGenesin opened this issue Aug 2, 2019 · 5 comments · Fixed by #601
Closed

[1.51.0 -> 2.3.3 Upgrade] TypeError: quote_from_bytes() expected bytes #597

NicolaGenesin opened this issue Aug 2, 2019 · 5 comments · Fixed by #601
Labels

Comments

@NicolaGenesin
Copy link

Python Version: 3.6
Issue after updating from stripe==1.51.0 to stripe==2.3.3

We're using the Charge APIs, separating Auth from Capture:

1) charge = stripe.Charge.create({..., capture=False}) works fine with both versions
... some time later ...
2) charge = stripe.Charge.retrieve(payment_id) works fine and returns the same exact object for both versions. This is an example using apple pay in test mode:

<Charge charge id=ch_1F2wzuLu48Aj7sQoaFzwnJP6 at 0x7f6901665a98> JSON: {
  "amount": 50,
  "amount_refunded": 0,
  "application": null,
  "application_fee": null,
  "application_fee_amount": null,
  "balance_transaction": null,
  "billing_details": {
    "address": {
      "city": null,
      "country": null,
      "line1": null,
      "line2": null,
      "postal_code": null,
      "state": null
    },
    "email": null,
    "name": null,
    "phone": null
  },
  "captured": false,
  "created": 1564737866,
  "currency": "XXXXXXXXXX",
  "customer": null,
  "description": "XXXXXXXXXX",
  "destination": null,
  "dispute": null,
  "failure_code": null,
  "failure_message": null,
  "fraud_details": {},
  "id": "XXXXXXXXXX",
  "invoice": null,
  "livemode": false,
  "metadata": {
    "Sale ID": "XXXXXXXXXX",
    "Session ID": "XXXXXXXXXX"
  },
  "object": "charge",
  "on_behalf_of": null,
  "order": null,
  "outcome": {
    "network_status": "approved_by_network",
    "reason": null,
    "risk_level": "normal",
    "risk_score": 64,
    "seller_message": "Payment complete.",
    "type": "authorized"
  },
  "paid": true,
  "payment_intent": null,
  "payment_method": "XXXXXXXXXX",
  "payment_method_details": {
    "card": {
      "brand": "mastercard",
      "checks": {
        "address_line1_check": null,
        "address_postal_code_check": null,
        "cvc_check": null
      },
      "country": "GB",
      "exp_month": 4,
      "exp_year": 2026,
      "fingerprint": "XXXXXXXXXX",
      "funding": "debit",
      "last4": "XXXXXXXXXX",
      "three_d_secure": null,
      "wallet": {
        "apple_pay": {},
        "dynamic_last4": "XXXXXXXXXX",
        "type": "apple_pay"
      }
    },
    "type": "card"
  },
  "receipt_email": null,
  "receipt_number": null,
  "receipt_url": "https://pay.stripe.com/receipts/XXXXXXXXXX",
  "refunded": false,
  "refunds": {
    "data": [],
    "has_more": false,
    "object": "list",
    "total_count": 0,
    "url": "/v1/charges/XXXXXXXXXX/refunds"
  },
  "review": null,
  "shipping": null,
  "source": {
    "amount": 50,
    "card": {
      "address_line1_check": null,
      "address_zip_check": null,
      "brand": "XXXXXXXXXX",
      "country": "XXXXXXXXXX",
      "cvc_check": null,
      "dynamic_last4": "XXXXXXXXXX",
      "exp_month": 4,
      "exp_year": 2025,
      "fingerprint": "XXXXXXXXXX",
      "funding": "debit",
      "last4": "XXXXXXXXXX",
      "name": null,
      "three_d_secure": "optional",
      "tokenization_method": "apple_pay"
    },
    "client_secret": "XXXXXXXXXX",
    "created": 1564737867,
    "currency": "gbp",
    "flow": "none",
    "id": "XXXXXXXXXX",
    "livemode": false,
    "metadata": {},
    "object": "source",
    "owner": {
      "address": null,
      "email": null,
      "name": null,
      "phone": null,
      "verified_address": null,
      "verified_email": null,
      "verified_name": null,
      "verified_phone": null
    },
    "statement_descriptor": null,
    "status": "consumed",
    "type": "card",
    "usage": "single_use"
  },
  "source_transfer": null,
  "statement_descriptor": null,
  "status": "succeeded",
  "transfer_data": null,
  "transfer_group": null
}

3) result_charge = stripe.Charge.capture(charge) fails for stripe==2.3.3 with the following error

TypeError: quote_from_bytes() expected bytes
  File "celery/app/trace.py", line 240, in trace_task
    R = retval = fun(*args, **kwargs)
  File "celery/app/trace.py", line 438, in __protected_call__
    return self.run(*args, **kwargs)
  File "booking/tasks/booking/sales/request.py", line 144, in run
    status, error, processing_amount = self.capture_payment(sale)
  File "booking/tasks/booking/sales/request.py", line 66, in capture_payment
    result_charge = stripe.Charge.capture(charge)
  File "stripe/util.py", line 223, in _wrapper
    return class_method(*args, **kwargs)
  File "stripe/api_resources/abstract/custom_method.py", line 20, in custom_method_request
    quote_plus(util.utf8(sid)),
  File "urllib/parse.py", line 805, in quote_plus
    string = quote(string, safe + space, encoding, errors)
  File "urllib/parse.py", line 789, in quote
    return quote_from_bytes(string, safe)
  File "urllib/parse.py", line 814, in quote_from_bytes
    raise TypeError("quote_from_bytes() expected bytes")
@ob-stripe
Copy link
Contributor

@NicolaGenesin Charge.capture expects a charge ID, not a full charge object. This should work:

result_charge = stripe.Charge.capture(charge.id)

@NicolaGenesin
Copy link
Author

@ob-stripe thank you for quick response, I confirm you this works. Wondering why you dropped support for stripe.Charge.capture(charge) (working for 1.51.0)

@ob-stripe
Copy link
Contributor

Prior to version 2.24.0, Charge.capture() was an instance method only, i.e. it was meant to be called like this:

charge = stripe.Charge.retrieve("ch_123")
charge.capture()

Due to the way instance methods work in Python (instance methods are really functions that accept the instance variable as their first argument), this syntax also worked:

charge = stripe.Charge.retrieve("ch_123")
stripe.Charge.capture(charge)

We are moving away from using instance methods and are now recommending the use of class methods instead. In version 2.24.0, we added support for calling instance methods like Charge.capture as class methods instead.

Python does not normally support declaring instance methods and class methods with the same name (because it would require declaring two functions with the same name). We worked around this limitation with a somewhat hacky solution (#543) that let us support both these syntaxes:

charge = stripe.Charge.capture("ch_123") # class method

charge = stripe.Charge.retrieve("ch_123")
charge.capture() # instance method

but it unfortunately broke support for the syntax you're using. That wasn't an intended consequence, but calling an instance method as method(instance) instead of instance.method() is (AFAIK) very uncommon and we didn't anticipate it could break anybody (we normally release breaking changes as major versions).

Hope that helped! I'm leaving the issue open for now, as we might be able to restore the previous behavior, or at least provide a clearer error message.

@NicolaGenesin
Copy link
Author

@ob-stripe that's helpful. Thank you for your explanation (now it makes sense to me as well)

@ob-stripe
Copy link
Contributor

We just released stripe-python 2.33.2 which should restore support for the syntax you were using (stripe.Charge.capture(charge)).

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

Successfully merging a pull request may close this issue.

2 participants