csobpg 0.0.1__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.
- csobpg-0.0.1/LICENSE +21 -0
- csobpg-0.0.1/PKG-INFO +160 -0
- csobpg-0.0.1/README.md +129 -0
- csobpg-0.0.1/csobpg/__init__.py +1 -0
- csobpg-0.0.1/csobpg/http/__init__.py +17 -0
- csobpg-0.0.1/csobpg/http/base.py +104 -0
- csobpg-0.0.1/csobpg/http/requests_client.py +46 -0
- csobpg-0.0.1/csobpg/http/urllib_client.py +62 -0
- csobpg-0.0.1/csobpg/v19/__init__.py +18 -0
- csobpg-0.0.1/csobpg/v19/api.py +209 -0
- csobpg-0.0.1/csobpg/v19/errors.py +225 -0
- csobpg-0.0.1/csobpg/v19/key.py +36 -0
- csobpg-0.0.1/csobpg/v19/request/__init__.py +9 -0
- csobpg-0.0.1/csobpg/v19/request/base.py +39 -0
- csobpg-0.0.1/csobpg/v19/request/dttm.py +30 -0
- csobpg-0.0.1/csobpg/v19/request/echo.py +16 -0
- csobpg-0.0.1/csobpg/v19/request/fields.py +64 -0
- csobpg-0.0.1/csobpg/v19/request/payment_close.py +26 -0
- csobpg-0.0.1/csobpg/v19/request/payment_init/__init__.py +143 -0
- csobpg-0.0.1/csobpg/v19/request/payment_init/cart.py +66 -0
- csobpg-0.0.1/csobpg/v19/request/payment_init/currency.py +17 -0
- csobpg-0.0.1/csobpg/v19/request/payment_init/customer/__init__.py +13 -0
- csobpg-0.0.1/csobpg/v19/request/payment_init/customer/account.py +69 -0
- csobpg-0.0.1/csobpg/v19/request/payment_init/customer/data.py +75 -0
- csobpg-0.0.1/csobpg/v19/request/payment_init/customer/login.py +47 -0
- csobpg-0.0.1/csobpg/v19/request/payment_init/order/__init__.py +16 -0
- csobpg-0.0.1/csobpg/v19/request/payment_init/order/address.py +58 -0
- csobpg-0.0.1/csobpg/v19/request/payment_init/order/data.py +142 -0
- csobpg-0.0.1/csobpg/v19/request/payment_init/order/delivery.py +38 -0
- csobpg-0.0.1/csobpg/v19/request/payment_init/payment.py +18 -0
- csobpg-0.0.1/csobpg/v19/request/payment_init/webpage.py +41 -0
- csobpg-0.0.1/csobpg/v19/request/payment_process.py +25 -0
- csobpg-0.0.1/csobpg/v19/request/payment_refund.py +26 -0
- csobpg-0.0.1/csobpg/v19/request/payment_reverse.py +19 -0
- csobpg-0.0.1/csobpg/v19/request/payment_status.py +25 -0
- csobpg-0.0.1/csobpg/v19/request/url.py +9 -0
- csobpg-0.0.1/csobpg/v19/response/__init__.py +19 -0
- csobpg-0.0.1/csobpg/v19/response/base.py +93 -0
- csobpg-0.0.1/csobpg/v19/response/payment_close.py +66 -0
- csobpg-0.0.1/csobpg/v19/response/payment_init.py +66 -0
- csobpg-0.0.1/csobpg/v19/response/payment_process.py +76 -0
- csobpg-0.0.1/csobpg/v19/response/payment_refund.py +13 -0
- csobpg-0.0.1/csobpg/v19/response/payment_reverse.py +61 -0
- csobpg-0.0.1/csobpg/v19/response/payment_status.py +319 -0
- csobpg-0.0.1/csobpg/v19/signature.py +72 -0
- csobpg-0.0.1/pyproject.toml +45 -0
csobpg-0.0.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Andrii Nechaiev
|
|
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.
|
csobpg-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: csobpg
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Library for communication with ČSOB API
|
|
5
|
+
Home-page: https://github.com/litteratum/csobpg
|
|
6
|
+
License: MIT
|
|
7
|
+
Author: Andrii Nechaiev
|
|
8
|
+
Author-email: andrewnech@gmail.com
|
|
9
|
+
Requires-Python: >=3.7,<3.12
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
14
|
+
Classifier: Programming Language :: Python
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
22
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
23
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
24
|
+
Classifier: Topic :: Utilities
|
|
25
|
+
Provides-Extra: requests-client
|
|
26
|
+
Requires-Dist: pycryptodome (>=3.17,<4.0)
|
|
27
|
+
Requires-Dist: requests (>=2.31.0,<3.0.0) ; extra == "requests-client"
|
|
28
|
+
Project-URL: Repository, https://github.com/litteratum/csobpg
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# CSOB client
|
|
32
|
+
Python library for communicating with ČSOB (<https://platbakartou.csob.cz/>) payment gateway API. The API is described here: <https://github.com/csob/paymentgateway>.
|
|
33
|
+
|
|
34
|
+
The library currently implements API v.1.9 only.
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
## Installation
|
|
38
|
+
```bash
|
|
39
|
+
pip install csobpg
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Basic usage
|
|
43
|
+
### API client initialization
|
|
44
|
+
```python
|
|
45
|
+
from csobpg.v19 import APIClient
|
|
46
|
+
|
|
47
|
+
client = APIClient("merchantId", "merch_private.key", "csob.pub", base_url=..., http_client=...)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### HTTP client
|
|
51
|
+
The library uses the `HTTPClient` interface for making HTTP requests.
|
|
52
|
+
|
|
53
|
+
It provides the following HTTP clients:
|
|
54
|
+
* `csobpg.http.urllib_client.UrllibHTTPClient` - the default one using the `urllib` under the hood
|
|
55
|
+
* `csobpg.http.requests_client.RequestsHTTPClient` - an extra HTTP client using the `requests` under the hood. The library must be installed with the `requests-client` extra to be able to use this client
|
|
56
|
+
|
|
57
|
+
It is also possible to implement the `HTTPClient` interface to use your custom HTTP client.
|
|
58
|
+
|
|
59
|
+
The HTTP client is set as the following:
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from csobpg.v19 import APIClient
|
|
63
|
+
from csobpg.http import HTTPClient
|
|
64
|
+
|
|
65
|
+
class CustomHTTPClient(HTTPClient):
|
|
66
|
+
...
|
|
67
|
+
|
|
68
|
+
client = APIClient(..., http_client=CustomHTTPClient(...))
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Payment initialization
|
|
72
|
+
```python
|
|
73
|
+
from csobpg.v19 import Cart, CartItem
|
|
74
|
+
|
|
75
|
+
response = client.init_payment(
|
|
76
|
+
order_no="2233823251",
|
|
77
|
+
total_amount=100,
|
|
78
|
+
return_url="http://127.0.0.1:5000/",
|
|
79
|
+
cart=Cart([CartItem("Apples", 1, 100)]),
|
|
80
|
+
merchant_data=b"Hello, World!",
|
|
81
|
+
)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Get payment URL
|
|
85
|
+
```python
|
|
86
|
+
url = client.get_payment_process_url(pay_id)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Process the gateway redirect
|
|
90
|
+
```python
|
|
91
|
+
response = client.process_gateway_return(data_dict)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Get payment status
|
|
95
|
+
```python
|
|
96
|
+
response = client.get_payment_status(pay_id)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Reverse payment
|
|
100
|
+
```python
|
|
101
|
+
response = client.reverse_payment(pay_id)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Refund payment
|
|
105
|
+
```python
|
|
106
|
+
response = client.refund_payment(pay_id, amount=100)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Exceptions handling
|
|
110
|
+
```python
|
|
111
|
+
from csobpg.v19.errors import APIError, APIClientError
|
|
112
|
+
from csobpg.http import HTTPRequestError
|
|
113
|
+
|
|
114
|
+
try:
|
|
115
|
+
response = client.<operation>(...)
|
|
116
|
+
except APIError as exc:
|
|
117
|
+
# handle API error
|
|
118
|
+
# it is raised on any API error. You may also catch the specific API error
|
|
119
|
+
except APIClientError as exc:
|
|
120
|
+
# handle API client error
|
|
121
|
+
# it is raised when API returns unexpected response (e.g. invalid JSON, invalid signature)
|
|
122
|
+
except HTTPRequestError as exc:
|
|
123
|
+
# handle HTTP error
|
|
124
|
+
# it is raised on any HTTP error
|
|
125
|
+
except ValueError as exc:
|
|
126
|
+
# handle value error
|
|
127
|
+
# it is raised on any library's misuse (e.g. passing invalid parameters)
|
|
128
|
+
# it always means developer's mistake
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### RSA keys management
|
|
132
|
+
The simples way to pass RSA keys is to pass their file paths:
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
from csobpg.v19 import APIClient
|
|
136
|
+
|
|
137
|
+
client = APIClient(..., "merch_private.key", "csob.pub")
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
The library will read the private key from the file when needed. The public key will be cached into the RAM.
|
|
141
|
+
|
|
142
|
+
If you want to change it, use special classes:
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
from csobpg.v19 import APIClient, FileRSAKey, CachedRSAKey
|
|
146
|
+
|
|
147
|
+
client = APIClient(..., FileRSAKey("merch_private.key"), FileRSAKey("csob.pub"))
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
You may also override the base RSAKey class to define your own key access strategy:
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
from csobpg.v19 import RSAKey
|
|
154
|
+
|
|
155
|
+
class MyRSAKey(RSAKey):
|
|
156
|
+
|
|
157
|
+
def __str__(self) -> str:
|
|
158
|
+
return "my key"
|
|
159
|
+
```
|
|
160
|
+
|
csobpg-0.0.1/README.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# CSOB client
|
|
2
|
+
Python library for communicating with ČSOB (<https://platbakartou.csob.cz/>) payment gateway API. The API is described here: <https://github.com/csob/paymentgateway>.
|
|
3
|
+
|
|
4
|
+
The library currently implements API v.1.9 only.
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
```bash
|
|
9
|
+
pip install csobpg
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Basic usage
|
|
13
|
+
### API client initialization
|
|
14
|
+
```python
|
|
15
|
+
from csobpg.v19 import APIClient
|
|
16
|
+
|
|
17
|
+
client = APIClient("merchantId", "merch_private.key", "csob.pub", base_url=..., http_client=...)
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### HTTP client
|
|
21
|
+
The library uses the `HTTPClient` interface for making HTTP requests.
|
|
22
|
+
|
|
23
|
+
It provides the following HTTP clients:
|
|
24
|
+
* `csobpg.http.urllib_client.UrllibHTTPClient` - the default one using the `urllib` under the hood
|
|
25
|
+
* `csobpg.http.requests_client.RequestsHTTPClient` - an extra HTTP client using the `requests` under the hood. The library must be installed with the `requests-client` extra to be able to use this client
|
|
26
|
+
|
|
27
|
+
It is also possible to implement the `HTTPClient` interface to use your custom HTTP client.
|
|
28
|
+
|
|
29
|
+
The HTTP client is set as the following:
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
from csobpg.v19 import APIClient
|
|
33
|
+
from csobpg.http import HTTPClient
|
|
34
|
+
|
|
35
|
+
class CustomHTTPClient(HTTPClient):
|
|
36
|
+
...
|
|
37
|
+
|
|
38
|
+
client = APIClient(..., http_client=CustomHTTPClient(...))
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Payment initialization
|
|
42
|
+
```python
|
|
43
|
+
from csobpg.v19 import Cart, CartItem
|
|
44
|
+
|
|
45
|
+
response = client.init_payment(
|
|
46
|
+
order_no="2233823251",
|
|
47
|
+
total_amount=100,
|
|
48
|
+
return_url="http://127.0.0.1:5000/",
|
|
49
|
+
cart=Cart([CartItem("Apples", 1, 100)]),
|
|
50
|
+
merchant_data=b"Hello, World!",
|
|
51
|
+
)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Get payment URL
|
|
55
|
+
```python
|
|
56
|
+
url = client.get_payment_process_url(pay_id)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Process the gateway redirect
|
|
60
|
+
```python
|
|
61
|
+
response = client.process_gateway_return(data_dict)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Get payment status
|
|
65
|
+
```python
|
|
66
|
+
response = client.get_payment_status(pay_id)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Reverse payment
|
|
70
|
+
```python
|
|
71
|
+
response = client.reverse_payment(pay_id)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Refund payment
|
|
75
|
+
```python
|
|
76
|
+
response = client.refund_payment(pay_id, amount=100)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Exceptions handling
|
|
80
|
+
```python
|
|
81
|
+
from csobpg.v19.errors import APIError, APIClientError
|
|
82
|
+
from csobpg.http import HTTPRequestError
|
|
83
|
+
|
|
84
|
+
try:
|
|
85
|
+
response = client.<operation>(...)
|
|
86
|
+
except APIError as exc:
|
|
87
|
+
# handle API error
|
|
88
|
+
# it is raised on any API error. You may also catch the specific API error
|
|
89
|
+
except APIClientError as exc:
|
|
90
|
+
# handle API client error
|
|
91
|
+
# it is raised when API returns unexpected response (e.g. invalid JSON, invalid signature)
|
|
92
|
+
except HTTPRequestError as exc:
|
|
93
|
+
# handle HTTP error
|
|
94
|
+
# it is raised on any HTTP error
|
|
95
|
+
except ValueError as exc:
|
|
96
|
+
# handle value error
|
|
97
|
+
# it is raised on any library's misuse (e.g. passing invalid parameters)
|
|
98
|
+
# it always means developer's mistake
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### RSA keys management
|
|
102
|
+
The simples way to pass RSA keys is to pass their file paths:
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
from csobpg.v19 import APIClient
|
|
106
|
+
|
|
107
|
+
client = APIClient(..., "merch_private.key", "csob.pub")
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
The library will read the private key from the file when needed. The public key will be cached into the RAM.
|
|
111
|
+
|
|
112
|
+
If you want to change it, use special classes:
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
from csobpg.v19 import APIClient, FileRSAKey, CachedRSAKey
|
|
116
|
+
|
|
117
|
+
client = APIClient(..., FileRSAKey("merch_private.key"), FileRSAKey("csob.pub"))
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
You may also override the base RSAKey class to define your own key access strategy:
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
from csobpg.v19 import RSAKey
|
|
124
|
+
|
|
125
|
+
class MyRSAKey(RSAKey):
|
|
126
|
+
|
|
127
|
+
def __str__(self) -> str:
|
|
128
|
+
return "my key"
|
|
129
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""The library package."""
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Package for dealing with HTTP."""
|
|
2
|
+
|
|
3
|
+
from .base import (
|
|
4
|
+
HTTPClient,
|
|
5
|
+
HTTPConnectionError,
|
|
6
|
+
HTTPRequestError,
|
|
7
|
+
HTTPResponse,
|
|
8
|
+
HTTPTimeoutError,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"HTTPClient",
|
|
13
|
+
"HTTPConnectionError",
|
|
14
|
+
"HTTPRequestError",
|
|
15
|
+
"HTTPResponse",
|
|
16
|
+
"HTTPTimeoutError",
|
|
17
|
+
]
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""Base client."""
|
|
2
|
+
|
|
3
|
+
import json as jsonlib
|
|
4
|
+
from abc import ABC, abstractmethod
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class HTTPRequestError(Exception):
|
|
9
|
+
"""Base HTTP request error."""
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class HTTPConnectionError(HTTPRequestError):
|
|
13
|
+
"""Any error related to connection."""
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class HTTPTimeoutError(HTTPRequestError):
|
|
17
|
+
"""HTTP request timed out."""
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class HTTPInvalidResponseError(HTTPRequestError):
|
|
21
|
+
"""HTTP response is invalid."""
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class HTTPResponse:
|
|
25
|
+
"""HTTP response wrapper."""
|
|
26
|
+
|
|
27
|
+
def __init__(
|
|
28
|
+
self, status_code: int, body: Optional[bytes], headers: dict
|
|
29
|
+
) -> None:
|
|
30
|
+
self.status_code = status_code
|
|
31
|
+
self.body = body or b""
|
|
32
|
+
self._headers = headers
|
|
33
|
+
|
|
34
|
+
self._json = None
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
# TODO: return dict
|
|
38
|
+
def json(self) -> Optional[dict]:
|
|
39
|
+
"""Return body as JSON."""
|
|
40
|
+
if self._json is not None:
|
|
41
|
+
return self._json
|
|
42
|
+
|
|
43
|
+
headers = {key.lower(): val for key, val in self._headers.items()}
|
|
44
|
+
if "application/json" in headers.get("content-type", ""):
|
|
45
|
+
try:
|
|
46
|
+
self._json = jsonlib.loads(self.body)
|
|
47
|
+
except Exception as exc:
|
|
48
|
+
raise HTTPInvalidResponseError(
|
|
49
|
+
f"Invalid JSON in response: {exc}"
|
|
50
|
+
) from exc
|
|
51
|
+
|
|
52
|
+
return self._json
|
|
53
|
+
|
|
54
|
+
def __str__(self) -> str:
|
|
55
|
+
return f"{self.__class__.__name__}(status={self.status_code})"
|
|
56
|
+
|
|
57
|
+
def __repr__(self) -> str:
|
|
58
|
+
return (
|
|
59
|
+
f"{self.__class__.__name__}("
|
|
60
|
+
f"status={self.status_code}, "
|
|
61
|
+
f"body={self.body}, "
|
|
62
|
+
f"headers={self._headers}"
|
|
63
|
+
")"
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class HTTPClient(ABC):
|
|
68
|
+
"""Base HTTP client."""
|
|
69
|
+
|
|
70
|
+
def __init__(self, request_timeout: float = 5) -> None:
|
|
71
|
+
self.request_timeout = request_timeout
|
|
72
|
+
|
|
73
|
+
# pylint: disable=too-many-arguments
|
|
74
|
+
@abstractmethod
|
|
75
|
+
def _request(
|
|
76
|
+
self,
|
|
77
|
+
method: str,
|
|
78
|
+
url: str,
|
|
79
|
+
json: Optional[dict] = None,
|
|
80
|
+
headers: Optional[dict] = None,
|
|
81
|
+
) -> HTTPResponse:
|
|
82
|
+
"""Perform request.
|
|
83
|
+
|
|
84
|
+
This method must handle all possible HTTP exceptions and raise them
|
|
85
|
+
as `HTTPRequestError`.
|
|
86
|
+
|
|
87
|
+
Headers may be extended if necessary.
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
def request(
|
|
91
|
+
self,
|
|
92
|
+
method: str,
|
|
93
|
+
url: str,
|
|
94
|
+
json: Optional[dict] = None,
|
|
95
|
+
headers: Optional[dict] = None,
|
|
96
|
+
) -> HTTPResponse:
|
|
97
|
+
"""Perform HTTP request with a given HTTP method.
|
|
98
|
+
|
|
99
|
+
:param method: HTTP method to use
|
|
100
|
+
:param url: API URL
|
|
101
|
+
:param json: JSON data to post
|
|
102
|
+
:param headers: headers
|
|
103
|
+
"""
|
|
104
|
+
return self._request(method, url, json, headers=headers)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""HTTP client which uses `requests` under the hood."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
import requests
|
|
6
|
+
|
|
7
|
+
from .base import (
|
|
8
|
+
HTTPClient,
|
|
9
|
+
HTTPConnectionError,
|
|
10
|
+
HTTPRequestError,
|
|
11
|
+
HTTPResponse,
|
|
12
|
+
HTTPTimeoutError,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class RequestsHTTPClient(HTTPClient):
|
|
17
|
+
"""`requests` HTTP client."""
|
|
18
|
+
|
|
19
|
+
def __init__(self, request_timeout: float = 5) -> None:
|
|
20
|
+
super().__init__(request_timeout)
|
|
21
|
+
self._session = requests.Session()
|
|
22
|
+
|
|
23
|
+
def _request(
|
|
24
|
+
self,
|
|
25
|
+
method: str,
|
|
26
|
+
url: str,
|
|
27
|
+
json: Optional[dict] = None,
|
|
28
|
+
headers: Optional[dict] = None,
|
|
29
|
+
) -> HTTPResponse:
|
|
30
|
+
try:
|
|
31
|
+
response: requests.Response = getattr(
|
|
32
|
+
self._session, method.lower()
|
|
33
|
+
)(url, json=json, timeout=self.request_timeout, headers=headers)
|
|
34
|
+
except ConnectionError as exc:
|
|
35
|
+
raise HTTPConnectionError(exc) from exc
|
|
36
|
+
except requests.Timeout as exc:
|
|
37
|
+
raise HTTPTimeoutError(exc) from exc
|
|
38
|
+
except requests.RequestException as exc:
|
|
39
|
+
raise HTTPRequestError(exc) from exc
|
|
40
|
+
|
|
41
|
+
return HTTPResponse(
|
|
42
|
+
response.status_code, response.content, dict(response.headers)
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
def __str__(self) -> str:
|
|
46
|
+
return self.__class__.__name__
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""HTTP client which uses `urllib` under the hood."""
|
|
2
|
+
|
|
3
|
+
import json as _jsonlib
|
|
4
|
+
import urllib
|
|
5
|
+
import urllib.error
|
|
6
|
+
import urllib.parse
|
|
7
|
+
import urllib.request
|
|
8
|
+
from typing import Optional
|
|
9
|
+
|
|
10
|
+
from .base import (
|
|
11
|
+
HTTPClient,
|
|
12
|
+
HTTPConnectionError,
|
|
13
|
+
HTTPRequestError,
|
|
14
|
+
HTTPResponse,
|
|
15
|
+
HTTPTimeoutError,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class UrllibHTTPClient(HTTPClient):
|
|
20
|
+
"""`urllib` HTTP client."""
|
|
21
|
+
|
|
22
|
+
# pylint: disable=too-many-arguments
|
|
23
|
+
def _request(
|
|
24
|
+
self,
|
|
25
|
+
method: str,
|
|
26
|
+
url: str,
|
|
27
|
+
json: Optional[dict] = None,
|
|
28
|
+
headers: Optional[dict] = None,
|
|
29
|
+
) -> HTTPResponse:
|
|
30
|
+
headers = headers or {}
|
|
31
|
+
data = None
|
|
32
|
+
|
|
33
|
+
if json:
|
|
34
|
+
headers["Content-Type"] = "application/json"
|
|
35
|
+
data = _jsonlib.dumps(json).encode()
|
|
36
|
+
|
|
37
|
+
try:
|
|
38
|
+
with urllib.request.urlopen(
|
|
39
|
+
urllib.request.Request(
|
|
40
|
+
url,
|
|
41
|
+
data=data,
|
|
42
|
+
headers=headers,
|
|
43
|
+
method=method.upper(),
|
|
44
|
+
),
|
|
45
|
+
timeout=self.request_timeout,
|
|
46
|
+
) as response:
|
|
47
|
+
return HTTPResponse(
|
|
48
|
+
response.status, response.read(), dict(response.headers)
|
|
49
|
+
)
|
|
50
|
+
except ConnectionError as exc:
|
|
51
|
+
raise HTTPConnectionError(exc) from exc
|
|
52
|
+
except TimeoutError as exc:
|
|
53
|
+
raise HTTPTimeoutError(exc) from exc
|
|
54
|
+
except urllib.error.HTTPError as exc:
|
|
55
|
+
return HTTPResponse(
|
|
56
|
+
exc.status or 500, exc.read(), dict(exc.headers)
|
|
57
|
+
)
|
|
58
|
+
except urllib.error.URLError as exc:
|
|
59
|
+
raise HTTPRequestError(exc) from exc
|
|
60
|
+
|
|
61
|
+
def __str__(self) -> str:
|
|
62
|
+
return self.__class__.__name__
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Client for API v.1.9."""
|
|
2
|
+
|
|
3
|
+
from .api import APIClient
|
|
4
|
+
from .key import FileRSAKey, RAMRSAKey, RSAKey
|
|
5
|
+
from .request.payment_init.cart import Cart, CartItem
|
|
6
|
+
from .request.payment_init.currency import Currency
|
|
7
|
+
from .response import PaymentStatus
|
|
8
|
+
|
|
9
|
+
__all__ = (
|
|
10
|
+
"APIClient",
|
|
11
|
+
"Cart",
|
|
12
|
+
"CartItem",
|
|
13
|
+
"Currency",
|
|
14
|
+
"RAMRSAKey",
|
|
15
|
+
"FileRSAKey",
|
|
16
|
+
"RSAKey",
|
|
17
|
+
"PaymentStatus",
|
|
18
|
+
)
|