csobpg 0.0.1__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.
- csobpg/__init__.py +1 -0
- csobpg/http/__init__.py +17 -0
- csobpg/http/base.py +104 -0
- csobpg/http/requests_client.py +46 -0
- csobpg/http/urllib_client.py +62 -0
- csobpg/v19/__init__.py +18 -0
- csobpg/v19/api.py +209 -0
- csobpg/v19/errors.py +225 -0
- csobpg/v19/key.py +36 -0
- csobpg/v19/request/__init__.py +9 -0
- csobpg/v19/request/base.py +39 -0
- csobpg/v19/request/dttm.py +30 -0
- csobpg/v19/request/echo.py +16 -0
- csobpg/v19/request/fields.py +64 -0
- csobpg/v19/request/payment_close.py +26 -0
- csobpg/v19/request/payment_init/__init__.py +143 -0
- csobpg/v19/request/payment_init/cart.py +66 -0
- csobpg/v19/request/payment_init/currency.py +17 -0
- csobpg/v19/request/payment_init/customer/__init__.py +13 -0
- csobpg/v19/request/payment_init/customer/account.py +69 -0
- csobpg/v19/request/payment_init/customer/data.py +75 -0
- csobpg/v19/request/payment_init/customer/login.py +47 -0
- csobpg/v19/request/payment_init/order/__init__.py +16 -0
- csobpg/v19/request/payment_init/order/address.py +58 -0
- csobpg/v19/request/payment_init/order/data.py +142 -0
- csobpg/v19/request/payment_init/order/delivery.py +38 -0
- csobpg/v19/request/payment_init/payment.py +18 -0
- csobpg/v19/request/payment_init/webpage.py +41 -0
- csobpg/v19/request/payment_process.py +25 -0
- csobpg/v19/request/payment_refund.py +26 -0
- csobpg/v19/request/payment_reverse.py +19 -0
- csobpg/v19/request/payment_status.py +25 -0
- csobpg/v19/request/url.py +9 -0
- csobpg/v19/response/__init__.py +19 -0
- csobpg/v19/response/base.py +93 -0
- csobpg/v19/response/payment_close.py +66 -0
- csobpg/v19/response/payment_init.py +66 -0
- csobpg/v19/response/payment_process.py +76 -0
- csobpg/v19/response/payment_refund.py +13 -0
- csobpg/v19/response/payment_reverse.py +61 -0
- csobpg/v19/response/payment_status.py +319 -0
- csobpg/v19/signature.py +72 -0
- csobpg-0.0.1.dist-info/LICENSE +21 -0
- csobpg-0.0.1.dist-info/METADATA +160 -0
- csobpg-0.0.1.dist-info/RECORD +46 -0
- csobpg-0.0.1.dist-info/WHEEL +4 -0
csobpg/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""The library package."""
|
csobpg/http/__init__.py
ADDED
|
@@ -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
|
+
]
|
csobpg/http/base.py
ADDED
|
@@ -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__
|
csobpg/v19/__init__.py
ADDED
|
@@ -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
|
+
)
|
csobpg/v19/api.py
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"""API client."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional, Union
|
|
4
|
+
|
|
5
|
+
from csobpg.http import HTTPClient
|
|
6
|
+
from csobpg.http.urllib_client import UrllibHTTPClient
|
|
7
|
+
|
|
8
|
+
from .key import FileRSAKey, RAMRSAKey, RSAKey
|
|
9
|
+
from .request import EchoRequest as _EchoRequest
|
|
10
|
+
from .request import PaymentCloseRequest as _PaymentCloseRequest
|
|
11
|
+
from .request import PaymentInitRequest as _PaymentInitRequest
|
|
12
|
+
from .request import PaymentProcessRequest as _PaymentProcessRequest
|
|
13
|
+
from .request import PaymentRefundRequest as _PaymentRefundRequest
|
|
14
|
+
from .request import PaymentReverseRequest as _PaymentReverseRequest
|
|
15
|
+
from .request import PaymentStatusRequest as _PaymentStatusRequest
|
|
16
|
+
from .request import payment_init as _payment_init
|
|
17
|
+
from .response import (
|
|
18
|
+
PaymentCloseResponse,
|
|
19
|
+
PaymentInitResponse,
|
|
20
|
+
PaymentProcessResponse,
|
|
21
|
+
PaymentRefundResponse,
|
|
22
|
+
PaymentReverseResponse,
|
|
23
|
+
PaymentStatusResponse,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class APIClient:
|
|
28
|
+
"""API client."""
|
|
29
|
+
|
|
30
|
+
def __init__(
|
|
31
|
+
self,
|
|
32
|
+
merchant_id: str,
|
|
33
|
+
private_key: Union[str, RSAKey],
|
|
34
|
+
public_key: Union[str, RSAKey],
|
|
35
|
+
base_url: str = "https://api.platebnibrana.csob.cz/api/v1.9",
|
|
36
|
+
http_client: HTTPClient = UrllibHTTPClient(),
|
|
37
|
+
) -> None:
|
|
38
|
+
# pylint:disable=too-many-arguments
|
|
39
|
+
self.merchant_id = merchant_id
|
|
40
|
+
self.base_url = base_url.rstrip("/")
|
|
41
|
+
|
|
42
|
+
if isinstance(private_key, str):
|
|
43
|
+
self.private_key = FileRSAKey(private_key)
|
|
44
|
+
else:
|
|
45
|
+
self.private_key = private_key
|
|
46
|
+
|
|
47
|
+
if isinstance(public_key, str):
|
|
48
|
+
self.public_key = RAMRSAKey(public_key)
|
|
49
|
+
else:
|
|
50
|
+
self.public_key = public_key
|
|
51
|
+
|
|
52
|
+
self._http_client = http_client
|
|
53
|
+
|
|
54
|
+
def init_payment(
|
|
55
|
+
self,
|
|
56
|
+
order_no: str,
|
|
57
|
+
total_amount: int,
|
|
58
|
+
return_url: str,
|
|
59
|
+
return_method: _payment_init.ReturnMethod = _payment_init.ReturnMethod.POST,
|
|
60
|
+
payment_operation: _payment_init.PaymentOperation = _payment_init.PaymentOperation.PAYMENT,
|
|
61
|
+
payment_method: _payment_init.PaymentMethod = _payment_init.PaymentMethod.CARD,
|
|
62
|
+
currency: _payment_init.Currency = _payment_init.Currency.CZK,
|
|
63
|
+
close_payment: bool = True,
|
|
64
|
+
ttl_sec: int = 600,
|
|
65
|
+
cart: Optional[_payment_init.Cart] = None,
|
|
66
|
+
customer: Optional[_payment_init.CustomerData] = None,
|
|
67
|
+
order: Optional[_payment_init.OrderData] = None,
|
|
68
|
+
merchant_data: Optional[bytes] = None,
|
|
69
|
+
customer_id: Optional[str] = None,
|
|
70
|
+
payment_expiry: Optional[int] = None,
|
|
71
|
+
# pylint:disable=line-too-long, too-many-locals
|
|
72
|
+
page_appearance: _payment_init.WebPageAppearanceConfig = _payment_init.WebPageAppearanceConfig(),
|
|
73
|
+
) -> PaymentInitResponse:
|
|
74
|
+
"""Init payment."""
|
|
75
|
+
request = _PaymentInitRequest(
|
|
76
|
+
self.merchant_id,
|
|
77
|
+
str(self.private_key),
|
|
78
|
+
order_no=order_no,
|
|
79
|
+
total_amount=total_amount,
|
|
80
|
+
return_url=return_url,
|
|
81
|
+
return_method=return_method,
|
|
82
|
+
payment_operation=payment_operation,
|
|
83
|
+
payment_method=payment_method,
|
|
84
|
+
currency=currency,
|
|
85
|
+
close_payment=close_payment,
|
|
86
|
+
ttl_sec=ttl_sec,
|
|
87
|
+
cart=cart,
|
|
88
|
+
customer=customer,
|
|
89
|
+
order=order,
|
|
90
|
+
merchant_data=merchant_data,
|
|
91
|
+
customer_id=customer_id,
|
|
92
|
+
payment_expiry=payment_expiry,
|
|
93
|
+
page_appearance=page_appearance,
|
|
94
|
+
)
|
|
95
|
+
return PaymentInitResponse.from_json(
|
|
96
|
+
self._call_api(
|
|
97
|
+
"post",
|
|
98
|
+
self._build_url(request.endpoint),
|
|
99
|
+
json=request.to_json(),
|
|
100
|
+
),
|
|
101
|
+
str(self.public_key),
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
def get_payment_status(self, pay_id: str) -> PaymentStatusResponse:
|
|
105
|
+
"""Request payment status information."""
|
|
106
|
+
request = _PaymentStatusRequest(
|
|
107
|
+
self.merchant_id, str(self.private_key), pay_id
|
|
108
|
+
)
|
|
109
|
+
return PaymentStatusResponse.from_json(
|
|
110
|
+
self._call_api("get", url=self._build_url(request.endpoint)),
|
|
111
|
+
str(self.public_key),
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
def reverse_payment(self, pay_id: str) -> PaymentReverseResponse:
|
|
115
|
+
"""Reverse payment.
|
|
116
|
+
|
|
117
|
+
:param pay_id: payment ID
|
|
118
|
+
"""
|
|
119
|
+
request = _PaymentReverseRequest(
|
|
120
|
+
self.merchant_id, str(self.private_key), pay_id
|
|
121
|
+
)
|
|
122
|
+
return PaymentReverseResponse.from_json(
|
|
123
|
+
self._call_api(
|
|
124
|
+
"put", self._build_url(request.endpoint), request.to_json()
|
|
125
|
+
),
|
|
126
|
+
str(self.public_key),
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
def close_payment(
|
|
130
|
+
self, pay_id: str, total_amount: Optional[int] = None
|
|
131
|
+
) -> PaymentCloseResponse:
|
|
132
|
+
"""Close payment (move to settlement).
|
|
133
|
+
|
|
134
|
+
:param total_amount: close the payment with this amount. It must be
|
|
135
|
+
less or equal to the original amount and provided in hundredths of
|
|
136
|
+
the base currency
|
|
137
|
+
"""
|
|
138
|
+
request = _PaymentCloseRequest(
|
|
139
|
+
self.merchant_id, str(self.private_key), pay_id, total_amount
|
|
140
|
+
)
|
|
141
|
+
return PaymentCloseResponse.from_json(
|
|
142
|
+
self._call_api(
|
|
143
|
+
"put",
|
|
144
|
+
self._build_url(request.endpoint),
|
|
145
|
+
json=request.to_json(),
|
|
146
|
+
),
|
|
147
|
+
str(self.public_key),
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
def refund_payment(
|
|
151
|
+
self, pay_id: str, amount: Optional[int] = None
|
|
152
|
+
) -> PaymentRefundResponse:
|
|
153
|
+
"""Refund payment.
|
|
154
|
+
|
|
155
|
+
:param pay_id: payment ID
|
|
156
|
+
:param amount: amount to refund. It must be less or equal to the
|
|
157
|
+
original amount and provided in hundredths of the base currency.
|
|
158
|
+
If not provided, the full amount will be refunded.
|
|
159
|
+
"""
|
|
160
|
+
request = _PaymentRefundRequest(
|
|
161
|
+
self.merchant_id, str(self.private_key), pay_id, amount
|
|
162
|
+
)
|
|
163
|
+
return PaymentRefundResponse.from_json(
|
|
164
|
+
self._call_api(
|
|
165
|
+
"put", self._build_url(request.endpoint), request.to_json()
|
|
166
|
+
),
|
|
167
|
+
str(self.public_key),
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
def get_payment_process_url(self, pay_id: str) -> str:
|
|
171
|
+
"""Build payment URL.
|
|
172
|
+
|
|
173
|
+
:param pay_id: pay_id obtained from `payment_init`
|
|
174
|
+
:return: url to process payment
|
|
175
|
+
"""
|
|
176
|
+
return self._build_url(
|
|
177
|
+
_PaymentProcessRequest(
|
|
178
|
+
self.merchant_id, str(self.private_key), pay_id
|
|
179
|
+
).endpoint
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
def echo(self) -> None:
|
|
183
|
+
"""Make an echo request."""
|
|
184
|
+
request = _EchoRequest(self.merchant_id, str(self.private_key))
|
|
185
|
+
self._call_api(
|
|
186
|
+
"post", self._build_url(request.endpoint), request.to_json()
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
def process_gateway_return(self, datadict: dict) -> PaymentProcessResponse:
|
|
190
|
+
"""Process gateway return."""
|
|
191
|
+
data = {}
|
|
192
|
+
|
|
193
|
+
for key in datadict:
|
|
194
|
+
data[key] = (
|
|
195
|
+
int(datadict[key])
|
|
196
|
+
if key in ("resultCode", "paymentStatus")
|
|
197
|
+
else datadict[key]
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
return PaymentProcessResponse.from_json(data, str(self.public_key))
|
|
201
|
+
|
|
202
|
+
def _call_api(
|
|
203
|
+
self, method: str, url: str, json: Optional[dict] = None
|
|
204
|
+
) -> dict:
|
|
205
|
+
http_response = self._http_client.request(method, url, json)
|
|
206
|
+
return http_response.json or {}
|
|
207
|
+
|
|
208
|
+
def _build_url(self, endpoint: str) -> str:
|
|
209
|
+
return f"{self.base_url}/{endpoint.strip('/')}/"
|
csobpg/v19/errors.py
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
"""API errors."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class APIClientError(Exception):
|
|
5
|
+
"""API client error."""
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class APIInvalidSignatureError(APIClientError):
|
|
9
|
+
"""API returned invalid signature."""
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class APIError(Exception):
|
|
13
|
+
"""API error."""
|
|
14
|
+
|
|
15
|
+
def __init__(self, code: int, message: str) -> None:
|
|
16
|
+
"""Init API error.
|
|
17
|
+
|
|
18
|
+
:param code: error code
|
|
19
|
+
:message: error message
|
|
20
|
+
"""
|
|
21
|
+
self.code = code
|
|
22
|
+
self.message = message
|
|
23
|
+
super().__init__(f"{self.code}: {self.message}")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class APIMissingParamError(APIError):
|
|
27
|
+
"""API missing param error."""
|
|
28
|
+
|
|
29
|
+
def __init__(self, message: str) -> None:
|
|
30
|
+
super().__init__(100, message)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class APIInvalidParamError(APIError):
|
|
34
|
+
"""API invalid param error."""
|
|
35
|
+
|
|
36
|
+
def __init__(self, message: str) -> None:
|
|
37
|
+
super().__init__(110, message)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class APIMerchantBlockedError(APIError):
|
|
41
|
+
"""API merchant is blocked error."""
|
|
42
|
+
|
|
43
|
+
def __init__(self, message: str) -> None:
|
|
44
|
+
super().__init__(120, message)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class APISessionExpiredError(APIError):
|
|
48
|
+
"""API session (request) expired error."""
|
|
49
|
+
|
|
50
|
+
def __init__(self, message: str) -> None:
|
|
51
|
+
super().__init__(130, message)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class APIPaymentNotFoundError(APIError):
|
|
55
|
+
"""API payment not found error."""
|
|
56
|
+
|
|
57
|
+
def __init__(self, message: str) -> None:
|
|
58
|
+
super().__init__(140, message)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class APIPaymentInInvalidStateError(APIError):
|
|
62
|
+
"""API payment is in invalid state error."""
|
|
63
|
+
|
|
64
|
+
def __init__(self, message: str) -> None:
|
|
65
|
+
super().__init__(150, message)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class APIPaymentMethodDisabledError(APIError):
|
|
69
|
+
"""API payment method is disabled error."""
|
|
70
|
+
|
|
71
|
+
def __init__(self, message: str) -> None:
|
|
72
|
+
super().__init__(160, message)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class APIPaymentMethodUnavailableError(APIError):
|
|
76
|
+
"""API payment method is unavailable error."""
|
|
77
|
+
|
|
78
|
+
def __init__(self, message: str) -> None:
|
|
79
|
+
super().__init__(170, message)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class APIOperationNotAllowedError(APIError):
|
|
83
|
+
"""API operation is not allowed error."""
|
|
84
|
+
|
|
85
|
+
def __init__(self, message: str) -> None:
|
|
86
|
+
super().__init__(180, message)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class APIPaymentMethodError(APIError):
|
|
90
|
+
"""API payment method error."""
|
|
91
|
+
|
|
92
|
+
def __init__(self, message: str) -> None:
|
|
93
|
+
super().__init__(190, message)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class APIDuplicatePurchaseIDError(APIError):
|
|
97
|
+
"""API duplicate purchaseId error."""
|
|
98
|
+
|
|
99
|
+
def __init__(self, message: str) -> None:
|
|
100
|
+
super().__init__(200, message)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class APIEETRejectedError(APIError):
|
|
104
|
+
"""API EET rejected error."""
|
|
105
|
+
|
|
106
|
+
def __init__(self, message: str) -> None:
|
|
107
|
+
super().__init__(500, message)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class APIMallPaymentPrecheckDeclinedError(APIError):
|
|
111
|
+
"""API mall payment declined in pre-check error."""
|
|
112
|
+
|
|
113
|
+
def __init__(self, message: str) -> None:
|
|
114
|
+
super().__init__(600, message)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class APIOneClickTemplateNotFoundError(APIError):
|
|
118
|
+
"""API one click template not found error."""
|
|
119
|
+
|
|
120
|
+
def __init__(self, message: str) -> None:
|
|
121
|
+
super().__init__(700, message)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class APIOneClickTemplatePaymentExpiredError(APIError):
|
|
125
|
+
"""API one click template payment expired error."""
|
|
126
|
+
|
|
127
|
+
def __init__(self, message: str) -> None:
|
|
128
|
+
super().__init__(710, message)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class APIOneClickTemplateCardExpiredError(APIError):
|
|
132
|
+
"""API one click template card expired error."""
|
|
133
|
+
|
|
134
|
+
def __init__(self, message: str) -> None:
|
|
135
|
+
super().__init__(720, message)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class APIOneClickTemplateCustomerRejectedError(APIError):
|
|
139
|
+
"""API one click template customer rejected error.
|
|
140
|
+
|
|
141
|
+
the OneClick template was cancelled at the customer's request.
|
|
142
|
+
"""
|
|
143
|
+
|
|
144
|
+
def __init__(self, message: str) -> None:
|
|
145
|
+
super().__init__(730, message)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
class APIOneClickTemplatePaymentReversedError(APIError):
|
|
149
|
+
"""API one click template payment reversed error."""
|
|
150
|
+
|
|
151
|
+
def __init__(self, message: str) -> None:
|
|
152
|
+
super().__init__(740, message)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class APICardholderAccountClosedError(APIError):
|
|
156
|
+
"""API cardholder account closed error."""
|
|
157
|
+
|
|
158
|
+
def __init__(self, message: str) -> None:
|
|
159
|
+
super().__init__(750, message)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class APICustomerNotFoundError(APIError):
|
|
163
|
+
"""API customer not found error."""
|
|
164
|
+
|
|
165
|
+
def __init__(self, message: str) -> None:
|
|
166
|
+
super().__init__(800, message)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
class APICustomerFoundNoSavedCardsError(APIError):
|
|
170
|
+
"""API customer found no saved cards error."""
|
|
171
|
+
|
|
172
|
+
def __init__(self, message: str) -> None:
|
|
173
|
+
super().__init__(810, message)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
class APICustomerFoundSavedCardsFoundError(APIError):
|
|
177
|
+
"""API customer found saved cards found error."""
|
|
178
|
+
|
|
179
|
+
def __init__(self, message: str) -> None:
|
|
180
|
+
super().__init__(820, message)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class APIInternalError(APIError):
|
|
184
|
+
"""API internal error."""
|
|
185
|
+
|
|
186
|
+
def __init__(self, message: str) -> None:
|
|
187
|
+
super().__init__(900, message)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
_ERROR_FOR_CODE = {
|
|
191
|
+
100: APIMissingParamError,
|
|
192
|
+
110: APIInvalidParamError,
|
|
193
|
+
120: APIMerchantBlockedError,
|
|
194
|
+
130: APISessionExpiredError,
|
|
195
|
+
140: APIPaymentNotFoundError,
|
|
196
|
+
150: APIPaymentInInvalidStateError,
|
|
197
|
+
160: APIPaymentMethodDisabledError,
|
|
198
|
+
170: APIPaymentMethodUnavailableError,
|
|
199
|
+
180: APIOperationNotAllowedError,
|
|
200
|
+
190: APIPaymentMethodError,
|
|
201
|
+
200: APIDuplicatePurchaseIDError,
|
|
202
|
+
500: APIEETRejectedError,
|
|
203
|
+
600: APIMallPaymentPrecheckDeclinedError,
|
|
204
|
+
700: APIOneClickTemplateNotFoundError,
|
|
205
|
+
710: APIOneClickTemplatePaymentExpiredError,
|
|
206
|
+
720: APIOneClickTemplateCardExpiredError,
|
|
207
|
+
730: APIOneClickTemplateCustomerRejectedError,
|
|
208
|
+
740: APIOneClickTemplatePaymentReversedError,
|
|
209
|
+
750: APICardholderAccountClosedError,
|
|
210
|
+
800: APICustomerNotFoundError,
|
|
211
|
+
810: APICustomerFoundNoSavedCardsError,
|
|
212
|
+
820: APICustomerFoundSavedCardsFoundError,
|
|
213
|
+
900: APIInternalError,
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def raise_for_result_code(result_code: int, result_message: str) -> None:
|
|
218
|
+
"""Raise APIError if resultCode != 0."""
|
|
219
|
+
if result_code == 0:
|
|
220
|
+
return
|
|
221
|
+
|
|
222
|
+
try:
|
|
223
|
+
raise _ERROR_FOR_CODE[result_code](result_message)
|
|
224
|
+
except KeyError:
|
|
225
|
+
raise APIError(result_code, result_message) from None
|