PAYNEX — документация по интеграции для мерчантов
P2P-процессинг депозитов и выплат. Вы (мерчант) создаёте заявки через API, клиент переводит деньги другому клиенту напрямую, мы матчим встречные потоки и подтверждаем перевод. Итог приходит вам вебхуком.
- Base URL:
https://<ваш-домен>(в dev:http://127.0.0.1:8000) - Все суммы — в копейках (5000.00 ₽ =
500000). Никаких float. - Формат — JSON, UTF-8.
1. Авторизация
Каждый запрос к API — заголовок X-API-Key с вашим ключом.
X-API-Key: paynex_live_xxxxxxxxxxxxxxxxxxxx
Отдельно у вас есть 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.
payment_method = "card"→recipient_card_number(12–19 цифр), опц.recipient_card_holder.payment_method = "sbp"→recipient_sbp_phone(любой формат), опц.recipient_sbp_bank_code(код из справочника банков, напр.SBER,TCS,ALFA,VTB,GAZPROM).
Запрос (СБП):
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. Вебхуки (как получать результат)
Когда статус платежа меняется, мы шлём POST на ваш webhook_url с JSON-телом и подписью.
Заголовки:
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"])
Идемпотентность и ретраи
- Обрабатывайте события идемпотентно по
event_id— мы можем доставить одно событие повторно. - При неудаче (не 2xx или таймаут) ретраим с backoff:
1, 5, 30, 120, 600, 1800, 7200, 21600секунд — 8 попыток, ~8.5 часов. Ответьте2xxкак только приняли событие.
7. Антифрод — что важно знать мерчанту
Чек о переводе технически невозможно на 100% отличить от хорошей подделки по самому файлу. Поэтому решение об одобрении опирается на поведение и историю, а не только на чек:
- Trust-тиры (гейтинг по истории). Чем меньше у клиента ПОДТВЕРЖДЁННЫХ депозитов, тем
ниже потолок суммы для авто-одобрения. Крупный депозит от нового клиента уйдёт на
ручную проверку (
awaiting_verificationдольше), итог — вебхуком. Это бьёт по экономике фрода: прокачка доверенного аккаунта стоит денег и времени. - Передавайте
player_full_name(из вашего KYC) — включает сверку с ФИО отправителя на чеке (анти-треугольник). Без него этот слой защиты не работает. - Дроп-карты / velocity / повторное использование чека — ловятся автоматически.
- Не зачисляйте средства до
completed. Часть депозитов проходит ручную проверку — это нормальный режим, а не ошибка.
8. Лимиты и ошибки
- Rate limit: ~100 запросов/мин с одного IP и ~200/мин на мерчанта (на создание).
Превышение →
429. - Коды:
200/201ок;400бизнес-ошибка;401/403авторизация;422валидация тела (напр. для выплаты не передан реквизит под метод);429лимит;501фича не поддержана.
9. Песочница (dev)
- Swagger:
http://127.0.0.1:8000/docs(кнопка Authorize → вставьтеX-API-Key). - Тестовый ключ из
scripts/seed_dev.py. Подробнее об антифрод-сигналах —docs/receipt_detection_system.md.