mangopay4-python-sdk 3.52.0__tar.gz

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.
Files changed (93) hide show
  1. mangopay4_python_sdk-3.52.0/DESCRIPTION.md +1 -0
  2. mangopay4_python_sdk-3.52.0/LICENSE +22 -0
  3. mangopay4_python_sdk-3.52.0/MANIFEST.in +8 -0
  4. mangopay4_python_sdk-3.52.0/Makefile +8 -0
  5. mangopay4_python_sdk-3.52.0/PKG-INFO +53 -0
  6. mangopay4_python_sdk-3.52.0/README.md +53 -0
  7. mangopay4_python_sdk-3.52.0/mangopay/__init__.py +27 -0
  8. mangopay4_python_sdk-3.52.0/mangopay/api.py +381 -0
  9. mangopay4_python_sdk-3.52.0/mangopay/auth.py +130 -0
  10. mangopay4_python_sdk-3.52.0/mangopay/base.py +289 -0
  11. mangopay4_python_sdk-3.52.0/mangopay/compat.py +16 -0
  12. mangopay4_python_sdk-3.52.0/mangopay/constants.py +466 -0
  13. mangopay4_python_sdk-3.52.0/mangopay/exceptions.py +26 -0
  14. mangopay4_python_sdk-3.52.0/mangopay/fields.py +1345 -0
  15. mangopay4_python_sdk-3.52.0/mangopay/page.py +17 -0
  16. mangopay4_python_sdk-3.52.0/mangopay/query.py +280 -0
  17. mangopay4_python_sdk-3.52.0/mangopay/ratelimit.py +5 -0
  18. mangopay4_python_sdk-3.52.0/mangopay/resources.py +3219 -0
  19. mangopay4_python_sdk-3.52.0/mangopay/signals.py +14 -0
  20. mangopay4_python_sdk-3.52.0/mangopay/utils.py +1582 -0
  21. mangopay4_python_sdk-3.52.0/mangopay4_python_sdk.egg-info/PKG-INFO +53 -0
  22. mangopay4_python_sdk-3.52.0/mangopay4_python_sdk.egg-info/SOURCES.txt +92 -0
  23. mangopay4_python_sdk-3.52.0/mangopay4_python_sdk.egg-info/dependency_links.txt +1 -0
  24. mangopay4_python_sdk-3.52.0/mangopay4_python_sdk.egg-info/entry_points.txt +2 -0
  25. mangopay4_python_sdk-3.52.0/mangopay4_python_sdk.egg-info/requires.txt +25 -0
  26. mangopay4_python_sdk-3.52.0/mangopay4_python_sdk.egg-info/top_level.txt +1 -0
  27. mangopay4_python_sdk-3.52.0/setup.cfg +7 -0
  28. mangopay4_python_sdk-3.52.0/setup.py +43 -0
  29. mangopay4_python_sdk-3.52.0/tests/__init__.py +6 -0
  30. mangopay4_python_sdk-3.52.0/tests/fixtures/card.json +16 -0
  31. mangopay4_python_sdk-3.52.0/tests/fixtures/cardregistrations.json +16 -0
  32. mangopay4_python_sdk-3.52.0/tests/fixtures/cardregistrations_update.json +14 -0
  33. mangopay4_python_sdk-3.52.0/tests/fixtures/declarative_user.json +26 -0
  34. mangopay4_python_sdk-3.52.0/tests/fixtures/legal_user.json +28 -0
  35. mangopay4_python_sdk-3.52.0/tests/fixtures/legal_user_wallet.json +14 -0
  36. mangopay4_python_sdk-3.52.0/tests/fixtures/legal_user_wallet_89.json +14 -0
  37. mangopay4_python_sdk-3.52.0/tests/fixtures/legal_user_wallet_99.json +14 -0
  38. mangopay4_python_sdk-3.52.0/tests/fixtures/list_ubo_declarations.json +12 -0
  39. mangopay4_python_sdk-3.52.0/tests/fixtures/natural_user.json +25 -0
  40. mangopay4_python_sdk-3.52.0/tests/fixtures/natural_user_wallet.json +14 -0
  41. mangopay4_python_sdk-3.52.0/tests/fixtures/natural_user_wallet_9.json +14 -0
  42. mangopay4_python_sdk-3.52.0/tests/fixtures/ubo.json +20 -0
  43. mangopay4_python_sdk-3.52.0/tests/fixtures/ubo_declaration.json +10 -0
  44. mangopay4_python_sdk-3.52.0/tests/fixtures/ubo_declaration_submit.json +10 -0
  45. mangopay4_python_sdk-3.52.0/tests/fixtures/ubo_update.json +20 -0
  46. mangopay4_python_sdk-3.52.0/tests/fixtures/user_list_2_per_page.json +18 -0
  47. mangopay4_python_sdk-3.52.0/tests/fixtures/user_list_2_per_page_page1.json +18 -0
  48. mangopay4_python_sdk-3.52.0/tests/fixtures/user_list_3_per_page_page2.json +26 -0
  49. mangopay4_python_sdk-3.52.0/tests/fixtures/user_list_full.json +92 -0
  50. mangopay4_python_sdk-3.52.0/tests/fixtures/user_list_page1.json +82 -0
  51. mangopay4_python_sdk-3.52.0/tests/fixtures/user_wallet.json +14 -0
  52. mangopay4_python_sdk-3.52.0/tests/images/image.jpg +0 -0
  53. mangopay4_python_sdk-3.52.0/tests/mocks.py +304 -0
  54. mangopay4_python_sdk-3.52.0/tests/resources/TestKycPageFile.png +0 -0
  55. mangopay4_python_sdk-3.52.0/tests/resources/settlement_sample.csv +8 -0
  56. mangopay4_python_sdk-3.52.0/tests/resources.py +9 -0
  57. mangopay4_python_sdk-3.52.0/tests/settings.py +15 -0
  58. mangopay4_python_sdk-3.52.0/tests/test_bankaccounts.py +501 -0
  59. mangopay4_python_sdk-3.52.0/tests/test_banking_aliases.py +93 -0
  60. mangopay4_python_sdk-3.52.0/tests/test_base.py +827 -0
  61. mangopay4_python_sdk-3.52.0/tests/test_cards.py +365 -0
  62. mangopay4_python_sdk-3.52.0/tests/test_client_wallets.py +200 -0
  63. mangopay4_python_sdk-3.52.0/tests/test_clients.py +64 -0
  64. mangopay4_python_sdk-3.52.0/tests/test_conversion.py +204 -0
  65. mangopay4_python_sdk-3.52.0/tests/test_country_authorizations.py +57 -0
  66. mangopay4_python_sdk-3.52.0/tests/test_deposits.py +69 -0
  67. mangopay4_python_sdk-3.52.0/tests/test_disputes.py +381 -0
  68. mangopay4_python_sdk-3.52.0/tests/test_documentconsult.py +176 -0
  69. mangopay4_python_sdk-3.52.0/tests/test_emoney.py +26 -0
  70. mangopay4_python_sdk-3.52.0/tests/test_idempotency.py +57 -0
  71. mangopay4_python_sdk-3.52.0/tests/test_identity_verification.py +44 -0
  72. mangopay4_python_sdk-3.52.0/tests/test_kyc_document.py +37 -0
  73. mangopay4_python_sdk-3.52.0/tests/test_kycs.py +312 -0
  74. mangopay4_python_sdk-3.52.0/tests/test_mandates.py +97 -0
  75. mangopay4_python_sdk-3.52.0/tests/test_notifications.py +277 -0
  76. mangopay4_python_sdk-3.52.0/tests/test_payins.py +2470 -0
  77. mangopay4_python_sdk-3.52.0/tests/test_payouts.py +208 -0
  78. mangopay4_python_sdk-3.52.0/tests/test_preauthorizations.py +850 -0
  79. mangopay4_python_sdk-3.52.0/tests/test_rate_limit.py +14 -0
  80. mangopay4_python_sdk-3.52.0/tests/test_recipients.py +191 -0
  81. mangopay4_python_sdk-3.52.0/tests/test_refunds.py +422 -0
  82. mangopay4_python_sdk-3.52.0/tests/test_reports.py +117 -0
  83. mangopay4_python_sdk-3.52.0/tests/test_reports_v2.py +133 -0
  84. mangopay4_python_sdk-3.52.0/tests/test_repudiations.py +15 -0
  85. mangopay4_python_sdk-3.52.0/tests/test_settlements.py +38 -0
  86. mangopay4_python_sdk-3.52.0/tests/test_tokens.py +61 -0
  87. mangopay4_python_sdk-3.52.0/tests/test_transactions.py +216 -0
  88. mangopay4_python_sdk-3.52.0/tests/test_transfers.py +226 -0
  89. mangopay4_python_sdk-3.52.0/tests/test_ubos.py +102 -0
  90. mangopay4_python_sdk-3.52.0/tests/test_users.py +990 -0
  91. mangopay4_python_sdk-3.52.0/tests/test_utils.py +49 -0
  92. mangopay4_python_sdk-3.52.0/tests/test_virtual_account.py +52 -0
  93. mangopay4_python_sdk-3.52.0/tests/test_wallets.py +356 -0
