payme-pkg 2.6.5__py3-none-any.whl → 3.0.17__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 payme-pkg might be problematic. Click here for more details.

Files changed (51) hide show
  1. payme/__init__.py +1 -0
  2. payme/admin.py +12 -6
  3. payme/apps.py +0 -4
  4. payme/classes/cards.py +203 -0
  5. payme/classes/client.py +30 -0
  6. payme/classes/http.py +107 -0
  7. payme/classes/initializer.py +82 -0
  8. payme/classes/receipts.py +298 -0
  9. payme/const.py +12 -0
  10. payme/exceptions/__init__.py +5 -0
  11. payme/exceptions/general.py +275 -0
  12. payme/exceptions/webhook.py +125 -0
  13. payme/models.py +114 -40
  14. payme/types/response/__init__.py +4 -0
  15. payme/types/response/cards.py +110 -0
  16. payme/types/response/receipts.py +215 -0
  17. payme/types/response/webhook.py +136 -0
  18. payme/urls.py +2 -2
  19. payme/util.py +26 -0
  20. payme/views.py +287 -113
  21. payme_pkg-3.0.17.dist-info/METADATA +193 -0
  22. payme_pkg-3.0.17.dist-info/RECORD +29 -0
  23. {payme_pkg-2.6.5.dist-info → payme_pkg-3.0.17.dist-info}/WHEEL +1 -1
  24. payme/cards/__init__.py +0 -1
  25. payme/cards/subscribe_cards.py +0 -166
  26. payme/decorators/decorators.py +0 -34
  27. payme/errors/exceptions.py +0 -89
  28. payme/methods/cancel_transaction.py +0 -54
  29. payme/methods/check_perform_transaction.py +0 -26
  30. payme/methods/check_transaction.py +0 -43
  31. payme/methods/create_transaction.py +0 -68
  32. payme/methods/generate_link.py +0 -83
  33. payme/methods/get_statement.py +0 -65
  34. payme/methods/perform_transaction.py +0 -47
  35. payme/migrations/0001_initial.py +0 -48
  36. payme/receipts/__init__.py +0 -1
  37. payme/receipts/subscribe_receipts.py +0 -217
  38. payme/serializers.py +0 -86
  39. payme/utils/__init__.py +0 -0
  40. payme/utils/get_params.py +0 -24
  41. payme/utils/logging.py +0 -9
  42. payme/utils/make_aware_datetime.py +0 -21
  43. payme/utils/support.py +0 -8
  44. payme/utils/to_json.py +0 -13
  45. payme_pkg-2.6.5.dist-info/METADATA +0 -13
  46. payme_pkg-2.6.5.dist-info/RECORD +0 -36
  47. /payme/{decorators → classes}/__init__.py +0 -0
  48. /payme/{errors → types}/__init__.py +0 -0
  49. /payme/{methods → types/request}/__init__.py +0 -0
  50. {payme_pkg-2.6.5.dist-info → payme_pkg-3.0.17.dist-info}/LICENSE.txt +0 -0
  51. {payme_pkg-2.6.5.dist-info → payme_pkg-3.0.17.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,136 @@
1
+ from dataclasses import dataclass, field
2
+ from typing import List, Optional, Dict
3
+
4
+
5
+ class CommonResponse:
6
+ """
7
+ The common response structure
8
+ """
9
+ def as_resp(self):
10
+ response = {'result': {}}
11
+ for key, value in self.__dict__.items():
12
+ response['result'][key] = value
13
+ return response
14
+
15
+
16
+ @dataclass
17
+ class Shipping(CommonResponse):
18
+ """
19
+ Shipping information response structure
20
+ """
21
+ title: str
22
+ price: int
23
+
24
+
25
+ @dataclass
26
+ class Item(CommonResponse):
27
+ """
28
+ Item information response structure
29
+ """
30
+ discount: int
31
+ title: str
32
+ price: int
33
+ count: int
34
+ code: str
35
+ units: int
36
+ vat_percent: int
37
+ package_code: str
38
+
39
+ def as_resp(self):
40
+ return {
41
+ "discount": self.discount,
42
+ "title": self.title,
43
+ "price": self.price,
44
+ "count": self.count,
45
+ "code": self.code,
46
+ "units": self.units,
47
+ "vat_percent": self.vat_percent,
48
+ "package_code": self.package_code
49
+ }
50
+
51
+
52
+ @dataclass
53
+ class CheckPerformTransaction(CommonResponse):
54
+ """
55
+ Receipt information response structure for transaction checks.
56
+ """
57
+ allow: bool
58
+ additional: Optional[Dict[str, str]] = None
59
+ receipt_type: Optional[int] = None
60
+ shipping: Optional[Shipping] = None
61
+ items: List[Item] = field(default_factory=list)
62
+
63
+ def add_item(self, item: Item):
64
+ self.items.append(item)
65
+
66
+ def as_resp(self):
67
+ detail_dict = {}
68
+ receipt_dict = {"allow": self.allow}
69
+
70
+ if self.additional:
71
+ receipt_dict["additional"] = self.additional
72
+
73
+ if isinstance(self.receipt_type, int):
74
+ detail_dict["receipt_type"] = self.receipt_type
75
+
76
+ if self.shipping:
77
+ detail_dict["shipping"] = self.shipping.as_resp()
78
+
79
+ if self.items:
80
+ detail_dict["items"] = [item.as_resp() for item in self.items]
81
+
82
+ if detail_dict:
83
+ receipt_dict["detail"] = detail_dict
84
+
85
+ return {"result": receipt_dict}
86
+
87
+
88
+ @dataclass
89
+ class CreateTransaction(CommonResponse):
90
+ """
91
+ The create transaction request
92
+ """
93
+ transaction: str
94
+ state: str
95
+ create_time: str
96
+
97
+
98
+ @dataclass
99
+ class PerformTransaction(CommonResponse):
100
+ """
101
+ The perform transaction response
102
+ """
103
+ transaction: str
104
+ state: str
105
+ perform_time: str
106
+
107
+
108
+ @dataclass
109
+ class CancelTransaction(CommonResponse):
110
+ """
111
+ The cancel transaction request
112
+ """
113
+ transaction: str
114
+ state: str
115
+ cancel_time: str
116
+
117
+
118
+ @dataclass
119
+ class CheckTransaction(CommonResponse):
120
+ """
121
+ The check transaction request
122
+ """
123
+ transaction: str
124
+ state: str
125
+ reason: str
126
+ create_time: str
127
+ perform_time: Optional[str] = None
128
+ cancel_time: Optional[str] = None
129
+
130
+
131
+ @dataclass
132
+ class GetStatement(CommonResponse):
133
+ """
134
+ The check perform transactions response
135
+ """
136
+ transactions: List[str]
payme/urls.py CHANGED
@@ -1,8 +1,8 @@
1
1
  from django.urls import path
2
2
 
3
- from payme.views import MerchantAPIView
3
+ from payme.views import PaymeWebHookAPIView
4
4
 
5
5
 
6
6
  urlpatterns = [
7
- path("merchant/", MerchantAPIView.as_view())
7
+ path("update/", PaymeWebHookAPIView.as_view())
8
8
  ]
payme/util.py ADDED
@@ -0,0 +1,26 @@
1
+ from datetime import datetime
2
+
3
+
4
+ def time_to_payme(datatime) -> int:
5
+ """
6
+ Convert datetime object to Payme's datetime format.
7
+
8
+ Payme's datetime format is in the format: YYYY-MM-DD HH:MM:SS.ssssss
9
+
10
+ Args:
11
+ datatime (datetime): The datetime object to convert.
12
+
13
+ Returns:
14
+ str: The datetime object in Payme's datetime format.
15
+ """
16
+ if not datatime:
17
+ return 0
18
+
19
+ return int(datatime.timestamp() * 1000)
20
+
21
+
22
+ def time_to_service(milliseconds: int) -> datetime:
23
+ """
24
+ Converts milliseconds since the epoch to a datetime object.
25
+ """
26
+ return datetime.fromtimestamp(milliseconds / 1000)
payme/views.py CHANGED
@@ -1,163 +1,337 @@
1
1
  import base64
2
+ import logging
2
3
  import binascii
4
+ from decimal import Decimal
3
5
 
4
6
  from django.conf import settings
7
+ from django.utils.module_loading import import_string
5
8
 
6
- from rest_framework.views import APIView
9
+ from rest_framework import views
7
10
  from rest_framework.response import Response
8
- from rest_framework.exceptions import ValidationError
9
11
 
10
- from payme.utils.logging import logger
12
+ from payme import exceptions
13
+ from payme.types import response
14
+ from payme.models import PaymeTransactions
15
+ from payme.util import time_to_payme, time_to_service
11
16
 
12
- from payme.errors.exceptions import MethodNotFound
13
- from payme.errors.exceptions import PermissionDenied
14
- from payme.errors.exceptions import PerformTransactionDoesNotExist
17
+ logger = logging.getLogger(__name__)
18
+ AccountModel = import_string(settings.PAYME_ACCOUNT_MODEL)
15
19
 
16
- from payme.methods.get_statement import GetStatement
17
- from payme.methods.check_transaction import CheckTransaction
18
- from payme.methods.cancel_transaction import CancelTransaction
19
- from payme.methods.create_transaction import CreateTransaction
20
- from payme.methods.perform_transaction import PerformTransaction
21
- from payme.methods.check_perform_transaction import CheckPerformTransaction
22
20
 
21
+ def handle_exceptions(func):
22
+ """
23
+ Decorator to handle exceptions and raise appropriate Payme exceptions.
24
+ """
25
+ def wrapper(*args, **kwargs):
26
+ try:
27
+ return func(*args, **kwargs)
28
+ except KeyError as exc:
29
+ message = "Invalid parameters received."
30
+ logger.error(f"{message}: {exc}s {exc} {args} {kwargs}")
31
+ raise exceptions.InternalServiceError(message) from exc
32
+
33
+ except AccountModel.DoesNotExist as exc:
34
+ logger.error(f"Account does not exist: {exc} {args} {kwargs}")
35
+ raise exceptions.AccountDoesNotExist(str(exc)) from exc
36
+
37
+ except PaymeTransactions.DoesNotExist as exc:
38
+ logger.error(f"Transaction does not exist: {exc} {args} {kwargs}")
39
+ raise exceptions.AccountDoesNotExist(str(exc)) from exc
23
40
 
24
- class MerchantAPIView(APIView):
41
+ except exceptions.exception_whitelist as exc:
42
+ # No need to raise exception for exception whitelist
43
+ raise exc
44
+ except Exception as exc:
45
+ logger.error(f"Unexpected error: {exc} {args} {kwargs}")
46
+ raise exceptions.InternalServiceError(str(exc)) from exc
47
+
48
+ return wrapper
49
+
50
+
51
+ class PaymeWebHookAPIView(views.APIView):
25
52
  """
26
- MerchantAPIView class provides payme call back functionality.
53
+ A webhook view for Payme.
27
54
  """
28
- permission_classes = ()
29
55
  authentication_classes = ()
30
56
 
31
- def post(self, request) -> Response:
57
+ def post(self, request, *args, **kwargs):
58
+ """
59
+ Handle the incoming webhook request.
60
+ """
61
+ self.check_authorize(request)
62
+
63
+ payme_methods = {
64
+ "GetStatement": self.get_statement,
65
+ "CancelTransaction": self.cancel_transaction,
66
+ "PerformTransaction": self.perform_transaction,
67
+ "CreateTransaction": self.create_transaction,
68
+ "CheckTransaction": self.check_transaction,
69
+ "CheckPerformTransaction": self.check_perform_transaction,
70
+ }
71
+
72
+ try:
73
+ method = request.data["method"]
74
+ params = request.data["params"]
75
+ except KeyError as exc:
76
+ message = f"Error processing webhook: {exc}"
77
+ raise exceptions.InternalServiceError(message) from exc
78
+
79
+ if method in payme_methods:
80
+ result = payme_methods[method](params)
81
+ return Response(result)
82
+
83
+ raise exceptions.MethodNotFound("Method not supported yet!")
84
+
85
+ @staticmethod
86
+ def check_authorize(request):
32
87
  """
33
- Payme sends post request to our call back url.
34
- That methods are includes 6 methods
35
- - CheckPerformTransaction
36
- - CreateTransaction
37
- - PerformTransaction
38
- - CancelTransaction
39
- - CheckTransaction
40
- - GetStatement
88
+ Verify the integrity of the request using the merchant key.
41
89
  """
42
90
  password = request.META.get('HTTP_AUTHORIZATION')
43
- if self.authorize(password):
44
- incoming_data: dict = request.data
45
- incoming_method: str = incoming_data.get("method")
46
-
47
- logger.info("Call back data is incoming %s", incoming_data)
48
-
49
- try:
50
- paycom_method = self.get_paycom_method_by_name(
51
- incoming_method=incoming_method
52
- )
53
- except ValidationError as error:
54
- logger.error("Validation Error occurred: %s", error)
55
- raise MethodNotFound() from error
56
-
57
- except PerformTransactionDoesNotExist as error:
58
- logger.error("PerformTransactionDoesNotExist Error occurred: %s", error)
59
- raise PerformTransactionDoesNotExist() from error
60
-
61
- order_id, action = paycom_method(incoming_data.get("params"))
62
-
63
- if isinstance(paycom_method, CreateTransaction):
64
- self.create_transaction(
65
- order_id=order_id,
66
- action=action,
67
- )
91
+ if not password:
92
+ raise exceptions.PermissionDenied("Missing authentication credentials")
68
93
 
69
- if isinstance(paycom_method, PerformTransaction):
70
- self.perform_transaction(
71
- order_id=order_id,
72
- action=action,
73
- )
94
+ password = password.split()[-1]
95
+
96
+ try:
97
+ password = base64.b64decode(password).decode('utf-8')
98
+ except (binascii.Error, UnicodeDecodeError) as exc:
99
+ raise exceptions.PermissionDenied("Decoding error in authentication credentials") from exc
100
+
101
+ try:
102
+ payme_key = password.split(':')[-1]
103
+ except IndexError as exc:
104
+ message = "Invalid merchant key format in authentication credentials"
105
+ raise exceptions.PermissionDenied(message) from exc
106
+
107
+ if payme_key != settings.PAYME_KEY:
108
+ raise exceptions.PermissionDenied("Invalid merchant key specified")
109
+
110
+ @handle_exceptions
111
+ def fetch_account(self, params: dict):
112
+ """
113
+ Fetch account based on settings and params.
114
+ """
115
+ account_field = settings.PAYME_ACCOUNT_FIELD
116
+
117
+ account_value = params['account'].get(account_field)
118
+ if not account_value:
119
+ raise exceptions.InvalidAccount("Missing account field in parameters.")
120
+
121
+ # hard change
122
+ if account_field == "order_id":
123
+ account_field = "id"
124
+
125
+ account = AccountModel.objects.get(**{account_field: account_value})
126
+
127
+ return account
74
128
 
75
- if isinstance(paycom_method, CancelTransaction):
76
- self.cancel_transaction(
77
- order_id=order_id,
78
- action=action,
129
+ @handle_exceptions
130
+ def validate_amount(self, account, amount):
131
+ """
132
+ Validates if the amount matches for one-time payment accounts.
133
+ """
134
+ if not settings.PAYME_ONE_TIME_PAYMENT:
135
+ return True
136
+
137
+ expected_amount = Decimal(getattr(account, settings.PAYME_AMOUNT_FIELD)) * 100
138
+ received_amount = Decimal(amount)
139
+
140
+ if expected_amount != received_amount:
141
+ raise exceptions.IncorrectAmount(
142
+ f"Invalid amount. Expected: {expected_amount}, received: {received_amount}"
79
143
  )
80
144
 
81
- return Response(data=action)
145
+ return True
82
146
 
83
- def get_paycom_method_by_name(self, incoming_method: str) -> object:
147
+ @handle_exceptions
148
+ def check_perform_transaction(self, params) -> response.CheckPerformTransaction:
84
149
  """
85
- Use this static method to get the paycom method by name.
86
- :param incoming_method: string -> incoming method name
150
+ Handle the pre_create_transaction action.
87
151
  """
88
- available_methods: dict = {
89
- "CheckPerformTransaction": CheckPerformTransaction,
90
- "CreateTransaction": CreateTransaction,
91
- "PerformTransaction": PerformTransaction,
92
- "CancelTransaction": CancelTransaction,
93
- "CheckTransaction": CheckTransaction,
94
- "GetStatement": GetStatement
95
- }
152
+ account = self.fetch_account(params)
153
+ self.validate_amount(account, params.get('amount'))
96
154
 
97
- try:
98
- merchant_method = available_methods[incoming_method]
99
- except Exception as error:
100
- error_message = "Unavailable method: %s", incoming_method
101
- logger.error(error_message)
102
- raise MethodNotFound(error_message=error_message) from error
155
+ result = response.CheckPerformTransaction(allow=True)
156
+ return result.as_resp()
157
+
158
+ @handle_exceptions
159
+ def create_transaction(self, params) -> response.CreateTransaction:
160
+ """
161
+ Handle the create_transaction action.
162
+ """
163
+ transaction_id = params["id"]
164
+ amount = Decimal(params.get('amount', 0))
165
+ account = self.fetch_account(params)
103
166
 
104
- merchant_method = merchant_method()
167
+ self.validate_amount(account, amount)
105
168
 
106
- return merchant_method
169
+ defaults = {
170
+ "amount": amount,
171
+ "state": PaymeTransactions.INITIATING,
172
+ "account": account,
173
+ }
107
174
 
108
- @staticmethod
109
- def authorize(password: str) -> bool:
175
+ # Handle already existing transaction with the same ID for one-time payments
176
+ if settings.PAYME_ONE_TIME_PAYMENT:
177
+ # Check for an existing transaction with a different transaction_id for the given account
178
+ if PaymeTransactions.objects.filter(account=account).exclude(transaction_id=transaction_id).exists():
179
+ message = f"Transaction {transaction_id} already exists (Payme)."
180
+ logger.warning(message)
181
+ raise exceptions.TransactionAlreadyExists(message)
182
+
183
+ transaction, _ = PaymeTransactions.objects.get_or_create(
184
+ transaction_id=transaction_id,
185
+ defaults=defaults
186
+ )
187
+
188
+ result = response.CreateTransaction(
189
+ transaction=transaction.transaction_id,
190
+ state=transaction.state,
191
+ create_time=time_to_payme(transaction.created_at),
192
+ )
193
+ result = result.as_resp()
194
+
195
+ # callback event
196
+ self.handle_created_payment(params, result)
197
+
198
+ return result
199
+
200
+ @handle_exceptions
201
+ def perform_transaction(self, params) -> response.PerformTransaction:
110
202
  """
111
- Authorize the Merchant.
112
- :param password: string -> Merchant authorization password
203
+ Handle the successful payment.
113
204
  """
114
- is_payme: bool = False
115
- error_message: str = ""
205
+ transaction = PaymeTransactions.get_by_transaction_id(transaction_id=params["id"])
206
+
207
+ if transaction.is_performed():
208
+ result = response.PerformTransaction(
209
+ transaction=transaction.transaction_id,
210
+ state=transaction.state,
211
+ perform_time=time_to_payme(transaction.performed_at),
212
+ )
213
+ return result.as_resp()
116
214
 
117
- if not isinstance(password, str):
118
- error_message = "Request from an unauthorized source!"
119
- logger.error(error_message)
120
- raise PermissionDenied(error_message=error_message)
215
+ transaction.mark_as_performed()
121
216
 
122
- password = password.split()[-1]
217
+ result = response.PerformTransaction(
218
+ transaction=transaction.transaction_id,
219
+ state=transaction.state,
220
+ perform_time=time_to_payme(transaction.performed_at),
221
+ )
222
+ result = result.as_resp()
123
223
 
124
- try:
125
- password = base64.b64decode(password).decode('utf-8')
126
- except (binascii.Error, UnicodeDecodeError) as error:
127
- error_message = "Error when authorize request to merchant!"
128
- logger.error(error_message)
224
+ # callback successfully event
225
+ self.handle_successfully_payment(params, result)
129
226
 
130
- raise PermissionDenied(error_message=error_message) from error
227
+ return result
131
228
 
132
- merchant_key = password.split(':')[-1]
229
+ @handle_exceptions
230
+ def check_transaction(self, params: dict) -> dict | str | response.CheckPerformTransaction:
231
+ """
232
+ Handle check transaction request.
233
+ """
234
+ transaction = PaymeTransactions.get_by_transaction_id(transaction_id=params["id"])
235
+
236
+ result = response.CheckTransaction(
237
+ transaction=transaction.transaction_id,
238
+ state=transaction.state,
239
+ reason=transaction.cancel_reason,
240
+ create_time=time_to_payme(transaction.created_at),
241
+ perform_time=time_to_payme(transaction.performed_at),
242
+ cancel_time=time_to_payme(transaction.cancelled_at),
243
+ )
244
+
245
+ return result.as_resp()
133
246
 
134
- if merchant_key == settings.PAYME.get('PAYME_KEY'):
135
- is_payme = True
247
+ @handle_exceptions
248
+ def cancel_transaction(self, params) -> response.CancelTransaction:
249
+ """
250
+ Handle the cancelled payment.
251
+ """
252
+ transaction = PaymeTransactions.get_by_transaction_id(transaction_id=params["id"])
136
253
 
137
- if merchant_key != settings.PAYME.get('PAYME_KEY'):
138
- logger.error("Invalid key in request!")
254
+ if transaction.is_cancelled():
255
+ return self._cancel_response(transaction)
139
256
 
140
- if is_payme is False:
141
- raise PermissionDenied(
142
- error_message="Unavailable data for unauthorized users!"
257
+ if transaction.is_performed():
258
+ transaction.mark_as_cancelled(
259
+ cancel_reason=params["reason"],
260
+ state=PaymeTransactions.CANCELED
143
261
  )
262
+ elif transaction.is_created_in_payme():
263
+ transaction.mark_as_cancelled(
264
+ cancel_reason=params["reason"],
265
+ state=PaymeTransactions.CANCELED_DURING_INIT
266
+ )
267
+
268
+ result = self._cancel_response(transaction)
144
269
 
145
- return is_payme
270
+ # callback cancelled transaction event
271
+ self.handle_cancelled_payment(params, result)
272
+
273
+ return result
274
+
275
+ @handle_exceptions
276
+ def get_statement(self, params) -> response.GetStatement:
277
+ """
278
+ Retrieves a statement of transactions.
279
+ """
280
+ date_range = [time_to_service(params['from']), time_to_service(params['to'])]
281
+
282
+ transactions = PaymeTransactions.objects.filter(
283
+ created_at__range=date_range
284
+ ).order_by('-created_at')
285
+
286
+ result = response.GetStatement(transactions=[])
287
+
288
+ for transaction in transactions:
289
+ result.transactions.append({
290
+ "transaction": transaction.transaction_id,
291
+ "amount": transaction.amount,
292
+ "account": {
293
+ settings.PAYME_ACCOUNT_FIELD: transaction.account.id
294
+ },
295
+ "reason": transaction.cancel_reason,
296
+ "state": transaction.state,
297
+ "create_time": time_to_payme(transaction.created_at),
298
+ "perform_time": time_to_payme(transaction.performed_at),
299
+ "cancel_time": time_to_payme(transaction.cancelled_at),
300
+ })
301
+
302
+ return result.as_resp()
303
+
304
+ def _cancel_response(self, transaction):
305
+ """
306
+ Helper method to generate cancel transaction response.
307
+ """
308
+ result = response.CancelTransaction(
309
+ transaction=transaction.transaction_id,
310
+ state=transaction.state,
311
+ cancel_time=time_to_payme(transaction.cancelled_at),
312
+ )
313
+ return result.as_resp()
314
+
315
+ def handle_pre_payment(self, params, result, *args, **kwargs):
316
+ """
317
+ Handle the pre_create_transaction action. You can override this method
318
+ """
319
+ print(f"Transaction pre_created for this params: {params} and pre_created_result: {result}")
146
320
 
147
- def create_transaction(self, order_id, action) -> None:
321
+ def handle_created_payment(self, params, result, *args, **kwargs):
148
322
  """
149
- need implement in your view class
323
+ Handle the successful payment. You can override this method
150
324
  """
151
- pass
325
+ print(f"Transaction created for this params: {params} and cr_result: {result}")
152
326
 
153
- def perform_transaction(self, order_id, action) -> None:
327
+ def handle_successfully_payment(self, params, result, *args, **kwargs):
154
328
  """
155
- need implement in your view class
329
+ Handle the successful payment. You can override this method
156
330
  """
157
- pass
331
+ print(f"Transaction successfully performed for this params: {params} and performed_result: {result}")
158
332
 
159
- def cancel_transaction(self, order_id, action) -> None:
333
+ def handle_cancelled_payment(self, params, result, *args, **kwargs):
160
334
  """
161
- need implement in your view class
335
+ Handle the cancelled payment. You can override this method
162
336
  """
163
- pass
337
+ print(f"Transaction cancelled for this params: {params} and cancelled_result: {result}")