Интеграция Paynet
Paynet принимает оплату в своём приложении и в терминалах. Он обращается к вашему серверу по JSON-RPC 2.0: находит счёт, проводит платёж и позже проверяет его. Это руководство показывает обе стороны с PayTechUZ.
Установка
pip install "paytechuz[django]"
Платёжная ссылка
from paytechuz.gateways.paynet.client import PaynetGateway
paynet = PaynetGateway(merchant_id="your_merchant_id")
# Сумма в тийинах, необязательно
link = paynet.create_payment(id="order_123", amount=15000000)
# https://app.paynet.uz/?m=your_merchant_id&c=order_123&a=15000000
У ссылки нет адреса возврата. На компьютере показывается QR-код, на телефоне открывается приложение Paynet.
Вебхук для Django
Настройки
# settings.py
INSTALLED_APPS = [
# ...
'paytechuz.integrations.django',
]
PAYTECHUZ = {
'PAYNET': {
'SERVICE_ID': 'your_service_id',
'USERNAME': 'your_paynet_login',
'PASSWORD': 'your_paynet_password',
'ACCOUNT_MODEL': 'shop.models.Order',
'ACCOUNT_FIELD': 'order_id', # поле, которое Paynet присылает в params.fields
'AMOUNT_FIELD': 'amount', # в модели в сумах; Paynet присылает тийины
'ONE_TIME_PAYMENT': True,
},
}
Выполните python manage.py migrate. Он создаёт таблицу payments и
paynet_service_credentials, где хранится пароль, который меняет Paynet.
View
# views.py
from paytechuz.integrations.django.views import BasePaynetWebhookView
from shop.models import Order
class PaynetWebhookView(BasePaynetWebhookView):
def successfully_payment(self, params, transaction):
Order.objects.filter(id=transaction.account_id).update(status='paid')
def cancelled_payment(self, params, transaction):
Order.objects.filter(id=transaction.account_id).update(status='cancelled')
def get_check_data(self, params, account): # необязательно: GetInformation
return {'fields': {'amount': int(account.amount)}}
# urls.py
path('payments/webhook/paynet/', PaynetWebhookView.as_view(), name='paynet_webhook'),
successfully_payment выполняется в той же транзакции базы данных, что и
запись платежа. Если он падает, ничего не сохраняется и Paynet получает 102
— с клиента не списываются деньги за платёж, который вы не приняли.
Что отвечает вебхук
| Метод | Поведение |
|---|---|
GetInformation | возвращает данные счёта; 302, если счёта нет или он уже оплачен |
PerformTransaction | оплачивает счёт один раз; 201 на повторный transactionId, 413 на неверную сумму, 411 без transactionId |
CheckTransaction | состояние 1 оплачен, 2 отменён, 3 неизвестен |
CancelTransaction | 202, если уже отменён, 203, если неизвестен |
GetStatement | платежи за dateFrom…dateTo (YYYY-MM-DD HH:MM:SS), 414 на неверную дату |
ChangePassword | сохраняет новый пароль, не короче 8 символов |
Неверный логин получает 412 и HTTP 401 ещё до разбора тела запроса. Пустой
логин или пароль в настройках отклоняет любой вызов.
Смена пароля
Paynet меняет пароль вебхука через ChangePassword. PayTechUZ сохраняет его
в PaynetCredential (или в модели из CREDENTIAL_MODEL) и дальше проверяет
вызовы по нему. Настройка PASSWORD действует только до первой смены, поэтому
повторный деплой не вернёт старый пароль.
Sandbox
Отметьте сервис как sandbox, пока его проверяет тестер Paynet:
PAYTECHUZ = {
'PAYNET': {
'KASSAS': {
'test': {'SERVICE_ID': '1003', 'USERNAME': '...', 'PASSWORD': '...',
'IS_SANDBOX': True},
},
# ACCOUNT_MODEL и остальное как выше
},
}
Каждый вызов пишется в лог; платежи сохраняются, но вместо
successfully_payment вызывается sandbox_payment(params, transaction).
Оплаченный счёт отвечает 302, как и в боевом режиме, а после перехода
сервиса в бой sandbox-записи не попадают в выписку.
Несколько сервисов Paynet на одном URL — на странице Несколько касс.