-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtkassa.py
More file actions
151 lines (129 loc) · 5.61 KB
/
tkassa.py
File metadata and controls
151 lines (129 loc) · 5.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
import re
import requests
from hashlib import sha256
from typing import Optional
from models import Receipt
from models import InitResponse
from models import GetStateResponse
def camel_to_snake(name: str) -> str:
pattern = re.compile(r"(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])")
return pattern.sub('_', name).lower()
class TKassa:
"""
!!! Внимание !!!
Этот код не является полноценным SDK для API Т-кассы.
Он ограничен реализацией стандартного платежа и
предназначен только для демонстрационных целей.
- Документация T-kassa: https://www.tbank.ru/kassa/dev/payments/#section/Nachalo-raboty
- Протокол EACQ C PCI DSS: https://www.tbank.ru/static/documents/merchant_api_protocoI_eacq_pci_dss.pdf
"""
def __init__(
self,
terminal_key: str,
password: str,
base_url: str,
) -> None:
self.__terminal_key = terminal_key
self.__password = password
self.base_url = base_url
self.headers = {
'Content-Type': 'application/json'
}
def _generate_token(
self,
payload: dict
) -> str:
"""
Для запросов к MAPI, Т-касса требует добавлять к телу запроса поле 'Token',
которое содержит хэш тела запроса и пароля
- Документация: https://www.tbank.ru/kassa/dev/payments/#section/Token
"""
token_data = {**payload, "Password": self.__password}
token_str = ''.join(
[str(token_data[key]) for key in sorted(token_data.keys()) if not isinstance(token_data[key], dict)]
)
return sha256(token_str.encode("utf-8")).hexdigest()
def _call_api(
self,
method: str,
data: dict,
with_token: bool = True,
with_terminal: bool = True,
) -> dict:
if with_terminal:
data["TerminalKey"] = self.__terminal_key
if with_token:
data["Token"] = self._generate_token(data)
response = requests.post(self.base_url + method, headers=self.headers, json=data)
response.raise_for_status()
return response.json()
def init(
self,
amount: int,
order_id: str,
description: Optional[str] = None,
receipt: Optional[Receipt] = None,
) -> InitResponse:
"""
Метод инициирует платежную сессию
:параметр amount int[0 < x < 1_000_000_000]: Сумма в копейках
:параметр order_id: Уникальный идентификатор заказа
:параметр description: Описание заказа. Значение параметра будет отображено на платежной форме.
:параметр receipt: Данные с чека. Обязателен, если подключена онлайн-касса.
* В API метод Init может принимать и другие параметры. Все они перечислены в документации.
- Документация: https://www.tbank.ru/kassa/dev/payments/#tag/Standartnyj-platezh/operation/Init
"""
method = "Init"
payload = {
"Amount": amount,
"OrderId": order_id,
}
if description is not None:
payload["Description"] = description
if receipt is not None:
payload["Receipt"] = {
"Taxation": receipt.taxation,
"Items": [
{
"Name": item.name,
"Price": item.price,
"Quantity": item.quantity,
"Amount": item.amount,
"Tax": item.tax
}
for item in receipt.items
],
}
if receipt.phone is not None:
payload["Receipt"]["Phone"] = receipt.phone
if receipt.email is not None:
payload["Receipt"]["Email"] = receipt.email
response = self._call_api(method, payload)
if not response["Success"]:
raise RuntimeError(
f"Неудача, метод {method}\n"
f"Ответ API:\n {response}"
)
return InitResponse(
**{camel_to_snake(key):value for key, value in response.items()}
)
def get_state(self, payment_id: str) -> GetStateResponse:
"""
Возвращает статус заказа
:параметр payment_id: Уникальный идентификатор заказа
* В API метод GetState может принимать и другие параметры. Все они перечислены в документации.
- Документация: https://www.tbank.ru/kassa/dev/payments/#tag/Standartnyj-platezh/operation/GetState
"""
method = "GetState"
payload = {
"PaymentId": payment_id
}
response = self._call_api(method, payload)
if not response["Success"]:
raise RuntimeError(
f"Неудача, метод {method}\n"
f"Ответ API:\n {response}"
)
return GetStateResponse(
**{camel_to_snake(key):value for key, value in response.items()}
)