AppEngine Paypal integration giving SSLCertificateError on localhost, using Python

464 views Asked by At

i am integrating paypalrestsdk in my AppEngine project. When, using my local dev_appserver, i try to create a payment on the PayPal sandbox, i have the following error:

SSLCertificateError: Invalid and/or missing SSL certificate for URL: https://api.sandbox.paypal.com/v1/oauth2/token

So, i have tried to provide the correct pem file, downloading it from here and setting up the correct ssl_option attribute:

# Setting up the correct path to the .pem file
cert = os.path.join(ROOT, 'certs/api.sandbox.paypal.com_SHA-2_01132018.pem')
logger.info("Using SSL certificate: %s", cert)
return Api(
    mode=get_paypal_environment(), # sandbox or live
    client_id=flask.current_app.config["PAYPAL_CLIENT_ID"],
    client_secret=flask.current_app.config["PAYPAL_CLIENT_SECRET"],
    ssl_options={"cert": cert}
)

Here there is the PayPalRestSDK documentation that gives details on how to provide certificate. I have double checked the path created is correct.

I have have found a bug report here that talks about a similar problem.

Also, i have tried the solution suggested here and still does not work.

On a live instance, on appspot, this all works perfectly.

Here's the relevant part of my requirements.txt:

Flask==0.10.1
itsdangerous==0.24
paramiko==1.15.1
pycrypto==2.6.1
Flask-OAuthlib==0.9.1
google-api-python-client==1.4.0
paypalrestsdk==1.11.1
requests[security]==2.9.1

Is anyone having the same issue ?

1

There are 1 answers

0
Codiak On

OK, I believed I've solved this one, at least in my case, which I'll describe below.

This seemed to be due to two issues:

Issue #1) PayPal is migrating to supporting only TLS 1.2, and has started by switching over the sandbox URLs, with the production URLs to come later. This explains why things are broken only when connecting from the sandbox, and why it used to work but doesn't now. More on this here.

Issue #2) My local install of Python didn't support TLS 1.2. Here is an easy way to check:

$ python
>>> import ssl
>>> print ssl._PROTOCOL_NAMES

If you don't see PROTOCOL_TLSv1_2 listed, this is definitely the issue. In my case, I was using the builtin version on Python on Mac OS X 10.11, which had a pretty old version on OpenSSL built in.

So how to fix it? Well, in my case, this worked pretty well (copied mostly from here):

$ brew update
$ brew install openssl
$ brew link openssl --force 
$ brew install python --with-brewed-openssl
$ sudo ln -s /usr/local/Cellar/python/2.7.11/bin/python /usr/local/bin/python

Now if you run the test I listed above, you should see the 1.2 protocol listed.

This should make everything work again, good luck!