PageRenderTime 52ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/stripe/api_requestor.py

https://github.com/theill/incognito-launch-page
Python | 346 lines | 329 code | 17 blank | 0 comment | 23 complexity | 336f6fcd35942047d2f6d4ae7a697f77 MD5 | raw file
  1. import calendar
  2. import datetime
  3. import platform
  4. import time
  5. import os
  6. import ssl
  7. import socket
  8. import urllib
  9. import urlparse
  10. import warnings
  11. import stripe
  12. from stripe import error, http_client, version, util, certificate_blacklist
  13. def _encode_datetime(dttime):
  14. if dttime.tzinfo and dttime.tzinfo.utcoffset(dttime) is not None:
  15. utc_timestamp = calendar.timegm(dttime.utctimetuple())
  16. else:
  17. utc_timestamp = time.mktime(dttime.timetuple())
  18. return int(utc_timestamp)
  19. def _api_encode(data):
  20. for key, value in data.iteritems():
  21. key = util.utf8(key)
  22. if value is None:
  23. continue
  24. elif hasattr(value, 'stripe_id'):
  25. yield (key, value.stripe_id)
  26. elif isinstance(value, list) or isinstance(value, tuple):
  27. for subvalue in value:
  28. yield ("%s[]" % (key,), util.utf8(subvalue))
  29. elif isinstance(value, dict):
  30. subdict = dict(('%s[%s]' % (key, subkey), subvalue) for
  31. subkey, subvalue in value.iteritems())
  32. for subkey, subvalue in _api_encode(subdict):
  33. yield (subkey, subvalue)
  34. elif isinstance(value, datetime.datetime):
  35. yield (key, _encode_datetime(value))
  36. else:
  37. yield (key, util.utf8(value))
  38. def _build_api_url(url, query):
  39. scheme, netloc, path, base_query, fragment = urlparse.urlsplit(url)
  40. if base_query:
  41. query = '%s&%s' % (base_query, query)
  42. return urlparse.urlunsplit((scheme, netloc, path, query, fragment))
  43. class APIRequestor(object):
  44. _CERTIFICATE_VERIFIED = False
  45. def __init__(self, key=None, client=None):
  46. self.api_key = key
  47. from stripe import verify_ssl_certs
  48. self._client = client or http_client.new_default_http_client(
  49. verify_ssl_certs=verify_ssl_certs)
  50. @classmethod
  51. def api_url(cls, url=''):
  52. warnings.warn(
  53. 'The `api_url` class method of APIRequestor is '
  54. 'deprecated and will be removed in version 2.0.'
  55. 'If you need public access to this function, please email us '
  56. 'at support@stripe.com.',
  57. DeprecationWarning)
  58. return '%s%s' % (stripe.api_base, url)
  59. @classmethod
  60. def _deprecated_encode(cls, stk, key, value):
  61. warnings.warn(
  62. 'The encode_* class methods of APIRequestor are deprecated and '
  63. 'will be removed in version 2.0. '
  64. 'If you need public access to this function, please email us '
  65. 'at support@stripe.com.',
  66. DeprecationWarning, stacklevel=2)
  67. stk.extend(_api_encode({key: value}))
  68. @classmethod
  69. def encode_dict(cls, stk, key, value):
  70. cls._deprecated_encode(stk, key, value)
  71. @classmethod
  72. def encode_list(cls, stk, key, value):
  73. cls._deprecated_encode(stk, key, value)
  74. @classmethod
  75. def encode_datetime(cls, stk, key, value):
  76. cls._deprecated_encode(stk, key, value)
  77. @classmethod
  78. def encode_none(cls, stk, key, value):
  79. cls._deprecated_encode(stk, key, value)
  80. @classmethod
  81. def encode(cls, d):
  82. """
  83. Internal: encode a string for url representation
  84. """
  85. warnings.warn(
  86. 'The `encode` class method of APIRequestor is deprecated and '
  87. 'will be removed in version 2.0.'
  88. 'If you need public access to this function, please email us '
  89. 'at support@stripe.com.',
  90. DeprecationWarning)
  91. return urllib.urlencode(list(_api_encode(d)))
  92. @classmethod
  93. def build_url(cls, url, params):
  94. warnings.warn(
  95. 'The `build_url` class method of APIRequestor is deprecated and '
  96. 'will be removed in version 2.0.'
  97. 'If you need public access to this function, please email us '
  98. 'at support@stripe.com.',
  99. DeprecationWarning)
  100. return _build_api_url(url, cls.encode(params))
  101. def request(self, method, url, params=None):
  102. self._check_ssl_cert()
  103. rbody, rcode, my_api_key = self.request_raw(
  104. method.lower(), url, params)
  105. resp = self.interpret_response(rbody, rcode)
  106. return resp, my_api_key
  107. def handle_api_error(self, rbody, rcode, resp):
  108. try:
  109. err = resp['error']
  110. except (KeyError, TypeError):
  111. raise error.APIError(
  112. "Invalid response object from API: %r (HTTP response code "
  113. "was %d)" % (rbody, rcode),
  114. rbody, rcode, resp)
  115. if rcode in [400, 404]:
  116. raise error.InvalidRequestError(
  117. err.get('message'), err.get('param'), rbody, rcode, resp)
  118. elif rcode == 401:
  119. raise error.AuthenticationError(
  120. err.get('message'), rbody, rcode, resp)
  121. elif rcode == 402:
  122. raise error.CardError(err.get('message'), err.get('param'),
  123. err.get('code'), rbody, rcode, resp)
  124. else:
  125. raise error.APIError(err.get('message'), rbody, rcode, resp)
  126. def request_raw(self, method, url, params=None):
  127. """
  128. Mechanism for issuing an API call
  129. """
  130. from stripe import api_version
  131. if self.api_key:
  132. my_api_key = self.api_key
  133. else:
  134. from stripe import api_key
  135. my_api_key = api_key
  136. if my_api_key is None:
  137. raise error.AuthenticationError(
  138. 'No API key provided. (HINT: set your API key using '
  139. '"stripe.api_key = <API-KEY>"). You can generate API keys '
  140. 'from the Stripe web interface. See https://stripe.com/api '
  141. 'for details, or email support@stripe.com if you have any '
  142. 'questions.')
  143. abs_url = '%s%s' % (stripe.api_base, url)
  144. encoded_params = urllib.urlencode(list(_api_encode(params or {})))
  145. if method == 'get' or method == 'delete':
  146. if params:
  147. abs_url = _build_api_url(abs_url, encoded_params)
  148. post_data = None
  149. elif method == 'post':
  150. post_data = encoded_params
  151. else:
  152. raise error.APIConnectionError(
  153. 'Unrecognized HTTP method %r. This may indicate a bug in the '
  154. 'Stripe bindings. Please contact support@stripe.com for '
  155. 'assistance.' % (method,))
  156. ua = {
  157. 'bindings_version': version.VERSION,
  158. 'lang': 'python',
  159. 'publisher': 'stripe',
  160. 'httplib': self._client.name,
  161. }
  162. for attr, func in [['lang_version', platform.python_version],
  163. ['platform', platform.platform],
  164. ['uname', lambda: ' '.join(platform.uname())]]:
  165. try:
  166. val = func()
  167. except Exception, e:
  168. val = "!! %s" % (e,)
  169. ua[attr] = val
  170. headers = {
  171. 'X-Stripe-Client-User-Agent': util.json.dumps(ua),
  172. 'User-Agent': 'Stripe/v1 PythonBindings/%s' % (version.VERSION,),
  173. 'Authorization': 'Bearer %s' % (my_api_key,)
  174. }
  175. if api_version is not None:
  176. headers['Stripe-Version'] = api_version
  177. rbody, rcode = self._client.request(
  178. method, abs_url, headers, post_data)
  179. util.logger.info(
  180. 'API request to %s returned (response code, response body) of '
  181. '(%d, %r)',
  182. abs_url, rcode, rbody)
  183. return rbody, rcode, my_api_key
  184. def interpret_response(self, rbody, rcode):
  185. try:
  186. if hasattr(rbody, 'decode'):
  187. rbody = rbody.decode('utf-8')
  188. resp = util.json.loads(rbody)
  189. except Exception:
  190. raise error.APIError(
  191. "Invalid response body from API: %s "
  192. "(HTTP response code was %d)" % (rbody, rcode),
  193. rbody, rcode)
  194. if not (200 <= rcode < 300):
  195. self.handle_api_error(rbody, rcode, resp)
  196. return resp
  197. def _check_ssl_cert(self):
  198. """Preflight the SSL certificate presented by the backend.
  199. This isn't 100% bulletproof, in that we're not actually validating the
  200. transport used to communicate with Stripe, merely that the first
  201. attempt to does not use a revoked certificate.
  202. Unfortunately the interface to OpenSSL doesn't make it easy to check
  203. the certificate before sending potentially sensitive data on the wire.
  204. This approach raises the bar for an attacker significantly."""
  205. from stripe import verify_ssl_certs
  206. if verify_ssl_certs and not self._CERTIFICATE_VERIFIED:
  207. uri = urlparse.urlparse(stripe.api_base)
  208. try:
  209. certificate = ssl.get_server_certificate(
  210. (uri.hostname, uri.port or 443))
  211. der_cert = ssl.PEM_cert_to_DER_cert(certificate)
  212. except socket.error, e:
  213. raise error.APIConnectionError(e)
  214. except TypeError:
  215. # The Google App Engine development server blocks the C socket
  216. # module which causes a type error when using the SSL library
  217. if ('APPENGINE_RUNTIME' in os.environ and
  218. 'Dev' in os.environ.get('SERVER_SOFTWARE', '')):
  219. self._CERTIFICATE_VERIFIED = True
  220. warnings.warn(
  221. 'We were unable to verify Stripe\'s SSL certificate '
  222. 'due to a bug in the Google App Engine development '
  223. 'server. Please alert us immediately at '
  224. 'support@stripe.com if this message appears in your '
  225. 'production logs.')
  226. return
  227. else:
  228. raise
  229. self._CERTIFICATE_VERIFIED = certificate_blacklist.verify(
  230. uri.hostname, der_cert)
  231. # Deprecated request handling. Will all be removed in 2.0
  232. def _deprecated_request(self, impl, method, url, headers, params):
  233. warnings.warn(
  234. 'The *_request functions of APIRequestor are deprecated and '
  235. 'will be removed in version 2.0. Please use the client classes '
  236. ' in `stripe.http_client` instead',
  237. DeprecationWarning, stacklevel=2)
  238. method = method.lower()
  239. if method == 'get' or method == 'delete':
  240. if params:
  241. url = self.build_url(url, params)
  242. post_data = None
  243. elif method == 'post':
  244. post_data = self.encode(params)
  245. else:
  246. raise error.APIConnectionError(
  247. 'Unrecognized HTTP method %r. This may indicate a bug in the '
  248. 'Stripe bindings. Please contact support@stripe.com for '
  249. 'assistance.' % (method,))
  250. client = impl(verify_ssl_certs=self._client._verify_ssl_certs)
  251. return client.request(method, url, headers, post_data)
  252. def _deprecated_handle_error(self, impl, *args):
  253. warnings.warn(
  254. 'The handle_*_error functions of APIRequestor are deprecated and '
  255. 'will be removed in version 2.0. Please use the client classes '
  256. ' in `stripe.http_client` instead',
  257. DeprecationWarning, stacklevel=2)
  258. client = impl(verify_ssl_certs=self._client._verify_ssl_certs)
  259. return client._handle_request_error(*args)
  260. def requests_request(self, meth, abs_url, headers, params):
  261. from stripe.http_client import RequestsClient
  262. return self._deprecated_request(RequestsClient, meth, abs_url,
  263. headers, params)
  264. def handle_requests_error(self, err):
  265. from stripe.http_client import RequestsClient
  266. return self._deprecated_handle_error(RequestsClient, err)
  267. def pycurl_request(self, meth, abs_url, headers, params):
  268. from stripe.http_client import PycurlClient
  269. return self._deprecated_request(PycurlClient, meth, abs_url,
  270. headers, params)
  271. def handle_pycurl_error(self, err):
  272. from stripe.http_client import PycurlClient
  273. return self._deprecated_handle_error(PycurlClient, err)
  274. def urlfetch_request(self, meth, abs_url, headers, params):
  275. from stripe.http_client import UrlFetchClient
  276. return self._deprecated_request(UrlFetchClient, meth, abs_url,
  277. headers, params)
  278. def handle_urlfetch_error(self, err, abs_url):
  279. from stripe.http_client import UrlFetchClient
  280. return self._deprecated_handle_error(UrlFetchClient, err, abs_url)
  281. def urllib2_request(self, meth, abs_url, headers, params):
  282. from stripe.http_client import Urllib2Client
  283. return self._deprecated_request(Urllib2Client, meth, abs_url,
  284. headers, params)
  285. def handle_urllib2_error(self, err, abs_url):
  286. from stripe.http_client import Urllib2Client
  287. return self._deprecated_handle_error(Urllib2Client, err)