---
title: Интеграция Paynet
description: Интеграция Paynet в Python-проект с PayTechUZ — платёжные ссылки, JSON-RPC вебхук, смена пароля, sandbox-сервисы и пример на Django.
source: https://pay-tech.uz/ru/docs/integrations/paynet
---

# Интеграция Paynet

Paynet принимает оплату в своём приложении и в терминалах. Он обращается к
вашему серверу по JSON-RPC 2.0: находит счёт, проводит платёж и позже
проверяет его. Это руководство показывает обе стороны с PayTechUZ.

## Установка

```bash
pip install "paytechuz[django]"
```

## Платёжная ссылка

```python
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

### Настройки

```python
# 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

```python
# 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)}}
```

```python
# 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:

```python
PAYTECHUZ = {
    'PAYNET': {
        'KASSAS': {
            'test': {'SERVICE_ID': '1003', 'USERNAME': '...', 'PASSWORD': '...',
                     'IS_SANDBOX': True},
        },
        # ACCOUNT_MODEL и остальное как выше
    },
}
```

Каждый вызов пишется в лог; платежи сохраняются, но вместо
`successfully_payment` вызывается `sandbox_payment(params, transaction)`.
Оплаченный счёт отвечает `302`, как и в боевом режиме, а после перехода
сервиса в бой sandbox-записи не попадают в выписку.

Несколько сервисов Paynet на одном URL — на странице
[Несколько касс](./multiple-kassas).

## Дополнительная информация

- [Несколько касс](./multiple-kassas)
- [Интеграция с Django](./django)
- [GitHub репозиторий PayTechUZ](https://github.com/PayTechUz/paytechuz)
