payme-pkg 2.6.7__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.
- payme/__init__.py +1 -0
- payme/admin.py +12 -6
- payme/apps.py +0 -4
- payme/classes/cards.py +203 -0
- payme/classes/client.py +30 -0
- payme/classes/http.py +107 -0
- payme/classes/initializer.py +82 -0
- payme/classes/receipts.py +298 -0
- payme/const.py +12 -0
- payme/exceptions/__init__.py +5 -0
- payme/exceptions/general.py +275 -0
- payme/exceptions/webhook.py +125 -0
- payme/models.py +113 -46
- payme/types/response/__init__.py +4 -0
- payme/types/response/cards.py +110 -0
- payme/types/response/receipts.py +215 -0
- payme/types/response/webhook.py +136 -0
- payme/urls.py +2 -2
- payme/util.py +26 -0
- payme/views.py +287 -113
- payme_pkg-3.0.17.dist-info/METADATA +193 -0
- payme_pkg-3.0.17.dist-info/RECORD +29 -0
- payme_pkg-3.0.17.dist-info/top_level.txt +1 -0
- core/asgi.py +0 -16
- core/settings.py +0 -133
- core/urls.py +0 -25
- core/wsgi.py +0 -16
- my_app/admin.py +0 -3
- my_app/apps.py +0 -6
- my_app/models.py +0 -3
- my_app/tests.py +0 -3
- my_app/views.py +0 -16
- payme/cards/__init__.py +0 -1
- payme/cards/subscribe_cards.py +0 -166
- payme/decorators/__init__.py +0 -0
- payme/decorators/decorators.py +0 -34
- payme/errors/__init__.py +0 -0
- payme/errors/exceptions.py +0 -89
- payme/methods/__init__.py +0 -0
- payme/methods/cancel_transaction.py +0 -54
- payme/methods/check_perform_transaction.py +0 -26
- payme/methods/check_transaction.py +0 -43
- payme/methods/create_transaction.py +0 -68
- payme/methods/generate_link.py +0 -83
- payme/methods/get_statement.py +0 -65
- payme/methods/perform_transaction.py +0 -47
- payme/migrations/0001_initial.py +0 -48
- payme/receipts/__init__.py +0 -1
- payme/receipts/subscribe_receipts.py +0 -217
- payme/serializers.py +0 -86
- payme/utils/__init__.py +0 -0
- payme/utils/get_params.py +0 -24
- payme/utils/logging.py +0 -9
- payme/utils/make_aware_datetime.py +0 -21
- payme/utils/support.py +0 -8
- payme/utils/to_json.py +0 -13
- payme_pkg-2.6.7.dist-info/METADATA +0 -13
- payme_pkg-2.6.7.dist-info/RECORD +0 -48
- payme_pkg-2.6.7.dist-info/top_level.txt +0 -3
- {core → payme/classes}/__init__.py +0 -0
- {my_app → payme/types}/__init__.py +0 -0
- {my_app/migrations → payme/types/request}/__init__.py +0 -0
- {payme_pkg-2.6.7.dist-info → payme_pkg-3.0.17.dist-info}/LICENSE.txt +0 -0
- {payme_pkg-2.6.7.dist-info → payme_pkg-3.0.17.dist-info}/WHEEL +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
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
|
|
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
|
|
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
|
-
|
|
13
|
-
|
|
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
|
-
|
|
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
|
-
|
|
53
|
+
A webhook view for Payme.
|
|
27
54
|
"""
|
|
28
|
-
permission_classes = ()
|
|
29
55
|
authentication_classes = ()
|
|
30
56
|
|
|
31
|
-
def post(self, request
|
|
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
|
-
|
|
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
|
|
44
|
-
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
|
145
|
+
return True
|
|
82
146
|
|
|
83
|
-
|
|
147
|
+
@handle_exceptions
|
|
148
|
+
def check_perform_transaction(self, params) -> response.CheckPerformTransaction:
|
|
84
149
|
"""
|
|
85
|
-
|
|
86
|
-
:param incoming_method: string -> incoming method name
|
|
150
|
+
Handle the pre_create_transaction action.
|
|
87
151
|
"""
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
-
|
|
167
|
+
self.validate_amount(account, amount)
|
|
105
168
|
|
|
106
|
-
|
|
169
|
+
defaults = {
|
|
170
|
+
"amount": amount,
|
|
171
|
+
"state": PaymeTransactions.INITIATING,
|
|
172
|
+
"account": account,
|
|
173
|
+
}
|
|
107
174
|
|
|
108
|
-
|
|
109
|
-
|
|
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
|
-
|
|
112
|
-
:param password: string -> Merchant authorization password
|
|
203
|
+
Handle the successful payment.
|
|
113
204
|
"""
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
125
|
-
|
|
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
|
-
|
|
227
|
+
return result
|
|
131
228
|
|
|
132
|
-
|
|
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
|
-
|
|
135
|
-
|
|
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
|
|
138
|
-
|
|
254
|
+
if transaction.is_cancelled():
|
|
255
|
+
return self._cancel_response(transaction)
|
|
139
256
|
|
|
140
|
-
if
|
|
141
|
-
|
|
142
|
-
|
|
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
|
-
|
|
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
|
|
321
|
+
def handle_created_payment(self, params, result, *args, **kwargs):
|
|
148
322
|
"""
|
|
149
|
-
|
|
323
|
+
Handle the successful payment. You can override this method
|
|
150
324
|
"""
|
|
151
|
-
|
|
325
|
+
print(f"Transaction created for this params: {params} and cr_result: {result}")
|
|
152
326
|
|
|
153
|
-
def
|
|
327
|
+
def handle_successfully_payment(self, params, result, *args, **kwargs):
|
|
154
328
|
"""
|
|
155
|
-
|
|
329
|
+
Handle the successful payment. You can override this method
|
|
156
330
|
"""
|
|
157
|
-
|
|
331
|
+
print(f"Transaction successfully performed for this params: {params} and performed_result: {result}")
|
|
158
332
|
|
|
159
|
-
def
|
|
333
|
+
def handle_cancelled_payment(self, params, result, *args, **kwargs):
|
|
160
334
|
"""
|
|
161
|
-
|
|
335
|
+
Handle the cancelled payment. You can override this method
|
|
162
336
|
"""
|
|
163
|
-
|
|
337
|
+
print(f"Transaction cancelled for this params: {params} and cancelled_result: {result}")
|