def request(self, method, url, headers, post_data=None): if six.PY3 and isinstance(post_data, six.string_types): post_data = post_data.encode("utf-8") req = urllib.request.Request(url, post_data, headers) if method not in ("get", "post"): req.get_method = lambda: method.upper() try: # use the custom proxy tied opener, if any. # otherwise, fall to the default urllib opener. response = ( self._opener.open(req) if self._opener else urllib.request.urlopen(req) ) rbody = response.read() rcode = response.code headers = dict(response.info()) except urllib.error.HTTPError as e: rcode = e.code rbody = e.read() headers = dict(e.info()) except (urllib.error.URLError, ValueError) as e: self._handle_request_error(e) lh = dict((k.lower(), v) for k, v in six.iteritems(dict(headers))) return rbody, rcode, lh
def add_params(self, params): for key, value in six.iteritems(params): if value is None: continue self._write(self.param_header()) self._write(self.line_break) if hasattr(value, "read"): filename = "blob" if hasattr(value, "name"): # Convert the filename to string, just in case it's not # already one. E.g. `tempfile.TemporaryFile` has a `name` # attribute but it's an `int`. filename = str(value.name) self._write('Content-Disposition: form-data; name="') self._write(key) self._write('"; filename="') self._write(filename) self._write('"') self._write(self.line_break) self._write("Content-Type: application/octet-stream") self._write(self.line_break) self._write(self.line_break) self._write_file(value) else: self._write('Content-Disposition: form-data; name="') self._write(key) self._write('"') self._write(self.line_break) self._write(self.line_break) self._write(value) self._write(self.line_break)
def refresh_from(self, values, api_key=None, partial=False, stripe_version=None, stripe_account=None, last_response=None): self.api_key = api_key or getattr(values, 'api_key', None) self.stripe_version = \ stripe_version or getattr(values, 'stripe_version', None) self.stripe_account = \ stripe_account or getattr(values, 'stripe_account', None) self._last_response = \ last_response or getattr(values, '_last_response', None) # Wipe old state before setting new. This is useful for e.g. # updating a customer, where there is no persistent card # parameter. Mark those values which don't persist as transient if partial: self._unsaved_values = (self._unsaved_values - set(values)) else: removed = set(self.keys()) - set(values) self._transient_values = self._transient_values | removed self._unsaved_values = set() self.clear() self._transient_values = self._transient_values - set(values) for k, v in six.iteritems(values): super(StripeObject, self).__setitem__( k, util.convert_to_stripe_object(v, api_key, stripe_version, stripe_account)) self._previous = values
def request(self, method, url, headers, post_data=None): if six.PY3 and isinstance(post_data, six.string_types): post_data = post_data.encode("utf-8") req = urllib.request.Request(url, post_data, headers) if method not in ("get", "post"): req.get_method = lambda: method.upper() try: # use the custom proxy tied opener, if any. # otherwise, fall to the default urllib opener. response = (self._opener.open(req) if self._opener else urllib.request.urlopen(req)) rbody = response.read() rcode = response.code headers = dict(response.info()) except urllib.error.HTTPError as e: rcode = e.code rbody = e.read() headers = dict(e.info()) except (urllib.error.URLError, ValueError) as e: self._handle_request_error(e) lh = dict((k.lower(), v) for k, v in six.iteritems(dict(headers))) return rbody, rcode, lh
def refresh_from(self, values, api_key=None, partial=False, stripe_version=None, stripe_account=None, last_response=None): self.api_key = api_key or getattr(values, 'api_key', None) self.stripe_version = \ stripe_version or getattr(values, 'stripe_version', None) self.stripe_account = \ stripe_account or getattr(values, 'stripe_account', None) self._last_response = last_response # Wipe old state before setting new. This is useful for e.g. # updating a customer, where there is no persistent card # parameter. Mark those values which don't persist as transient if partial: self._unsaved_values = (self._unsaved_values - set(values)) else: removed = set(self.keys()) - set(values) self._transient_values = self._transient_values | removed self._unsaved_values = set() self.clear() self._transient_values = self._transient_values - set(values) for k, v in six.iteritems(values): super(StripeObject, self).__setitem__( k, util.convert_to_stripe_object(v, api_key, stripe_version, stripe_account)) self._previous = values
def add_params(self, params): for key, value in six.iteritems(params): if value is None: continue self._write(self.param_header()) self._write(self.line_break) if hasattr(value, 'read'): filename = value.name if hasattr(value, 'name') else "blob" self._write("Content-Disposition: form-data; name=\"") self._write(key) self._write("\"; filename=\"") self._write(filename) self._write("\"") self._write(self.line_break) self._write("Content-Type: application/octet-stream") self._write(self.line_break) self._write(self.line_break) self._write_file(value) else: self._write("Content-Disposition: form-data; name=\"") self._write(key) self._write("\"") self._write(self.line_break) self._write(self.line_break) self._write(value) self._write(self.line_break)
def request(self, method, url, headers, post_data=None): b = util.io.BytesIO() rheaders = util.io.BytesIO() # Pycurl's design is a little weird: although we set per-request # options on this object, it's also capable of maintaining established # connections. Here we call reset() between uses to make sure it's in a # pristine state, but notably reset() doesn't reset connections, so we # still get to take advantage of those by virtue of re-using the same # object. self._curl.reset() proxy = self._get_proxy(url) if proxy: if proxy.hostname: self._curl.setopt(pycurl.PROXY, proxy.hostname) if proxy.port: self._curl.setopt(pycurl.PROXYPORT, proxy.port) if proxy.username or proxy.password: self._curl.setopt( pycurl.PROXYUSERPWD, "%s:%s" % (proxy.username, proxy.password), ) if method == "get": self._curl.setopt(pycurl.HTTPGET, 1) elif method == "post": self._curl.setopt(pycurl.POST, 1) self._curl.setopt(pycurl.POSTFIELDS, post_data) else: self._curl.setopt(pycurl.CUSTOMREQUEST, method.upper()) # pycurl doesn't like unicode URLs self._curl.setopt(pycurl.URL, util.utf8(url)) self._curl.setopt(pycurl.WRITEFUNCTION, b.write) self._curl.setopt(pycurl.HEADERFUNCTION, rheaders.write) self._curl.setopt(pycurl.NOSIGNAL, 1) self._curl.setopt(pycurl.CONNECTTIMEOUT, 30) self._curl.setopt(pycurl.TIMEOUT, 80) self._curl.setopt( pycurl.HTTPHEADER, ["%s: %s" % (k, v) for k, v in six.iteritems(dict(headers))], ) if self._verify_ssl_certs: self._curl.setopt(pycurl.CAINFO, stripe.ca_bundle_path) else: self._curl.setopt(pycurl.SSL_VERIFYHOST, False) try: self._curl.perform() except pycurl.error as e: self._handle_request_error(e) rbody = b.getvalue().decode("utf-8") rcode = self._curl.getinfo(pycurl.RESPONSE_CODE) headers = self.parse_headers(rheaders.getvalue().decode("utf-8")) return rbody, rcode, headers
def __deepcopy__(self, memo): copied = self.__copy__() memo[id(self)] = copied for k, v in six.iteritems(self): # Call parent's __setitem__ to avoid checks that we've added in the # overridden version that can throw exceptions. super(StripeObject, copied).__setitem__(k, deepcopy(v, memo)) return copied
def test_param_encoding(self, requestor, mock_response, check_call): mock_response("{}", 200) requestor.request("get", "", self.ENCODE_INPUTS) expectation = [] for type_, values in six.iteritems(self.ENCODE_EXPECTATIONS): expectation.extend([(k % (type_,), str(v)) for k, v in values]) check_call("get", QueryMatcher(expectation))
def test_param_encoding(self, requestor, mock_response, check_call): mock_response('{}', 200) requestor.request('get', '', self.ENCODE_INPUTS) expectation = [] for type_, values in six.iteritems(self.ENCODE_EXPECTATIONS): expectation.extend([(k % (type_, ), str(v)) for k, v in values]) check_call('get', QueryMatcher(expectation))
def serialize(self, previous): params = super(Account, self).serialize(previous) previous = previous or self._previous or {} for k, v in six.iteritems(self): if (k == "individual" and isinstance(v, stripe.api_resources.Person) and k not in params): params[k] = v.serialize(previous.get(k, None)) return params
def __copy__(self): copied = StripeObject(self.get('id'), self.api_key, stripe_version=self.stripe_version, stripe_account=self.stripe_account) copied._retrieve_params = self._retrieve_params for k, v in six.iteritems(self): # Call parent's __setitem__ to avoid checks that we've added in the # overridden version that can throw exceptions. super(StripeObject, copied).__setitem__(k, v) return copied
def serialize(self, previous): params = super(Account, self).serialize(previous) previous = previous or self._previous or {} for k, v in six.iteritems(self): if ( k == "individual" and isinstance(v, stripe.api_resources.Person) and k not in params ): params[k] = v.serialize(previous.get(k, None)) return params
def __init__(self, verify_ssl_certs=True, proxy=None): super(PycurlClient, self).__init__(verify_ssl_certs=verify_ssl_certs, proxy=proxy) # Initialize this within the object so that we can reuse connections. self._curl = pycurl.Curl() # need to urlparse the proxy, since PyCurl # consumes the proxy url in small pieces if self._proxy: # now that we have the parser, get the proxy url pieces proxy = self._proxy for scheme, value in six.iteritems(proxy): proxy[scheme] = urlparse(value)
def to_dict_recursive(self): def maybe_to_dict_recursive(value): if value is None: return None elif isinstance(value, StripeObject): return value.to_dict_recursive() else: return value return { key: list(map(maybe_to_dict_recursive, value)) if isinstance( value, list) else maybe_to_dict_recursive(value) for key, value in six.iteritems(dict(self)) }
def convert_to_dict(obj): """Converts a StripeObject back to a regular dict. Nested StripeObjects are also converted back to regular dicts. :param obj: The StripeObject to convert. :returns: The StripeObject as a dict. """ if isinstance(obj, list): return [convert_to_dict(i) for i in obj] # This works by virtue of the fact that StripeObjects _are_ dicts. The dict # comprehension returns a regular dict and recursively applies the # conversion to each value. elif isinstance(obj, dict): return {k: convert_to_dict(v) for k, v in six.iteritems(obj)} else: return obj
def serialize(self, previous): params = {} unsaved_keys = self._unsaved_values or set() previous = previous or self._previous or {} for k, v in six.iteritems(self): if k == 'id' or (isinstance(k, str) and k.startswith('_')): continue elif isinstance(v, stripe.api_resources.abstract.APIResource): continue elif hasattr(v, 'serialize'): params[k] = v.serialize(previous.get(k, None)) elif k in unsaved_keys: params[k] = _compute_diff(v, previous.get(k, None)) elif k == 'additional_owners' and v is not None: params[k] = _serialize_list(v, previous.get(k, None)) return params
def serialize(self, previous): params = {} unsaved_keys = self._unsaved_values or set() previous = previous or self._previous or {} for k, v in six.iteritems(self): if k == 'id' or (isinstance(k, str) and k.startswith('_')): continue elif isinstance(v, stripe.api_resources.abstract.APIResource): continue elif hasattr(v, 'serialize'): child = v.serialize(previous.get(k, None)) if child != {}: params[k] = child elif k in unsaved_keys: params[k] = _compute_diff(v, previous.get(k, None)) elif k == 'additional_owners' and v is not None: params[k] = _serialize_list(v, previous.get(k, None)) return params
def _api_encode(data): for key, value in six.iteritems(data): key = util.utf8(key) if value is None: continue elif hasattr(value, "stripe_id"): yield (key, value.stripe_id) elif isinstance(value, list) or isinstance(value, tuple): for i, sv in enumerate(value): if isinstance(sv, dict): subdict = _encode_nested_dict("%s[%d]" % (key, i), sv) for k, v in _api_encode(subdict): yield (k, v) else: yield ("%s[%d]" % (key, i), util.utf8(sv)) elif isinstance(value, dict): subdict = _encode_nested_dict(key, value) for subkey, subvalue in _api_encode(subdict): yield (subkey, subvalue) elif isinstance(value, datetime.datetime): yield (key, _encode_datetime(value)) else: yield (key, util.utf8(value))
def request_raw(self, method, url, params=None, supplied_headers=None): """ Mechanism for issuing an API call """ if self.api_key: my_api_key = self.api_key else: from stripe import api_key my_api_key = api_key if my_api_key is None: raise error.AuthenticationError( "No API key provided. (HINT: set your API key using " '"stripe.api_key = <API-KEY>"). You can generate API keys ' "from the Stripe web interface. See https://stripe.com/api " "for details, or email [email protected] if you have any " "questions.") abs_url = "%s%s" % (self.api_base, url) encoded_params = urlencode(list(_api_encode(params or {}))) # Don't use strict form encoding by changing the square bracket control # characters back to their literals. This is fine by the server, and # makes these parameter strings easier to read. encoded_params = encoded_params.replace("%5B", "[").replace("%5D", "]") if method == "get" or method == "delete": if params: abs_url = _build_api_url(abs_url, encoded_params) post_data = None elif method == "post": if (supplied_headers is not None and supplied_headers.get("Content-Type") == "multipart/form-data"): generator = MultipartDataGenerator() generator.add_params(params or {}) post_data = generator.get_post_data() supplied_headers[ "Content-Type"] = "multipart/form-data; boundary=%s" % ( generator.boundary, ) else: post_data = encoded_params else: raise error.APIConnectionError( "Unrecognized HTTP method %r. This may indicate a bug in the " "Stripe bindings. Please contact [email protected] for " "assistance." % (method, )) headers = self.request_headers(my_api_key, method) if supplied_headers is not None: for key, value in six.iteritems(supplied_headers): headers[key] = value util.log_info("Request to Stripe api", method=method, path=abs_url) util.log_debug( "Post details", post_data=encoded_params, api_version=self.api_version, ) request_start = _now_ms() rbody, rcode, rheaders = self._client.request_with_retries( method, abs_url, headers, post_data) util.log_info("Stripe API response", path=abs_url, response_code=rcode) util.log_debug("API response body", body=rbody) if "Request-Id" in rheaders: request_id = rheaders["Request-Id"] util.log_debug( "Dashboard link for request", link=util.dashboard_link(request_id), ) if stripe.enable_telemetry: request_duration_ms = _now_ms() - request_start self._last_request_metrics = RequestMetrics( request_id, request_duration_ms) return rbody, rcode, rheaders, my_api_key
def _encode_nested_dict(key, data, fmt="%s[%s]"): d = {} for subkey, subvalue in six.iteritems(data): d[fmt % (key, subkey)] = subvalue return d
def _extra_match(self, other): for k, v in six.iteritems(self.extra): if other[k] != v: return False return True
def request_raw(self, method, url, params=None, supplied_headers=None): """ Mechanism for issuing an API call """ if self.api_key: my_api_key = self.api_key else: from stripe import api_key my_api_key = api_key if my_api_key is None: raise error.AuthenticationError( 'No API key provided. (HINT: set your API key using ' '"stripe.api_key = <API-KEY>"). You can generate API keys ' 'from the Stripe web interface. See https://stripe.com/api ' 'for details, or email [email protected] if you have any ' 'questions.') abs_url = '%s%s' % (self.api_base, url) encoded_params = urlencode(list(_api_encode(params or {}))) # Don't use strict form encoding by changing the square bracket control # characters back to their literals. This is fine by the server, and # makes these parameter strings easier to read. encoded_params = encoded_params.replace('%5B', '[').replace('%5D', ']') if method == 'get' or method == 'delete': if params: abs_url = _build_api_url(abs_url, encoded_params) post_data = None elif method == 'post': if supplied_headers is not None and \ supplied_headers.get("Content-Type") == \ "multipart/form-data": generator = MultipartDataGenerator() generator.add_params(params or {}) post_data = generator.get_post_data() supplied_headers["Content-Type"] = \ "multipart/form-data; boundary=%s" % (generator.boundary,) else: post_data = encoded_params else: raise error.APIConnectionError( 'Unrecognized HTTP method %r. This may indicate a bug in the ' 'Stripe bindings. Please contact [email protected] for ' 'assistance.' % (method, )) headers = self.request_headers(my_api_key, method) if supplied_headers is not None: for key, value in six.iteritems(supplied_headers): headers[key] = value util.log_info('Request to Stripe api', method=method, path=abs_url) util.log_debug('Post details', post_data=encoded_params, api_version=self.api_version) rbody, rcode, rheaders = self._client.request_with_retries( method, abs_url, headers, post_data) util.log_info('Stripe API response', path=abs_url, response_code=rcode) util.log_debug('API response body', body=rbody) if 'Request-Id' in rheaders: util.log_debug('Dashboard link for request', link=util.dashboard_link(rheaders['Request-Id'])) return rbody, rcode, rheaders, my_api_key
def to_dict_recursive(self): d = dict(self) for k, v in six.iteritems(d): if isinstance(v, StripeObject): d[k] = v.to_dict_recursive() return d
def parse_headers(self, data): if "\r\n" not in data: return {} raw_headers = data.split("\r\n", 1)[1] headers = email.message_from_string(raw_headers) return dict((k.lower(), v) for k, v in six.iteritems(dict(headers)))
def request_raw(self, method, url, params=None, supplied_headers=None): """ Mechanism for issuing an API call """ if self.api_key: my_api_key = self.api_key else: from stripe import api_key my_api_key = api_key if my_api_key is None: raise error.AuthenticationError( "No API key provided. (HINT: set your API key using " '"stripe.api_key = <API-KEY>"). You can generate API keys ' "from the Stripe web interface. See https://stripe.com/api " "for details, or email [email protected] if you have any " "questions." ) abs_url = "%s%s" % (self.api_base, url) encoded_params = urlencode(list(_api_encode(params or {}))) # Don't use strict form encoding by changing the square bracket control # characters back to their literals. This is fine by the server, and # makes these parameter strings easier to read. encoded_params = encoded_params.replace("%5B", "[").replace("%5D", "]") if method == "get" or method == "delete": if params: abs_url = _build_api_url(abs_url, encoded_params) post_data = None elif method == "post": if ( supplied_headers is not None and supplied_headers.get("Content-Type") == "multipart/form-data" ): generator = MultipartDataGenerator() generator.add_params(params or {}) post_data = generator.get_post_data() supplied_headers[ "Content-Type" ] = "multipart/form-data; boundary=%s" % (generator.boundary,) else: post_data = encoded_params else: raise error.APIConnectionError( "Unrecognized HTTP method %r. This may indicate a bug in the " "Stripe bindings. Please contact [email protected] for " "assistance." % (method,) ) headers = self.request_headers(my_api_key, method) if supplied_headers is not None: for key, value in six.iteritems(supplied_headers): headers[key] = value util.log_info("Request to Stripe api", method=method, path=abs_url) util.log_debug( "Post details", post_data=encoded_params, api_version=self.api_version, ) rbody, rcode, rheaders = self._client.request_with_retries( method, abs_url, headers, post_data ) util.log_info("Stripe API response", path=abs_url, response_code=rcode) util.log_debug("API response body", body=rbody) if "Request-Id" in rheaders: request_id = rheaders["Request-Id"] util.log_debug( "Dashboard link for request", link=util.dashboard_link(request_id), ) return rbody, rcode, rheaders, my_api_key