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 +27 -0
- mangopay/api.py +381 -0
- mangopay/auth.py +130 -0
- mangopay/base.py +289 -0
- mangopay/compat.py +16 -0
- mangopay/constants.py +466 -0
- mangopay/exceptions.py +26 -0
- mangopay/fields.py +1345 -0
- mangopay/page.py +17 -0
- mangopay/query.py +280 -0
- mangopay/ratelimit.py +5 -0
- mangopay/resources.py +3219 -0
- mangopay/signals.py +14 -0
- mangopay/utils.py +1582 -0
- mangopay4_python_sdk-3.52.0.dist-info/METADATA +53 -0
- mangopay4_python_sdk-3.52.0.dist-info/RECORD +20 -0
- mangopay4_python_sdk-3.52.0.dist-info/WHEEL +6 -0
- mangopay4_python_sdk-3.52.0.dist-info/entry_points.txt +2 -0
- mangopay4_python_sdk-3.52.0.dist-info/licenses/LICENSE +22 -0
- mangopay4_python_sdk-3.52.0.dist-info/top_level.txt +1 -0
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
|