payme-pkg 2.5.3__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 +0 -0
- payme/admin.py +11 -0
- payme/apps.py +10 -0
- payme/cards/__init__.py +1 -0
- payme/cards/subscribe_cards.py +166 -0
- payme/decorators/__init__.py +0 -0
- payme/decorators/decorators.py +34 -0
- payme/errors/__init__.py +0 -0
- payme/errors/exceptions.py +89 -0
- payme/methods/__init__.py +0 -0
- payme/methods/cancel_transaction.py +54 -0
- payme/methods/check_perform_transaction.py +26 -0
- payme/methods/check_transaction.py +43 -0
- payme/methods/create_transaction.py +68 -0
- payme/methods/generate_link.py +74 -0
- payme/methods/get_statement.py +65 -0
- payme/methods/perform_transaction.py +47 -0
- payme/migrations/0001_initial.py +48 -0
- payme/migrations/__init__.py +0 -0
- payme/models.py +61 -0
- payme/receipts/__init__.py +1 -0
- payme/receipts/subscribe_receipts.py +217 -0
- payme/serializers.py +88 -0
- payme/urls.py +8 -0
- payme/utils/__init__.py +0 -0
- payme/utils/get_params.py +24 -0
- payme/utils/logging.py +9 -0
- payme/utils/make_aware_datetime.py +21 -0
- payme/utils/support.py +8 -0
- payme/utils/to_json.py +12 -0
- payme/views.py +163 -0
- payme_pkg-2.5.3.dist-info/LICENSE.txt +20 -0
- payme_pkg-2.5.3.dist-info/METADATA +17 -0
- payme_pkg-2.5.3.dist-info/RECORD +36 -0
- payme_pkg-2.5.3.dist-info/WHEEL +5 -0
- payme_pkg-2.5.3.dist-info/top_level.txt +1 -0
payme/__init__.py
ADDED
|
File without changes
|
payme/admin.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from django.contrib import admin
|
|
2
|
+
|
|
3
|
+
from payme.models import CUSTOM_ORDER
|
|
4
|
+
from payme.models import Order as DefaultOrderModel
|
|
5
|
+
|
|
6
|
+
from payme.models import MerchatTransactionsModel
|
|
7
|
+
|
|
8
|
+
if not CUSTOM_ORDER:
|
|
9
|
+
admin.site.register(DefaultOrderModel)
|
|
10
|
+
|
|
11
|
+
admin.site.register(MerchatTransactionsModel)
|
payme/apps.py
ADDED
payme/cards/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .import subscribe_cards
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
from payme.utils.to_json import to_json
|
|
2
|
+
from payme.decorators.decorators import payme_request
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class PaymeSubscribeCards:
|
|
6
|
+
"""
|
|
7
|
+
The PaymeSubscribeCards class inclues
|
|
8
|
+
all paycom methods which are belongs to cards.
|
|
9
|
+
|
|
10
|
+
Parameters
|
|
11
|
+
----------
|
|
12
|
+
base_url: str — The base url of the paycom api
|
|
13
|
+
paycom_id: str — The paycom_id uses to identify
|
|
14
|
+
timeout: int — How many seconds to wait for the server to send data
|
|
15
|
+
|
|
16
|
+
Full method documentation
|
|
17
|
+
-------------------------
|
|
18
|
+
https://developer.help.paycom.uz/metody-subscribe-api/
|
|
19
|
+
"""
|
|
20
|
+
def __init__(
|
|
21
|
+
self,
|
|
22
|
+
base_url: str,
|
|
23
|
+
paycom_id: str,
|
|
24
|
+
timeout=5
|
|
25
|
+
) -> "PaymeSubscribeCards":
|
|
26
|
+
self.base_url: str = base_url
|
|
27
|
+
self.timeout: int = timeout
|
|
28
|
+
self.headers: dict = {
|
|
29
|
+
"X-Auth": paycom_id,
|
|
30
|
+
}
|
|
31
|
+
self.__methods: dict = {
|
|
32
|
+
"cards_check": "cards.check",
|
|
33
|
+
"cards_create": "cards.create",
|
|
34
|
+
"cards_remove": "cards.remove",
|
|
35
|
+
"cards_verify": "cards.verify",
|
|
36
|
+
"cards_get_verify_code": "cards.get_verify_code",
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@payme_request
|
|
40
|
+
def __request(self, data) -> dict:
|
|
41
|
+
"""
|
|
42
|
+
Use this private method to request.
|
|
43
|
+
On success,response will be OK with format JSON.
|
|
44
|
+
|
|
45
|
+
Parameters
|
|
46
|
+
----------
|
|
47
|
+
data: dict — Includes request data.
|
|
48
|
+
|
|
49
|
+
Returns dictionary Payme Response
|
|
50
|
+
---------------------------------
|
|
51
|
+
"""
|
|
52
|
+
return data
|
|
53
|
+
|
|
54
|
+
def cards_create(self, number: str, expire: str, save: bool = True) -> dict:
|
|
55
|
+
"""
|
|
56
|
+
Use this method to create a new card's token.
|
|
57
|
+
|
|
58
|
+
Parameters
|
|
59
|
+
----------
|
|
60
|
+
number: str — The card number maximum length 18 char
|
|
61
|
+
expire: str — The card expiration string maximum length 5 char
|
|
62
|
+
save: bool \
|
|
63
|
+
Type of token. Optional parameter
|
|
64
|
+
The option is enabled or disabled depending on the application's business logic
|
|
65
|
+
If the flag is true, the token can be used for further payments
|
|
66
|
+
if the flag is false the token can only be used once
|
|
67
|
+
The one-time token is deleted after payment
|
|
68
|
+
|
|
69
|
+
Full method documentation
|
|
70
|
+
-------------------------
|
|
71
|
+
https://developer.help.paycom.uz/metody-subscribe-api/cards.create
|
|
72
|
+
"""
|
|
73
|
+
data: dict = {
|
|
74
|
+
"method": self.__methods.get("cards_create"),
|
|
75
|
+
"params": {
|
|
76
|
+
"card": {
|
|
77
|
+
"number": number,
|
|
78
|
+
"expire": expire,
|
|
79
|
+
},
|
|
80
|
+
"save": save,
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return self.__request(to_json(**data))
|
|
84
|
+
|
|
85
|
+
def card_get_verify_code(self, token: str) -> dict:
|
|
86
|
+
"""
|
|
87
|
+
Use this method to get the verification code.
|
|
88
|
+
|
|
89
|
+
Parameters
|
|
90
|
+
----------
|
|
91
|
+
token: str — The card's non-active token
|
|
92
|
+
|
|
93
|
+
Full method documentation
|
|
94
|
+
-------------------------
|
|
95
|
+
https://developer.help.paycom.uz/metody-subscribe-api/cards.get_verify_code
|
|
96
|
+
"""
|
|
97
|
+
data: dict = {
|
|
98
|
+
"method": self.__methods.get('cards_get_verify_code'),
|
|
99
|
+
"params": {
|
|
100
|
+
"token": token,
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return self.__request(to_json(**data))
|
|
104
|
+
|
|
105
|
+
def cards_verify(self, verify_code: int, token: str) -> dict:
|
|
106
|
+
"""
|
|
107
|
+
Verification of the card using the code sent via SMS.
|
|
108
|
+
|
|
109
|
+
Parameters
|
|
110
|
+
----------
|
|
111
|
+
verify_code: int — Code for verification
|
|
112
|
+
token: str — The card's non-active token
|
|
113
|
+
|
|
114
|
+
Full method documentation
|
|
115
|
+
-------------------------
|
|
116
|
+
https://developer.help.paycom.uz/metody-subscribe-api/cards.verify
|
|
117
|
+
"""
|
|
118
|
+
data: dict = {
|
|
119
|
+
"method": self.__methods.get("cards_verify"),
|
|
120
|
+
"params": {
|
|
121
|
+
"token": token,
|
|
122
|
+
"code": verify_code
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return self.__request(to_json(**data))
|
|
126
|
+
|
|
127
|
+
def cards_check(self, token: str) -> dict:
|
|
128
|
+
"""
|
|
129
|
+
Checking the card token active or non-active.
|
|
130
|
+
|
|
131
|
+
Parameters
|
|
132
|
+
----------
|
|
133
|
+
token: str — The card's non-active token
|
|
134
|
+
|
|
135
|
+
Full method documentation
|
|
136
|
+
-------------------------
|
|
137
|
+
https://developer.help.paycom.uz/metody-subscribe-api/cards.check
|
|
138
|
+
"""
|
|
139
|
+
data: dict = {
|
|
140
|
+
"method": self.__methods.get("cards_check"),
|
|
141
|
+
"params": {
|
|
142
|
+
"token": token,
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return self.__request(to_json(**data))
|
|
147
|
+
|
|
148
|
+
def cards_remove(self, token: str) -> dict:
|
|
149
|
+
"""
|
|
150
|
+
Delete card's token on success returns success.
|
|
151
|
+
|
|
152
|
+
Parameters
|
|
153
|
+
----------
|
|
154
|
+
token: str — The card's non-active token
|
|
155
|
+
|
|
156
|
+
Full method documentation
|
|
157
|
+
-------------------------
|
|
158
|
+
https://developer.help.paycom.uz/metody-subscribe-api/cards.remove
|
|
159
|
+
"""
|
|
160
|
+
data: dict = {
|
|
161
|
+
"method": self.__methods.get("cards_remove"),
|
|
162
|
+
"params": {
|
|
163
|
+
"token": token,
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return self.__request(to_json(**data))
|
|
File without changes
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
|
|
3
|
+
from requests import request
|
|
4
|
+
from requests.exceptions import Timeout
|
|
5
|
+
from requests.exceptions import RequestException
|
|
6
|
+
|
|
7
|
+
from payme.utils.logging import logger
|
|
8
|
+
|
|
9
|
+
from ..errors.exceptions import PaymeTimeoutException
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def payme_request(func):
|
|
13
|
+
"""
|
|
14
|
+
Payme request decorator.
|
|
15
|
+
"""
|
|
16
|
+
@functools.wraps(func)
|
|
17
|
+
def wrapper(self, data):
|
|
18
|
+
response = None
|
|
19
|
+
req_data = {
|
|
20
|
+
"method": "POST",
|
|
21
|
+
"url": self.base_url,
|
|
22
|
+
"data": data,
|
|
23
|
+
"headers": self.headers,
|
|
24
|
+
"timeout": self.timeout,
|
|
25
|
+
}
|
|
26
|
+
try:
|
|
27
|
+
response = request(**req_data)
|
|
28
|
+
response.raise_for_status()
|
|
29
|
+
except (Timeout, RequestException) as error:
|
|
30
|
+
logger.info("Payme request has been failed as error: %s", error)
|
|
31
|
+
raise PaymeTimeoutException() from error
|
|
32
|
+
return response.json()
|
|
33
|
+
|
|
34
|
+
return wrapper
|
payme/errors/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
from rest_framework.exceptions import APIException
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class BasePaymeException(APIException):
|
|
5
|
+
"""
|
|
6
|
+
BasePaymeException it's APIException.
|
|
7
|
+
"""
|
|
8
|
+
status_code = 200
|
|
9
|
+
error_code = None
|
|
10
|
+
message = None
|
|
11
|
+
|
|
12
|
+
# pylint: disable=super-init-not-called
|
|
13
|
+
def __init__(self, error_message: str = None):
|
|
14
|
+
detail: dict = {
|
|
15
|
+
"error": {
|
|
16
|
+
"code": self.error_code,
|
|
17
|
+
"message": self.message,
|
|
18
|
+
"data": error_message
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
self.detail = detail
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class PermissionDenied(BasePaymeException):
|
|
25
|
+
"""
|
|
26
|
+
PermissionDenied APIException \
|
|
27
|
+
That is raised when the client is not allowed to server.
|
|
28
|
+
"""
|
|
29
|
+
status_code = 200
|
|
30
|
+
error_code = -32504
|
|
31
|
+
message = "Permission denied"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class MethodNotFound(BasePaymeException):
|
|
35
|
+
"""
|
|
36
|
+
MethodNotFound APIException \
|
|
37
|
+
That is raised when the method does not exist.
|
|
38
|
+
"""
|
|
39
|
+
status_code = 405
|
|
40
|
+
error_code = -32601
|
|
41
|
+
message = 'Method not found'
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class TooManyRequests(BasePaymeException):
|
|
45
|
+
"""
|
|
46
|
+
TooManyRequests APIException \
|
|
47
|
+
That is raised when the request exceeds the limit.
|
|
48
|
+
"""
|
|
49
|
+
status_code = 200
|
|
50
|
+
error_code = -31099
|
|
51
|
+
message = {
|
|
52
|
+
"uz": "Buyurtma tolovni amalga oshirish jarayonida",
|
|
53
|
+
"ru": "Транзакция в очереди",
|
|
54
|
+
"en": "Order payment status is queued"
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class IncorrectAmount(BasePaymeException):
|
|
59
|
+
"""
|
|
60
|
+
IncorrectAmount APIException \
|
|
61
|
+
That is raised when the amount is not incorrect.
|
|
62
|
+
"""
|
|
63
|
+
status_code = 200
|
|
64
|
+
error_code = -31001
|
|
65
|
+
message = {
|
|
66
|
+
'ru': 'Неверная сумма',
|
|
67
|
+
'uz': 'Incorrect amount',
|
|
68
|
+
'en': 'Incorrect amount',
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class PerformTransactionDoesNotExist(BasePaymeException):
|
|
73
|
+
"""
|
|
74
|
+
PerformTransactionDoesNotExist APIException \
|
|
75
|
+
That is raised when a transaction does not exist or deleted.
|
|
76
|
+
"""
|
|
77
|
+
status_code = 200
|
|
78
|
+
error_code = -31050
|
|
79
|
+
message = {
|
|
80
|
+
"uz": "Buyurtma topilmadi",
|
|
81
|
+
"ru": "Заказ не существует",
|
|
82
|
+
"en": "Order does not exists"
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class PaymeTimeoutException(Exception):
|
|
87
|
+
"""
|
|
88
|
+
Payme timeout exception that means that payme is working slowly.
|
|
89
|
+
"""
|
|
File without changes
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import time
|
|
2
|
+
|
|
3
|
+
from django.db import transaction
|
|
4
|
+
|
|
5
|
+
from payme.utils.logging import logger
|
|
6
|
+
from payme.models import MerchatTransactionsModel
|
|
7
|
+
from payme.errors.exceptions import PerformTransactionDoesNotExist
|
|
8
|
+
from payme.serializers import MerchatTransactionsModelSerializer as MTMS
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CancelTransaction:
|
|
12
|
+
"""
|
|
13
|
+
CancelTransaction class
|
|
14
|
+
That is used to cancel a transaction.
|
|
15
|
+
|
|
16
|
+
Full method documentation
|
|
17
|
+
-------------------------
|
|
18
|
+
https://developer.help.paycom.uz/metody-merchant-api/canceltransaction
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
@transaction.atomic
|
|
22
|
+
def __call__(self, params: dict):
|
|
23
|
+
clean_data: dict = MTMS.get_validated_data(
|
|
24
|
+
params=params
|
|
25
|
+
)
|
|
26
|
+
try:
|
|
27
|
+
with transaction.atomic():
|
|
28
|
+
transactions: MerchatTransactionsModel = \
|
|
29
|
+
MerchatTransactionsModel.objects.filter(
|
|
30
|
+
_id=clean_data.get('_id'),
|
|
31
|
+
).first()
|
|
32
|
+
if transactions.cancel_time == 0:
|
|
33
|
+
transactions.cancel_time = int(time.time() * 1000)
|
|
34
|
+
if transactions.perform_time == 0:
|
|
35
|
+
transactions.state = -1
|
|
36
|
+
if transactions.perform_time != 0:
|
|
37
|
+
transactions.state = -2
|
|
38
|
+
transactions.reason = clean_data.get("reason")
|
|
39
|
+
transactions.save()
|
|
40
|
+
|
|
41
|
+
except PerformTransactionDoesNotExist as error:
|
|
42
|
+
logger.error("Paycom transaction does not exist: %s", error)
|
|
43
|
+
raise PerformTransactionDoesNotExist() from error
|
|
44
|
+
|
|
45
|
+
response: dict = {
|
|
46
|
+
"result": {
|
|
47
|
+
"state": transactions.state,
|
|
48
|
+
"cancel_time": transactions.cancel_time,
|
|
49
|
+
"transaction": transactions.transaction_id,
|
|
50
|
+
"reason": int(transactions.reason),
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return transactions.order_id, response
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from payme.utils.get_params import get_params
|
|
2
|
+
from payme.serializers import MerchatTransactionsModelSerializer
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class CheckPerformTransaction:
|
|
6
|
+
"""
|
|
7
|
+
CheckPerformTransaction class
|
|
8
|
+
That's used to check perform transaction.
|
|
9
|
+
|
|
10
|
+
Full method documentation
|
|
11
|
+
-------------------------
|
|
12
|
+
https://developer.help.paycom.uz/metody-merchant-api/checktransaction
|
|
13
|
+
"""
|
|
14
|
+
def __call__(self, params: dict) -> dict:
|
|
15
|
+
serializer = MerchatTransactionsModelSerializer(
|
|
16
|
+
data=get_params(params)
|
|
17
|
+
)
|
|
18
|
+
serializer.is_valid(raise_exception=True)
|
|
19
|
+
|
|
20
|
+
response = {
|
|
21
|
+
"result": {
|
|
22
|
+
"allow": True,
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return None, response
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from django.db import DatabaseError
|
|
2
|
+
|
|
3
|
+
from payme.utils.logging import logger
|
|
4
|
+
from payme.models import MerchatTransactionsModel
|
|
5
|
+
from payme.serializers import MerchatTransactionsModelSerializer as MTMS
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class CheckTransaction:
|
|
9
|
+
"""
|
|
10
|
+
CheckTransaction class
|
|
11
|
+
That's used to check transaction
|
|
12
|
+
|
|
13
|
+
Full method documentation
|
|
14
|
+
-------------------------
|
|
15
|
+
https://developer.help.paycom.uz/metody-merchant-api/checkperformtransaction
|
|
16
|
+
"""
|
|
17
|
+
def __call__(self, params: dict) -> None:
|
|
18
|
+
clean_data: dict = MTMS.get_validated_data(
|
|
19
|
+
params=params
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
transaction = \
|
|
24
|
+
MerchatTransactionsModel.objects.get(
|
|
25
|
+
_id=clean_data.get("_id"),
|
|
26
|
+
)
|
|
27
|
+
response = {
|
|
28
|
+
"result": {
|
|
29
|
+
"create_time": int(transaction.created_at_ms),
|
|
30
|
+
"perform_time": transaction.perform_time,
|
|
31
|
+
"cancel_time": transaction.cancel_time,
|
|
32
|
+
"transaction": transaction.transaction_id,
|
|
33
|
+
"state": transaction.state,
|
|
34
|
+
"reason": None,
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if transaction.reason is not None:
|
|
38
|
+
response["result"]["reason"] = int(transaction.reason)
|
|
39
|
+
|
|
40
|
+
except DatabaseError as error:
|
|
41
|
+
logger.error("Error getting transaction in database: %s", error)
|
|
42
|
+
|
|
43
|
+
return None, response
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
import time
|
|
3
|
+
import datetime
|
|
4
|
+
|
|
5
|
+
from payme.utils.logging import logger
|
|
6
|
+
from payme.utils.get_params import get_params
|
|
7
|
+
from payme.models import MerchatTransactionsModel
|
|
8
|
+
from payme.errors.exceptions import TooManyRequests
|
|
9
|
+
from payme.serializers import MerchatTransactionsModelSerializer
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class CreateTransaction:
|
|
13
|
+
"""
|
|
14
|
+
CreateTransaction class
|
|
15
|
+
That's used to create transaction
|
|
16
|
+
|
|
17
|
+
Full method documentation
|
|
18
|
+
-------------------------
|
|
19
|
+
https://developer.help.paycom.uz/metody-merchant-api/createtransaction
|
|
20
|
+
"""
|
|
21
|
+
def __call__(self, params: dict) -> dict:
|
|
22
|
+
serializer = MerchatTransactionsModelSerializer(
|
|
23
|
+
data=get_params(params)
|
|
24
|
+
)
|
|
25
|
+
serializer.is_valid(raise_exception=True)
|
|
26
|
+
order_id = serializer.validated_data.get("order_id")
|
|
27
|
+
|
|
28
|
+
try:
|
|
29
|
+
transaction = MerchatTransactionsModel.objects.filter(
|
|
30
|
+
order_id=order_id
|
|
31
|
+
).last()
|
|
32
|
+
|
|
33
|
+
if transaction is not None:
|
|
34
|
+
if transaction._id != serializer.validated_data.get("_id"):
|
|
35
|
+
raise TooManyRequests()
|
|
36
|
+
|
|
37
|
+
except TooManyRequests as error:
|
|
38
|
+
logger.error("Too many requests for transaction %s", error)
|
|
39
|
+
raise TooManyRequests() from error
|
|
40
|
+
|
|
41
|
+
if transaction is None:
|
|
42
|
+
transaction, _ = \
|
|
43
|
+
MerchatTransactionsModel.objects.get_or_create(
|
|
44
|
+
_id=serializer.validated_data.get('_id'),
|
|
45
|
+
order_id=serializer.validated_data.get('order_id'),
|
|
46
|
+
transaction_id=uuid.uuid4(),
|
|
47
|
+
amount=serializer.validated_data.get('amount'),
|
|
48
|
+
created_at_ms=int(time.time() * 1000),
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
if transaction:
|
|
52
|
+
response: dict = {
|
|
53
|
+
"result": {
|
|
54
|
+
"create_time": int(transaction.created_at_ms),
|
|
55
|
+
"transaction": transaction.transaction_id,
|
|
56
|
+
"state": int(transaction.state),
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return order_id, response
|
|
61
|
+
|
|
62
|
+
@staticmethod
|
|
63
|
+
def _convert_ms_to_datetime(time_ms: str) -> int:
|
|
64
|
+
"""Use this format to convert from time ms to datetime format.
|
|
65
|
+
"""
|
|
66
|
+
readable_datetime = datetime.datetime.fromtimestamp(time_ms / 1000)
|
|
67
|
+
|
|
68
|
+
return readable_datetime
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
from decimal import Decimal
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
from django.conf import settings
|
|
6
|
+
|
|
7
|
+
PAYME_ID = settings.PAYME.get('PAYME_ID')
|
|
8
|
+
PAYME_ACCOUNT = settings.PAYME.get('PAYME_ACCOUNT')
|
|
9
|
+
PAYME_CALL_BACK_URL = settings.PAYME.get('PAYME_CALL_BACK_URL')
|
|
10
|
+
PAYME_URL = settings.PAYME.get("PAYME_URL")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class GeneratePayLink:
|
|
15
|
+
"""
|
|
16
|
+
GeneratePayLink dataclass
|
|
17
|
+
That's used to generate pay lint for each order.
|
|
18
|
+
|
|
19
|
+
Parameters
|
|
20
|
+
----------
|
|
21
|
+
order_id: int — The order_id for paying
|
|
22
|
+
amount: int — The amount belong to the order
|
|
23
|
+
|
|
24
|
+
Returns str — pay link
|
|
25
|
+
----------------------
|
|
26
|
+
|
|
27
|
+
Full method documentation
|
|
28
|
+
-------------------------
|
|
29
|
+
https://developer.help.paycom.uz/initsializatsiya-platezhey/
|
|
30
|
+
"""
|
|
31
|
+
order_id: str
|
|
32
|
+
amount: Decimal
|
|
33
|
+
|
|
34
|
+
def generate_link(self) -> str:
|
|
35
|
+
"""
|
|
36
|
+
GeneratePayLink for each order.
|
|
37
|
+
"""
|
|
38
|
+
generated_pay_link: str = "{payme_url}/{encode_params}"
|
|
39
|
+
params: str = 'm={payme_id};ac.{payme_account}={order_id};a={amount};c={call_back_url}'
|
|
40
|
+
|
|
41
|
+
params = params.format(
|
|
42
|
+
payme_id=PAYME_ID,
|
|
43
|
+
payme_account=PAYME_ACCOUNT,
|
|
44
|
+
order_id=self.order_id,
|
|
45
|
+
amount=self.amount,
|
|
46
|
+
call_back_url=PAYME_CALL_BACK_URL
|
|
47
|
+
)
|
|
48
|
+
encode_params = base64.b64encode(params.encode("utf-8"))
|
|
49
|
+
return generated_pay_link.format(
|
|
50
|
+
payme_url=PAYME_URL,
|
|
51
|
+
encode_params=str(encode_params, 'utf-8')
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
@staticmethod
|
|
55
|
+
def to_tiyin(amount: Decimal) -> Decimal:
|
|
56
|
+
"""
|
|
57
|
+
Convert from soum to tiyin.
|
|
58
|
+
|
|
59
|
+
Parameters
|
|
60
|
+
----------
|
|
61
|
+
amount: Decimal -> order amount
|
|
62
|
+
"""
|
|
63
|
+
return amount * 100
|
|
64
|
+
|
|
65
|
+
@staticmethod
|
|
66
|
+
def to_soum(amount: Decimal) -> Decimal:
|
|
67
|
+
"""
|
|
68
|
+
Convert from tiyin to soum.
|
|
69
|
+
|
|
70
|
+
Parameters
|
|
71
|
+
----------
|
|
72
|
+
amount: Decimal -> order amount
|
|
73
|
+
"""
|
|
74
|
+
return amount / 100
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
from django.db import DatabaseError
|
|
2
|
+
|
|
3
|
+
from payme.utils.logging import logger
|
|
4
|
+
from payme.models import MerchatTransactionsModel
|
|
5
|
+
from payme.serializers import MerchatTransactionsModelSerializer as MTMS
|
|
6
|
+
from payme.utils.make_aware_datetime import make_aware_datetime as mad
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class GetStatement:
|
|
10
|
+
"""
|
|
11
|
+
GetStatement class
|
|
12
|
+
Transaction information is used for reconciliation
|
|
13
|
+
of merchant and Payme Business transactions.
|
|
14
|
+
|
|
15
|
+
Full method documentation
|
|
16
|
+
-------------------------
|
|
17
|
+
https://developer.help.paycom.uz/metody-merchant-api/getstatement
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __call__(self, params: dict):
|
|
21
|
+
clean_data: dict = MTMS.get_validated_data(
|
|
22
|
+
params=params
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
start_date, end_date = mad(
|
|
26
|
+
int(clean_data.get("start_date")),
|
|
27
|
+
int(clean_data.get("end_date"))
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
try:
|
|
31
|
+
transactions = \
|
|
32
|
+
MerchatTransactionsModel.objects.filter(
|
|
33
|
+
created_at__gte=start_date,
|
|
34
|
+
created_at__lte=end_date
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
if not transactions: # no transactions found for the period
|
|
38
|
+
return {"result": {"transactions": []}}
|
|
39
|
+
|
|
40
|
+
statements = [
|
|
41
|
+
{
|
|
42
|
+
'id': t._id,
|
|
43
|
+
'time': int(t.created_at.timestamp()),
|
|
44
|
+
'amount': t.amount,
|
|
45
|
+
'account': {'order_id': t.order_id},
|
|
46
|
+
'create_time': t.state,
|
|
47
|
+
'perform_time': t.perform_time,
|
|
48
|
+
'cancel_time': t.cancel_time,
|
|
49
|
+
'transaction': t.order_id,
|
|
50
|
+
'state': t.state,
|
|
51
|
+
'reason': t.reason,
|
|
52
|
+
'receivers': [] # not implemented
|
|
53
|
+
} for t in transactions
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
response: dict = {
|
|
57
|
+
"result": {
|
|
58
|
+
"transactions": statements
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
except DatabaseError as error:
|
|
62
|
+
logger.error("Error getting transaction in database: %s", error)
|
|
63
|
+
response = {"result": {"transactions": []}}
|
|
64
|
+
|
|
65
|
+
return None, response
|