@@ -0,0 +1 @@
1
+ This SDK is a client library for interacting with the Mangopay API.
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 MANGOPAY
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
@@ -0,0 +1,8 @@
1
+ include LICENSE
2
+ include MANIFEST.in
3
+ include README.md
4
+ include DESCRIPTION.md
5
+ include Makefile
6
+
7
+ recursive-include mangopay *
8
+ recursive-include tests *
@@ -0,0 +1,8 @@
1
+ install:
2
+ pip install -r requirements/base.txt
3
+
4
+ test:
5
+ nosetests -s tests --with-coverage --cover-erase --cover-package=tests
6
+
7
+ release:
8
+ python setup.py sdist register upload -s
@@ -0,0 +1,53 @@
1
+ Metadata-Version: 2.4
2
+ Name: mangopay4-python-sdk
3
+ Version: 3.52.0
4
+ Summary: A client library written in python to work with mangopay v2 api
5
+ Home-page: https://github.com/Mangopay/mangopay2-python-sdk
6
+ Author: Mangopay (www.mangopay.com)
7
+ Author-email: support@mangopay.com
8
+ License: MIT
9
+ Keywords: mangopay api development emoney sdk
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Topic :: Software Development :: Build Tools
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ License-File: LICENSE
18
+ Requires-Dist: requests
19
+ Requires-Dist: simplejson
20
+ Requires-Dist: blinker
21
+ Requires-Dist: six
22
+ Requires-Dist: pytz
23
+ Provides-Extra: dev
24
+ Requires-Dist: responses; extra == "dev"
25
+ Requires-Dist: nose; extra == "dev"
26
+ Requires-Dist: coverage; extra == "dev"
27
+ Requires-Dist: httplib2; extra == "dev"
28
+ Requires-Dist: pyopenssl; extra == "dev"
29
+ Requires-Dist: ndg-httpsclient; extra == "dev"
30
+ Requires-Dist: pyasn1; extra == "dev"
31
+ Requires-Dist: exam; extra == "dev"
32
+ Provides-Extra: test
33
+ Requires-Dist: responses; extra == "test"
34
+ Requires-Dist: nose; extra == "test"
35
+ Requires-Dist: coverage; extra == "test"
36
+ Requires-Dist: httplib2; extra == "test"
37
+ Requires-Dist: pyopenssl; extra == "test"
38
+ Requires-Dist: ndg-httpsclient; extra == "test"
39
+ Requires-Dist: pyasn1; extra == "test"
40
+ Requires-Dist: exam; extra == "test"
41
+ Dynamic: author
42
+ Dynamic: author-email
43
+ Dynamic: classifier
44
+ Dynamic: description
45
+ Dynamic: home-page
46
+ Dynamic: keywords
47
+ Dynamic: license
48
+ Dynamic: license-file
49
+ Dynamic: provides-extra
50
+ Dynamic: requires-dist
51
+ Dynamic: summary
52
+
53
+ This SDK is a client library for interacting with the Mangopay API.
@@ -0,0 +1,53 @@
1
+ Mangopay Python SDK
2
+ =================================================
3
+
4
+ MangopaySDK is a Python client library to work with
5
+ [Mangopay REST API](http://docs.mangopay.com/api-references/).
6
+
7
+
8
+ Installation
9
+ ------------
10
+
11
+ mangopaysdk requires:
12
+
13
+ * [requests](https://pypi.python.org/pypi/requests)
14
+ * [blinker](https://pypi.python.org/pypi/blinker)
15
+
16
+ To install the package:
17
+
18
+ ```
19
+ pip install mangopay4-python-sdk
20
+ ```
21
+
22
+ By installing this package with [pip](https://pypi.python.org/pypi/pip), all dependencies will be installed for you.
23
+
24
+
25
+ Documentation
26
+ -------------
27
+
28
+ For documentation and examples of usage for the Mangopay API please refer to: [API references](https://docs.mangopay.com/api-references/)
29
+
30
+ See [docs](https://github.com/Mangopay/mangopay2-python-sdk/blob/master/docs/usage.rst) to find examples.
31
+
32
+ License
33
+ -------------------------------------------------
34
+ MangopaySDK is distributed under MIT license, see LICENSE file.
35
+
36
+ Contact
37
+ -------------------------------------------------
38
+ Report bugs or suggest features using [issue tracker at GitHub](https://github.com/Mangopay/mangopay2-python-sdk/issues).
39
+
40
+
41
+ Account creation
42
+ -------------------------------------------------
43
+ You can get yourself a free sandbox account or sign up for a production account by [registering on the Mangopay site](https://www.mangopay.com/start/) (note that validation of your production account involves several steps, so think about doing it in advance of when you actually want to go live).
44
+
45
+ Inspiration
46
+ -----------
47
+
48
+ The data model of python-mangopay is highly inspired by [peewee](https://github.com/coleifer/peewee).
49
+
50
+ Credits
51
+ -----------
52
+
53
+ The inital basis of this SDK was generously coded and donated by [Ulule](https://www.ulule.com/)
@@ -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)
@@ -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)
@@ -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