{
  "openapi": "3.1.0",
  "info": {
    "title": "Payso API",
    "version": "1.0.0",
    "description": "Принимайте платежи СБП в своём интерфейсе и управляйте выплатами через единый API. Один API-ключ и общий рублёвый баланс на платёжную систему; мерчант указывается в каждом платеже.\n\n## Быстрый старт\n\n1. Получите доступ у администратора Payso. В [личном кабинете](https://payso.tech) смените временный пароль и подключите 2FA.\n2. В разделе **Мерчанты** скопируйте ID подключения. Это UUID Payso, а не номер вашей кассы.\n3. В разделе **API и интеграция** создайте API-ключ и секрет подписи. Они показываются один раз.\n4. Укажите публичный HTTPS-адрес для webhook и создайте платёж через POST /v1/payments.\n5. Покажите покупателю QR из sbpUrl или откройте эту ссылку. Подтверждайте покупку только после статуса paid.\n\n**Базовый адрес:** https://api.payso.tech/v1. Приём работает через СБП H2H, без отдельной пейформы. Входящие платежи — от 1 ₽ до лимита подключения. Зачисление — сумма платежа за вычетом 3,8%, с округлением до копеек.\n\nПримеры запросов ниже выполняются на вашем сервере. API работает с реальными платежами; отдельного sandbox нет. Встроенная отправка из браузера документации отключена: используйте примеры с вычислением HMAC.\n\n## Авторизация и подпись\n\nВсе запросы требуют заголовок **Authorization: Bearer <API_KEY>**. Храните ключ и секрет на своём сервере; не включайте их в клиентский JavaScript. GET-запросам достаточно Bearer-ключа.\n\nДля каждого POST передавайте **Content-Type: application/json**, **X-Payso-Timestamp** (Unix timestamp в секундах) и **X-Payso-Signature**. Допустимое отклонение времени — 5 минут.\n\n~~~text\nsignature = HMAC-SHA256(API_SECRET,\n  METHOD + \"\\n\" + PATH + \"\\n\" + TIMESTAMP + \"\\n\" + RAW_BODY\n).hex_lowercase()\n~~~\n\nMETHOD — POST в верхнем регистре. PATH — публичный путь, например /v1/payments, без домена и query string. Подписывайте **точные байты JSON в UTF-8**, которые отправляете: изменение пробелов или порядка полей меняет подпись. Не используйте внутренний путь /api/payso/v1.\n\nВ каждом POST-методе есть готовые примеры **Node.js** и **Python** с подписью. Они читают PAYSO_API_KEY и PAYSO_API_SECRET из переменных окружения. Денежные значения передавайте строкой с точкой или JSON-числом; до двух знаков после точки. Суммы выплат — целые рубли.\n\nЛимит — **600 запросов в минуту** на платёжную систему. Максимальный размер тела — **64 КБ**. Перевыпуск ключа сразу отзывает старый ключ для новых API-запросов.\n\n## Платежи и статусы\n\nСоздание возвращает HTTP 200, когда результат доступен, либо **HTTP 202 со статусом creating**, если ссылка СБП ещё не получена. Это не отказ: используйте GET /v1/payments/{id} или дождитесь payment.ready. Повтор создания с тем же externalId не создаёт второй счёт.\n\n| Статус | Значение |\n| --- | --- |\n| creating | Ссылка СБП ещё не получена, результат создания уточняется. |\n| pending | Ссылка готова, ожидается оплата. |\n| paid | Оплата подтверждена. |\n| expired | Срок счёта истёк. При подтверждённой поздней оплате возможен переход в paid. |\n\nОриентируйтесь на expiresAt; срок действия ссылки у банка может быть короче. netAmount — зачисление после удержания 3,8%. Холды задаются условиями подключения мерчанта. Платёж не считается оплаченным только на основании открытия ссылки или возврата покупателя.\n\n## Выплаты\n\n| Способ | Сумма заявки | Комиссия | Исполнение |\n| --- | --- | --- | --- |\n| card — карта РФ | 2 000–100 000 ₽ | 0 ₽ | После подтверждения администратором. |\n| usdt_trc20 | 500–500 000 ₽ | 240 ₽ из суммы заявки | Вручную; затем в ответе появляется txHash. |\n\nДля USDT используется фиксинг **Rapira на 09:00 МСК без наценки**. Сумма USDT = (amount − 240) / rate, округление вниз до 6 знаков. Например, при amount = 8240 и rate = 80 получатель получает 100 USDT.\n\nСначала запросите POST /v1/payouts/quote. Для TRC20 передайте точную строку rate из расчёта как expectedRate при создании: если курс изменился, API ответит 409. Котировка фиксируется при создании заявки. Расчёт без создания ничего не резервирует.\n\nПри создании сумма резервируется сразу. pending — ожидает рассмотрения; approved — подтверждена и находится в работе; paid — исполнена; rejected — отклонена, резерв освобождён. **processing = unknown** означает, что результат карточной отправки уточняется: резерв сохраняется, новую заявку вместо неё создавать не нужно.\n\n## Webhook\n\nPayso отправляет POST JSON на callbackUrl операции либо адрес из кабинета. Адрес и секрет фиксируются при создании операции.\n\n| Событие | Значение |\n| --- | --- |\n| payment.ready | Получена ссылка СБП. |\n| payment.paid | Оплата подтверждена. |\n| payment.expired | Истёк срок счёта. |\n| payout.approved | Выплата подтверждена. |\n| payout.paid | Выплата исполнена. |\n| payout.rejected | Выплата отклонена. |\n\nЗаголовки: **X-Payso-Event-Id**, **X-Payso-Timestamp**, **X-Payso-Signature**. Подпись уведомления отличается от подписи запроса к API:\n\n~~~text\nsignature = HMAC-SHA256(API_SECRET, TIMESTAMP + \"\\n\" + RAW_BODY).hex_lowercase()\n~~~\n\nПроверяйте подпись по исходному телу до JSON-разбора, сравнивайте в постоянное время. Проверяйте timestamp с допуском 5 минут. После ротации ключа ранее созданные операции продолжают использовать прежний секрет — сохраните его до их завершения.\n\n~~~javascript\nimport { createHmac, timingSafeEqual } from 'node:crypto';\n\nexport function verifyWebhook(secret, timestamp, signature, rawBody) {\n  if (!/^\\d+$/.test(timestamp) || !/^[a-f0-9]{64}$/.test(signature)) return false;\n  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;\n  const expected = createHmac('sha256', secret)\n    .update(timestamp + '\\n', 'utf8').update(rawBody).digest();\n  return timingSafeEqual(expected, Buffer.from(signature, 'hex'));\n}\n~~~\n\nrawBody — исходный Buffer тела HTTP-запроса. После проверки подписи сопоставьте id, externalId, сумму и валюту со своей операцией. У payout.receiveAmount валюта определяется method: card — RUB, usdt_trc20 — USDT.\n\nОбрабатывайте eventId идемпотентно и отвечайте HTTP 2xx после надёжного сохранения события. При ошибке — до 20 попыток доставки с растущим интервалом до часа. Дубли и доставка не по порядку возможны; при сомнении запросите состояние через GET. Не понижайте уже подтверждённый paid из-за старого уведомления. Схемы и примеры уведомлений находятся в разделе **Webhook-события** ниже.\n\n## Ошибки и повторы\n\n| HTTP | Что означает |\n| --- | --- |\n| 400 | Параметры, лимит суммы, реквизиты или недостаточный баланс. |\n| 401 | Неверный ключ, подпись или timestamp. |\n| 403 | Доступ платёжной системы или мерчанта отключён. |\n| 404 | Операция или мерчант не найдены в вашей платёжной системе. |\n| 409 | externalId занят другими параметрами либо изменился expectedRate. |\n| 413 | Тело превышает 64 КБ. |\n| 429 | Превышен лимит запросов. |\n| 500 / 502 / 503 | Временная ошибка или недоступность сервиса, приёма, выплаты, курса. |\n\nAPI обычно возвращает JSON с полем message; для внутренних ошибок может добавляться reference. На уровне прокси формат ошибки может отличаться.\n\nПовтор создания с тем же externalId и теми же параметрами возвращает прежнюю операцию. Другие параметры с тем же ID дают 409. При сетевом таймауте повторяйте **исходный запрос с тем же externalId**, обновив timestamp и подпись. Для известного UUID используйте GET.\n\nexternalId уникален в пределах всей платёжной системы, а не отдельного мерчанта. Если нумерация заказов пересекается, включайте ID мерчанта в externalId. Пространства идентификаторов платежей и выплат раздельные."
  },
  "servers": [
    {
      "url": "https://api.payso.tech"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "paths": {
    "/v1/balance": {
      "get": {
        "summary": "Общий баланс",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Успешный ответ",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Balance"
                },
                "example": {
                  "currency": "RUB",
                  "available": "48000.00",
                  "hold": "9620.00",
                  "frozen": "0.00",
                  "reserved": "2000.00",
                  "total": "57620.00"
                }
              }
            }
          },
          "default": {
            "description": "Ошибка запроса; HTTP-код и message поясняют причину.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Некорректные параметры запроса"
                }
              }
            }
          }
        },
        "tags": [
          "Баланс"
        ],
        "operationId": "getBalance",
        "description": "available — доступно к новой выплате. reserved уже вычтен из available. total = available + hold + frozen, без зарезервированных выплат. Все суммы в RUB.",
        "x-codeSamples": [
          {
            "lang": "JavaScript",
            "label": "Node.js",
            "source": "const response = await fetch('https://api.payso.tech/v1/balance', {\n  headers: { Authorization: `Bearer ${process.env.PAYSO_API_KEY}` }\n});\nconst data = await response.json();\nif (!response.ok) throw new Error(data.message || `HTTP ${response.status}`);\nconsole.log(data);"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import json\nimport os\nimport urllib.request\n\nrequest = urllib.request.Request(\n    'https://api.payso.tech/v1/balance',\n    headers={'Authorization': 'Bearer ' + os.environ['PAYSO_API_KEY']}\n)\nwith urllib.request.urlopen(request, timeout=60) as response:\n    print(json.load(response))"
          }
        ]
      }
    },
    "/v1/rates": {
      "get": {
        "summary": "Курс, лимиты и комиссии",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Успешный ответ",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Rates"
                },
                "example": {
                  "rapira": "80.0",
                  "fixing": "09:00 Europe/Moscow",
                  "markupPercent": "0",
                  "card": {
                    "min": 2000,
                    "max": 100000,
                    "feeRub": 0
                  },
                  "usdt_trc20": {
                    "min": 500,
                    "max": 500000,
                    "feeRub": 240
                  }
                }
              }
            }
          },
          "default": {
            "description": "Ошибка запроса; HTTP-код и message поясняют причину.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Некорректные параметры запроса"
                }
              }
            }
          }
        },
        "tags": [
          "Баланс"
        ],
        "operationId": "getRates",
        "description": "Фиксинг Rapira на 09:00 МСК, наценка 0%. rapira=null означает, что курс временно недоступен. Карточная выплата не требует конвертации. Приведённый курс — пример, актуальный возвращается API.",
        "x-codeSamples": [
          {
            "lang": "JavaScript",
            "label": "Node.js",
            "source": "const response = await fetch('https://api.payso.tech/v1/rates', {\n  headers: { Authorization: `Bearer ${process.env.PAYSO_API_KEY}` }\n});\nconst data = await response.json();\nif (!response.ok) throw new Error(data.message || `HTTP ${response.status}`);\nconsole.log(data);"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import json\nimport os\nimport urllib.request\n\nrequest = urllib.request.Request(\n    'https://api.payso.tech/v1/rates',\n    headers={'Authorization': 'Bearer ' + os.environ['PAYSO_API_KEY']}\n)\nwith urllib.request.urlopen(request, timeout=60) as response:\n    print(json.load(response))"
          }
        ]
      }
    },
    "/v1/merchants": {
      "get": {
        "summary": "Подключённые мерчанты",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Успешный ответ",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Merchant"
                      }
                    }
                  },
                  "required": []
                },
                "example": {
                  "items": [
                    {
                      "id": "11111111-1111-4111-8111-111111111111",
                      "name": "Магазин",
                      "url": "https://shop.example",
                      "max_amount": 100000,
                      "active": true,
                      "hold_hours": 24
                    }
                  ]
                }
              }
            }
          },
          "default": {
            "description": "Ошибка запроса; HTTP-код и message поясняют причину.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Некорректные параметры запроса"
                }
              }
            }
          }
        },
        "tags": [
          "Мерчанты"
        ],
        "operationId": "listMerchants",
        "description": "Мерчантов подключает администратор Payso. Используйте id из этого ответа как merchantId. Минимум приёма — 1 ₽, максимум — max_amount; hold_hours задаёт срок холда.",
        "x-codeSamples": [
          {
            "lang": "JavaScript",
            "label": "Node.js",
            "source": "const response = await fetch('https://api.payso.tech/v1/merchants', {\n  headers: { Authorization: `Bearer ${process.env.PAYSO_API_KEY}` }\n});\nconst data = await response.json();\nif (!response.ok) throw new Error(data.message || `HTTP ${response.status}`);\nconsole.log(data);"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import json\nimport os\nimport urllib.request\n\nrequest = urllib.request.Request(\n    'https://api.payso.tech/v1/merchants',\n    headers={'Authorization': 'Bearer ' + os.environ['PAYSO_API_KEY']}\n)\nwith urllib.request.urlopen(request, timeout=60) as response:\n    print(json.load(response))"
          }
        ]
      }
    },
    "/v1/payments": {
      "post": {
        "summary": "Создать платёж СБП",
        "parameters": [
          {
            "name": "X-Payso-Timestamp",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Unix timestamp в секундах. Допуск 300 секунд."
          },
          {
            "name": "X-Payso-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "HMAC-SHA256(secret, METHOD + LF + публичный PATH + LF + TIMESTAMP + LF + RAW_BODY), hex в нижнем регистре."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PaymentCreate"
              },
              "example": {
                "merchantId": "11111111-1111-4111-8111-111111111111",
                "externalId": "order-1001",
                "amount": "1000.00",
                "description": "Заказ №1001"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Успешный ответ",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Payment"
                },
                "example": {
                  "id": "22222222-2222-4222-8222-222222222222",
                  "merchantId": "11111111-1111-4111-8111-111111111111",
                  "merchant": "Магазин",
                  "externalId": "order-1001",
                  "amount": "1000.00",
                  "netAmount": "962.00",
                  "currency": "RUB",
                  "status": "pending",
                  "description": "Заказ №1001",
                  "sbpUrl": "https://qr.nspk.ru/EXAMPLE",
                  "createdAt": "2026-09-17T09:00:00.000+00:00",
                  "paidAt": null,
                  "expiresAt": "2026-09-17T13:00:00.000+00:00"
                }
              }
            }
          },
          "202": {
            "description": "Ссылка СБП ещё создаётся. Получите результат через GET или webhook.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Payment"
                },
                "example": {
                  "id": "22222222-2222-4222-8222-222222222222",
                  "merchantId": "11111111-1111-4111-8111-111111111111",
                  "merchant": "Магазин",
                  "externalId": "order-1001",
                  "amount": "1000.00",
                  "netAmount": "962.00",
                  "currency": "RUB",
                  "status": "creating",
                  "description": "Заказ №1001",
                  "sbpUrl": null,
                  "createdAt": "2026-09-17T09:00:00.000+00:00",
                  "paidAt": null,
                  "expiresAt": "2026-09-17T13:00:00.000+00:00"
                }
              }
            }
          },
          "default": {
            "description": "Ошибка запроса; HTTP-код и message поясняют причину.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Некорректные параметры запроса"
                }
              }
            }
          }
        },
        "tags": [
          "Платежи"
        ],
        "operationId": "createPayment",
        "description": "Сумма от 1 ₽ до max_amount мерчанта, до двух знаков после точки. netAmount — сумма после удержания 3,8%. Покажите QR из sbpUrl. HTTP 202 означает, что ссылка ещё создаётся: опрашивайте GET по id или ждите payment.ready. Все ссылки и реквизиты в примерах иллюстрируют формат.",
        "x-codeSamples": [
          {
            "lang": "JavaScript",
            "label": "Node.js",
            "source": "import { createHmac } from 'node:crypto';\n\nconst path = '/v1/payments';\nconst timestamp = String(Math.floor(Date.now() / 1000));\nconst raw = JSON.stringify({\n  \"merchantId\": \"11111111-1111-4111-8111-111111111111\",\n  \"externalId\": \"order-1001\",\n  \"amount\": \"1000.00\",\n  \"description\": \"Заказ №1001\"\n});\nconst signature = createHmac('sha256', process.env.PAYSO_API_SECRET)\n  .update(`POST\\n${path}\\n${timestamp}\\n${raw}`, 'utf8')\n  .digest('hex');\nconst response = await fetch(`https://api.payso.tech${path}`, {\n  method: 'POST',\n  headers: {\n    Authorization: `Bearer ${process.env.PAYSO_API_KEY}`,\n    'Content-Type': 'application/json',\n    'X-Payso-Timestamp': timestamp,\n    'X-Payso-Signature': signature\n  },\n  body: raw\n});\nconst data = await response.json();\nif (!response.ok) throw new Error(data.message || `HTTP ${response.status}`);\nconsole.log(data);"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import json\nimport os\nimport urllib.request\nimport hashlib\nimport hmac\nimport time\n\npath = '/v1/payments'\ntimestamp = str(int(time.time()))\nbody = {\n    \"merchantId\": \"11111111-1111-4111-8111-111111111111\",\n    \"externalId\": \"order-1001\",\n    \"amount\": \"1000.00\",\n    \"description\": \"Заказ №1001\"\n}\nraw = json.dumps(body, ensure_ascii=False, separators=(',', ':')).encode('utf-8')\nmessage = ('POST\\n' + path + '\\n' + timestamp + '\\n').encode('utf-8') + raw\nsignature = hmac.new(os.environ['PAYSO_API_SECRET'].encode('utf-8'), message, hashlib.sha256).hexdigest()\nrequest = urllib.request.Request(\n    'https://api.payso.tech' + path, data=raw, method='POST',\n    headers={\n        'Authorization': 'Bearer ' + os.environ['PAYSO_API_KEY'],\n        'Content-Type': 'application/json',\n        'X-Payso-Timestamp': timestamp,\n        'X-Payso-Signature': signature\n    }\n)\nwith urllib.request.urlopen(request, timeout=60) as response:\n    print(json.load(response))"
          }
        ]
      },
      "get": {
        "summary": "История платежей",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0,
              "maximum": 100000
            },
            "description": "Номер страницы с 0, размер страницы — 25 записей."
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "enum": [
                "",
                "pending",
                "paid",
                "expired"
              ]
            },
            "description": "Фильтр статуса; pending включает creating. Пусто — все статусы."
          },
          {
            "name": "query",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Поиск по externalId или точному UUID платежа."
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Payment"
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "pageSize": {
                      "const": 25
                    }
                  },
                  "required": []
                },
                "example": {
                  "items": [
                    {
                      "id": "22222222-2222-4222-8222-222222222222",
                      "merchantId": "11111111-1111-4111-8111-111111111111",
                      "merchant": "Магазин",
                      "externalId": "order-1001",
                      "amount": "1000.00",
                      "netAmount": "962.00",
                      "currency": "RUB",
                      "status": "pending",
                      "description": "Заказ №1001",
                      "sbpUrl": "https://qr.nspk.ru/EXAMPLE",
                      "createdAt": "2026-09-17T09:00:00.000+00:00",
                      "paidAt": null,
                      "expiresAt": "2026-09-17T13:00:00.000+00:00"
                    }
                  ],
                  "total": 1,
                  "pageSize": 25
                }
              }
            }
          },
          "default": {
            "description": "Ошибка запроса; HTTP-код и message поясняют причину.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Некорректные параметры запроса"
                }
              }
            }
          }
        },
        "tags": [
          "Платежи"
        ],
        "operationId": "listPayments",
        "description": "Страница отсчитывается от 0, по 25 записей. status=pending включает creating. query ищет по externalId или точному UUID платежа. Ответ содержит только операции вашей платёжной системы.",
        "x-codeSamples": [
          {
            "lang": "JavaScript",
            "label": "Node.js",
            "source": "const response = await fetch('https://api.payso.tech/v1/payments', {\n  headers: { Authorization: `Bearer ${process.env.PAYSO_API_KEY}` }\n});\nconst data = await response.json();\nif (!response.ok) throw new Error(data.message || `HTTP ${response.status}`);\nconsole.log(data);"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import json\nimport os\nimport urllib.request\n\nrequest = urllib.request.Request(\n    'https://api.payso.tech/v1/payments',\n    headers={'Authorization': 'Bearer ' + os.environ['PAYSO_API_KEY']}\n)\nwith urllib.request.urlopen(request, timeout=60) as response:\n    print(json.load(response))"
          }
        ]
      }
    },
    "/v1/payments/{id}": {
      "get": {
        "summary": "Статус платежа",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "UUID операции Payso, не externalId."
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Payment"
                },
                "example": {
                  "id": "22222222-2222-4222-8222-222222222222",
                  "merchantId": "11111111-1111-4111-8111-111111111111",
                  "merchant": "Магазин",
                  "externalId": "order-1001",
                  "amount": "1000.00",
                  "netAmount": "962.00",
                  "currency": "RUB",
                  "status": "pending",
                  "description": "Заказ №1001",
                  "sbpUrl": "https://qr.nspk.ru/EXAMPLE",
                  "createdAt": "2026-09-17T09:00:00.000+00:00",
                  "paidAt": null,
                  "expiresAt": "2026-09-17T13:00:00.000+00:00"
                }
              }
            }
          },
          "default": {
            "description": "Ошибка запроса; HTTP-код и message поясняют причину.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Некорректные параметры запроса"
                }
              }
            }
          }
        },
        "tags": [
          "Платежи"
        ],
        "operationId": "getPayment",
        "description": "id — UUID Payso, полученный при создании, не externalId. Подтверждайте заказ только по статусу paid. При позднем подтверждении оплаты возможен переход expired → paid.",
        "x-codeSamples": [
          {
            "lang": "JavaScript",
            "label": "Node.js",
            "source": "const response = await fetch('https://api.payso.tech/v1/payments/22222222-2222-4222-8222-222222222222', {\n  headers: { Authorization: `Bearer ${process.env.PAYSO_API_KEY}` }\n});\nconst data = await response.json();\nif (!response.ok) throw new Error(data.message || `HTTP ${response.status}`);\nconsole.log(data);"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import json\nimport os\nimport urllib.request\n\nrequest = urllib.request.Request(\n    'https://api.payso.tech/v1/payments/22222222-2222-4222-8222-222222222222',\n    headers={'Authorization': 'Bearer ' + os.environ['PAYSO_API_KEY']}\n)\nwith urllib.request.urlopen(request, timeout=60) as response:\n    print(json.load(response))"
          }
        ]
      }
    },
    "/v1/payouts/quote": {
      "post": {
        "summary": "Рассчитать выплату",
        "parameters": [
          {
            "name": "X-Payso-Timestamp",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Unix timestamp в секундах. Допуск 300 секунд."
          },
          {
            "name": "X-Payso-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "HMAC-SHA256(secret, METHOD + LF + публичный PATH + LF + TIMESTAMP + LF + RAW_BODY), hex в нижнем регистре."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QuoteRequest"
              },
              "example": {
                "method": "usdt_trc20",
                "amount": "8240"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Успешный ответ",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Quote"
                },
                "example": {
                  "method": "usdt_trc20",
                  "amount": "8240.00",
                  "feeRub": "240.00",
                  "receiveAmount": "100.000000",
                  "receiveCurrency": "USDT",
                  "rate": "80.0"
                }
              }
            }
          },
          "default": {
            "description": "Ошибка запроса; HTTP-код и message поясняют причину.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Некорректные параметры запроса"
                }
              }
            }
          }
        },
        "tags": [
          "Выплаты"
        ],
        "operationId": "quotePayout",
        "description": "Расчёт без списания и резерва. Карты: 2 000–100 000 ₽, комиссия 0 ₽. TRC20: 500–500 000 ₽, комиссия 240 ₽ из суммы. Сумма заявки — целые рубли. Для TRC20 передайте rate как expectedRate в запросе создания.",
        "x-codeSamples": [
          {
            "lang": "JavaScript",
            "label": "Node.js",
            "source": "import { createHmac } from 'node:crypto';\n\nconst path = '/v1/payouts/quote';\nconst timestamp = String(Math.floor(Date.now() / 1000));\nconst raw = JSON.stringify({\n  \"method\": \"usdt_trc20\",\n  \"amount\": \"8240\"\n});\nconst signature = createHmac('sha256', process.env.PAYSO_API_SECRET)\n  .update(`POST\\n${path}\\n${timestamp}\\n${raw}`, 'utf8')\n  .digest('hex');\nconst response = await fetch(`https://api.payso.tech${path}`, {\n  method: 'POST',\n  headers: {\n    Authorization: `Bearer ${process.env.PAYSO_API_KEY}`,\n    'Content-Type': 'application/json',\n    'X-Payso-Timestamp': timestamp,\n    'X-Payso-Signature': signature\n  },\n  body: raw\n});\nconst data = await response.json();\nif (!response.ok) throw new Error(data.message || `HTTP ${response.status}`);\nconsole.log(data);"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import json\nimport os\nimport urllib.request\nimport hashlib\nimport hmac\nimport time\n\npath = '/v1/payouts/quote'\ntimestamp = str(int(time.time()))\nbody = {\n    \"method\": \"usdt_trc20\",\n    \"amount\": \"8240\"\n}\nraw = json.dumps(body, ensure_ascii=False, separators=(',', ':')).encode('utf-8')\nmessage = ('POST\\n' + path + '\\n' + timestamp + '\\n').encode('utf-8') + raw\nsignature = hmac.new(os.environ['PAYSO_API_SECRET'].encode('utf-8'), message, hashlib.sha256).hexdigest()\nrequest = urllib.request.Request(\n    'https://api.payso.tech' + path, data=raw, method='POST',\n    headers={\n        'Authorization': 'Bearer ' + os.environ['PAYSO_API_KEY'],\n        'Content-Type': 'application/json',\n        'X-Payso-Timestamp': timestamp,\n        'X-Payso-Signature': signature\n    }\n)\nwith urllib.request.urlopen(request, timeout=60) as response:\n    print(json.load(response))"
          }
        ]
      }
    },
    "/v1/payouts": {
      "post": {
        "summary": "Создать заявку на выплату",
        "parameters": [
          {
            "name": "X-Payso-Timestamp",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Unix timestamp в секундах. Допуск 300 секунд."
          },
          {
            "name": "X-Payso-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "HMAC-SHA256(secret, METHOD + LF + публичный PATH + LF + TIMESTAMP + LF + RAW_BODY), hex в нижнем регистре."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PayoutCreate"
              },
              "example": {
                "externalId": "payout-1001",
                "method": "card",
                "amount": "2000",
                "address": "4111111111111111"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Успешный ответ",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Payout"
                },
                "example": {
                  "id": "33333333-3333-4333-8333-333333333333",
                  "externalId": "payout-1001",
                  "method": "card",
                  "amount": "2000.00",
                  "feeRub": "0.00",
                  "currency": "RUB",
                  "receiveAmount": "2000.00",
                  "receiveCurrency": "RUB",
                  "rate": null,
                  "destination": "•••• 1111",
                  "status": "pending",
                  "processing": null,
                  "txHash": null,
                  "createdAt": "2026-09-17T09:00:00.000+00:00"
                }
              }
            }
          },
          "default": {
            "description": "Ошибка запроса; HTTP-код и message поясняют причину.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Некорректные параметры запроса"
                }
              }
            }
          }
        },
        "tags": [
          "Выплаты"
        ],
        "operationId": "createPayout",
        "description": "Резервирует amount с общего баланса. Карта отправляется после подтверждения администратора; TRC20 исполняется вручную. Котировка фиксируется при создании. При изменении переданного expectedRate возвращается 409. Повторяйте запрос с тем же externalId при сетевой ошибке. Номер карты в примере иллюстрирует формат, используйте реквизиты получателя.",
        "x-codeSamples": [
          {
            "lang": "JavaScript",
            "label": "Node.js",
            "source": "import { createHmac } from 'node:crypto';\n\nconst path = '/v1/payouts';\nconst timestamp = String(Math.floor(Date.now() / 1000));\nconst raw = JSON.stringify({\n  \"externalId\": \"payout-1001\",\n  \"method\": \"card\",\n  \"amount\": \"2000\",\n  \"address\": \"4111111111111111\"\n});\nconst signature = createHmac('sha256', process.env.PAYSO_API_SECRET)\n  .update(`POST\\n${path}\\n${timestamp}\\n${raw}`, 'utf8')\n  .digest('hex');\nconst response = await fetch(`https://api.payso.tech${path}`, {\n  method: 'POST',\n  headers: {\n    Authorization: `Bearer ${process.env.PAYSO_API_KEY}`,\n    'Content-Type': 'application/json',\n    'X-Payso-Timestamp': timestamp,\n    'X-Payso-Signature': signature\n  },\n  body: raw\n});\nconst data = await response.json();\nif (!response.ok) throw new Error(data.message || `HTTP ${response.status}`);\nconsole.log(data);"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import json\nimport os\nimport urllib.request\nimport hashlib\nimport hmac\nimport time\n\npath = '/v1/payouts'\ntimestamp = str(int(time.time()))\nbody = {\n    \"externalId\": \"payout-1001\",\n    \"method\": \"card\",\n    \"amount\": \"2000\",\n    \"address\": \"4111111111111111\"\n}\nraw = json.dumps(body, ensure_ascii=False, separators=(',', ':')).encode('utf-8')\nmessage = ('POST\\n' + path + '\\n' + timestamp + '\\n').encode('utf-8') + raw\nsignature = hmac.new(os.environ['PAYSO_API_SECRET'].encode('utf-8'), message, hashlib.sha256).hexdigest()\nrequest = urllib.request.Request(\n    'https://api.payso.tech' + path, data=raw, method='POST',\n    headers={\n        'Authorization': 'Bearer ' + os.environ['PAYSO_API_KEY'],\n        'Content-Type': 'application/json',\n        'X-Payso-Timestamp': timestamp,\n        'X-Payso-Signature': signature\n    }\n)\nwith urllib.request.urlopen(request, timeout=60) as response:\n    print(json.load(response))"
          }
        ]
      },
      "get": {
        "summary": "История выплат",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0,
              "maximum": 100000
            },
            "description": "Номер страницы с 0, размер страницы — 25 записей."
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Payout"
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "pageSize": {
                      "const": 25
                    }
                  },
                  "required": []
                },
                "example": {
                  "items": [
                    {
                      "id": "33333333-3333-4333-8333-333333333333",
                      "externalId": "payout-1001",
                      "method": "card",
                      "amount": "2000.00",
                      "feeRub": "0.00",
                      "currency": "RUB",
                      "receiveAmount": "2000.00",
                      "receiveCurrency": "RUB",
                      "rate": null,
                      "destination": "•••• 1111",
                      "status": "pending",
                      "processing": null,
                      "txHash": null,
                      "createdAt": "2026-09-17T09:00:00.000+00:00"
                    }
                  ],
                  "total": 1,
                  "pageSize": 25
                }
              }
            }
          },
          "default": {
            "description": "Ошибка запроса; HTTP-код и message поясняют причину.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Некорректные параметры запроса"
                }
              }
            }
          }
        },
        "tags": [
          "Выплаты"
        ],
        "operationId": "listPayouts",
        "description": "По 25 заявок на страницу, нумерация с 0. Результаты ограничены вашей платёжной системой.",
        "x-codeSamples": [
          {
            "lang": "JavaScript",
            "label": "Node.js",
            "source": "const response = await fetch('https://api.payso.tech/v1/payouts', {\n  headers: { Authorization: `Bearer ${process.env.PAYSO_API_KEY}` }\n});\nconst data = await response.json();\nif (!response.ok) throw new Error(data.message || `HTTP ${response.status}`);\nconsole.log(data);"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import json\nimport os\nimport urllib.request\n\nrequest = urllib.request.Request(\n    'https://api.payso.tech/v1/payouts',\n    headers={'Authorization': 'Bearer ' + os.environ['PAYSO_API_KEY']}\n)\nwith urllib.request.urlopen(request, timeout=60) as response:\n    print(json.load(response))"
          }
        ]
      }
    },
    "/v1/payouts/{id}": {
      "get": {
        "summary": "Статус выплаты",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "UUID операции Payso, не externalId."
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Payout"
                },
                "example": {
                  "id": "33333333-3333-4333-8333-333333333333",
                  "externalId": "payout-1001",
                  "method": "card",
                  "amount": "2000.00",
                  "feeRub": "0.00",
                  "currency": "RUB",
                  "receiveAmount": "2000.00",
                  "receiveCurrency": "RUB",
                  "rate": null,
                  "destination": "•••• 1111",
                  "status": "pending",
                  "processing": null,
                  "txHash": null,
                  "createdAt": "2026-09-17T09:00:00.000+00:00"
                }
              }
            }
          },
          "default": {
            "description": "Ошибка запроса; HTTP-код и message поясняют причину.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Некорректные параметры запроса"
                }
              }
            }
          }
        },
        "tags": [
          "Выплаты"
        ],
        "operationId": "getPayout",
        "description": "id — UUID Payso, полученный при создании. processing=unknown требует сверки, резерв сохраняется. Не создавайте дубль. После исполнения TRC20 возвращается txHash.",
        "x-codeSamples": [
          {
            "lang": "JavaScript",
            "label": "Node.js",
            "source": "const response = await fetch('https://api.payso.tech/v1/payouts/33333333-3333-4333-8333-333333333333', {\n  headers: { Authorization: `Bearer ${process.env.PAYSO_API_KEY}` }\n});\nconst data = await response.json();\nif (!response.ok) throw new Error(data.message || `HTTP ${response.status}`);\nconsole.log(data);"
          },
          {
            "lang": "Python",
            "label": "Python",
            "source": "import json\nimport os\nimport urllib.request\n\nrequest = urllib.request.Request(\n    'https://api.payso.tech/v1/payouts/33333333-3333-4333-8333-333333333333',\n    headers={'Authorization': 'Bearer ' + os.environ['PAYSO_API_KEY']}\n)\nwith urllib.request.urlopen(request, timeout=60) as response:\n    print(json.load(response))"
          }
        ]
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "ps_live_...",
        "description": "API-ключ платёжной системы из кабинета Payso. Для POST также нужны X-Payso-Timestamp и HMAC-подпись X-Payso-Signature."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string",
            "description": "Описание ошибки."
          },
          "reference": {
            "type": "string",
            "description": "Идентификатор внутренней ошибки для обращения в поддержку; может отсутствовать."
          }
        },
        "required": [
          "message"
        ]
      },
      "Balance": {
        "type": "object",
        "properties": {
          "currency": {
            "const": "RUB",
            "description": "Валюта учёта."
          },
          "available": {
            "type": "string",
            "pattern": "^\\d+(\\.\\d+)?$",
            "examples": [
              "2000.00"
            ],
            "description": "Доступно для новой заявки на выплату."
          },
          "hold": {
            "type": "string",
            "pattern": "^\\d+(\\.\\d+)?$",
            "examples": [
              "2000.00"
            ],
            "description": "Зачисления, срок холда которых ещё не истёк."
          },
          "frozen": {
            "type": "string",
            "pattern": "^\\d+(\\.\\d+)?$",
            "examples": [
              "2000.00"
            ],
            "description": "Замороженные средства."
          },
          "reserved": {
            "type": "string",
            "pattern": "^\\d+(\\.\\d+)?$",
            "examples": [
              "2000.00"
            ],
            "description": "Сумма заявок в работе; уже вычтена из available."
          },
          "total": {
            "type": "string",
            "pattern": "^\\d+(\\.\\d+)?$",
            "examples": [
              "2000.00"
            ],
            "description": "available + hold + frozen. Зарезервированные выплаты не включены."
          }
        },
        "required": [
          "currency",
          "available",
          "hold",
          "frozen",
          "reserved",
          "total"
        ],
        "examples": [
          {
            "currency": "RUB",
            "available": "48000.00",
            "hold": "9620.00",
            "frozen": "0.00",
            "reserved": "2000.00",
            "total": "57620.00"
          }
        ]
      },
      "Merchant": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "ID подключения Payso для поля merchantId."
          },
          "name": {
            "type": "string",
            "description": "Название магазина."
          },
          "url": {
            "type": "string",
            "description": "Адрес магазина."
          },
          "max_amount": {
            "type": "number",
            "description": "Максимальная сумма входящего платежа в RUB."
          },
          "active": {
            "type": "boolean",
            "description": "Доступен ли приём по подключению."
          },
          "hold_hours": {
            "type": "integer",
            "description": "Срок холда поступлений в часах."
          }
        },
        "required": [
          "id",
          "name",
          "max_amount",
          "active"
        ],
        "examples": [
          {
            "id": "11111111-1111-4111-8111-111111111111",
            "name": "Магазин",
            "url": "https://shop.example",
            "max_amount": 100000,
            "active": true,
            "hold_hours": 24
          }
        ]
      },
      "PaymentCreate": {
        "type": "object",
        "properties": {
          "merchantId": {
            "type": "string",
            "format": "uuid",
            "description": "UUID из GET /v1/merchants."
          },
          "externalId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 190,
            "description": "Уникальный ID заказа в пределах всей платёжной системы."
          },
          "amount": {
            "oneOf": [
              {
                "type": "string",
                "pattern": "^\\d+(\\.\\d{1,2})?$",
                "examples": [
                  "2000.00"
                ]
              },
              {
                "type": "number",
                "minimum": 1,
                "multipleOf": 0.01
              }
            ],
            "description": "Сумма покупателю в RUB. От 1 ₽ до лимита мерчанта, до 2 знаков после точки."
          },
          "description": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255,
            "description": "Описание покупки."
          },
          "callbackUrl": {
            "type": "string",
            "format": "uri",
            "maxLength": 500,
            "description": "Публичный HTTPS-адрес уведомлений. По умолчанию — адрес из кабинета."
          }
        },
        "required": [
          "merchantId",
          "externalId",
          "amount",
          "description"
        ],
        "examples": [
          {
            "merchantId": "11111111-1111-4111-8111-111111111111",
            "externalId": "order-1001",
            "amount": "1000.00",
            "description": "Заказ №1001"
          }
        ]
      },
      "Payment": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID платежа Payso."
          },
          "merchantId": {
            "type": "string",
            "format": "uuid",
            "description": "UUID подключения мерчанта."
          },
          "merchant": {
            "type": "string",
            "description": "Название магазина."
          },
          "externalId": {
            "type": "string",
            "description": "Ваш ID заказа."
          },
          "amount": {
            "type": "string",
            "pattern": "^\\d+(\\.\\d+)?$",
            "examples": [
              "2000.00"
            ],
            "description": "Сумма покупателю в RUB."
          },
          "netAmount": {
            "type": "string",
            "pattern": "^\\d+(\\.\\d+)?$",
            "examples": [
              "2000.00"
            ],
            "description": "Зачисление за вычетом 3,8%, с округлением до копеек."
          },
          "currency": {
            "const": "RUB",
            "description": "Валюта платежа."
          },
          "status": {
            "enum": [
              "creating",
              "pending",
              "paid",
              "expired"
            ],
            "description": "creating — ссылка создаётся; pending — ожидает оплаты; paid — оплачен; expired — срок истёк."
          },
          "description": {
            "type": "string",
            "description": "Описание покупки."
          },
          "sbpUrl": {
            "type": [
              "string",
              "null"
            ],
            "description": "Ссылка СБП для QR или перехода в банк. null, пока не получена."
          },
          "createdAt": {
            "type": "string",
            "description": "Дата создания."
          },
          "paidAt": {
            "type": [
              "string",
              "null"
            ],
            "description": "Дата подтверждения оплаты, иначе null."
          },
          "expiresAt": {
            "type": "string",
            "description": "Срок действия счёта."
          }
        },
        "required": [
          "id",
          "externalId",
          "amount",
          "netAmount",
          "status"
        ],
        "examples": [
          {
            "id": "22222222-2222-4222-8222-222222222222",
            "merchantId": "11111111-1111-4111-8111-111111111111",
            "merchant": "Магазин",
            "externalId": "order-1001",
            "amount": "1000.00",
            "netAmount": "962.00",
            "currency": "RUB",
            "status": "pending",
            "description": "Заказ №1001",
            "sbpUrl": "https://qr.nspk.ru/EXAMPLE",
            "createdAt": "2026-09-17T09:00:00.000+00:00",
            "paidAt": null,
            "expiresAt": "2026-09-17T13:00:00.000+00:00"
          }
        ]
      },
      "QuoteRequest": {
        "type": "object",
        "properties": {
          "method": {
            "enum": [
              "card",
              "usdt_trc20"
            ],
            "description": "card — карта РФ; usdt_trc20 — USDT в сети TRON."
          },
          "amount": {
            "oneOf": [
              {
                "type": "string",
                "pattern": "^\\d+(\\.0{1,2})?$",
                "examples": [
                  "2000.00"
                ]
              },
              {
                "type": "number",
                "minimum": 500,
                "multipleOf": 1
              }
            ],
            "description": "Общая сумма заявки в целых рублях. Карта: 2 000–100 000; TRC20: 500–500 000."
          }
        },
        "required": [
          "method",
          "amount"
        ],
        "examples": [
          {
            "method": "usdt_trc20",
            "amount": "8240"
          }
        ]
      },
      "PayoutCreate": {
        "type": "object",
        "properties": {
          "externalId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 190,
            "description": "Уникальный ID выплаты в пределах платёжной системы; отдельно от ID платежей."
          },
          "method": {
            "enum": [
              "card",
              "usdt_trc20"
            ],
            "description": "card — карта РФ; usdt_trc20 — USDT в сети TRON."
          },
          "amount": {
            "oneOf": [
              {
                "type": "string",
                "pattern": "^\\d+(\\.0{1,2})?$",
                "examples": [
                  "2000.00"
                ]
              },
              {
                "type": "number",
                "minimum": 500,
                "multipleOf": 1
              }
            ],
            "description": "Общая сумма списания в целых рублях. Карта: 2 000–100 000; TRC20: 500–500 000."
          },
          "address": {
            "type": "string",
            "description": "Номер карты: 16–19 цифр без пробелов, с корректной контрольной суммой. Для TRC20 — валидный адрес TRON.",
            "maxLength": 190
          },
          "callbackUrl": {
            "type": "string",
            "format": "uri",
            "maxLength": 500,
            "description": "Публичный HTTPS-адрес уведомлений. По умолчанию — адрес из кабинета."
          },
          "expectedRate": {
            "type": [
              "string",
              "null"
            ],
            "description": "Точная строка rate из расчёта. Несовпадение даёт 409. Для карты — null. Не участвует в идентичности повторной заявки."
          }
        },
        "required": [
          "externalId",
          "method",
          "amount",
          "address"
        ],
        "examples": [
          {
            "externalId": "payout-1001",
            "method": "card",
            "amount": "2000",
            "address": "4111111111111111"
          }
        ]
      },
      "Quote": {
        "type": "object",
        "properties": {
          "method": {
            "type": "string",
            "description": "Способ выплаты.",
            "enum": [
              "card",
              "usdt_trc20"
            ]
          },
          "amount": {
            "type": "string",
            "pattern": "^\\d+(\\.\\d+)?$",
            "examples": [
              "2000.00"
            ],
            "description": "Общая сумма заявки в RUB."
          },
          "feeRub": {
            "type": "string",
            "pattern": "^\\d+(\\.\\d+)?$",
            "examples": [
              "2000.00"
            ],
            "description": "Комиссия в RUB, удерживается из amount."
          },
          "receiveAmount": {
            "type": "string",
            "pattern": "^\\d+(\\.\\d+)?$",
            "examples": [
              "2000.00"
            ],
            "description": "Сумма получателю в receiveCurrency."
          },
          "receiveCurrency": {
            "enum": [
              "RUB",
              "USDT"
            ],
            "description": "RUB для карты, USDT для TRC20."
          },
          "rate": {
            "type": [
              "string",
              "null"
            ],
            "description": "Rapira RUB/USDT без наценки; для карты null."
          }
        },
        "required": [
          "method",
          "amount",
          "feeRub",
          "receiveAmount",
          "receiveCurrency",
          "rate"
        ],
        "examples": [
          {
            "method": "usdt_trc20",
            "amount": "8240.00",
            "feeRub": "240.00",
            "receiveAmount": "100.000000",
            "receiveCurrency": "USDT",
            "rate": "80.0"
          }
        ]
      },
      "Payout": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID заявки Payso."
          },
          "externalId": {
            "type": "string",
            "description": "Ваш ID выплаты."
          },
          "method": {
            "enum": [
              "card",
              "usdt_trc20"
            ],
            "description": "Способ выплаты."
          },
          "amount": {
            "type": "string",
            "pattern": "^\\d+(\\.\\d+)?$",
            "examples": [
              "2000.00"
            ],
            "description": "Сумма заявки в RUB."
          },
          "feeRub": {
            "type": "string",
            "pattern": "^\\d+(\\.\\d+)?$",
            "examples": [
              "2000.00"
            ],
            "description": "Комиссия из суммы заявки в RUB."
          },
          "currency": {
            "const": "RUB",
            "description": "Валюта суммы заявки."
          },
          "receiveAmount": {
            "type": "string",
            "pattern": "^\\d+(\\.\\d+)?$",
            "examples": [
              "2000.00"
            ],
            "description": "Сумма получателю."
          },
          "receiveCurrency": {
            "enum": [
              "RUB",
              "USDT"
            ],
            "description": "RUB для карты, USDT для TRC20."
          },
          "rate": {
            "type": [
              "number",
              "null"
            ],
            "description": "Зафиксированный курс RUB/USDT числом; для карты null."
          },
          "destination": {
            "type": "string",
            "description": "Маскированная карта (последние 4 цифры) или адрес TRON."
          },
          "status": {
            "enum": [
              "pending",
              "approved",
              "paid",
              "rejected"
            ],
            "description": "pending — рассмотрение; approved — в работе; paid — исполнена; rejected — отклонена."
          },
          "processing": {
            "type": [
              "string",
              "null"
            ],
            "description": "Состояние карточной отправки. unknown — требуется сверка, резерв сохраняется.",
            "enum": [
              null,
              "submitting",
              "pending",
              "unknown",
              "success",
              "declined"
            ]
          },
          "txHash": {
            "type": [
              "string",
              "null"
            ],
            "description": "Hash исполненной TRC20-транзакции, иначе null."
          },
          "createdAt": {
            "type": "string",
            "description": "Дата создания."
          }
        },
        "required": [
          "id",
          "externalId",
          "method",
          "amount",
          "feeRub",
          "currency",
          "receiveAmount",
          "receiveCurrency",
          "rate",
          "destination",
          "status",
          "processing",
          "txHash",
          "createdAt"
        ],
        "examples": [
          {
            "id": "33333333-3333-4333-8333-333333333333",
            "externalId": "payout-1001",
            "method": "card",
            "amount": "2000.00",
            "feeRub": "0.00",
            "currency": "RUB",
            "receiveAmount": "2000.00",
            "receiveCurrency": "RUB",
            "rate": null,
            "destination": "•••• 1111",
            "status": "pending",
            "processing": null,
            "txHash": null,
            "createdAt": "2026-09-17T09:00:00.000+00:00"
          }
        ]
      },
      "Rates": {
        "type": "object",
        "properties": {
          "rapira": {
            "type": [
              "string",
              "null"
            ],
            "description": "Фиксинг RUB/USDT либо null при недоступности."
          },
          "fixing": {
            "type": "string",
            "description": "Время и часовой пояс фиксинга."
          },
          "markupPercent": {
            "const": "0",
            "description": "Наценка на курс — 0%."
          },
          "card": {
            "type": "object",
            "properties": {
              "min": {
                "const": 2000
              },
              "max": {
                "const": 100000
              },
              "feeRub": {
                "const": 0
              }
            },
            "required": [],
            "description": "Лимиты суммы и комиссия карточных выплат, RUB."
          },
          "usdt_trc20": {
            "type": "object",
            "properties": {
              "min": {
                "const": 500
              },
              "max": {
                "const": 500000
              },
              "feeRub": {
                "const": 240
              }
            },
            "required": [],
            "description": "Лимиты рублёвой заявки и комиссия сети, RUB."
          }
        },
        "required": [
          "rapira",
          "fixing",
          "markupPercent",
          "card",
          "usdt_trc20"
        ],
        "examples": [
          {
            "rapira": "80.0",
            "fixing": "09:00 Europe/Moscow",
            "markupPercent": "0",
            "card": {
              "min": 2000,
              "max": 100000,
              "feeRub": 0
            },
            "usdt_trc20": {
              "min": 500,
              "max": 500000,
              "feeRub": 240
            }
          }
        ]
      },
      "PaymentEvent": {
        "type": "object",
        "required": [
          "eventId",
          "event",
          "id",
          "externalId",
          "merchantId",
          "amount",
          "netAmount",
          "currency",
          "status",
          "sbpUrl"
        ],
        "properties": {
          "eventId": {
            "type": "string",
            "format": "uuid",
            "description": "Уникальный ID события для защиты от повторной обработки."
          },
          "event": {
            "type": "string",
            "enum": [
              "payment.ready",
              "payment.paid",
              "payment.expired"
            ]
          },
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID платежа Payso."
          },
          "externalId": {
            "type": "string"
          },
          "merchantId": {
            "type": "string",
            "format": "uuid"
          },
          "amount": {
            "type": "string"
          },
          "netAmount": {
            "type": "string"
          },
          "currency": {
            "const": "RUB"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "paid",
              "expired"
            ]
          },
          "sbpUrl": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "examples": [
          {
            "eventId": "44444444-4444-4444-8444-444444444444",
            "event": "payment.paid",
            "id": "22222222-2222-4222-8222-222222222222",
            "externalId": "order-1001",
            "merchantId": "11111111-1111-4111-8111-111111111111",
            "amount": "1000.00",
            "netAmount": "962.00",
            "currency": "RUB",
            "status": "paid",
            "sbpUrl": "https://qr.nspk.ru/EXAMPLE"
          }
        ]
      },
      "PayoutEvent": {
        "type": "object",
        "required": [
          "eventId",
          "event",
          "id",
          "externalId",
          "amount",
          "feeRub",
          "currency",
          "status",
          "method",
          "receiveAmount",
          "txHash"
        ],
        "properties": {
          "eventId": {
            "type": "string",
            "format": "uuid",
            "description": "Уникальный ID события для защиты от повторной обработки."
          },
          "event": {
            "type": "string",
            "enum": [
              "payout.approved",
              "payout.paid",
              "payout.rejected"
            ]
          },
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "UUID выплаты Payso."
          },
          "externalId": {
            "type": "string"
          },
          "amount": {
            "type": "string"
          },
          "feeRub": {
            "type": "string"
          },
          "currency": {
            "const": "RUB"
          },
          "status": {
            "type": "string",
            "enum": [
              "approved",
              "paid",
              "rejected"
            ]
          },
          "method": {
            "type": "string",
            "enum": [
              "card",
              "usdt_trc20"
            ]
          },
          "receiveAmount": {
            "type": "string",
            "description": "RUB для card, USDT для usdt_trc20."
          },
          "txHash": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "examples": [
          {
            "eventId": "55555555-5555-4555-8555-555555555555",
            "event": "payout.paid",
            "id": "33333333-3333-4333-8333-333333333333",
            "externalId": "payout-1001",
            "amount": "2000",
            "feeRub": "0",
            "currency": "RUB",
            "status": "paid",
            "method": "card",
            "receiveAmount": "2000.00",
            "txHash": null
          }
        ]
      }
    }
  },
  "externalDocs": {
    "url": "https://dev.payso.tech"
  },
  "tags": [
    {
      "name": "Мерчанты",
      "description": "Подключения вашей платёжной системы и индивидуальные лимиты."
    },
    {
      "name": "Платежи",
      "description": "СБП H2H: создание счёта, ссылка для QR и подтверждённый статус оплаты."
    },
    {
      "name": "Баланс",
      "description": "Общий рублёвый баланс и текущий фиксинг для выплат."
    },
    {
      "name": "Выплаты",
      "description": "Карты РФ и USDT TRC20. Заявка резервирует средства до исполнения или отклонения."
    },
    {
      "name": "Webhook-события",
      "description": "Исходящие уведомления Payso на ваш HTTPS-адрес. Это не методы на api.payso.tech."
    }
  ],
  "webhooks": {
    "paymentStatus": {
      "post": {
        "operationId": "paymentStatus",
        "tags": [
          "Webhook-события"
        ],
        "summary": "Уведомление о платеже",
        "description": "Payso вызывает ваш callbackUrl. Подпись: HMAC-SHA256(API_SECRET, TIMESTAMP + LF + RAW_BODY). Используйте секрет, действовавший при создании операции. Проверьте подпись и обработайте eventId идемпотентно.",
        "security": [],
        "parameters": [
          {
            "name": "X-Payso-Event-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Совпадает с eventId в теле."
          },
          {
            "name": "X-Payso-Timestamp",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Unix timestamp в секундах."
          },
          {
            "name": "X-Payso-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Подпись уведомления в нижнем hex-регистре."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PaymentEvent"
              },
              "example": {
                "eventId": "44444444-4444-4444-8444-444444444444",
                "event": "payment.paid",
                "id": "22222222-2222-4222-8222-222222222222",
                "externalId": "order-1001",
                "merchantId": "11111111-1111-4111-8111-111111111111",
                "amount": "1000.00",
                "netAmount": "962.00",
                "currency": "RUB",
                "status": "paid",
                "sbpUrl": "https://qr.nspk.ru/EXAMPLE"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Событие надёжно сохранено. Допустим любой HTTP 2xx."
          }
        }
      }
    },
    "payoutStatus": {
      "post": {
        "operationId": "payoutStatus",
        "tags": [
          "Webhook-события"
        ],
        "summary": "Уведомление о выплате",
        "description": "Payso вызывает ваш callbackUrl. Подпись: HMAC-SHA256(API_SECRET, TIMESTAMP + LF + RAW_BODY). Используйте секрет, действовавший при создании операции. Проверьте подпись и обработайте eventId идемпотентно.",
        "security": [],
        "parameters": [
          {
            "name": "X-Payso-Event-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Совпадает с eventId в теле."
          },
          {
            "name": "X-Payso-Timestamp",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Unix timestamp в секундах."
          },
          {
            "name": "X-Payso-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Подпись уведомления в нижнем hex-регистре."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PayoutEvent"
              },
              "example": {
                "eventId": "55555555-5555-4555-8555-555555555555",
                "event": "payout.paid",
                "id": "33333333-3333-4333-8333-333333333333",
                "externalId": "payout-1001",
                "amount": "2000",
                "feeRub": "0",
                "currency": "RUB",
                "status": "paid",
                "method": "card",
                "receiveAmount": "2000.00",
                "txHash": null
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Событие надёжно сохранено. Допустим любой HTTP 2xx."
          }
        }
      }
    }
  }
}
