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
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Payme receipts operations.
|
|
3
|
+
"""
|
|
4
|
+
# base64 is used indirectly through generate_basic_auth
|
|
5
|
+
import logging
|
|
6
|
+
from typing import Dict, Any, Optional
|
|
7
|
+
|
|
8
|
+
from paytechuz.core.http import HttpClient
|
|
9
|
+
from paytechuz.core.constants import PaymeEndpoints
|
|
10
|
+
from paytechuz.core.utils import handle_exceptions, generate_basic_auth
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
class PaymeReceipts:
|
|
15
|
+
"""
|
|
16
|
+
Payme receipts operations.
|
|
17
|
+
|
|
18
|
+
This class provides methods for working with receipts in the Payme payment system,
|
|
19
|
+
including creating receipts, paying receipts, and checking receipt status.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
http_client: HttpClient,
|
|
25
|
+
payme_id: str,
|
|
26
|
+
payme_key: Optional[str] = None
|
|
27
|
+
):
|
|
28
|
+
"""
|
|
29
|
+
Initialize the Payme receipts component.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
http_client: HTTP client for making requests
|
|
33
|
+
payme_id: Payme merchant ID
|
|
34
|
+
payme_key: Payme merchant key for authentication
|
|
35
|
+
"""
|
|
36
|
+
self.http_client = http_client
|
|
37
|
+
self.payme_id = payme_id
|
|
38
|
+
self.payme_key = payme_key
|
|
39
|
+
|
|
40
|
+
def _get_auth_headers(self, language: str = 'uz') -> Dict[str, str]:
|
|
41
|
+
"""
|
|
42
|
+
Get authentication headers for Payme API.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
language: Language code (uz, ru, en)
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
Dict containing authentication headers
|
|
49
|
+
"""
|
|
50
|
+
headers = {"Accept-Language": language}
|
|
51
|
+
|
|
52
|
+
if self.payme_key:
|
|
53
|
+
auth = generate_basic_auth(self.payme_id, self.payme_key)
|
|
54
|
+
headers["Authorization"] = auth
|
|
55
|
+
|
|
56
|
+
return headers
|
|
57
|
+
|
|
58
|
+
@handle_exceptions
|
|
59
|
+
def create(
|
|
60
|
+
self,
|
|
61
|
+
amount: int,
|
|
62
|
+
account: Dict[str, Any],
|
|
63
|
+
**kwargs
|
|
64
|
+
) -> Dict[str, Any]:
|
|
65
|
+
"""
|
|
66
|
+
Create a new receipt.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
amount: Payment amount in tiyin (1 som = 100 tiyin)
|
|
70
|
+
account: Account information (e.g., {"account_id": "12345"})
|
|
71
|
+
**kwargs: Additional parameters
|
|
72
|
+
- description: Payment description
|
|
73
|
+
- detail: Payment details
|
|
74
|
+
- callback_url: URL to redirect after payment
|
|
75
|
+
- return_url: URL to return after payment
|
|
76
|
+
- phone: Customer phone number
|
|
77
|
+
- email: Customer email
|
|
78
|
+
- language: Language code (uz, ru, en)
|
|
79
|
+
- expire_minutes: Payment expiration time in minutes
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
Dict containing receipt creation response
|
|
83
|
+
"""
|
|
84
|
+
# Extract additional parameters
|
|
85
|
+
description = kwargs.get('description', 'Payment')
|
|
86
|
+
detail = kwargs.get('detail', {})
|
|
87
|
+
callback_url = kwargs.get('callback_url')
|
|
88
|
+
return_url = kwargs.get('return_url')
|
|
89
|
+
phone = kwargs.get('phone')
|
|
90
|
+
email = kwargs.get('email')
|
|
91
|
+
language = kwargs.get('language', 'uz')
|
|
92
|
+
expire_minutes = kwargs.get('expire_minutes', 60) # Default 1 hour
|
|
93
|
+
|
|
94
|
+
# Prepare request data
|
|
95
|
+
data = {
|
|
96
|
+
"method": PaymeEndpoints.RECEIPTS_CREATE,
|
|
97
|
+
"params": {
|
|
98
|
+
"amount": amount,
|
|
99
|
+
"account": account,
|
|
100
|
+
"description": description,
|
|
101
|
+
"detail": detail,
|
|
102
|
+
"merchant_id": self.payme_id
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
# Add optional parameters
|
|
107
|
+
if callback_url:
|
|
108
|
+
data["params"]["callback_url"] = callback_url
|
|
109
|
+
|
|
110
|
+
if return_url:
|
|
111
|
+
data["params"]["return_url"] = return_url
|
|
112
|
+
|
|
113
|
+
if phone:
|
|
114
|
+
data["params"]["phone"] = phone
|
|
115
|
+
|
|
116
|
+
if email:
|
|
117
|
+
data["params"]["email"] = email
|
|
118
|
+
|
|
119
|
+
if expire_minutes:
|
|
120
|
+
data["params"]["expire_minutes"] = expire_minutes
|
|
121
|
+
|
|
122
|
+
# Get authentication headers
|
|
123
|
+
headers = self._get_auth_headers(language)
|
|
124
|
+
|
|
125
|
+
# Make request
|
|
126
|
+
response = self.http_client.post(
|
|
127
|
+
endpoint="",
|
|
128
|
+
json_data=data,
|
|
129
|
+
headers=headers
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
return response
|
|
133
|
+
|
|
134
|
+
@handle_exceptions
|
|
135
|
+
def pay(
|
|
136
|
+
self,
|
|
137
|
+
receipt_id: str,
|
|
138
|
+
token: str,
|
|
139
|
+
**kwargs
|
|
140
|
+
) -> Dict[str, Any]:
|
|
141
|
+
"""
|
|
142
|
+
Pay a receipt with a card token.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
receipt_id: Receipt ID
|
|
146
|
+
token: Card token
|
|
147
|
+
**kwargs: Additional parameters
|
|
148
|
+
- language: Language code (uz, ru, en)
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
Dict containing receipt payment response
|
|
152
|
+
"""
|
|
153
|
+
# Extract additional parameters
|
|
154
|
+
language = kwargs.get('language', 'uz')
|
|
155
|
+
|
|
156
|
+
# Prepare request data
|
|
157
|
+
data = {
|
|
158
|
+
"method": PaymeEndpoints.RECEIPTS_PAY,
|
|
159
|
+
"params": {
|
|
160
|
+
"id": receipt_id,
|
|
161
|
+
"token": token
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
# Get authentication headers
|
|
166
|
+
headers = self._get_auth_headers(language)
|
|
167
|
+
|
|
168
|
+
# Make request
|
|
169
|
+
response = self.http_client.post(
|
|
170
|
+
endpoint="",
|
|
171
|
+
json_data=data,
|
|
172
|
+
headers=headers
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
return response
|
|
176
|
+
|
|
177
|
+
@handle_exceptions
|
|
178
|
+
def send(
|
|
179
|
+
self,
|
|
180
|
+
receipt_id: str,
|
|
181
|
+
phone: str,
|
|
182
|
+
**kwargs
|
|
183
|
+
) -> Dict[str, Any]:
|
|
184
|
+
"""
|
|
185
|
+
Send a receipt to a phone number.
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
receipt_id: Receipt ID
|
|
189
|
+
phone: Phone number
|
|
190
|
+
**kwargs: Additional parameters
|
|
191
|
+
- language: Language code (uz, ru, en)
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
Dict containing receipt sending response
|
|
195
|
+
"""
|
|
196
|
+
# Extract additional parameters
|
|
197
|
+
language = kwargs.get('language', 'uz')
|
|
198
|
+
|
|
199
|
+
# Prepare request data
|
|
200
|
+
data = {
|
|
201
|
+
"method": PaymeEndpoints.RECEIPTS_SEND,
|
|
202
|
+
"params": {
|
|
203
|
+
"id": receipt_id,
|
|
204
|
+
"phone": phone
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
# Get authentication headers
|
|
209
|
+
headers = self._get_auth_headers(language)
|
|
210
|
+
|
|
211
|
+
# Make request
|
|
212
|
+
response = self.http_client.post(
|
|
213
|
+
endpoint="",
|
|
214
|
+
json_data=data,
|
|
215
|
+
headers=headers
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
return response
|
|
219
|
+
|
|
220
|
+
@handle_exceptions
|
|
221
|
+
def check(self, receipt_id: str, **kwargs) -> Dict[str, Any]:
|
|
222
|
+
"""
|
|
223
|
+
Check receipt status.
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
receipt_id: Receipt ID
|
|
227
|
+
**kwargs: Additional parameters
|
|
228
|
+
- language: Language code (uz, ru, en)
|
|
229
|
+
|
|
230
|
+
Returns:
|
|
231
|
+
Dict containing receipt status response
|
|
232
|
+
"""
|
|
233
|
+
# Extract additional parameters
|
|
234
|
+
language = kwargs.get('language', 'uz')
|
|
235
|
+
|
|
236
|
+
# Prepare request data
|
|
237
|
+
data = {
|
|
238
|
+
"method": PaymeEndpoints.RECEIPTS_CHECK,
|
|
239
|
+
"params": {
|
|
240
|
+
"id": receipt_id
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
# Get authentication headers
|
|
245
|
+
headers = self._get_auth_headers(language)
|
|
246
|
+
|
|
247
|
+
# Make request
|
|
248
|
+
response = self.http_client.post(
|
|
249
|
+
endpoint="",
|
|
250
|
+
json_data=data,
|
|
251
|
+
headers=headers
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
return response
|
|
255
|
+
|
|
256
|
+
@handle_exceptions
|
|
257
|
+
def cancel(
|
|
258
|
+
self,
|
|
259
|
+
receipt_id: str,
|
|
260
|
+
reason: Optional[str] = None,
|
|
261
|
+
**kwargs
|
|
262
|
+
) -> Dict[str, Any]:
|
|
263
|
+
"""
|
|
264
|
+
Cancel a receipt.
|
|
265
|
+
|
|
266
|
+
Args:
|
|
267
|
+
receipt_id: Receipt ID
|
|
268
|
+
reason: Cancellation reason
|
|
269
|
+
**kwargs: Additional parameters
|
|
270
|
+
- language: Language code (uz, ru, en)
|
|
271
|
+
|
|
272
|
+
Returns:
|
|
273
|
+
Dict containing receipt cancellation response
|
|
274
|
+
"""
|
|
275
|
+
# Extract additional parameters
|
|
276
|
+
language = kwargs.get('language', 'uz')
|
|
277
|
+
|
|
278
|
+
# Prepare request data
|
|
279
|
+
data = {
|
|
280
|
+
"method": PaymeEndpoints.RECEIPTS_CANCEL,
|
|
281
|
+
"params": {
|
|
282
|
+
"id": receipt_id
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
# Add reason if provided
|
|
287
|
+
if reason:
|
|
288
|
+
data["params"]["reason"] = reason
|
|
289
|
+
|
|
290
|
+
# Get authentication headers
|
|
291
|
+
headers = self._get_auth_headers(language)
|
|
292
|
+
|
|
293
|
+
# Make request
|
|
294
|
+
response = self.http_client.post(
|
|
295
|
+
endpoint="",
|
|
296
|
+
json_data=data,
|
|
297
|
+
headers=headers
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
return response
|
|
301
|
+
|
|
302
|
+
@handle_exceptions
|
|
303
|
+
def get(self, receipt_id: str, **kwargs) -> Dict[str, Any]:
|
|
304
|
+
"""
|
|
305
|
+
Get receipt details.
|
|
306
|
+
|
|
307
|
+
Args:
|
|
308
|
+
receipt_id: Receipt ID
|
|
309
|
+
**kwargs: Additional parameters
|
|
310
|
+
- language: Language code (uz, ru, en)
|
|
311
|
+
|
|
312
|
+
Returns:
|
|
313
|
+
Dict containing receipt details response
|
|
314
|
+
"""
|
|
315
|
+
# Extract additional parameters
|
|
316
|
+
language = kwargs.get('language', 'uz')
|
|
317
|
+
|
|
318
|
+
# Prepare request data
|
|
319
|
+
data = {
|
|
320
|
+
"method": PaymeEndpoints.RECEIPTS_GET,
|
|
321
|
+
"params": {
|
|
322
|
+
"id": receipt_id
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
# Get authentication headers
|
|
327
|
+
headers = self._get_auth_headers(language)
|
|
328
|
+
|
|
329
|
+
# Make request
|
|
330
|
+
response = self.http_client.post(
|
|
331
|
+
endpoint="",
|
|
332
|
+
json_data=data,
|
|
333
|
+
headers=headers
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
return response
|
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Payme webhook handler.
|
|
3
|
+
"""
|
|
4
|
+
import base64
|
|
5
|
+
import binascii
|
|
6
|
+
import logging
|
|
7
|
+
from typing import Dict, Any, Optional, Callable
|
|
8
|
+
|
|
9
|
+
from paytechuz.core.base import BaseWebhookHandler
|
|
10
|
+
from paytechuz.core.constants import TransactionState, PaymeCancelReason
|
|
11
|
+
from paytechuz.core.exceptions import (
|
|
12
|
+
PermissionDenied,
|
|
13
|
+
MethodNotFound,
|
|
14
|
+
TransactionNotFound,
|
|
15
|
+
AccountNotFound,
|
|
16
|
+
InternalServiceError,
|
|
17
|
+
TransactionCancelled
|
|
18
|
+
)
|
|
19
|
+
from paytechuz.core.utils import handle_exceptions
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
class PaymeWebhookHandler(BaseWebhookHandler):
|
|
24
|
+
"""
|
|
25
|
+
Payme webhook handler.
|
|
26
|
+
|
|
27
|
+
This class handles webhook requests from the Payme payment system,
|
|
28
|
+
including transaction creation, confirmation, and cancellation.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def __init__(
|
|
32
|
+
self,
|
|
33
|
+
merchant_key: str,
|
|
34
|
+
find_transaction_func: Callable[[str], Dict[str, Any]],
|
|
35
|
+
find_account_func: Callable[[Dict[str, Any]], Dict[str, Any]],
|
|
36
|
+
create_transaction_func: Callable[[Dict[str, Any]], Dict[str, Any]],
|
|
37
|
+
perform_transaction_func: Callable[[str], bool],
|
|
38
|
+
cancel_transaction_func: Callable[[str, int], bool],
|
|
39
|
+
get_statement_func: Optional[Callable[[int, int], list]] = None,
|
|
40
|
+
check_perform_func: Optional[Callable[[Dict[str, Any]], bool]] = None,
|
|
41
|
+
set_fiscal_data_func: Optional[
|
|
42
|
+
Callable[[str, Dict[str, Any]], bool]
|
|
43
|
+
] = None
|
|
44
|
+
):
|
|
45
|
+
"""
|
|
46
|
+
Initialize the Payme webhook handler.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
merchant_key: Payme merchant key for authentication
|
|
50
|
+
find_transaction_func: Function to find a transaction by ID
|
|
51
|
+
find_account_func: Function to find an account by parameters
|
|
52
|
+
create_transaction_func: Function to create a transaction
|
|
53
|
+
perform_transaction_func: Function to perform a transaction
|
|
54
|
+
cancel_transaction_func: Function to cancel a transaction
|
|
55
|
+
get_statement_func: Function to get transaction statement
|
|
56
|
+
check_perform_func: Function to check transaction can be performed
|
|
57
|
+
set_fiscal_data_func: Function to set fiscal data for a transaction
|
|
58
|
+
"""
|
|
59
|
+
self.merchant_key = merchant_key
|
|
60
|
+
self.find_transaction = find_transaction_func
|
|
61
|
+
self.find_account = find_account_func
|
|
62
|
+
self.create_transaction = create_transaction_func
|
|
63
|
+
self.perform_transaction = perform_transaction_func
|
|
64
|
+
self.cancel_transaction = cancel_transaction_func
|
|
65
|
+
self.get_statement = get_statement_func
|
|
66
|
+
self.check_perform = check_perform_func
|
|
67
|
+
self.set_fiscal_data = set_fiscal_data_func
|
|
68
|
+
|
|
69
|
+
def _check_auth(self, auth_header: Optional[str]) -> None:
|
|
70
|
+
"""
|
|
71
|
+
Check authentication header.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
auth_header: Authentication header
|
|
75
|
+
|
|
76
|
+
Raises:
|
|
77
|
+
PermissionDenied: If authentication fails
|
|
78
|
+
"""
|
|
79
|
+
if not auth_header:
|
|
80
|
+
raise PermissionDenied("Missing authentication credentials")
|
|
81
|
+
|
|
82
|
+
try:
|
|
83
|
+
auth_parts = auth_header.split()
|
|
84
|
+
if len(auth_parts) != 2 or auth_parts[0].lower() != 'basic':
|
|
85
|
+
raise PermissionDenied("Invalid authentication format")
|
|
86
|
+
|
|
87
|
+
auth_decoded = base64.b64decode(auth_parts[1]).decode('utf-8')
|
|
88
|
+
_, password = auth_decoded.split(':')
|
|
89
|
+
|
|
90
|
+
if password != self.merchant_key:
|
|
91
|
+
raise PermissionDenied("Invalid merchant key")
|
|
92
|
+
except (binascii.Error, UnicodeDecodeError, ValueError) as e:
|
|
93
|
+
logger.error(f"Authentication error: {e}")
|
|
94
|
+
raise PermissionDenied("Authentication error")
|
|
95
|
+
|
|
96
|
+
@handle_exceptions
|
|
97
|
+
def handle_webhook(
|
|
98
|
+
self, data: Dict[str, Any], auth_header: Optional[str] = None
|
|
99
|
+
) -> Dict[str, Any]:
|
|
100
|
+
"""
|
|
101
|
+
Handle webhook data from Payme.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
data: The webhook data received from Payme
|
|
105
|
+
auth_header: Authentication header
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
Dict containing the response to be sent back to Payme
|
|
109
|
+
|
|
110
|
+
Raises:
|
|
111
|
+
PermissionDenied: If authentication fails
|
|
112
|
+
MethodNotFound: If the requested method is not supported
|
|
113
|
+
"""
|
|
114
|
+
# Check authentication
|
|
115
|
+
self._check_auth(auth_header)
|
|
116
|
+
|
|
117
|
+
# Extract method and params
|
|
118
|
+
try:
|
|
119
|
+
method = data.get('method')
|
|
120
|
+
params = data.get('params', {})
|
|
121
|
+
request_id = data.get('id', 0)
|
|
122
|
+
except (KeyError, TypeError) as e:
|
|
123
|
+
logger.error(f"Invalid webhook data: {e}")
|
|
124
|
+
raise InternalServiceError("Invalid webhook data")
|
|
125
|
+
|
|
126
|
+
# Map methods to handler functions
|
|
127
|
+
method_handlers = {
|
|
128
|
+
'CheckPerformTransaction': self._handle_check_perform,
|
|
129
|
+
'CreateTransaction': self._handle_create_transaction,
|
|
130
|
+
'PerformTransaction': self._handle_perform_transaction,
|
|
131
|
+
'CheckTransaction': self._handle_check_transaction,
|
|
132
|
+
'CancelTransaction': self._handle_cancel_transaction,
|
|
133
|
+
'GetStatement': self._handle_get_statement,
|
|
134
|
+
'SetFiscalData': self._handle_set_fiscal_data,
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
# Call the appropriate handler
|
|
138
|
+
if method in method_handlers:
|
|
139
|
+
result = method_handlers[method](params)
|
|
140
|
+
return {
|
|
141
|
+
'jsonrpc': '2.0',
|
|
142
|
+
'id': request_id,
|
|
143
|
+
'result': result
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
logger.warning(f"Method not found: {method}")
|
|
147
|
+
raise MethodNotFound(f"Method not supported: {method}")
|
|
148
|
+
|
|
149
|
+
def _handle_check_perform(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
150
|
+
"""
|
|
151
|
+
Handle CheckPerformTransaction method.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
params: Method parameters
|
|
155
|
+
|
|
156
|
+
Returns:
|
|
157
|
+
Dict containing the response
|
|
158
|
+
"""
|
|
159
|
+
if not self.check_perform:
|
|
160
|
+
# Default implementation if no custom function is provided
|
|
161
|
+
account = self.find_account(params.get('account', {}))
|
|
162
|
+
if not account:
|
|
163
|
+
raise AccountNotFound("Account not found")
|
|
164
|
+
|
|
165
|
+
return {'allow': True}
|
|
166
|
+
|
|
167
|
+
# Call custom function
|
|
168
|
+
result = self.check_perform(params)
|
|
169
|
+
return {'allow': result}
|
|
170
|
+
|
|
171
|
+
def _handle_create_transaction(
|
|
172
|
+
self, params: Dict[str, Any]
|
|
173
|
+
) -> Dict[str, Any]:
|
|
174
|
+
"""
|
|
175
|
+
Handle CreateTransaction method.
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
params: Method parameters
|
|
179
|
+
|
|
180
|
+
Returns:
|
|
181
|
+
Dict containing the response
|
|
182
|
+
"""
|
|
183
|
+
transaction_id = params.get('id')
|
|
184
|
+
|
|
185
|
+
# Check if transaction already exists
|
|
186
|
+
try:
|
|
187
|
+
existing_transaction = self.find_transaction(transaction_id)
|
|
188
|
+
|
|
189
|
+
# If transaction exists, return its details
|
|
190
|
+
return {
|
|
191
|
+
'transaction': existing_transaction['id'],
|
|
192
|
+
'state': existing_transaction['state'],
|
|
193
|
+
'create_time': existing_transaction['create_time'],
|
|
194
|
+
}
|
|
195
|
+
except TransactionNotFound:
|
|
196
|
+
# Transaction doesn't exist, create a new one
|
|
197
|
+
pass
|
|
198
|
+
|
|
199
|
+
# Find account
|
|
200
|
+
account = self.find_account(params.get('account', {}))
|
|
201
|
+
if not account:
|
|
202
|
+
raise AccountNotFound("Account not found")
|
|
203
|
+
|
|
204
|
+
# Create transaction
|
|
205
|
+
transaction = self.create_transaction({
|
|
206
|
+
'id': transaction_id,
|
|
207
|
+
'account': account,
|
|
208
|
+
'amount': params.get('amount'),
|
|
209
|
+
'time': params.get('time'),
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
return {
|
|
213
|
+
'transaction': transaction['id'],
|
|
214
|
+
'state': transaction['state'],
|
|
215
|
+
'create_time': transaction['create_time'],
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
def _handle_perform_transaction(
|
|
219
|
+
self, params: Dict[str, Any]
|
|
220
|
+
) -> Dict[str, Any]:
|
|
221
|
+
"""
|
|
222
|
+
Handle PerformTransaction method.
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
params: Method parameters
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
Dict containing the response
|
|
229
|
+
"""
|
|
230
|
+
transaction_id = params.get('id')
|
|
231
|
+
|
|
232
|
+
# Find transaction
|
|
233
|
+
transaction = self.find_transaction(transaction_id)
|
|
234
|
+
|
|
235
|
+
# Perform transaction
|
|
236
|
+
self.perform_transaction(transaction_id)
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
'transaction': transaction['id'],
|
|
240
|
+
'state': transaction['state'],
|
|
241
|
+
'perform_time': transaction.get('perform_time', 0),
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
def _handle_check_transaction(
|
|
245
|
+
self, params: Dict[str, Any]
|
|
246
|
+
) -> Dict[str, Any]:
|
|
247
|
+
"""
|
|
248
|
+
Handle CheckTransaction method.
|
|
249
|
+
|
|
250
|
+
Args:
|
|
251
|
+
params: Method parameters
|
|
252
|
+
|
|
253
|
+
Returns:
|
|
254
|
+
Dict containing the response
|
|
255
|
+
"""
|
|
256
|
+
transaction_id = params.get('id')
|
|
257
|
+
|
|
258
|
+
# Find transaction
|
|
259
|
+
transaction = self.find_transaction(transaction_id)
|
|
260
|
+
|
|
261
|
+
return {
|
|
262
|
+
'transaction': transaction['id'],
|
|
263
|
+
'state': transaction['state'],
|
|
264
|
+
'create_time': transaction['create_time'],
|
|
265
|
+
'perform_time': transaction.get('perform_time', 0),
|
|
266
|
+
'cancel_time': transaction.get('cancel_time', 0),
|
|
267
|
+
'reason': transaction.get('reason'),
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
def _cancel_response(self, transaction: Dict[str, Any]) -> Dict[str, Any]:
|
|
271
|
+
"""
|
|
272
|
+
Helper method to generate cancel transaction response.
|
|
273
|
+
|
|
274
|
+
Args:
|
|
275
|
+
transaction: Transaction data
|
|
276
|
+
|
|
277
|
+
Returns:
|
|
278
|
+
Dict containing the response
|
|
279
|
+
"""
|
|
280
|
+
return {
|
|
281
|
+
'transaction': transaction['id'],
|
|
282
|
+
'state': transaction['state'],
|
|
283
|
+
'cancel_time': transaction.get('cancel_time', 0),
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
def _handle_cancel_transaction(
|
|
287
|
+
self, params: Dict[str, Any]
|
|
288
|
+
) -> Dict[str, Any]:
|
|
289
|
+
"""
|
|
290
|
+
Handle CancelTransaction method.
|
|
291
|
+
|
|
292
|
+
Args:
|
|
293
|
+
params: Method parameters
|
|
294
|
+
|
|
295
|
+
Returns:
|
|
296
|
+
Dict containing the response
|
|
297
|
+
"""
|
|
298
|
+
transaction_id = params.get('id')
|
|
299
|
+
reason = params.get(
|
|
300
|
+
'reason', PaymeCancelReason.REASON_MERCHANT_DECISION
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
# Find transaction
|
|
304
|
+
transaction = self.find_transaction(transaction_id)
|
|
305
|
+
|
|
306
|
+
# Check if transaction is already cancelled
|
|
307
|
+
canceled_states = [
|
|
308
|
+
TransactionState.CANCELED.value,
|
|
309
|
+
TransactionState.CANCELED_DURING_INIT.value
|
|
310
|
+
]
|
|
311
|
+
if transaction.get('state') in canceled_states:
|
|
312
|
+
# If transaction is already cancelled, return the existing data
|
|
313
|
+
return self._cancel_response(transaction)
|
|
314
|
+
|
|
315
|
+
# Check if transaction can be cancelled based on its current state
|
|
316
|
+
if transaction.get('state') == TransactionState.SUCCESSFULLY.value:
|
|
317
|
+
# Transaction was successfully performed, can be cancelled
|
|
318
|
+
pass
|
|
319
|
+
elif transaction.get('state') == TransactionState.INITIATING.value:
|
|
320
|
+
# Transaction is in initiating state, can be cancelled
|
|
321
|
+
pass
|
|
322
|
+
else:
|
|
323
|
+
# If transaction is in another state, it cannot be cancelled
|
|
324
|
+
raise TransactionCancelled(
|
|
325
|
+
f"Transaction {transaction_id} cannot be cancelled"
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
# Cancel transaction
|
|
329
|
+
self.cancel_transaction(transaction_id, reason)
|
|
330
|
+
|
|
331
|
+
# Get updated transaction
|
|
332
|
+
updated_transaction = self.find_transaction(transaction_id)
|
|
333
|
+
|
|
334
|
+
# Return cancel response
|
|
335
|
+
return self._cancel_response(updated_transaction)
|
|
336
|
+
|
|
337
|
+
def _handle_get_statement(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
338
|
+
"""
|
|
339
|
+
Handle GetStatement method.
|
|
340
|
+
|
|
341
|
+
Args:
|
|
342
|
+
params: Method parameters
|
|
343
|
+
|
|
344
|
+
Returns:
|
|
345
|
+
Dict containing the response
|
|
346
|
+
"""
|
|
347
|
+
if not self.get_statement:
|
|
348
|
+
raise MethodNotFound("GetStatement method not implemented")
|
|
349
|
+
|
|
350
|
+
from_date = params.get('from')
|
|
351
|
+
to_date = params.get('to')
|
|
352
|
+
|
|
353
|
+
# Get statement
|
|
354
|
+
transactions = self.get_statement(from_date, to_date)
|
|
355
|
+
|
|
356
|
+
return {'transactions': transactions}
|
|
357
|
+
|
|
358
|
+
def _handle_set_fiscal_data(
|
|
359
|
+
self, params: Dict[str, Any]
|
|
360
|
+
) -> Dict[str, Any]:
|
|
361
|
+
"""
|
|
362
|
+
Handle SetFiscalData method.
|
|
363
|
+
|
|
364
|
+
Args:
|
|
365
|
+
params: Method parameters
|
|
366
|
+
|
|
367
|
+
Returns:
|
|
368
|
+
Dict containing the response
|
|
369
|
+
"""
|
|
370
|
+
if not self.set_fiscal_data:
|
|
371
|
+
raise MethodNotFound("SetFiscalData method not implemented")
|
|
372
|
+
|
|
373
|
+
transaction_id = params.get('id')
|
|
374
|
+
fiscal_data = params.get('fiscal_data', {})
|
|
375
|
+
|
|
376
|
+
# Set fiscal data
|
|
377
|
+
success = self.set_fiscal_data(transaction_id, fiscal_data)
|
|
378
|
+
|
|
379
|
+
return {'success': success}
|
integrations/__init__.py
ADDED
|
File without changes
|