braintree / braintree_python Goto Github PK
View Code? Open in Web Editor NEWBraintree Python library
Home Page: https://developer.paypal.com/braintree/docs/start/overview
License: MIT License
Braintree Python library
Home Page: https://developer.paypal.com/braintree/docs/start/overview
License: MIT License
It would be very convenient for our application's administrators if its web interface could link directly to Braintree's web page for any given subscription, customer, etc. Would you folks consider adding a method on such objects to generate the appropriate URL for viewing the object in Braintree's UI?
There are webhooks for every state change of a subscription, and for all attempted transactions, but there is no webhook for when a subscription changes plan.
In our application, different plans open up extra features of the application. The standard plan has a limited feature set, while the Delux Ultimate Pro++ plan has all of the features. People can upgrade and downgrade their plan at any time. Tracking these changes is quite important to our application.
With the other webhooks provided by braintree, our application can rely solely on the events posted to us by braintree for subscription status management. This great, because it allows us to not make a duplicate backend in our application to manage subscriptions, as the client can just use braintree if they want to manually add, manage and remove subscriptions. However, if the client upgrades a user to another plan through the braintree administration interface, our application will not know about it and the customer will not be upgraded.
Could a webhook be added to notify applications when the plan of a subscription changes?
http://www.braintreepaymentsolutions.com/docs/python/subscriptions/retry_charge
The documentation says to use Subscription.retryCharge(), but calling that method yields a warning that it is deprecated in favor of retry_charge().
While testing, I came across the following exception when calling Subscription.find(""):
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python2.6/dist-packages/braintree/subscription.py", line 109, in find
return Configuration.gateway().subscription.find(subscription_id)
File "/usr/local/lib/python2.6/dist-packages/braintree/subscription_gateway.py", line 33, in find
response = self.config.http().get("/subscriptions/" + subscription_id)
File "/usr/local/lib/python2.6/dist-packages/braintree/util/http.py", line 47, in get
return self.__http_do("GET", path)
File "/usr/local/lib/python2.6/dist-packages/braintree/util/http.py", line 70, in __http_do
Http.raise_exception_from_status(status)
File "/usr/local/lib/python2.6/dist-packages/braintree/util/http.py", line 34, in raise_exception_from_status
raise UnexpectedError("Unexpected HTTP_RESPONSE " + str(status))
braintree.exceptions.unexpected_error.UnexpectedError: Unexpected HTTP_RESPONSE 405
Not a big deal, since I don't expect to pass an empty string to find() in practice, but I thought you folks would like to know since it seems like the expected result would have been a NotFoundError.
I believe setup.py is missing the following package: braintree.util.http_strategy
When I attempt to install the latest python version (2.14.0) I get the following error:
ImportError: No module named http_strategy.pycurl_strategy
This error happens when running braintree.Customer.create({'email': email})
where email is the user's email address.
Traceback (most recent call last):
File "/opt/wakatime/current/venv/local/lib/python2.7/site-packages/flask/app.py", line 1817, in wsgi_app
response = self.full_dispatch_request()
File "/opt/wakatime/current/venv/local/lib/python2.7/site-packages/flask/app.py", line 1477, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/opt/wakatime/current/venv/local/lib/python2.7/site-packages/flask/app.py", line 1381, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/opt/wakatime/current/venv/local/lib/python2.7/site-packages/flask/app.py", line 1475, in full_dispatch_request
rv = self.dispatch_request()
File "/opt/wakatime/current/venv/local/lib/python2.7/site-packages/flask/app.py", line 1461, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/opt/wakatime/current/venv/local/lib/python2.7/site-packages/flask_login.py", line 792, in decorated_view
return func(*args, **kwargs)
File "/opt/wakatime/current/app/api_v1.py", line 295, in post_users_current_payment_method
app.current_user.update_payment_method(processor_type, **form.data)
File "/opt/wakatime/current/app/models.py", line 924, in update_payment_method
self.processor.update_payment_method(kwargs['token'])
File "/opt/wakatime/current/app/models.py", line 3435, in update_payment_method
'customer_id': self.customer.id,
File "/opt/wakatime/current/app/models.py", line 3319, in customer
self._customer = PaymentProcessor.get_customer(self.user, self.type)[0]
File "/opt/wakatime/current/app/models.py", line 3294, in get_customer
'email': user.email,
File "/opt/wakatime/current/venv/local/lib/python2.7/site-packages/braintree/customer.py", line 102, in create
return Configuration.gateway().customer.create(params)
File "/opt/wakatime/current/venv/local/lib/python2.7/site-packages/braintree/customer_gateway.py", line 26, in create
return self._post("/customers", {"customer": params})
File "/opt/wakatime/current/venv/local/lib/python2.7/site-packages/braintree/customer_gateway.py", line 88, in _post
response = self.config.http().post(self.config.base_merchant_path() + url, params)
File "/opt/wakatime/current/venv/local/lib/python2.7/site-packages/braintree/util/http.py", line 49, in post
return self.__http_do("POST", path, params)
File "/opt/wakatime/current/venv/local/lib/python2.7/site-packages/braintree/util/http.py", line 72, in __http_do
raise e
TypeError: cannot make memory view because object does not have the buffer interface
We (Venmo) recently open sourced https://github.com/venmo/btnamespace. Linking to it in your README might save your users some headaches =)
The documentation says to use Transaction.refund_id
, but when used, the SDK reports it as deprecated and suggests refund_ids
instead.
Running the example causes an exception:
Traceback (most recent call last):
File "braintree_test.py", line 26, in <module>
"expiration_year": "2012"
File "/home/gonvaled/.virtualenvs/python2.7.3-wavilon1/local/lib/python2.7/site-packages/braintree/transaction.py", line 297, in sale
return Transaction.create(params)
File "/home/gonvaled/.virtualenvs/python2.7.3-wavilon1/local/lib/python2.7/site-packages/braintree/transaction.py", line 392, in create
return Configuration.gateway().transaction.create(params)
File "/home/gonvaled/.virtualenvs/python2.7.3-wavilon1/local/lib/python2.7/site-packages/braintree/transaction_gateway.py", line 33, in create
return self._post("/transactions", {"transaction": params})
File "/home/gonvaled/.virtualenvs/python2.7.3-wavilon1/local/lib/python2.7/site-packages/braintree/transaction_gateway.py", line 137, in _post
response = self.config.http().post(url, params)
File "/home/gonvaled/.virtualenvs/python2.7.3-wavilon1/local/lib/python2.7/site-packages/braintree/util/http.py", line 40, in post
return self.__http_do("POST", path, params)
File "/home/gonvaled/.virtualenvs/python2.7.3-wavilon1/local/lib/python2.7/site-packages/braintree/util/http.py", line 55, in __http_do
status, response_body = http_strategy.http_do(http_verb, full_path, self.__headers(), request_body)
File "/home/gonvaled/.virtualenvs/python2.7.3-wavilon1/local/lib/python2.7/site-packages/braintree/util/http_strategy/requests_strategy.py", line 12, in http_do
response = requests.request(
NameError: global name 'requests' is not defined
import braintree
braintree.Configuration.configure(
braintree.Environment.Sandbox,
BRAINTREE_MERCHANT_ID,
BRAINTREE_PUBLIC_KEY,
BRAINTREE_PRIVATE_KEY
)
result = braintree.Transaction.sale({
"amount": "1000.00",
"credit_card": {
"number": "4111111111111111",
"expiration_month": "05",
"expiration_year": "2012"
}
})
if result.is_success:
print "success!: " + result.transaction.id
elif result.transaction:
print "Error processing transaction:"
print " message: " + result.message
print " code: " + result.transaction.processor_response_code
print " text: " + result.transaction.processor_response_text
else:
print "message: " + result.message
for error in result.errors.deep_errors:
print "attribute: " + error.attribute
print " code: " + error.code
print " message: " + error.message
I have python 2.7.3, and according to pip freeze:
requests==1.1.0
braintree==2.26.0
Running on Crunchbang Linux
:
$ lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description: Debian GNU/Linux 7.2 (wheezy)
Release: 7.2
Codename: wheezy
$ uname -a
Linux pegasus 3.2.0-4-686-pae #1 SMP Debian 3.2.51-1 i686 GNU/Linux
It would be great if we could get the dictionnary representing a braintree object.
We could extract data or send some objects directly to the clients like plans for example.
I managed to do this by adding this small function to your AttributeGetter
but it's just monkey patching :
def serialize(self):
data = {}
for key in self._setattrs:
if hasattr(getattr(self, key), "serialize"):
data[key] = getattr(self, key).serialize()
else:
data[key] = getattr(self, key)
return data
If I want to get the dictionnary from an object, I'll be able to do my_object.serialize()
After paying with a fake-android-pay-amex-nonce
, if I inspect the response and try to get the bin
or last_4
, they are None
, which causes masked_number
to also fail
ipdb> type(credit_card)
<class 'braintree.credit_card.CreditCard'>
ipdb> pi credit_card
credit_card._setattrs = [u'bin', u'expiration_month', u'unique_number_identifier', u'prepaid', u'expirat
credit_card.billing_address= None
credit_card.bin = None
credit_card.card_type = None
credit_card.cardholder_name= None
credit_card.commercial = u'Unknown'
credit_card.country_of_issuance= u'Unknown'
credit_card.customer_location= None
credit_card.debit = u'Unknown'
credit_card.durbin_regulated= u'Unknown'
credit_card.expiration_month= ''
credit_card.expiration_year= ''
credit_card.gateway = <braintree.braintree_gateway.BraintreeGateway object at 0x4670a10>
credit_card.healthcare = u'Unknown'
credit_card.image_url = u'https://assets.braintreegateway.com/payment_method_logo/unknown.png?environmen
credit_card.is_expired = <function expired at 0x3221578>
credit_card.issuing_bank = u'Unknown'
credit_card.last_4 = None
credit_card.payroll = u'Unknown'
credit_card.prepaid = u'Unknown'
credit_card.product_id = u'Unknown'
credit_card.token = u'74tj66'
credit_card.unique_number_identifier= None
credit_card.venmo_sdk = False
ipdb> credit_card.bin
ipdb> credit_card.last_4
ipdb> credit_card.masked_number
*** TypeError: unsupported operand type(s) for +: 'NoneType' and 'str'
The documentation lists all the exceptions Braintree can throw, but does not say where to find them in the braintree package. It is easy to find them once you go looking, but stating in the documentation where to find each exception would be even clearer.
Additionally, the exceptions are listed in the documentation in a human friendly way with spaces, while the actual exception classes are named slightly differently.
For a good example, look at the errors and exceptions section of the requests
documentation. It states each error it throws, with the actual class name of the error, states which package it exists in, and mentioned the base class each exception inherits from. All this information should be found in the documentation.
I have recently updated the requests library in my project to version 2.6.0. Specifically, I ran "pip install --upgrade requests[security]" and the following requirements were changed:
< XlsxWriter==0.6.2
---
> XlsxWriter==0.6.3
17d16
< cffi==0.9.2
20d18
< cryptography==0.8
33d30
< django-sql-stacktrace==0.2.2
40d36
< enum34==1.0.4
65d60
< ndg-httpsclient==0.3.3
71,73d65
< pyOpenSSL==0.14
< pyasn1==0.1.7
< pycparser==2.10
81c73
< requests==2.6.0
---
> requests==2.4.3
87c79
< six==1.9.0
---
> six==1.8.0
After making that update I see the following errors occurring when attempting to communicate with braintree:
File "/Users/andrew/code/EatWith/EatWith/apps/credit_cards/braintree/braintree_interaction_manager.py", line 231, in braintree_sale
result = braintree.Transaction.sale(braintree_data)
File "/Users/andrew/.virtualenvs/EatWith/lib/python2.7/site-packages/braintree/transaction.py", line 312, in sale
return Transaction.create(params)
File "/Users/andrew/.virtualenvs/EatWith/lib/python2.7/site-packages/braintree/transaction.py", line 407, in create
return Configuration.gateway().transaction.create(params)
File "/Users/andrew/.virtualenvs/EatWith/lib/python2.7/site-packages/braintree/transaction_gateway.py", line 33, in create
return self._post("/transactions", {"transaction": params})
File "/Users/andrew/.virtualenvs/EatWith/lib/python2.7/site-packages/braintree/transaction_gateway.py", line 137, in _post
response = self.config.http().post(url, params)
File "/Users/andrew/.virtualenvs/EatWith/lib/python2.7/site-packages/braintree/util/http.py", line 49, in post
return self.__http_do("POST", path, params)
File "/Users/andrew/.virtualenvs/EatWith/lib/python2.7/site-packages/braintree/util/http.py", line 71, in __http_do
raise e
TypeError: data must be a byte string
I have observed this behavior both on localhost (on my macbook) running in the development environment, as well as running on the heroku cedar-14 stack (ubuntu based) in the production environment.
It seems to me that the most recent updates to the requests library have introduce an incompatibility with the braintree lib. Does this sound correct?
Also, I see support for different http strategies in the configuration object, but I can't find documentation or examples anywhere showing how to use a different strategy. Would appreciate any pointers to references that may be available.
How you run the unit tests in this library isn't obvious. I figured out how to test the unit tests:
pip install nose
nosetests tests/unit
Less clear how to run the integration tests:
nosetests test/integration/
[snip]
FAILED (errors=464, failures=2)
Am I meant to be running a server? It's making lots of urllib3 connections:
ConnectionError: ('Connection aborted.', error(61, 'Connection refused'))
-------------------- >> begin captured logging << --------------------
requests.packages.urllib3.connectionpool: INFO: Starting new HTTP connection (1): localhost
Got an issue and possible pull request to send to you, but don't know how to run the tests :)
On Ubuntu Lucid, pip install braintree fails with the following error:
VersionConflict: (M2Crypto 0.20.1 (/usr/lib/pymodules/python2.6), Requirement.parse('M2Crypto==0.20.2'))
From what I see in the m2crypto changelog, there's no reason the braintree package shouldn't work with m2crypto 0.20.1. Can you folks update your version requirement so it will work with Ubuntu's standard m2crypto installation?
http://websvn.osafoundation.org/filedetails.php?repname=m2crypto&path=%2Ftrunk%2FCHANGES
Please provide an informative __repr__
method to all Braintree classes, so we could see something more useful than <braintree.credit_card.CreditCard object at 0x038ED550>
.
Perhaps something like <Nancy's AmEx-4413 credit card at 0x038ED550>
.
I've been getting intermittent errors in parsing webhooks from braintree:
return self.__node_content(root, child.nodeValue)
File "/opt/rh/python27/root/usr/lib/python2.7/site-packages/braintree/util/parser.py", line 87, in __node_content
return self.__convert_to_datetime(content)
File "/opt/rh/python27/root/usr/lib/python2.7/site-packages/braintree/util/parser.py", line 42, in __convert_to_datetime
return datetime.strptime(value, "%Y-%m-%dT%H:%M:%SZ")
AttributeError: _strptime
I think the cause is this:
https://bugs.python.org/issue7980
In my case I'm using braintree_python in Django and runserver. It sounds like this would occur in our prod environment using uwsgi too. Since the webhooks can be retried, this isn't a terrible problem, but it could be an issue in areas other than webhook.
Since the dates are consistent and well formatted from Braintree, perhaps just working around strptime will be easier.
This line misses Python2 unicode strings, eventually causing an AttributeError
exception.
Traceback (most recent call last):
File "/opt/wakatime/current/venv/local/lib/python2.7/site-packages/braintree/subscription.py", line 150, in update
return Configuration.gateway().subscription.update(subscription_id, params)
File "/opt/wakatime/current/venv/local/lib/python2.7/site-packages/braintree/subscription_gateway.py", line 59, in update
Resource.verify_keys(params, Subscription.update_signature())
File "/opt/wakatime/current/venv/local/lib/python2.7/site-packages/braintree/resource.py", line 9, in verify_keys
params_keys = Resource.__flattened_params_keys(params)
File "/opt/wakatime/current/venv/local/lib/python2.7/site-packages/braintree/resource.py", line 27, in __flattened_params_keys
keys += Resource.__flattened_params_keys(val, full_key)
File "/opt/wakatime/current/venv/local/lib/python2.7/site-packages/braintree/resource.py", line 30, in __flattened_params_keys
keys += Resource.__flattened_params_keys(item, full_key)
File "/opt/wakatime/current/venv/local/lib/python2.7/site-packages/braintree/resource.py", line 24, in __flattened_params_keys
for key, val in params.items():
AttributeError: 'unicode' object has no attribute 'items'
Should be basestring
instead of str
, or even better use six.string_types for Python 3 compatibility.
Failed with Python 3.4.2. It works fine with Python 2.7.9. Below are the detailed errors:
.............................................................................E...E............................................................................................................................
======================================================================
ERROR: test_create_signature (tests.unit.test_payment_method_gateway.TestPaymentMethodGateway)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/felix/projects/arch/archlinuxcn/python-braintree/src/braintree_python-3.11.0/tests/unit/test_payment_method_gateway.py", line 67, in test_create_signature
self.assertItemsEqual(expected_signature, actual_signature)
AttributeError: 'TestPaymentMethodGateway' object has no attribute 'assertItemsEqual'
======================================================================
ERROR: test_update_signature (tests.unit.test_payment_method_gateway.TestPaymentMethodGateway)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/felix/projects/arch/archlinuxcn/python-braintree/src/braintree_python-3.11.0/tests/unit/test_payment_method_gateway.py", line 99, in test_update_signature
self.assertItemsEqual(expected_signature, actual_signature)
AttributeError: 'TestPaymentMethodGateway' object has no attribute 'assertItemsEqual'
----------------------------------------------------------------------
Ran 206 tests in 0.084s
FAILED (errors=2)
The 2to3 utility is needed to convert braintree_python to be compatible with python 3. This would be better if it was done as part of the setup.py (that way python 3 users could use pip) I have added the code that is needed to do this. Setuptools is needed for this to work but thats a pretty standard module. After doing this the
python setup.py build
python setup.py install
works fine.
import distutils.core
import sys
# a hack to just get the version without calling the package init
# which is necessary as the import will fail on python > 3 due to the
# 2to3 needing to be run as part of the install
sys.path.append("braintree")
from version import Version
# Importing setuptools adds some features like "setup.py develop", but
# it's optional so swallow the error if it's not there.
try:
import setuptools
except ImportError:
pass
major, minor = sys.version_info[:2]
kwargs = {}
if major >= 3:
import setuptools # setuptools is required for use_2to3
kwargs["use_2to3"] = True
distutils.core.setup(
name="braintree",
version=Version,
description="Braintree Python Library",
author="Braintree",
author_email="[email protected]",
url="https://www.braintreepayments.com/docs/python",
packages=["braintree", "braintree.exceptions", "braintree.util", "braintree.test", "braintree.util.http_strategy"],
package_data={"braintree": ["ssl/*"]},
install_requires=["requests>=0.11.1,<2.0"],
zip_safe=False,
**kwargs
)
This issue is not python-specific, but since I don't see a general Braintree tracker, I'm writing it up here.
The bracket characters in Transparent Redirect field names make Braintree integration rather a pain. Aside from looking confusingly similar to first-class subscripting syntax in various programming languages, I have seen two specific problems so far:
<input id="">
and <label for="">
tags from field names.There are workarounds for these problems, of course, but they make for overly-complicated special-case code, wasted time, and frustrated programmers.
Would you folks consider supporting an alternative syntax for embedding structure in your field names? Separating sub-fields with double-underscores ought to work nicely. For example, instead of writing credit_card[options][make_default]
one might write credit_card__options__make_default
, which would be no longer than the former syntax and would also work as a variable/id name in every language that comes to mind.
https://github.com/braintree/braintree_python/blob/master/braintree/payment_instrument_type.py
should support apple_pay_card
as well
All the exceptions in the braintree package derive directly from Exception, which makes them indistinguishable from standard python exceptions unless I write special cases for each one of them (and hope new ones never show up). For example:
try:
braintree.Customer.create( dict( first_name="John", last_name="Doe"))
except (AuthenticationError,
AuthorizationError,
DownForMaintenanceError,
ForgedQueryStringError,
NotFoundError,
ServerError,
UnexpectedError,
UpgradeRequiredError):
print "braintree raised an exception"
All these exception classes really need a common ancestor that doesn't belong to the python standard library. braintree.Error, perhaps.
When call Customer.update
, customer id was assumed to be a string. It should be more fault tolerant to allow other types like integer
to be allowed.
Here is the code to reproduce:
In [87]: bt_customer
Out[87]:
{'credit_card': {'billing_address': {'country_code_alpha2': 'US',
'extended_address': '',
'locality': 'New York',
'postal_code': '10016',
'region': 'NY',
'street_address': '2 Park Ave'},
'cardholder_name': 'Ye Wang',
'expiration_date': '03/15',
'number': '4000111111111115',
'options': {'make_default': True, 'update_existing_token': '1717648'}},
'first_name': 'Ye',
'last_name': 'Wang'}
In [88]: result1 = braintree.Customer.update(2963, bt_customer)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-88-5096251d7619> in <module>()
----> 1 result1 = braintree.Customer.update(2963, bt_customer)
/Users/ye/.envs/dbi/lib/python2.7/site-packages/braintree/customer.pyc in update(customer_id, params)
165 """
166
--> 167 return Configuration.gateway().customer.update(customer_id, params)
168
169 @staticmethod
/Users/ye/.envs/dbi/lib/python2.7/site-packages/braintree/customer_gateway.pyc in update(self, customer_id, params)
64 def update(self, customer_id, params={}):
65 Resource.verify_keys(params, Customer.update_signature())
---> 66 response = self.config.http().put("/customers/" + customer_id, {"customer": params})
67 if "customer" in response:
68 return SuccessfulResult({"customer": Customer(self.gateway, response["customer"])})
TypeError: cannot concatenate 'str' and 'int' objects
A simple fix of using string formatting on line 66 of customer_gateway.py
would fix it.
response = self.config.http().put("/customers/%s" % customer_id, {"customer": params})
This code:
WebhookTesting.sample_notification(
WebhookNotification.Kind.SubscriptionWentPastDue,
'some_id'
)
throws this exception:
Traceback (most recent call last):
File "/vagrant/plug/payment/tests/acceptance/test_braintree_webhooks.py", line 21, in test
'some_id'
File "/usr/local/lib/python2.7/dist-packages/braintree/webhook_testing.py", line 7, in sample_notification
return Configuration.gateway().webhook_testing.sample_notification(kind, id)
File "/usr/local/lib/python2.7/dist-packages/braintree/configuration.py", line 41, in gateway
return braintree.braintree_gateway.BraintreeGateway(Configuration.instantiate())
File "/usr/local/lib/python2.7/dist-packages/braintree/configuration.py", line 46, in instantiate
environment=Configuration.environment,
AttributeError: type object 'Configuration' has no attribute 'environment'
I am using version 3.11.1.
The ErrorResult class has a top-level message
attribute which is a convenient alternative to groveling through the children of errors
when only the first/main error message is needed for display. Unfortunately, there is no matching code
attribute, so there's no good way to take programmatic action based on what kind of error it is.
As far as I can tell, there is also no documentation indicating which child of errors
was used for the top-level message
, so grabbing an error code from there would seem unreliable at best.
I'm currently seeing this problem with Customer.create(), but it probably applies to all braintree API calls which contact the server. These calls are failing, raising a spurious NoCertificate exception with this message: "peer did not return certificate"
This appears to be caused by a known bug in m2crypto:
https://bugzilla.osafoundation.org/show_bug.cgi?id=2341
http://bytes.com/topic/python/answers/586302-wierd-m2crypto-bug-phony-peer-did-not-return-certificate-error
Our application calls socket.setdefaulttimeout() to make it gracefully handle cases when third-party libraries contact unresponsive servers. This is still pretty common in the python world, since per-connection timeout support was only recently added to urllib2 and many libraries have not yet started using that feature. That means we can't reasonably stop using socket.setdefaulttimeout(). Unfortunately, it causes braintree calls to fail.
So, as long as the braintree library depends on m2crypto for its https support, it seems we are stuck with either an incompatibility or (if we were to disable socket timeouts to work around the bug) a reliability problem.
Perhaps it would make sense for the braintree python library to use pycurl instead of m2crypto, or better yet, expose a pluggable http/https fetcher API? (The latter would be especially nice when we migrate to App Engine. Of course, such a plugin API should support caller-specified timeouts. :)
The docs mention than search results and such are always in UTC, but the datetime.datetime objects being created with this library are not timezone aware. I can understand not wanting to add pytz just to accomplish this, so you could define your own UTC object (as shown in the datetime docs) that could be used in this case.
In [12]: plans[0]
Out[12]: <Plan {billing_day_of_month: None, trial_duration: None, description: u'A brick', billing_frequency: 1, trial_period: False, price: u'10.00', currency_iso_code: u'USD', updated_at: datetime.datetime(2015, 4, 3, 15, 10, 30), trial_duration_unit: u'day', number_of_billing_cycles: None, discounts: [], add_ons: [], created_at: datetime.datetime(2015, 4, 3, 15, 10, 30), merchant_id: u'tbb7hb44zx28jhsh', id: u'concrete-brick', name: u'Concrete'} at 62648720>
In [13]: plans[0].price
Out[13]: u'10.00'
Would be easier to use if it was parsed as a decimal.
While testing the python api against the sandbox server, I am inducing a subscription failure using a $2,099.00 plan. When Subscription.create() fails, result.message is set but result.errors.size is 0. This breaks code that looks in result.errors.deep_errors[0] for the error code of the failure (see issue #6) apparently giving me no programmatic way to determine the error code.
It looks like there are some errors that return an error code, but the result of the parsing in ErrorResult is that the actual response code is thrown away.
Here's an example, given the gist at https://gist.github.com/andymckay/a9340d1b0a7ce483025e:
In [6]: from braintree.error_result import ErrorResult
In [7]: err = ErrorResult(None, data)
In [8]: data['message']
Out[8]: u'Invalid Secure Payment Data'
In [9]: data['transaction']['processor_response_code']
Out[9]: u'2078'
You can access that in the response, but once it gets parsed into a Transaction, its lost, because Transaction doesn't parse that at all:
In [10]: err.params
Out[10]:
{u'descriptor': {u'name': u'Mozilla*product', u'url': u'mozilla.org'},
u'payment_method_token': u'7fk5cg',
u'plan_id': u'mozilla-concrete-brick',
u'trial_period': u'false'}
In [12]: err.transaction
Out[12]: <Transaction {amount: Decimal('2078.00'), credit_card: {u'bin': u'411111', u'card_type': u'Visa', u'unique_number_identifier': u'21ee4272998b10107aeee9d50d6fe1ae', u'expiration_year': u'2016', u'prepaid': u'Unknown', u'durbin_regulated': u'Unknown', u'commercial': u'Unknown', u'healthcare': u'Unknown', u'payroll': u'Unknown', u'issuing_bank': u'Unknown', u'last_4': u'1111', u'expiration_month': u'12', u'cardholder_name': u'a', u'token': u'7fk5cg', u'customer_location': u'US', u'image_url': u'https://assets.braintreegateway.com/payment_method_logo/visa.png?environment=sandbox', u'country_of_issuance': u'Unknown', u'debit': u'Unknown', u'venmo_sdk': False, u'product_id': u'Unknown'}} at 4426039504>
Note that if you pass the Transaction information to the Verification object, you can get something useful:
In [15]: cc = CreditCardVerification(None, data['transaction'])
In [16]: cc.processor_response_code
Out[16]: u'2078'
We'd like to be able to return that unique to braintree response code up the stack, log it etc. But we can't unless we monkey patch ErrorResult. Unless I'm missing something on how to process the response for this kind of error.
AI responses are gzip-encoded, but the python library (probably the requests library) treats them as ASCII.
File "xtras/braintree/customer.py", line 126, in find
return Configuration.gateway().customer.find(customer_id)
File "xtras/braintree/customer_gateway.py", line 36, in find
response = self.config.http().get("/customers/" + customer_id)
File "xtras/braintree/util/http.py", line 57, in get
return self.__http_do("GET", path)
File "xtras/braintree/util/http.py", line 82, in __http_do
return XmlUtil.dict_from_xml(response_body)
File "xtras/braintree/util/xml_util.py", line 11, in dict_from_xml
return Parser(xml).parse()
File "xtras/braintree/util/parser.py", line 15, in __init__
self.doc = minidom.parseString("><".join(re.split(">\s+<", xml)).strip())
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/dom/minidom.py", line 1928, in parseString
return expatbuilder.parseString(string)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/dom/expatbuilder.py", line 940, in parseString
return builder.parseString(string)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/dom/expatbuilder.py", line 223, in parseString
parser.Parse(string, True)
UnicodeEncodeError: 'ascii' codec can't encode character u'\ufffd' in position 0: ordinal not in range(128)
Modifying http_do() in util/http.py to unzip the content, makes the library work again, but this is probably a fragile way to fix this. (unzip() is just a wrapper to unzip raw data, nothing special.)
def http_do(self, http_verb, path, headers, request_body):
response = self.__request_function(http_verb)(
self.environment.base_url + path,
headers=headers,
data=request_body,
verify=self.environment.ssl_certificate,
timeout=self.config.timeout
)
if response.headers['content-encoding']=='gzip':
return [response.status_code, unzip(response.content)]
else:
return [response.status_code, response.text]
There are a couple of places where the docs mention a method that is actually a property. Examples:
http://www.braintreepaymentsolutions.com/docs/python/general/result_objects#success_results
"If the API call was successful, the success
method on the result object will return true."
http://www.braintreepaymentsolutions.com/docs/python/general/validation_errors#errors_at_specific_levels
"From a specific level, you can also get the number of errors at that level using the size
method."
Since methods and properties have different calling semantics, this might lead a developer to write invalid code. For example, testing size()
instead of size
.
According to this: https://developers.braintreepayments.com/javascript+python/reference/request/payment-method/create
It looks like this method should allow these additional parameters:
However, trying to use any combination of those results in an error such as:
vi +9 /usr/local/lib/python2.7/dist-packages/braintree/payment_method.py # create
return Configuration.gateway().payment_method.create(params)
vi +22 /usr/local/lib/python2.7/dist-packages/braintree/payment_method_gateway.py # create
Resource.verify_keys(params, PaymentMethod.create_signature())
vi +17 /usr/local/lib/python2.7/dist-packages/braintree/resource.py # verify_keys
raise KeyError("Invalid keys: " + keys_string)
KeyError: 'Invalid keys: expiration_month, cardholder_name, expiration_year, cvv, number'
http://www.braintreepaymentsolutions.com/docs/python/general/result_objects#success_results
The docs say, "the success method on the result object will return true." That should be is_success
. As far as I have seen, there is no success
attribute in a result object.
braintree1/lib/python2.7/site-packages/requests/packages/urllib3/util/ssl_.py:315: SNIMissingWarning: An HTTPS request has been made, but the SNI (Subject Name Indication) extension to TLS is not available on this platform. This may cause the server to present an incorrect TLS certificate, which can cause validation failures. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#snimissingwarning.
SNIMissingWarning
braintree1/lib/python2.7/site-packages/requests/packages/urllib3/util/ssl_.py:120: InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning.
InsecurePlatformWarning
The example code in the configuration.py doc string currently refers to a constant called braintree.Environment.SANDBOX
, which does not exist. It should read braintree.Environment.Sandbox
.
Customers, Transactions, and probably other models have a find(id)
method that allows you to get a single instance back from Braintree. Plans do not have this. If I want to retrieve a single plan from Braintree, I have to get all plans and find the one with a matching id.
Could a Plan.find(plan_id)
method be created, similar to the methods for Customers and Transactions?
Hi,
Hope you can find some time to check on my problem.
Currently, i'm creating a maintenance application and display the payment method used by clients. Clients may have multiple payment method.
My concern here is i am not able to update the default payment method for the selected customer using this code.
result = braintree.CreditCard.update(token,{
"options": {
"make_default": True,
},
})
After checking the command prompt for any logs. I've found this
Below is the complete code of the method i've used.
Hope you can assist me.
Thanks!
Traceback (most recent call last):
File "/home/ubuntu/.virtualenvs/app/lib/python2.6/site-packages/django/core/handlers/base.py", line 117, in get_response
response = middleware_method(request, e)
File "/home/ubuntu/.virtualenvs/app/lib/python2.6/site-packages/django/core/handlers/base.py", line 111, in get_response
response = callback(request, *callback_args, **callback_kwargs)
File "/home/ubuntu/webapps/app/common/decorators.py", line 24, in new_func
return view_func(request, *args, **kwargs)
File "/home/ubuntu/.virtualenvs/app/lib/python2.6/site-packages/django/contrib/auth/decorators.py", line 23, in _wrapped_view
return view_func(request, *args, **kwargs)
File "/home/ubuntu/webapps/app/app/profiles/views/expert.py", line 651, in checkout
form_initial_data_dict = payments_helper.get_billing_info_dict(request.user)
File "/home/ubuntu/webapps/app/common/payments/utils.py", line 199, in get_billing_info_dict
customer = braintree.Customer.find(customer_id)
File "/home/ubuntu/.virtualenvs/app/lib/python2.6/site-packages/braintree/customer.py", line 111, in find
return Configuration.gateway().customer.find(customer_id)
File "/home/ubuntu/.virtualenvs/app/lib/python2.6/site-packages/braintree/customer_gateway.py", line 34, in find
response = self.config.http().get("/customers/" + customer_id)
File "/home/ubuntu/.virtualenvs/app/lib/python2.6/site-packages/braintree/util/http.py", line 47, in get
return self.__http_do("GET", path)
File "/home/ubuntu/.virtualenvs/app/lib/python2.6/site-packages/braintree/util/http.py", line 54, in __http_do
self.__verify_ssl()
File "/home/ubuntu/.virtualenvs/app/lib/python2.6/site-packages/braintree/util/http.py", line 116, in __verify_ssl
curl.perform()
error: (60, 'server certificate verification failed. CAfile: /home/ubuntu/.virtualenvs/app/lib/python2.6/site-packages/braintree/ssl/www_braintreegateway_com.ca.crt CRLfile: none')
[deleted]
I have python 3.3 running on fedora 19 64-bit with virtualenv and requests installed. When running
pip install braintree
I am given the following traceback error
Downloading/unpacking braintree
Downloading braintree-2.23.1.tar.gz
Running setup.py egg_info for package braintree
Traceback (most recent call last):
File "<string>", line 16, in <module>
File "/home/.../venv/build/braintree/setup.py", line 1, in <module>
import braintree
File "./braintree/__init__.py", line 1, in <module>
from braintree.add_on import AddOn
File "./braintree/add_on.py", line 1, in <module>
from braintree.configuration import Configuration
File "./braintree/configuration.py", line 4, in <module>
import braintree.util.http_strategy.pycurl_strategy
File "./braintree/util/http_strategy/pycurl_strategy.py", line 1, in <module>
import httplib
ImportError: No module named 'httplib'
Complete output from command python setup.py egg_info:
Traceback (most recent call last):
File "<string>", line 16, in <module>
File "/home/.../venv/build/braintree/setup.py", line 1, in <module>
import braintree
File "./braintree/__init__.py", line 1, in <module>
from braintree.add_on import AddOn
File "./braintree/add_on.py", line 1, in <module>
from braintree.configuration import Configuration
File "./braintree/configuration.py", line 4, in <module>
import braintree.util.http_strategy.pycurl_strategy
File "./braintree/util/http_strategy/pycurl_strategy.py", line 1, in <module>
import httplib
ImportError: No module named 'httplib'
----------------------------------------
Cleaning up...
Command python setup.py egg_info failed with error code 1 in
/home/.../venv/build/braintree
Storing complete log in ~/.pip/pip.log
When you set up webhook on braintree it sends a request to the server to check it can receive that request. The docs recommend getting that request, verifying and returning the response, for example:
https://developers.braintreepayments.com/javascript+python/guides/webhooks/create#bt-challenge
What they don't say is that the request from braintree will send Accept headers in the request that look like this:
[u'/; q=0.5', u'application/xml']
But the Braintree end of things does not accept any response or "application/xml". It only accepts one things and that is the response echoed back, no XML or JSON encoding applied. It does not accept /.
The request for the verify end point should send an Accept header of: text/plain.
The braintree package currently stores account-specific configuration settings in globally-shared class attributes. This prevents it from being used by applications that (like mine) handle traffic for multiple store fronts in the same process space.
The timestamp
s on WebhookNotification
instances are 'naive' datetime instances (see the datetime
docs). This means that they do not have any associated timezone - they are just a time. Python best practices state that datetime
/time
objects should always have a timezone. Given that your API states in other locations that times are returned as UTC, attaching the UTC timezone to datetime
s returned by the API would make everything consistent with itself and Python best practices.
I'd like to add a query parameter to my application's after-transparent-redirect landing url, indicating what page the user should be taken to next. This would allow a single endpoint to check transparent redirect results and send the user on his way, no matter what application flow is in progress when the transparent redirect takes place. (In other words, code reuse.) I'd also like to be able to use arbitrary query parameters for other reasons.
Unfortunately, TransparentRedirect.confirm() chokes when passed a query string without its expected query parameters, and the braintree api doesn't seem to offer away to distinguish its parameters from others that might be present. This effectively leaves two ways to avoid the non-braintree exception:
The first approach is cumbersome, especially when multiple non-braintree query parameters might be in use. The second approach is cleaner, but very fragile. This would be much cleaner if the braintree api provided a way to detect whether transparent redirect results are present in a query string, or if TransparentRedirect.confirm() reported missing query parameters efficiently and gracefully.
(Related note: the OpenID protocol also uses redirects, and prefixes its query parameters with 'openid.' to help with issues like this.)
See #23
When trying to parse the webhooks signature and payload, I am getting:
Traceback (most recent call last):
File "/src/djangoapp/apps/braintree/views.py", line 61, in webhook
request.POST['bt_signature'], request.POST['bt_payload']
File "/.virtualenvs/djangoapp/local/lib/python2.7/site-packages/braintree/webhook_notification.py", line 17, in parse
return Configuration.gateway().webhook_notification.parse(signature, payload)
File "/.virtualenvs/djangoapp/local/lib/python2.7/site-packages/braintree/webhook_notification_gateway.py", line 13, in parse
self.__validate_signature(signature, payload)
File "/.virtualenvs/djangoapp/local/lib/python2.7/site-packages/braintree/webhook_notification_gateway.py", line 32, in __validate_signature
if not Crypto.secure_compare(payload_signature, matching_signature):
File "/.virtualenvs/djangoapp/local/lib/python2.7/site-packages/braintree/util/crypto.py", line 15, in secure_compare
right_bytes = bytearray(right)
TypeError: unicode argument without an encoding
I am using Django 1.4, and have this code in my Django view which is throwing the exception:
webhook_notification = braintree.WebhookNotification.parse(
request.POST['bt_signature'], request.POST['bt_payload']
)
This was taken from your Python docs here: https://www.braintreepayments.com/docs/python/webhooks/parsing
I have 'braintree' listed as an install_requires in my setup.py file, using setuptools on Python 2.6. Running python setup.py develop
installs braintree as a zipped .egg, which causes this error:
error: (77, 'error setting certificate verify locations:\n CAfile: /path/to/virtualenv/lib/python2.6/site-packages/braintree-2.10.0-py2.6.egg/braintree/ssl/sandbox_braintreegateway_com.ca.crt\n CApath: /etc/ssl/certs\n')
Being a zipped .egg, Python cannot write to the data files within the braintree distribution. Setting zip_safe=False
in braintree_python/setup.py should correct this issue.
-Ron
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.