paytechuz 0.2.19__py3-none-any.whl → 0.2.21__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.
Potentially problematic release.
This version of paytechuz might be problematic. Click here for more details.
- core/__init__.py +0 -0
- core/base.py +97 -0
- core/constants.py +68 -0
- core/exceptions.py +190 -0
- core/http.py +268 -0
- core/payme/errors.py +25 -0
- core/utils.py +192 -0
- gateways/__init__.py +0 -0
- gateways/click/__init__.py +0 -0
- gateways/click/client.py +199 -0
- gateways/click/merchant.py +265 -0
- gateways/click/webhook.py +227 -0
- gateways/payme/__init__.py +0 -0
- gateways/payme/cards.py +222 -0
- gateways/payme/client.py +262 -0
- gateways/payme/receipts.py +336 -0
- gateways/payme/webhook.py +379 -0
- integrations/__init__.py +0 -0
- integrations/django/__init__.py +4 -0
- integrations/django/admin.py +78 -0
- integrations/django/apps.py +21 -0
- integrations/django/migrations/0001_initial.py +51 -0
- integrations/django/migrations/__init__.py +3 -0
- integrations/django/models.py +174 -0
- integrations/django/signals.py +46 -0
- integrations/django/views.py +102 -0
- integrations/django/webhooks.py +884 -0
- integrations/fastapi/__init__.py +21 -0
- integrations/fastapi/models.py +183 -0
- integrations/fastapi/routes.py +1038 -0
- integrations/fastapi/schemas.py +116 -0
- paytechuz/integrations/fastapi/routes.py +32 -27
- {paytechuz-0.2.19.dist-info → paytechuz-0.2.21.dist-info}/METADATA +33 -6
- paytechuz-0.2.21.dist-info/RECORD +67 -0
- {paytechuz-0.2.19.dist-info → paytechuz-0.2.21.dist-info}/WHEEL +1 -1
- paytechuz-0.2.21.dist-info/top_level.txt +4 -0
- paytechuz-0.2.19.dist-info/RECORD +0 -36
- paytechuz-0.2.19.dist-info/top_level.txt +0 -1
core/__init__.py
ADDED
|
File without changes
|
core/base.py
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base classes for payment gateways.
|
|
3
|
+
"""
|
|
4
|
+
from abc import ABC, abstractmethod
|
|
5
|
+
from typing import Dict, Any, Optional, Union
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class BasePaymentGateway(ABC):
|
|
9
|
+
"""
|
|
10
|
+
Base class for all payment gateways.
|
|
11
|
+
|
|
12
|
+
This abstract class defines the common interface that all payment gateways
|
|
13
|
+
must implement. It provides a consistent API for creating, checking, and
|
|
14
|
+
canceling payments regardless of the underlying payment provider.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(self, is_test_mode: bool = False):
|
|
18
|
+
"""
|
|
19
|
+
Initialize the payment gateway.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
is_test_mode (bool): Whether to use the test environment
|
|
23
|
+
"""
|
|
24
|
+
self.is_test_mode = is_test_mode
|
|
25
|
+
|
|
26
|
+
@abstractmethod
|
|
27
|
+
def create_payment(
|
|
28
|
+
self,
|
|
29
|
+
id: Union[int, str],
|
|
30
|
+
amount: Union[int, float, str],
|
|
31
|
+
**kwargs
|
|
32
|
+
) -> Dict[str, Any]:
|
|
33
|
+
"""
|
|
34
|
+
Create a payment.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
id: The account ID or order ID
|
|
38
|
+
amount: The payment amount
|
|
39
|
+
**kwargs: Additional parameters specific to the payment gateway
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
Dict containing payment details including transaction ID
|
|
43
|
+
"""
|
|
44
|
+
raise NotImplementedError
|
|
45
|
+
|
|
46
|
+
@abstractmethod
|
|
47
|
+
def check_payment(self, transaction_id: str) -> Dict[str, Any]:
|
|
48
|
+
"""
|
|
49
|
+
Check payment status.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
transaction_id: The transaction ID to check
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
Dict containing payment status and details
|
|
56
|
+
"""
|
|
57
|
+
raise NotImplementedError
|
|
58
|
+
|
|
59
|
+
@abstractmethod
|
|
60
|
+
def cancel_payment(
|
|
61
|
+
self,
|
|
62
|
+
transaction_id: str,
|
|
63
|
+
reason: Optional[str] = None
|
|
64
|
+
) -> Dict[str, Any]:
|
|
65
|
+
"""
|
|
66
|
+
Cancel payment.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
transaction_id: The transaction ID to cancel
|
|
70
|
+
reason: Optional reason for cancellation
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
Dict containing cancellation status and details
|
|
74
|
+
"""
|
|
75
|
+
raise NotImplementedError
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class BaseWebhookHandler(ABC):
|
|
79
|
+
"""
|
|
80
|
+
Base class for payment gateway webhook handlers.
|
|
81
|
+
|
|
82
|
+
This abstract class defines the common interface for handling webhook
|
|
83
|
+
callbacks from payment gateways.
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
@abstractmethod
|
|
87
|
+
def handle_webhook(self, data: Dict[str, Any]) -> Dict[str, Any]:
|
|
88
|
+
"""
|
|
89
|
+
Handle webhook data from payment gateway.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
data: The webhook data received from the payment gateway
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
Dict containing the response to be sent back to the payment gateway
|
|
96
|
+
"""
|
|
97
|
+
raise NotImplementedError
|
core/constants.py
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Constants for payment gateways.
|
|
3
|
+
"""
|
|
4
|
+
from enum import Enum
|
|
5
|
+
|
|
6
|
+
class TransactionState(Enum):
|
|
7
|
+
"""Transaction states."""
|
|
8
|
+
CREATED = 0
|
|
9
|
+
INITIATING = 1
|
|
10
|
+
SUCCESSFULLY = 2
|
|
11
|
+
CANCELED = -2
|
|
12
|
+
CANCELED_DURING_INIT = -1
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class PaymentGateway(Enum):
|
|
16
|
+
"""Payment gateway types."""
|
|
17
|
+
PAYME = "payme"
|
|
18
|
+
CLICK = "click"
|
|
19
|
+
|
|
20
|
+
class PaymeEndpoints:
|
|
21
|
+
"""Payme API endpoints."""
|
|
22
|
+
RECEIPTS_CREATE = "receipts/create"
|
|
23
|
+
RECEIPTS_PAY = "receipts/pay"
|
|
24
|
+
RECEIPTS_SEND = "receipts/send"
|
|
25
|
+
RECEIPTS_CHECK = "receipts/check"
|
|
26
|
+
RECEIPTS_CANCEL = "receipts/cancel"
|
|
27
|
+
RECEIPTS_GET = "receipts/get"
|
|
28
|
+
CARDS_CREATE = "cards/create"
|
|
29
|
+
CARDS_VERIFY = "cards/verify"
|
|
30
|
+
CARDS_CHECK = "cards/check"
|
|
31
|
+
CARDS_REMOVE = "cards/remove"
|
|
32
|
+
CARDS_GET_VERIFY_CODE = "cards/get_verify_code"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class PaymeNetworks:
|
|
36
|
+
"""Payme API networks."""
|
|
37
|
+
TEST_NET = "https://checkout.test.paycom.uz/api"
|
|
38
|
+
PROD_NET = "https://checkout.paycom.uz/api"
|
|
39
|
+
|
|
40
|
+
class ClickEndpoints:
|
|
41
|
+
"""Click API endpoints."""
|
|
42
|
+
PREPARE = "prepare"
|
|
43
|
+
COMPLETE = "complete"
|
|
44
|
+
MERCHANT_API = "merchant/api"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class ClickNetworks:
|
|
48
|
+
"""Click API networks."""
|
|
49
|
+
TEST_NET = "https://api.click.uz/v2/merchant"
|
|
50
|
+
PROD_NET = "https://api.click.uz/v2/merchant"
|
|
51
|
+
|
|
52
|
+
class ClickActions:
|
|
53
|
+
"""Click API actions."""
|
|
54
|
+
PREPARE = 0
|
|
55
|
+
COMPLETE = 1
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class PaymeCancelReason:
|
|
59
|
+
"""Payme cancel reason codes."""
|
|
60
|
+
REASON_USER_NOT_FOUND = 1
|
|
61
|
+
REASON_DEBIT_OPERATION_FAILED = 2
|
|
62
|
+
REASON_EXECUTION_ERROR = 3
|
|
63
|
+
REASON_TIMEOUT = 4
|
|
64
|
+
REASON_FUND_RETURNED = 5
|
|
65
|
+
REASON_UNKNOWN = 6
|
|
66
|
+
REASON_CANCELLED_BY_USER = 7
|
|
67
|
+
REASON_SUSPICIOUS_OPERATION = 8
|
|
68
|
+
REASON_MERCHANT_DECISION = 9
|
core/exceptions.py
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Exceptions for payment gateways.
|
|
3
|
+
"""
|
|
4
|
+
from typing import Optional, Dict, Any
|
|
5
|
+
|
|
6
|
+
class PaymentException(Exception):
|
|
7
|
+
"""Base exception for all payment exceptions."""
|
|
8
|
+
code = "payment_error"
|
|
9
|
+
message = "Payment error occurred"
|
|
10
|
+
data: Dict[str, Any] = {}
|
|
11
|
+
|
|
12
|
+
def __init__(self, message: Optional[str] = None, code: Optional[str] = None, data: Optional[Dict[str, Any]] = None):
|
|
13
|
+
"""
|
|
14
|
+
Initialize the exception.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
message: Custom error message
|
|
18
|
+
code: Custom error code
|
|
19
|
+
data: Additional error data
|
|
20
|
+
"""
|
|
21
|
+
self.message = message or self.message
|
|
22
|
+
self.code = code or self.code
|
|
23
|
+
self.data = data or self.data
|
|
24
|
+
super().__init__(self.message)
|
|
25
|
+
|
|
26
|
+
def as_dict(self) -> Dict[str, Any]:
|
|
27
|
+
"""
|
|
28
|
+
Convert the exception to a dictionary.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
Dict containing error details
|
|
32
|
+
"""
|
|
33
|
+
return {
|
|
34
|
+
"code": self.code,
|
|
35
|
+
"message": self.message,
|
|
36
|
+
"data": self.data
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
# Authentication and Authorization Exceptions
|
|
40
|
+
class AuthenticationError(PaymentException):
|
|
41
|
+
"""Exception raised when authentication fails."""
|
|
42
|
+
code = "authentication_error"
|
|
43
|
+
message = "Authentication failed"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class InvalidCredentials(AuthenticationError):
|
|
48
|
+
"""Exception raised when credentials are invalid."""
|
|
49
|
+
code = "invalid_credentials"
|
|
50
|
+
message = "Invalid credentials provided"
|
|
51
|
+
|
|
52
|
+
class PermissionDenied(AuthenticationError):
|
|
53
|
+
"""Exception raised when permission is denied."""
|
|
54
|
+
code = "permission_denied"
|
|
55
|
+
message = "Permission denied"
|
|
56
|
+
|
|
57
|
+
# Transaction Exceptions
|
|
58
|
+
class TransactionError(PaymentException):
|
|
59
|
+
"""Base exception for transaction errors."""
|
|
60
|
+
code = "transaction_error"
|
|
61
|
+
message = "Transaction error occurred"
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class TransactionNotFound(TransactionError):
|
|
65
|
+
"""Exception raised when a transaction is not found."""
|
|
66
|
+
code = "transaction_not_found"
|
|
67
|
+
message = "Transaction not found"
|
|
68
|
+
|
|
69
|
+
class TransactionAlreadyExists(TransactionError):
|
|
70
|
+
"""Exception raised when a transaction already exists."""
|
|
71
|
+
code = "transaction_already_exists"
|
|
72
|
+
message = "Transaction already exists"
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class TransactionCancelled(TransactionError):
|
|
76
|
+
"""Exception raised when a transaction is cancelled."""
|
|
77
|
+
code = "transaction_cancelled"
|
|
78
|
+
message = "Transaction has been cancelled"
|
|
79
|
+
|
|
80
|
+
class TransactionInProgress(TransactionError):
|
|
81
|
+
"""Exception raised when a transaction is in progress."""
|
|
82
|
+
code = "transaction_in_progress"
|
|
83
|
+
message = "Transaction is in progress"
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class TransactionCompleted(TransactionError):
|
|
87
|
+
"""Exception raised when a transaction is already completed."""
|
|
88
|
+
code = "transaction_completed"
|
|
89
|
+
message = "Transaction is already completed"
|
|
90
|
+
|
|
91
|
+
# Account Exceptions
|
|
92
|
+
class AccountError(PaymentException):
|
|
93
|
+
"""Base exception for account errors."""
|
|
94
|
+
code = "account_error"
|
|
95
|
+
message = "Account error occurred"
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class AccountNotFound(AccountError):
|
|
99
|
+
"""Exception raised when an account is not found."""
|
|
100
|
+
code = "account_not_found"
|
|
101
|
+
message = "Account not found"
|
|
102
|
+
|
|
103
|
+
class InvalidAccount(AccountError):
|
|
104
|
+
"""Exception raised when an account is invalid."""
|
|
105
|
+
code = "invalid_account"
|
|
106
|
+
message = "Invalid account"
|
|
107
|
+
|
|
108
|
+
# Amount Exceptions
|
|
109
|
+
class AmountError(PaymentException):
|
|
110
|
+
"""Base exception for amount errors."""
|
|
111
|
+
code = "amount_error"
|
|
112
|
+
message = "Amount error occurred"
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class InvalidAmount(AmountError):
|
|
116
|
+
"""Exception raised when an amount is invalid."""
|
|
117
|
+
code = "invalid_amount"
|
|
118
|
+
message = "Invalid amount"
|
|
119
|
+
|
|
120
|
+
class InsufficientFunds(AmountError):
|
|
121
|
+
"""Exception raised when there are insufficient funds."""
|
|
122
|
+
code = "insufficient_funds"
|
|
123
|
+
message = "Insufficient funds"
|
|
124
|
+
|
|
125
|
+
# Method Exceptions
|
|
126
|
+
class MethodError(PaymentException):
|
|
127
|
+
"""Base exception for method errors."""
|
|
128
|
+
code = "method_error"
|
|
129
|
+
message = "Method error occurred"
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class MethodNotFound(MethodError):
|
|
133
|
+
"""Exception raised when a method is not found."""
|
|
134
|
+
code = "method_not_found"
|
|
135
|
+
message = "Method not found"
|
|
136
|
+
|
|
137
|
+
class UnsupportedMethod(MethodError):
|
|
138
|
+
"""Exception raised when a method is not supported."""
|
|
139
|
+
code = "unsupported_method"
|
|
140
|
+
message = "Method not supported"
|
|
141
|
+
|
|
142
|
+
# System Exceptions
|
|
143
|
+
class SystemError(PaymentException):
|
|
144
|
+
"""Base exception for system errors."""
|
|
145
|
+
code = "system_error"
|
|
146
|
+
message = "System error occurred"
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
class InternalServiceError(SystemError):
|
|
150
|
+
"""Exception raised when an internal service error occurs."""
|
|
151
|
+
code = "internal_service_error"
|
|
152
|
+
message = "Internal service error"
|
|
153
|
+
|
|
154
|
+
class ExternalServiceError(SystemError):
|
|
155
|
+
"""Exception raised when an external service error occurs."""
|
|
156
|
+
code = "external_service_error"
|
|
157
|
+
message = "External service error"
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class TimeoutError(SystemError):
|
|
161
|
+
"""Exception raised when a timeout occurs."""
|
|
162
|
+
code = "timeout_error"
|
|
163
|
+
message = "Operation timed out"
|
|
164
|
+
|
|
165
|
+
# Create a list of exceptions that should not be wrapped
|
|
166
|
+
exception_whitelist = (
|
|
167
|
+
PaymentException,
|
|
168
|
+
AuthenticationError,
|
|
169
|
+
InvalidCredentials,
|
|
170
|
+
PermissionDenied,
|
|
171
|
+
TransactionError,
|
|
172
|
+
TransactionNotFound,
|
|
173
|
+
TransactionAlreadyExists,
|
|
174
|
+
TransactionCancelled,
|
|
175
|
+
TransactionInProgress,
|
|
176
|
+
TransactionCompleted,
|
|
177
|
+
AccountError,
|
|
178
|
+
AccountNotFound,
|
|
179
|
+
InvalidAccount,
|
|
180
|
+
AmountError,
|
|
181
|
+
InvalidAmount,
|
|
182
|
+
InsufficientFunds,
|
|
183
|
+
MethodError,
|
|
184
|
+
MethodNotFound,
|
|
185
|
+
UnsupportedMethod,
|
|
186
|
+
SystemError,
|
|
187
|
+
InternalServiceError,
|
|
188
|
+
ExternalServiceError,
|
|
189
|
+
TimeoutError,
|
|
190
|
+
)
|
core/http.py
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
"""
|
|
2
|
+
HTTP client for making requests to payment gateways.
|
|
3
|
+
"""
|
|
4
|
+
import json
|
|
5
|
+
import logging
|
|
6
|
+
from typing import Dict, Any, Optional, Union, List
|
|
7
|
+
|
|
8
|
+
import requests
|
|
9
|
+
from requests.exceptions import RequestException, Timeout, ConnectionError
|
|
10
|
+
|
|
11
|
+
from paytechuz.core.exceptions import (
|
|
12
|
+
ExternalServiceError,
|
|
13
|
+
TimeoutError as PaymentTimeoutError,
|
|
14
|
+
InternalServiceError
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
class HttpClient:
|
|
20
|
+
"""
|
|
21
|
+
HTTP client for making requests to payment gateways.
|
|
22
|
+
|
|
23
|
+
This class provides a simple interface for making HTTP requests to payment
|
|
24
|
+
gateways with proper error handling and logging.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
base_url: str,
|
|
30
|
+
headers: Optional[Dict[str, str]] = None,
|
|
31
|
+
timeout: int = 30,
|
|
32
|
+
verify_ssl: bool = True
|
|
33
|
+
):
|
|
34
|
+
"""
|
|
35
|
+
Initialize the HTTP client.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
base_url: Base URL for the API
|
|
39
|
+
headers: Default headers to include in all requests
|
|
40
|
+
timeout: Request timeout in seconds
|
|
41
|
+
verify_ssl: Whether to verify SSL certificates
|
|
42
|
+
"""
|
|
43
|
+
self.base_url = base_url.rstrip('/')
|
|
44
|
+
self.headers = headers or {}
|
|
45
|
+
self.timeout = timeout
|
|
46
|
+
self.verify_ssl = verify_ssl
|
|
47
|
+
|
|
48
|
+
def _build_url(self, endpoint: str) -> str:
|
|
49
|
+
"""
|
|
50
|
+
Build the full URL for the given endpoint.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
endpoint: API endpoint
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
Full URL
|
|
57
|
+
"""
|
|
58
|
+
endpoint = endpoint.lstrip('/')
|
|
59
|
+
return f"{self.base_url}/{endpoint}"
|
|
60
|
+
|
|
61
|
+
def _handle_response(self, response: requests.Response) -> Dict[str, Any]:
|
|
62
|
+
"""
|
|
63
|
+
Handle the response from the API.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
response: Response object
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
Response data as dictionary
|
|
70
|
+
|
|
71
|
+
Raises:
|
|
72
|
+
ExternalServiceError: If the response status code is not 2xx
|
|
73
|
+
"""
|
|
74
|
+
try:
|
|
75
|
+
response.raise_for_status()
|
|
76
|
+
return response.json()
|
|
77
|
+
except json.JSONDecodeError:
|
|
78
|
+
logger.error(f"Failed to decode JSON response: {response.text}")
|
|
79
|
+
raise InternalServiceError("Failed to decode JSON response")
|
|
80
|
+
except requests.HTTPError as e:
|
|
81
|
+
logger.error(f"HTTP error: {e}, Response: {response.text}")
|
|
82
|
+
try:
|
|
83
|
+
error_data = response.json()
|
|
84
|
+
except json.JSONDecodeError:
|
|
85
|
+
error_data = {"raw_response": response.text}
|
|
86
|
+
|
|
87
|
+
raise ExternalServiceError(
|
|
88
|
+
message=f"HTTP error: {response.status_code}",
|
|
89
|
+
data=error_data
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
def request(
|
|
93
|
+
self,
|
|
94
|
+
method: str,
|
|
95
|
+
endpoint: str,
|
|
96
|
+
params: Optional[Dict[str, Any]] = None,
|
|
97
|
+
data: Optional[Union[Dict[str, Any], List[Any]]] = None,
|
|
98
|
+
headers: Optional[Dict[str, str]] = None,
|
|
99
|
+
json_data: Optional[Union[Dict[str, Any], List[Any]]] = None,
|
|
100
|
+
timeout: Optional[int] = None
|
|
101
|
+
) -> Dict[str, Any]:
|
|
102
|
+
"""
|
|
103
|
+
Make an HTTP request.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
method: HTTP method (GET, POST, PUT, DELETE, etc.)
|
|
107
|
+
endpoint: API endpoint
|
|
108
|
+
params: Query parameters
|
|
109
|
+
data: Form data
|
|
110
|
+
headers: Request headers
|
|
111
|
+
json_data: JSON data
|
|
112
|
+
timeout: Request timeout in seconds
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
Response data as dictionary
|
|
116
|
+
|
|
117
|
+
Raises:
|
|
118
|
+
ExternalServiceError: If the request fails
|
|
119
|
+
TimeoutError: If the request times out
|
|
120
|
+
"""
|
|
121
|
+
url = self._build_url(endpoint)
|
|
122
|
+
request_headers = {**self.headers}
|
|
123
|
+
if headers:
|
|
124
|
+
request_headers.update(headers)
|
|
125
|
+
|
|
126
|
+
timeout = timeout or self.timeout
|
|
127
|
+
|
|
128
|
+
try:
|
|
129
|
+
response = requests.request(
|
|
130
|
+
method=method.upper(),
|
|
131
|
+
url=url,
|
|
132
|
+
params=params,
|
|
133
|
+
data=data,
|
|
134
|
+
headers=request_headers,
|
|
135
|
+
json=json_data,
|
|
136
|
+
timeout=timeout,
|
|
137
|
+
verify=self.verify_ssl
|
|
138
|
+
)
|
|
139
|
+
return self._handle_response(response)
|
|
140
|
+
except Timeout:
|
|
141
|
+
logger.error(f"Request timed out: {method} {url}")
|
|
142
|
+
raise PaymentTimeoutError(f"Request timed out: {method} {url}")
|
|
143
|
+
except ConnectionError as e:
|
|
144
|
+
logger.error(f"Connection error: {e}")
|
|
145
|
+
raise ExternalServiceError(f"Connection error: {str(e)}")
|
|
146
|
+
except RequestException as e:
|
|
147
|
+
logger.error(f"Request error: {e}")
|
|
148
|
+
raise ExternalServiceError(f"Request error: {str(e)}")
|
|
149
|
+
|
|
150
|
+
def get(
|
|
151
|
+
self,
|
|
152
|
+
endpoint: str,
|
|
153
|
+
params: Optional[Dict[str, Any]] = None,
|
|
154
|
+
headers: Optional[Dict[str, str]] = None,
|
|
155
|
+
timeout: Optional[int] = None
|
|
156
|
+
) -> Dict[str, Any]:
|
|
157
|
+
"""
|
|
158
|
+
Make a GET request.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
endpoint: API endpoint
|
|
162
|
+
params: Query parameters
|
|
163
|
+
headers: Request headers
|
|
164
|
+
timeout: Request timeout in seconds
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
Response data as dictionary
|
|
168
|
+
"""
|
|
169
|
+
return self.request(
|
|
170
|
+
method="GET",
|
|
171
|
+
endpoint=endpoint,
|
|
172
|
+
params=params,
|
|
173
|
+
headers=headers,
|
|
174
|
+
timeout=timeout
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
def post(
|
|
178
|
+
self,
|
|
179
|
+
endpoint: str,
|
|
180
|
+
data: Optional[Dict[str, Any]] = None,
|
|
181
|
+
json_data: Optional[Dict[str, Any]] = None,
|
|
182
|
+
params: Optional[Dict[str, Any]] = None,
|
|
183
|
+
headers: Optional[Dict[str, str]] = None,
|
|
184
|
+
timeout: Optional[int] = None
|
|
185
|
+
) -> Dict[str, Any]:
|
|
186
|
+
"""
|
|
187
|
+
Make a POST request.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
endpoint: API endpoint
|
|
191
|
+
data: Form data
|
|
192
|
+
json_data: JSON data
|
|
193
|
+
params: Query parameters
|
|
194
|
+
headers: Request headers
|
|
195
|
+
timeout: Request timeout in seconds
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
Response data as dictionary
|
|
199
|
+
"""
|
|
200
|
+
return self.request(
|
|
201
|
+
method="POST",
|
|
202
|
+
endpoint=endpoint,
|
|
203
|
+
data=data,
|
|
204
|
+
json_data=json_data,
|
|
205
|
+
params=params,
|
|
206
|
+
headers=headers,
|
|
207
|
+
timeout=timeout
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
def put(
|
|
211
|
+
self,
|
|
212
|
+
endpoint: str,
|
|
213
|
+
data: Optional[Dict[str, Any]] = None,
|
|
214
|
+
json_data: Optional[Dict[str, Any]] = None,
|
|
215
|
+
params: Optional[Dict[str, Any]] = None,
|
|
216
|
+
headers: Optional[Dict[str, str]] = None,
|
|
217
|
+
timeout: Optional[int] = None
|
|
218
|
+
) -> Dict[str, Any]:
|
|
219
|
+
"""
|
|
220
|
+
Make a PUT request.
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
endpoint: API endpoint
|
|
224
|
+
data: Form data
|
|
225
|
+
json_data: JSON data
|
|
226
|
+
params: Query parameters
|
|
227
|
+
headers: Request headers
|
|
228
|
+
timeout: Request timeout in seconds
|
|
229
|
+
|
|
230
|
+
Returns:
|
|
231
|
+
Response data as dictionary
|
|
232
|
+
"""
|
|
233
|
+
return self.request(
|
|
234
|
+
method="PUT",
|
|
235
|
+
endpoint=endpoint,
|
|
236
|
+
data=data,
|
|
237
|
+
json_data=json_data,
|
|
238
|
+
params=params,
|
|
239
|
+
headers=headers,
|
|
240
|
+
timeout=timeout
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
def delete(
|
|
244
|
+
self,
|
|
245
|
+
endpoint: str,
|
|
246
|
+
params: Optional[Dict[str, Any]] = None,
|
|
247
|
+
headers: Optional[Dict[str, str]] = None,
|
|
248
|
+
timeout: Optional[int] = None
|
|
249
|
+
) -> Dict[str, Any]:
|
|
250
|
+
"""
|
|
251
|
+
Make a DELETE request.
|
|
252
|
+
|
|
253
|
+
Args:
|
|
254
|
+
endpoint: API endpoint
|
|
255
|
+
params: Query parameters
|
|
256
|
+
headers: Request headers
|
|
257
|
+
timeout: Request timeout in seconds
|
|
258
|
+
|
|
259
|
+
Returns:
|
|
260
|
+
Response data as dictionary
|
|
261
|
+
"""
|
|
262
|
+
return self.request(
|
|
263
|
+
method="DELETE",
|
|
264
|
+
endpoint=endpoint,
|
|
265
|
+
params=params,
|
|
266
|
+
headers=headers,
|
|
267
|
+
timeout=timeout
|
|
268
|
+
)
|
core/payme/errors.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Payme API error codes.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
# System errors
|
|
6
|
+
SYSTEM_ERROR = -32400
|
|
7
|
+
INVALID_JSON_RPC = -32600
|
|
8
|
+
METHOD_NOT_FOUND = -32601
|
|
9
|
+
INVALID_PARAMS = -32602
|
|
10
|
+
INTERNAL_ERROR = -32603
|
|
11
|
+
|
|
12
|
+
# Authorization errors
|
|
13
|
+
AUTH_ERROR = -32504
|
|
14
|
+
AUTH_TOKEN_INVALID = -32504
|
|
15
|
+
AUTH_TOKEN_EXPIRED = -32504
|
|
16
|
+
|
|
17
|
+
# Business logic errors
|
|
18
|
+
INVALID_AMOUNT = -31001
|
|
19
|
+
INVALID_ACCOUNT = -31050
|
|
20
|
+
COULD_NOT_PERFORM = -31008
|
|
21
|
+
COULD_NOT_CANCEL = -31007
|
|
22
|
+
TRANSACTION_NOT_FOUND = -31003
|
|
23
|
+
TRANSACTION_ALREADY_EXISTS = -31060
|
|
24
|
+
TRANSACTION_ALREADY_CANCELLED = -31061
|
|
25
|
+
TRANSACTION_ALREADY_COMPLETED = -31062
|