mangopay4-python-sdk 3.52.0__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
mangopay/__init__.py ADDED
@@ -0,0 +1,27 @@
1
+ client_id = None
2
+ apikey = None
3
+ api_url = 'https://api.mangopay.com/v2.01/'
4
+ api_sandbox_url = 'https://api.sandbox.mangopay.com/v2.01/'
5
+ temp_dir = None
6
+ api_version = 2.01
7
+ sandbox = True
8
+ uk_header_flag = False
9
+
10
+ package_version = None
11
+ try:
12
+ with open('./setup.py', 'r') as f:
13
+ for line in f:
14
+ if line.startswith(' version'):
15
+ package_version = line.split('=')[1].replace("'", "").replace(",", "").replace("\n", "")
16
+ except:
17
+ None
18
+
19
+
20
+ from .api import APIRequest # noqa
21
+ from .utils import memoize
22
+
23
+
24
+ def _get_default_handler():
25
+ return APIRequest()
26
+
27
+ get_default_handler = memoize(_get_default_handler, {}, 0)
mangopay/api.py ADDED
@@ -0,0 +1,381 @@
1
+ # -*- coding: utf-8 -*-
2
+ from __future__ import print_function
3
+ from __future__ import unicode_literals
4
+
5
+ import logging
6
+ import platform
7
+ import time
8
+
9
+ import requests
10
+ import six
11
+ from requests.exceptions import ConnectionError, Timeout
12
+
13
+ import mangopay
14
+ from mangopay.auth import AuthorizationTokenManager
15
+ from mangopay.ratelimit import RateLimit
16
+ from .exceptions import APIError, DecodeError
17
+ from .signals import request_finished, request_started, request_error
18
+ from .utils import reraise_as
19
+
20
+ try:
21
+ import urllib.parse as urlrequest
22
+ except ImportError:
23
+ import urllib as urlrequest
24
+
25
+ try:
26
+ import simplejson as json
27
+ except ImportError:
28
+ import json
29
+
30
+
31
+ logger = logging.getLogger('mangopay')
32
+
33
+ requests_session = requests.Session()
34
+ rate_limits = None
35
+
36
+
37
+ class APIRequest(object):
38
+
39
+ def __init__(self, client_id=None, apikey=None, api_url=None, api_sandbox_url=None, sandbox=None,
40
+ timeout=30.0, storage_strategy=None, proxies=None, uk_header_flag=False):
41
+ global rate_limits
42
+ rate_limits = None
43
+ if (sandbox is None and mangopay.sandbox) or sandbox:
44
+ self.api_url = api_sandbox_url or mangopay.api_sandbox_url
45
+ else:
46
+ self.api_url = api_url or mangopay.api_url
47
+
48
+ self.client_id = client_id or mangopay.client_id
49
+ self.apikey = apikey or mangopay.apikey
50
+ self.auth_manager = AuthorizationTokenManager(self, storage_strategy)
51
+ self.timeout = timeout
52
+ self.proxies = proxies
53
+ self.uk_header_flag = uk_header_flag or mangopay.uk_header_flag
54
+
55
+ def set_rate_limit(self, rate_limit):
56
+ global rate_limits
57
+ rate_limits = rate_limit
58
+
59
+
60
+ def get_rate_limits(self):
61
+ return rate_limits
62
+
63
+ def request(self, method, url, data=None, idempotency_key=None, oauth_request=False, without_client_id=False,
64
+ is_v3=False, **params):
65
+ return self.custom_request(method, url, data, idempotency_key, oauth_request, True,
66
+ without_client_id, is_v3, **params)
67
+
68
+ def multipart_request(self, method, url, file, file_name, idempotency_key=None, oauth_request=False, is_v3=False):
69
+ return self.custom_multipart_request(method, url, file, file_name, idempotency_key, oauth_request, is_v3)
70
+
71
+ def custom_request(self, method, url, data=None, idempotency_key=None, oauth_request=False,
72
+ is_mangopay_request=False, without_client_id=False, is_v3=False, **params):
73
+ params = params or {}
74
+
75
+ headers = {}
76
+
77
+ if is_mangopay_request:
78
+ headers['User-Agent'] = 'Mangopay-SDK/' + str(
79
+ mangopay.package_version) + ' (Python/' + platform.python_version() + ')'
80
+ if oauth_request:
81
+ headers['Authorization'] = self.auth_manager.basic_token()
82
+ headers['Content-Type'] = 'application/x-www-form-urlencoded'
83
+ else:
84
+ headers['Authorization'] = self.auth_manager.get_token()
85
+ headers['Content-Type'] = 'application/json'
86
+
87
+ if idempotency_key:
88
+ headers['Idempotency-Key'] = idempotency_key
89
+
90
+ if self.uk_header_flag:
91
+ logger.warning('uk_header_flag is deprecated and will be removed in future versions')
92
+ headers['x-tenant-id'] = 'uk'
93
+ else:
94
+ if "data_XXX" in params:
95
+ params[str("data")] = params[str("data_XXX")]
96
+ params.__delitem__(str("data_XXX"))
97
+ headers['Content-Type'] = 'application/x-www-form-urlencoded'
98
+
99
+ # truncated_data = None
100
+
101
+ encoded_params = urlrequest.urlencode(params)
102
+
103
+ if is_mangopay_request:
104
+ if oauth_request or without_client_id:
105
+ url = self.api_url + url
106
+ else:
107
+ url = self._absolute_url(url, encoded_params)
108
+ else:
109
+ url = '%s?%s' % (url, encoded_params)
110
+
111
+ if is_v3:
112
+ url = url.replace('v2.01', 'V3.0')
113
+
114
+ if data or data == {}:
115
+ # truncated_data = truncatechars(copy.copy(data))
116
+
117
+ data = json.dumps(data, default=lambda x: x.to_api_json())
118
+
119
+ logger.debug('DATA[IN -> %s]\n\t- headers: %s\n\t- content: %s', url, headers, data)
120
+
121
+ ts = time.time()
122
+
123
+ # signal:
124
+ request_started.send(url=url, data=data, headers=headers, method=method)
125
+
126
+ try:
127
+ result = requests_session.request(method, url,
128
+ data=data,
129
+ headers=headers,
130
+ timeout=self.timeout,
131
+ proxies=self.proxies)
132
+ except ConnectionError as e:
133
+ msg = '{}'.format(e)
134
+
135
+ if msg:
136
+ msg = '%s: %s' % (type(e).__name__, msg)
137
+ else:
138
+ msg = type(e).__name__
139
+
140
+ reraise_as(APIError(msg))
141
+
142
+ except Timeout as e:
143
+ msg = '{}'.format(e)
144
+
145
+ if msg:
146
+ msg = '%s: %s' % (type(e).__name__, msg)
147
+ else:
148
+ msg = type(e).__name__
149
+
150
+ reraise_as(APIError(msg))
151
+ laps = time.time() - ts
152
+
153
+ # signal:
154
+ request_finished.send(url=url,
155
+ data=data,
156
+ headers=headers,
157
+ method=method,
158
+ result=result,
159
+ laps=laps)
160
+
161
+ logger.debug('DATA[OUT -> %s][%2.3f seconds]\n\t- status_code: %s\n\t- headers: %s\n\t- content: %s',
162
+ url,
163
+ laps,
164
+ result.status_code,
165
+ result.headers,
166
+ result.text if hasattr(result, 'text') else result.content
167
+ )
168
+
169
+ self.read_response_headers(result.headers)
170
+
171
+ if result.status_code not in (requests.codes.ok, requests.codes.not_found,
172
+ requests.codes.created, requests.codes.accepted,
173
+ requests.codes.no_content):
174
+ self._create_apierror(result, url=url, data=data, method=method)
175
+ elif result.status_code == requests.codes.no_content or (result.status_code == requests.codes.ok and result.content == b''):
176
+ return result, None
177
+ else:
178
+ if result.content:
179
+ try:
180
+ content = result.content
181
+
182
+ if six.PY3:
183
+ content = content.decode('utf-8')
184
+
185
+ return result, json.loads(content)
186
+ except ValueError:
187
+ if result.content.startswith(b'data='):
188
+ return result.content
189
+ self._create_decodeerror(result, url=url)
190
+ else:
191
+ self._create_decodeerror(result, url=url)
192
+
193
+ def custom_multipart_request(self, method, url, file, file_name, idempotency_key=None, oauth_request=False, is_v3=False):
194
+ if not isinstance(file, bytes):
195
+ raise TypeError("The 'file' parameter must be of type 'bytes'.")
196
+
197
+ headers = {'User-Agent': 'Mangopay-SDK/' + str(
198
+ mangopay.package_version) + ' (Python/' + platform.python_version() + ')'}
199
+
200
+ if oauth_request:
201
+ headers['Authorization'] = self.auth_manager.basic_token()
202
+ else:
203
+ headers['Authorization'] = self.auth_manager.get_token()
204
+
205
+ if idempotency_key:
206
+ headers['Idempotency-Key'] = idempotency_key
207
+
208
+ if self.uk_header_flag:
209
+ logger.warning('uk_header_flag is deprecated and will be removed in future versions')
210
+ headers['x-tenant-id'] = 'uk'
211
+
212
+ if oauth_request:
213
+ url = self.api_url + url
214
+ else:
215
+ url = self._absolute_url(url, '')
216
+
217
+ if is_v3:
218
+ url = url.replace('v2.01', 'V3.0')
219
+
220
+ files = {'file': (file_name, file)}
221
+
222
+ logger.debug('DATA[IN -> %s]\n\t- headers: %s\n\t- file: %s', url, headers, 'provided' if file else 'none')
223
+ ts = time.time()
224
+
225
+ request_started.send(url=url, data=None, headers=headers, method=method)
226
+
227
+ try:
228
+ result = requests_session.request(method, url,
229
+ files=files,
230
+ headers=headers,
231
+ timeout=self.timeout,
232
+ proxies=self.proxies)
233
+ except ConnectionError as e:
234
+ msg = f'{type(e).__name__}: {e}' if str(e) else type(e).__name__
235
+ reraise_as(APIError(msg))
236
+
237
+ except Timeout as e:
238
+ msg = f'{type(e).__name__}: {e}' if str(e) else type(e).__name__
239
+ reraise_as(APIError(msg))
240
+
241
+ laps = time.time() - ts
242
+
243
+ request_finished.send(url=url,
244
+ data=None,
245
+ headers=headers,
246
+ method=method,
247
+ result=result,
248
+ laps=laps)
249
+
250
+ logger.debug('DATA[OUT -> %s][%2.3f seconds]\n\t- status_code: %s\n\t- headers: %s\n\t- content: %s',
251
+ url,
252
+ laps,
253
+ result.status_code,
254
+ result.headers,
255
+ result.text if hasattr(result, 'text') else result.content
256
+ )
257
+
258
+ self.read_response_headers(result.headers)
259
+
260
+ if result.status_code not in (requests.codes.ok, requests.codes.not_found,
261
+ requests.codes.created, requests.codes.accepted,
262
+ requests.codes.no_content):
263
+ self._create_apierror(result, url=url, data=None, method=method)
264
+ elif result.status_code == requests.codes.no_content or (
265
+ result.status_code == requests.codes.ok and result.content == b''):
266
+ return result, None
267
+ else:
268
+ if result.content:
269
+ try:
270
+ content = result.content
271
+ if six.PY3:
272
+ content = content.decode('utf-8')
273
+ return result, json.loads(content)
274
+ except ValueError:
275
+ if result.content.startswith(b'data='):
276
+ return result.content
277
+ self._create_decodeerror(result, url=url)
278
+ else:
279
+ self._create_decodeerror(result, url=url)
280
+
281
+ def read_response_headers(self, headers):
282
+ rate_limit_reset = headers.get('x-ratelimit-reset')
283
+ rate_limit_remaining = headers.get('x-ratelimit-remaining')
284
+ rate_limit_made = headers.get('x-ratelimit')
285
+
286
+ if rate_limit_reset is not None and rate_limit_remaining is not None and rate_limit_made is not None:
287
+ rate_limit_reset = rate_limit_reset.split(',')
288
+ rate_limit_remaining = rate_limit_remaining.split(',')
289
+ rate_limit_made = rate_limit_made.split(',')
290
+
291
+ if len(rate_limit_reset) == len(rate_limit_remaining) and len(rate_limit_reset) == len(rate_limit_made):
292
+ current_time = int(time.time())
293
+ updated_rate_limits = []
294
+
295
+ for i, rlr in enumerate(rate_limit_reset):
296
+ rate_limit = RateLimit()
297
+ number_of_minutes = (int(rate_limit_reset[i].strip()) - current_time) / 60
298
+
299
+ if number_of_minutes <= 15:
300
+ rate_limit.interval_minutes = 15
301
+ elif number_of_minutes <= 30:
302
+ rate_limit.interval_minutes = 30
303
+ elif number_of_minutes <= 60:
304
+ rate_limit.interval_minutes = 60
305
+ elif number_of_minutes <= 60 * 24:
306
+ rate_limit.interval_minutes = 60 * 24
307
+
308
+ rate_limit.reset_time_millis = int(rate_limit_reset[i].strip())
309
+ rate_limit.calls_remaining = int(rate_limit_remaining[i].strip())
310
+ rate_limit.calls_made = int(rate_limit_made[i].strip())
311
+ updated_rate_limits.append(rate_limit)
312
+
313
+ if len(updated_rate_limits) > 0:
314
+ self.set_rate_limit(updated_rate_limits)
315
+
316
+ else:
317
+ logger.debug("Could not set rate limits: headers length should be the same")
318
+ else:
319
+ logger.debug("Could not set rate limits: missing headers")
320
+
321
+ def _absolute_url(self, url, encoded_params):
322
+ pattern = '%s%s%s'
323
+
324
+ if encoded_params:
325
+ pattern = '%s%s?%s'
326
+
327
+ return pattern % (self.api_url, self._construct_api_url(url), encoded_params)
328
+
329
+ def _construct_api_url(self, relative_url):
330
+ return '%s%s' % (self.client_id, relative_url)
331
+
332
+ def _create_apierror(self, result, url=None, data=None, method=None):
333
+ text = result.text if hasattr(result, 'text') else result.content
334
+
335
+ status_code = result.status_code
336
+
337
+ headers = result.headers
338
+
339
+ logger.error('API ERROR: status_code: %s | url: %s | method: %s | data: %r | headers: %s | content: %s',
340
+ status_code,
341
+ url,
342
+ method,
343
+ data,
344
+ headers,
345
+ text,
346
+ )
347
+
348
+ request_error.send(url=url, status_code=status_code, headers=headers)
349
+
350
+ try:
351
+ content = result.json()
352
+ except ValueError:
353
+ content = None
354
+
355
+ raise APIError(text, code=status_code, content=content, headers=headers)
356
+
357
+ def _create_decodeerror(self, result, url=None):
358
+
359
+ text = result.text if hasattr(result, 'text') else result.content
360
+
361
+ status_code = result.status_code
362
+
363
+ headers = result.headers
364
+
365
+ logger.error('DECODE ERROR: status_code: %s | headers: %s | content: %s',
366
+ status_code,
367
+ headers,
368
+ text,
369
+ )
370
+
371
+ request_error.send(url=url, status_code=status_code, headers=headers)
372
+
373
+ try:
374
+ content = result.json()
375
+ except ValueError:
376
+ content = None
377
+
378
+ raise DecodeError(text,
379
+ code=status_code,
380
+ headers=headers,
381
+ content=content)
mangopay/auth.py ADDED
@@ -0,0 +1,130 @@
1
+ import base64
2
+ import hashlib
3
+ import json
4
+ import os
5
+ import stat
6
+ import tempfile
7
+ import time
8
+
9
+ import six
10
+
11
+ import mangopay
12
+ from mangopay.exceptions import AuthenticationError
13
+
14
+
15
+ class StorageStrategyBase(object):
16
+
17
+ def get(self, env_key):
18
+ pass
19
+
20
+ def store(self, token, env_key):
21
+ pass
22
+
23
+
24
+ class StaticStorageStrategy(StorageStrategyBase):
25
+
26
+ _dict = dict()
27
+
28
+ def get(self, env_key):
29
+ return StaticStorageStrategy._dict[env_key]
30
+
31
+ def store(self, token, env_key):
32
+ StaticStorageStrategy._dict[env_key] = token
33
+
34
+
35
+ class FileStorageStrategy(StorageStrategyBase):
36
+
37
+ def __init__(self):
38
+ if mangopay.temp_dir is None:
39
+ mangopay.temp_dir = tempfile.gettempdir()
40
+
41
+ def get(self, env_key):
42
+ cache_path = os.path.join(mangopay.temp_dir, "cached-data." + env_key + ".py")
43
+
44
+ if not os.path.exists(cache_path):
45
+ return None
46
+
47
+ with open(cache_path, 'rb') as f:
48
+ serialized_obj = f.read().decode('UTF-8')
49
+ try:
50
+ cached = json.loads(serialized_obj[1:])
51
+ except:
52
+ cached = None
53
+
54
+ return cached
55
+
56
+ def store(self, token, env_key):
57
+ cache_path = os.path.join(mangopay.temp_dir, "cached-data." + env_key + ".py")
58
+ if token is None:
59
+ return
60
+
61
+ with open(cache_path, 'w') as f:
62
+ os.chmod(cache_path, stat.S_IRUSR | stat.S_IWUSR)
63
+ serialized_obj = "#" + json.dumps(token)
64
+ f.write(serialized_obj)
65
+
66
+
67
+ class AuthorizationTokenManager(object):
68
+
69
+ def __init__(self, handler, storage_strategy):
70
+ self._handler = handler
71
+ self.authorization = ApiAuthorization(handler)
72
+ if storage_strategy:
73
+ self.storage_strategy = storage_strategy
74
+ else:
75
+ self.storage_strategy = StaticStorageStrategy()
76
+
77
+ def basic_token(self):
78
+ return self.authorization.basic_token()
79
+
80
+ def get_token(self):
81
+ try:
82
+ token = self.storage_strategy.get(self.get_evn_key())
83
+ except KeyError:
84
+ token = None
85
+
86
+ if not token or not token['timestamp'] or token['timestamp'] <= time.time():
87
+ auth_result = self.authorization.oauth_token()
88
+ if auth_result[0].status_code == 200:
89
+ token = auth_result[1]
90
+ else:
91
+ return None
92
+ token['timestamp'] = time.time() + (int(token['expires_in']) - 30)
93
+ self.set_token(token)
94
+
95
+ return token['token_type'] + ' ' + token['access_token']
96
+
97
+ def set_token(self, token):
98
+ self.storage_strategy.store(token, self.get_evn_key())
99
+
100
+ def get_evn_key(self):
101
+ return hashlib.md5((self._handler.client_id + self._handler.api_url +
102
+ self._handler.apikey).encode('utf-8')).hexdigest()
103
+
104
+
105
+ class ApiAuthorization(object):
106
+
107
+ def __init__(self, handler):
108
+ self._handler = handler
109
+
110
+ def oauth_token(self):
111
+ result = self._handler.request(method='POST',
112
+ url='oauth/token',
113
+ data={'grant_type': 'client_credentials'},
114
+ oauth_request=True)
115
+ return result
116
+
117
+ def basic_token(self):
118
+ if self._handler.client_id is None or self._handler.apikey is None:
119
+ raise AuthenticationError(
120
+ 'Authentication failed. (Please set your Mangopay API username '
121
+ 'and password using "mangopay.client_id = CLIENT_ID" '
122
+ 'and "mangopay.apikey = APIKEY").')
123
+
124
+ credentials = '%s:%s' % (self._handler.client_id, self._handler.apikey)
125
+ credentials = base64.b64encode(credentials.encode('ascii'))
126
+
127
+ if six.PY3:
128
+ credentials = credentials.decode('utf-8')
129
+
130
+ return 'Basic %s' % credentials