PAYNEXИнтеграцияAPI (Swagger)API (ReDoc)

PAYNEX — документация по интеграции для мерчантов

P2P-процессинг депозитов и выплат. Вы (мерчант) создаёте заявки через API, клиент переводит деньги другому клиенту напрямую, мы матчим встречные потоки и подтверждаем перевод. Итог приходит вам вебхуком.


1. Авторизация

Передавайте ваш ключ одним из двух способов (что удобнее) — оба равнозначны:

X-API-Key: paynex_live_xxxxxxxxxxxxxxxxxxxx

или

Authorization: Bearer paynex_live_xxxxxxxxxxxxxxxxxxxx

В личном кабинете этот ключ называется Bearer Token. Отдельно есть Sign Key (он же api_secret) — он не передаётся в запросах, а нужен только для проверки подписи входящих вебхуков (см. §6). Оба значения — в ЛК → Настройки; храните на сервере.

Ошибки авторизации: 401 (нет/неверный ключ), 403 (мерчант отключён).


2. Создать депозит (Pay-In)

POST /api/v1/payments

Заголовки: X-API-Key, Content-Type: application/json, опц. Idempotency-Key.

Поле Тип Обяз. Описание
direction string да "payin"
amount_kopecks int да сумма в копейках (1..10^9)
currency string нет по умолч. "RUB"
payment_method string да "card" или "sbp"
merchant_player_id string да ваш ID клиента (напр. "user_42")
player_full_name string нет ФИО клиента из вашего KYC. Рекомендуется: включает анти-треугольник (сверку с ФИО на чеке)
merchant_payment_id string нет ваш ID транзакции для сверки
return_url string нет куда вернуть клиента после оплаты
webhook_url string нет URL вебхуков для этого платежа (иначе берётся дефолтный мерчанта)
external_metadata object нет любые ваши данные, вернём их в вебхуках без изменений
expires_in_seconds int нет TTL заявки, 60..21600, по умолч. 1800

Запрос:

curl -X POST https://<домен>/api/v1/payments \
  -H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \
  -H "Idempotency-Key: dep-2026-06-20-0001" \
  -d '{
    "direction": "payin",
    "amount_kopecks": 500000,
    "currency": "RUB",
    "payment_method": "sbp",
    "merchant_player_id": "user_42",
    "player_full_name": "Иванов Иван Иванович",
    "merchant_payment_id": "order_98765"
  }'

Ответ 201:

{
  "payment_id": "de09a768-28ff-4b91-920e-3d6c27f4cfb6",
  "status": "matching",
  "direction": "payin",
  "amount_kopecks": 500000,
  "currency": "RUB",
  "payment_method": "sbp",
  "checkout_url": "https://<домен>/pay/Qc6CllN0...",
  "expires_at": "2026-06-20T08:27:28Z",
  "created_at": "2026-06-20T07:57:28Z",
  "merchant_payment_id": "order_98765"
}

Дальше: редиректните клиента на checkout_url. На этой странице он видит реквизиты получателя, переводит деньги и загружает чек. Вам ничего больше вызывать не нужно — финальный статус придёт вебхуком. Итог сверяйте по payment_id (или вашему merchant_payment_id).

⚠️ Сумма может быть округлена под шаг матчинга мерчанта. Тогда в вебхуке будет amount_kopecks (фактическая) и original_amount_kopecks (что вы запросили). Списывайте/ зачисляйте по фактической.


3. Создать выплату (Pay-Out)

POST /api/v1/payouts — здесь обязательны реквизиты получателя.

Общие поля: amount_kopecks, currency, payment_method (card/sbp), merchant_player_id, опц. merchant_payment_id, expires_in_seconds.

Запрос (СБП):

curl -X POST https://<домен>/api/v1/payouts \
  -H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \
  -d '{
    "amount_kopecks": 500000, "currency": "RUB", "payment_method": "sbp",
    "merchant_player_id": "user_42",
    "recipient_sbp_phone": "+7 999 123-45-67",
    "recipient_sbp_bank_code": "SBER"
  }'

Ответ 201 содержит payment_id, status, recipient_last4 (для визуальной сверки) и пр.


4. Жизненный цикл и статусы

pending → matching → awaiting_payment → awaiting_verification → completed
                                          ↘ failed / expired / cancelled
Статус Значение
pending заявка создана
matching ищем встречную заявку в очереди
awaiting_payment пара найдена, ждём перевод от отправителя
awaiting_verification чек загружен, идёт проверка (авто/оператор)
completed успех: перевод подтверждён
failed провал (поддельный чек, отказ оператора и т.п.)
expired истёк таймер ожидания
cancelled отменён мерчантом/оператором

Зачисляйте средства клиенту ТОЛЬКО по completed (приходит вебхуком). Статус awaiting_verification означает, что чек ещё проверяется (см. §7 про антифрод).


5. Idempotency-Key

Для POST /payments и POST /payouts передавайте заголовок Idempotency-Key (любая ваша уникальная строка на операцию). Повторный запрос с тем же ключом вернёт тот же ранее созданный платёж, без дублирования. Используйте при ретраях по таймауту сети.


6. Вебхуки (как получать результат)

Куда слать — настраиваете вы: задайте дефолтный webhook URL в ЛК → Настройки (тумблер «Вебхук» + ссылка), либо передайте webhook_url прямо в POST /payments / /payouts для конкретной заявки. Пока URL не задан — события не отправляются (но копятся как «нет URL»).

Когда статус платежа меняется, мы шлём POST на ваш webhook URL с JSON-телом и подписью. Подпись считается на Sign Key (api_secret).

Заголовки:

Content-Type: application/json; charset=utf-8
X-Paynex-Event-Id:   <уникальный id события>
X-Paynex-Event-Type: payment.completed
X-Paynex-Timestamp:  1781162244
X-Paynex-Signature:  <hex HMAC-SHA256>

Тело:

{
  "event_id": "…",
  "event_type": "payment.completed",
  "created_at": "2026-06-20T08:00:00Z",
  "payment": {
    "id": "de09a768-…", "merchant_payment_id": "order_98765",
    "direction": "payin", "status": "completed",
    "amount_kopecks": 500000, "original_amount_kopecks": null,
    "remaining_kopecks": null, "currency": "RUB", "payment_method": "sbp",
    "external_metadata": {}, "expires_at": "…", "completed_at": "…", "created_at": "…"
  }
}

Типы событий: payment.created, payment.awaiting_payment, payment.completed, payment.failed, payment.expired, payment.cancelled.

Проверка подписи (ОБЯЗАТЕЛЬНО)

Подпись = HMAC_SHA256(api_secret, "{X-Paynex-Timestamp}.{сырое_тело_запроса}"). Сравнивайте с заголовком X-Paynex-Signature. Проверяйте, что timestamp не старше 5 минут (защита от replay). Тело берите как пришло, побайтово (не пересериализуйте).

import hmac, hashlib, time

def verify(api_secret: str, headers: dict, raw_body: bytes) -> bool:
    ts = headers["X-Paynex-Timestamp"]
    if abs(time.time() - int(ts)) > 300:          # старше 5 минут — отклоняем
        return False
    expected = hmac.new(
        api_secret.encode(), f"{ts}.".encode() + raw_body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, headers["X-Paynex-Signature"])

Идемпотентность и ретраи


7. Антифрод — что важно знать мерчанту

Чек о переводе технически невозможно на 100% отличить от хорошей подделки по самому файлу. Поэтому решение об одобрении опирается на поведение и историю, а не только на чек:


8. Лимиты и ошибки


9. Песочница (dev)