{
  "openapi": "3.1.0",
  "info": {
    "title": "Payra API",
    "version": "1.0.0",
    "summary": "Приём платежей по СБП и банковским картам: счета, возвраты, вебхуки",
    "description": "REST API платёжного шлюза Payra. Суммы — строки с двумя знаками, даты — ISO 8601 UTC, ошибки — { error: { code, message } }. Документация: https://payrapay.net/docs",
    "termsOfService": "https://payrapay.net/legal/offer",
    "contact": {
      "name": "Payra Support",
      "url": "https://payrapay.net/contacts"
    }
  },
  "servers": [
    {
      "url": "https://payrapay.net/api/v1",
      "description": "Production (и песочница по ключу sk_test_)"
    }
  ],
  "externalDocs": {
    "url": "https://payrapay.net/docs",
    "description": "Документация"
  },
  "tags": [
    {
      "name": "Invoices",
      "description": "Счета"
    },
    {
      "name": "Refunds",
      "description": "Возвраты"
    },
    {
      "name": "Payouts",
      "description": "Выводы средств с баланса"
    },
    {
      "name": "Reference",
      "description": "Справочники и баланс"
    },
    {
      "name": "Webhooks",
      "description": "Исходящие уведомления (описание, не эндпоинт)"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    },
    {
      "hmacSignature": [],
      "publicId": [],
      "hmacTimestamp": []
    }
  ],
  "paths": {
    "/invoices": {
      "post": {
        "tags": [
          "Invoices"
        ],
        "operationId": "createInvoice",
        "summary": "Создать счёт",
        "description": "Создаёт счёт и возвращает ссылку на страницу оплаты. orderId идемпотентен в рамках проекта: повтор с тем же orderId возвращает существующий счёт (200), с другими параметрами — 409 order_id_conflict.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateInvoiceRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Счёт с таким orderId уже существует (идемпотентный повтор)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InvoiceCreated"
                }
              }
            }
          },
          "201": {
            "description": "Счёт создан",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InvoiceCreated"
                }
              }
            }
          },
          "400": {
            "description": "Некорректный запрос",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "validation",
                    "message": "Некорректный запрос"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Неверный ключ или подпись",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "unauthorized",
                    "message": "Неверный ключ или подпись"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Проект не активен",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "project_not_active",
                    "message": "Проект не активен"
                  }
                }
              }
            }
          },
          "409": {
            "description": "orderId занят счётом с другими параметрами",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "order_id_conflict",
                    "message": "orderId занят счётом с другими параметрами"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Сумма вне лимитов или метод недоступен",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "amount_out_of_range",
                    "message": "Сумма вне лимитов или метод недоступен"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Превышен лимит запросов",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Через сколько секунд повторить"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Превышен лимит запросов"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Способ оплаты временно недоступен",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "provider_unavailable",
                    "message": "Способ оплаты временно недоступен"
                  }
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Invoices"
        ],
        "operationId": "listInvoices",
        "summary": "Список счетов",
        "description": "Счета проекта от новых к старым с курсорной пагинацией. Фильтр orderId возвращает 0 или 1 элемент.",
        "parameters": [
          {
            "name": "orderId",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "created",
                "pending",
                "paid",
                "failed",
                "expired",
                "refunded",
                "partially_refunded"
              ]
            }
          },
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "ISO 8601, UTC"
            },
            "description": "Создан не ранее (включительно)"
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "ISO 8601, UTC"
            },
            "description": "Создан ранее (исключительно)"
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "nextCursor из предыдущего ответа"
          }
        ],
        "responses": {
          "200": {
            "description": "Страница списка",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InvoiceList"
                }
              }
            }
          },
          "400": {
            "description": "Некорректные параметры",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "validation",
                    "message": "Некорректные параметры"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Неверный ключ или подпись",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "unauthorized",
                    "message": "Неверный ключ или подпись"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Превышен лимит запросов",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Через сколько секунд повторить"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Превышен лимит запросов"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/invoices/{id}": {
      "get": {
        "tags": [
          "Invoices"
        ],
        "operationId": "getInvoice",
        "summary": "Получить счёт",
        "description": "Текущее состояние счёта. Опрос одного счёта — не чаще 1 раза в 3 секунды (иначе 429 с Retry-After); для уведомлений используйте вебхуки.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Счёт",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Invoice"
                }
              }
            }
          },
          "401": {
            "description": "Неверный ключ или подпись",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "unauthorized",
                    "message": "Неверный ключ или подпись"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Счёт не найден",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "invoice_not_found",
                    "message": "Счёт не найден"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Слишком частый опрос",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Через сколько секунд повторить"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Слишком частый опрос"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/invoices/{id}/refund": {
      "post": {
        "tags": [
          "Refunds"
        ],
        "operationId": "refundInvoice",
        "summary": "Вернуть средства покупателю",
        "description": "Полный (без amount) или частичный возврат по оплаченному счёту. Сумма списывается с баланса мерчанта. Обрабатывается асинхронно: 202 + статус pending, итог — вебхук refund.succeeded / refund.failed.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "amount": {
                    "type": "string",
                    "pattern": "^\\d+\\.\\d{2}$",
                    "description": "Сумма возврата; по умолчанию — весь невозвращённый остаток",
                    "examples": [
                      "1500.00"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Возврат принят в обработку",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Refund"
                }
              }
            }
          },
          "401": {
            "description": "Неверный ключ или подпись",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "unauthorized",
                    "message": "Неверный ключ или подпись"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Счёт не найден",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "invoice_not_found",
                    "message": "Счёт не найден"
                  }
                }
              }
            }
          },
          "409": {
            "description": "Счёт не в статусе paid / partially_refunded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "conflict",
                    "message": "Счёт не в статусе paid / partially_refunded"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Сумма превышает остаток или недостаточно средств на балансе",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "refund_exceeds_amount",
                    "message": "Сумма превышает остаток или недостаточно средств на балансе"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/refunds/{id}": {
      "get": {
        "tags": [
          "Refunds"
        ],
        "operationId": "getRefund",
        "summary": "Получить возврат",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "examples": [
                "rf_01J7ZK9Q2M4N6P8R0S2T4V6W8X"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Возврат",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Refund"
                }
              }
            }
          },
          "401": {
            "description": "Неверный ключ или подпись",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "unauthorized",
                    "message": "Неверный ключ или подпись"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Возврат не найден",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "invoice_not_found",
                    "message": "Возврат не найден"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/methods": {
      "get": {
        "tags": [
          "Reference"
        ],
        "operationId": "listMethods",
        "summary": "Доступные способы оплаты",
        "responses": {
          "200": {
            "description": "Список методов",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "methods"
                  ],
                  "properties": {
                    "methods": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Method"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Неверный ключ или подпись",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "unauthorized",
                    "message": "Неверный ключ или подпись"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/balance": {
      "get": {
        "tags": [
          "Reference"
        ],
        "operationId": "getBalance",
        "summary": "Баланс мерчанта",
        "responses": {
          "200": {
            "description": "Баланс",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Balance"
                }
              }
            }
          },
          "401": {
            "description": "Неверный ключ или подпись",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "unauthorized",
                    "message": "Неверный ключ или подпись"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/payout-addresses": {
      "get": {
        "tags": [
          "Payouts"
        ],
        "operationId": "listPayoutAddresses",
        "summary": "Подтверждённые реквизиты для вывода",
        "description": "Реквизиты добавляются и подтверждаются (по письму) только в кабинете; API отдаёт подтверждённые с замаскированным account.",
        "responses": {
          "200": {
            "description": "Список реквизитов",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "items"
                  ],
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PayoutAddress"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Неверный ключ или подпись",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "unauthorized",
                    "message": "Неверный ключ или подпись"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/payouts": {
      "post": {
        "tags": [
          "Payouts"
        ],
        "operationId": "createPayout",
        "summary": "Создать заявку на вывод",
        "description": "Резервирует amount на балансе и создаёт заявку на вывод на подтверждённый реквизит (addressId или уже подтверждённая пара method + account). orderId делает запрос идемпотентным: повтор возвращает существующую заявку (200). Только живой ключ sk_live_.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreatePayoutRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Заявка с таким orderId уже существует (идемпотентный повтор)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Payout"
                }
              }
            }
          },
          "201": {
            "description": "Заявка создана",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Payout"
                }
              }
            }
          },
          "400": {
            "description": "Некорректный запрос",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "validation",
                    "message": "Некорректный запрос"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Неверный ключ или подпись",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "unauthorized",
                    "message": "Неверный ключ или подпись"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Тестовый ключ: выводы только по sk_live_",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "test_mode",
                    "message": "Тестовый ключ: выводы только по sk_live_"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Реквизит не найден",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "address_not_found",
                    "message": "Реквизит не найден"
                  }
                }
              }
            }
          },
          "409": {
            "description": "Уже три заявки в обработке",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "too_many_pending",
                    "message": "Уже три заявки в обработке"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Реквизит не подтверждён, сумма ниже минимума или больше баланса",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "address_not_confirmed",
                    "message": "Реквизит не подтверждён, сумма ниже минимума или больше баланса"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Превышен лимит запросов",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Через сколько секунд повторить"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Превышен лимит запросов"
                  }
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Payouts"
        ],
        "operationId": "listPayouts",
        "summary": "Список заявок на вывод",
        "description": "От новых к старым с курсорной пагинацией. Фильтр orderId возвращает одну заявку объектом.",
        "parameters": [
          {
            "name": "orderId",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "approved",
                "processing",
                "paid",
                "rejected",
                "cancelled"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "nextCursor из предыдущего ответа"
          }
        ],
        "responses": {
          "200": {
            "description": "Страница списка (или одна заявка при orderId)",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/PayoutList"
                    },
                    {
                      "$ref": "#/components/schemas/Payout"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Некорректные параметры",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "validation",
                    "message": "Некорректные параметры"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Неверный ключ или подпись",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "unauthorized",
                    "message": "Неверный ключ или подпись"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Заявка с таким orderId не найдена",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "not_found",
                    "message": "Заявка с таким orderId не найдена"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/payouts/{id}": {
      "get": {
        "tags": [
          "Payouts"
        ],
        "operationId": "getPayout",
        "summary": "Получить заявку на вывод",
        "description": "Опрос одной заявки — не чаще 1 раза в 3 секунды (иначе 429 с Retry-After); итог приходит вебхуками payout.paid / payout.rejected.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Заявка",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Payout"
                }
              }
            }
          },
          "401": {
            "description": "Неверный ключ или подпись",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "unauthorized",
                    "message": "Неверный ключ или подпись"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Заявка не найдена",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "not_found",
                    "message": "Заявка не найдена"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Слишком частый опрос",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Через сколько секунд повторить"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Слишком частый опрос"
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "webhooks": {
    "invoice.paid": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Счёт оплачен",
        "description": "POST на webhook_url проекта. Заголовки: X-Payra-Signature (sha256=HMAC-SHA256(rawBody, webhook_secret)), X-Payra-Timestamp, X-Payra-Event, X-Payra-Delivery-Id, X-Payra-Test. Ответ 2xx за 10 с; иначе повторы через 1м, 5м, 30м, 2ч, 12ч, 24ч.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InvoiceEvent"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Принято"
          }
        }
      }
    },
    "invoice.failed": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Платёж отклонён",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InvoiceEvent"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Принято"
          }
        }
      }
    },
    "invoice.expired": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Срок оплаты истёк",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InvoiceEvent"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Принято"
          }
        }
      }
    },
    "refund.succeeded": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Возврат выполнен",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RefundEvent"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Принято"
          }
        }
      }
    },
    "refund.failed": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Возврат не удался",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RefundEvent"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Принято"
          }
        }
      }
    },
    "payout.paid": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Вывод выплачен",
        "description": "Отправляется на webhook_url каждого проекта аккаунта, подписанного на событие. data.payout — как в GET /payouts/{id}.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PayoutEvent"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Принято"
          }
        }
      }
    },
    "payout.rejected": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Вывод отклонён (сумма вернулась на баланс)",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PayoutEvent"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Принято"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "sk_live_… | sk_test_…",
        "description": "Секретный ключ проекта. sk_test_ — песочница."
      },
      "publicId": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Public-Id",
        "description": "публичный ключ pk_live_/pk_test_ (вместе с X-Timestamp и X-Signature)"
      },
      "hmacTimestamp": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Timestamp",
        "description": "unix-время запроса в секундах; принимается в окне ±300 с от времени сервера (401 timestamp_invalid)."
      },
      "hmacSignature": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Signature",
        "description": "hex(HMAC-SHA256(`${X-Timestamp}.${rawBody}`, webhook_secret проекта)); для GET тело пустое → подписывается `${X-Timestamp}.`. Одна подпись принимается один раз в течение 10 минут (повтор → 401 signature_invalid). Принимается также в заголовке Signature."
      }
    },
    "schemas": {
      "Money": {
        "type": "string",
        "pattern": "^\\d+\\.\\d{2}$",
        "description": "Сумма в рублях строкой с двумя знаками",
        "examples": [
          "1500.00"
        ]
      },
      "Customer": {
        "type": "object",
        "description": "Данные покупателя (все поля необязательны)",
        "properties": {
          "email": {
            "type": [
              "string",
              "null"
            ],
            "format": "email"
          },
          "phone": {
            "type": [
              "string",
              "null"
            ],
            "description": "E.164"
          },
          "userId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Идентификатор покупателя в вашей системе"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "CreateInvoiceRequest": {
        "type": "object",
        "required": [
          "amount",
          "orderId"
        ],
        "properties": {
          "amount": {
            "oneOf": [
              {
                "type": "string",
                "pattern": "^\\d+\\.\\d{2}$",
                "description": "Сумма в рублях строкой с двумя знаками",
                "examples": [
                  "1500.00"
                ]
              },
              {
                "type": "number"
              }
            ],
            "description": "Сумма в рублях; в ответах всегда строка"
          },
          "orderId": {
            "type": "string",
            "maxLength": 128,
            "description": "Ваш номер заказа, уникален в рамках проекта (идемпотентность)"
          },
          "description": {
            "type": "string",
            "maxLength": 255
          },
          "method": {
            "type": "string",
            "enum": [
              "sbp",
              "card",
              "crypto"
            ],
            "description": "Не передан — покупатель выбирает сам. crypto (USDT) — только если приём USDT включён в проекте"
          },
          "customerFeePct": {
            "type": "number",
            "minimum": 0,
            "description": "Сколько процентов от суммы платит покупатель сверху: от 0 до вашей ставки по методу (например, при ставке СБП 9% — 0…9). Остаток ставки платите вы. Больше ставки → 422 customer_fee_pct_too_high. По умолчанию — ползунки в настройках проекта (вся ставка на покупателе). Имеет приоритет над customerFeeShare/feePayer"
          },
          "customerFeeShare": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "deprecated": true,
            "description": "Устаревшее: доля ставки на покупателе в % (100 = вся ставка). Пересчитывается в customerFeePct по ставке метода"
          },
          "feePayer": {
            "type": "string",
            "enum": [
              "merchant",
              "customer"
            ],
            "deprecated": true,
            "description": "Устаревшее: customer = вся ставка на покупателе, merchant = 0. Учитывается только без customerFeePct/customerFeeShare"
          },
          "successUrl": {
            "type": "string",
            "format": "uri"
          },
          "failUrl": {
            "type": "string",
            "format": "uri"
          },
          "expire": {
            "type": "integer",
            "minimum": 1,
            "maximum": 1440,
            "default": 60,
            "description": "Срок жизни счёта, минуты"
          },
          "customer": {
            "$ref": "#/components/schemas/Customer"
          },
          "customFields": {
            "type": "object",
            "additionalProperties": true,
            "description": "Произвольный JSON до 2 КБ; возвращается как есть"
          },
          "payerEmail": {
            "type": "string",
            "format": "email",
            "deprecated": true,
            "description": "Синоним customer.email"
          }
        }
      },
      "InvoiceCreated": {
        "type": "object",
        "required": [
          "id",
          "url",
          "status",
          "amount",
          "feePct",
          "feeAmount",
          "customerFeeAmount",
          "payableAmount",
          "netAmount",
          "expiresAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Страница оплаты"
          },
          "status": {
            "type": "string",
            "enum": [
              "created",
              "pending",
              "paid",
              "failed",
              "expired",
              "refunded",
              "partially_refunded"
            ]
          },
          "amount": {
            "type": "string",
            "pattern": "^\\d+\\.\\d{2}$",
            "description": "Сумма в рублях строкой с двумя знаками",
            "examples": [
              "1500.00"
            ]
          },
          "feeAmount": {
            "type": "string",
            "pattern": "^\\d+\\.\\d{2}$",
            "description": "Комиссия Payra целиком",
            "examples": [
              "1500.00"
            ]
          },
          "feePct": {
            "type": "string",
            "examples": [
              "9.00"
            ],
            "description": "Ваша ставка по методу (при не выбранном методе — наибольшая из ставок)"
          },
          "customerFeePct": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "4.00"
            ],
            "description": "% на покупателе по выбранному методу; null, пока метод не выбран"
          },
          "customerFeeShare": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "deprecated": true,
            "description": "Устаревшее: доля ставки на покупателе, %"
          },
          "customerFeeAmount": {
            "type": "string",
            "pattern": "^\\d+\\.\\d{2}$",
            "description": "Часть комиссии, которую платит покупатель сверху",
            "examples": [
              "1500.00"
            ]
          },
          "payableAmount": {
            "type": "string",
            "pattern": "^\\d+\\.\\d{2}$",
            "description": "Сколько платит покупатель: amount + customerFeeAmount",
            "examples": [
              "1500.00"
            ]
          },
          "netAmount": {
            "type": "string",
            "pattern": "^\\d+\\.\\d{2}$",
            "description": "Сколько зачисляется вам: amount − (feeAmount − customerFeeAmount)",
            "examples": [
              "1500.00"
            ]
          },
          "feePayer": {
            "type": "string",
            "enum": [
              "merchant",
              "customer"
            ],
            "deprecated": true,
            "description": "Производное: merchant, если покупатель платит 0%, иначе customer"
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601, UTC"
          }
        }
      },
      "Invoice": {
        "type": "object",
        "required": [
          "id",
          "url",
          "orderId",
          "status",
          "amount",
          "feePct",
          "feeAmount",
          "customerFeeAmount",
          "payableAmount",
          "netAmount",
          "currency",
          "isTest",
          "createdAt",
          "expiresAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "orderId": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "created",
              "pending",
              "paid",
              "failed",
              "expired",
              "refunded",
              "partially_refunded"
            ]
          },
          "amount": {
            "type": "string",
            "pattern": "^\\d+\\.\\d{2}$",
            "description": "Сумма в рублях строкой с двумя знаками",
            "examples": [
              "1500.00"
            ]
          },
          "feeAmount": {
            "type": "string",
            "pattern": "^\\d+\\.\\d{2}$",
            "description": "Сумма в рублях строкой с двумя знаками",
            "examples": [
              "1500.00"
            ]
          },
          "feePct": {
            "type": "string",
            "examples": [
              "9.00"
            ]
          },
          "customerFeePct": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "4.00"
            ]
          },
          "customerFeeShare": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "deprecated": true
          },
          "customerFeeAmount": {
            "type": "string",
            "pattern": "^\\d+\\.\\d{2}$",
            "description": "Сумма в рублях строкой с двумя знаками",
            "examples": [
              "1500.00"
            ]
          },
          "payableAmount": {
            "type": "string",
            "pattern": "^\\d+\\.\\d{2}$",
            "description": "Сумма в рублях строкой с двумя знаками",
            "examples": [
              "1500.00"
            ]
          },
          "netAmount": {
            "type": "string",
            "pattern": "^\\d+\\.\\d{2}$",
            "description": "Сумма в рублях строкой с двумя знаками",
            "examples": [
              "1500.00"
            ]
          },
          "refundedAmount": {
            "type": "string",
            "pattern": "^\\d+\\.\\d{2}$",
            "description": "Сумма в рублях строкой с двумя знаками",
            "examples": [
              "1500.00"
            ]
          },
          "currency": {
            "type": "string",
            "const": "RUB"
          },
          "method": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "sbp",
              "card",
              "crypto",
              null
            ],
            "description": "null, пока покупатель не выбрал способ"
          },
          "feePayer": {
            "type": "string",
            "enum": [
              "merchant",
              "customer"
            ],
            "deprecated": true,
            "description": "Производное: merchant, если покупатель платит 0%, иначе customer"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "customer": {
            "$ref": "#/components/schemas/Customer"
          },
          "customFields": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true
          },
          "isTest": {
            "type": "boolean"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601, UTC"
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601, UTC"
          },
          "paidAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "ISO 8601, UTC"
          },
          "availableAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Когда сумма станет доступна к выводу (холд проекта); null — без холда"
          }
        }
      },
      "InvoiceList": {
        "type": "object",
        "required": [
          "items",
          "hasMore"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Invoice"
            }
          },
          "nextCursor": {
            "type": [
              "string",
              "null"
            ]
          },
          "hasMore": {
            "type": "boolean"
          }
        }
      },
      "Refund": {
        "type": "object",
        "required": [
          "id",
          "invoiceId",
          "amount",
          "currency",
          "status",
          "createdAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "rf_01J7ZK9Q2M4N6P8R0S2T4V6W8X"
            ]
          },
          "invoiceId": {
            "type": "string",
            "format": "uuid"
          },
          "amount": {
            "type": "string",
            "pattern": "^\\d+\\.\\d{2}$",
            "description": "Сумма в рублях строкой с двумя знаками",
            "examples": [
              "1500.00"
            ]
          },
          "currency": {
            "type": "string",
            "const": "RUB"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "succeeded",
              "failed"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601, UTC"
          },
          "completedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "ISO 8601, UTC"
          }
        }
      },
      "Method": {
        "type": "object",
        "required": [
          "code",
          "title",
          "feePct",
          "min",
          "max",
          "currency",
          "enabled"
        ],
        "properties": {
          "code": {
            "type": "string",
            "enum": [
              "sbp",
              "card",
              "crypto"
            ]
          },
          "title": {
            "type": "string"
          },
          "feePct": {
            "type": "string",
            "examples": [
              "3.50"
            ]
          },
          "min": {
            "type": "string",
            "pattern": "^\\d+\\.\\d{2}$",
            "description": "Сумма в рублях строкой с двумя знаками",
            "examples": [
              "1500.00"
            ]
          },
          "max": {
            "type": "string",
            "pattern": "^\\d+\\.\\d{2}$",
            "description": "Сумма в рублях строкой с двумя знаками",
            "examples": [
              "1500.00"
            ]
          },
          "currency": {
            "type": "string",
            "const": "RUB"
          },
          "enabled": {
            "type": "boolean"
          }
        }
      },
      "Balance": {
        "type": "object",
        "required": [
          "available",
          "pending",
          "currency"
        ],
        "properties": {
          "available": {
            "type": "string",
            "pattern": "^\\d+\\.\\d{2}$",
            "description": "Доступно к выводу",
            "examples": [
              "1500.00"
            ]
          },
          "pending": {
            "type": "string",
            "pattern": "^\\d+\\.\\d{2}$",
            "description": "Оплачено, но в холде: станет доступно после холда проекта (по умолчанию 24 ч)",
            "examples": [
              "1500.00"
            ]
          },
          "reserved": {
            "type": "string",
            "pattern": "^\\d+\\.\\d{2}$",
            "description": "Заморожено заявками на вывод / возврат",
            "examples": [
              "1500.00"
            ]
          },
          "currency": {
            "type": "string",
            "const": "RUB"
          }
        }
      },
      "PayoutAddress": {
        "type": "object",
        "required": [
          "id",
          "method",
          "account",
          "status",
          "createdAt"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "method": {
            "type": "string",
            "enum": [
              "sbp",
              "usdt_trc20",
              "usdt_bep20"
            ]
          },
          "label": {
            "type": [
              "string",
              "null"
            ]
          },
          "account": {
            "type": "string",
            "description": "Телефон / адрес, замаскирован"
          },
          "bank": {
            "type": [
              "string",
              "null"
            ],
            "description": "Банк получателя (СБП)"
          },
          "status": {
            "type": "string",
            "const": "confirmed"
          },
          "confirmedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "ISO 8601, UTC"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601, UTC"
          }
        }
      },
      "CreatePayoutRequest": {
        "type": "object",
        "required": [
          "amount"
        ],
        "properties": {
          "amount": {
            "oneOf": [
              {
                "type": "string",
                "pattern": "^\\d+\\.\\d{2}$",
                "description": "Сумма в рублях строкой с двумя знаками",
                "examples": [
                  "1500.00"
                ]
              },
              {
                "type": "number"
              }
            ],
            "description": "Сумма в рублях, списывается с available; комиссия вычитается из неё"
          },
          "addressId": {
            "type": "integer",
            "description": "Подтверждённый реквизит (GET /payout-addresses). Обязателен без method + account"
          },
          "method": {
            "type": "string",
            "enum": [
              "sbp",
              "usdt_trc20",
              "usdt_bep20"
            ]
          },
          "account": {
            "type": "string",
            "description": "Полный телефон / адрес уже подтверждённого реквизита (вместе с method)"
          },
          "orderId": {
            "type": "string",
            "maxLength": 128,
            "description": "Ваш идентификатор заявки — ключ идемпотентности"
          },
          "comment": {
            "type": "string",
            "maxLength": 500
          }
        }
      },
      "Payout": {
        "type": "object",
        "required": [
          "id",
          "status",
          "amount",
          "feeAmount",
          "netAmount",
          "currency",
          "method",
          "source",
          "createdAt"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "approved",
              "processing",
              "paid",
              "rejected",
              "cancelled"
            ]
          },
          "amount": {
            "type": "string",
            "pattern": "^\\d+\\.\\d{2}$",
            "description": "Сумма в рублях строкой с двумя знаками",
            "examples": [
              "1500.00"
            ]
          },
          "feeAmount": {
            "type": "string",
            "pattern": "^\\d+\\.\\d{2}$",
            "description": "Сумма в рублях строкой с двумя знаками",
            "examples": [
              "1500.00"
            ]
          },
          "netAmount": {
            "type": "string",
            "pattern": "^\\d+\\.\\d{2}$",
            "description": "amount − feeAmount, уходит получателю",
            "examples": [
              "1500.00"
            ]
          },
          "currency": {
            "type": "string",
            "const": "RUB"
          },
          "method": {
            "type": "string",
            "enum": [
              "sbp",
              "usdt_trc20",
              "usdt_bep20"
            ]
          },
          "account": {
            "type": [
              "string",
              "null"
            ],
            "description": "Замаскированный телефон / адрес"
          },
          "bank": {
            "type": [
              "string",
              "null"
            ]
          },
          "addressId": {
            "type": [
              "integer",
              "null"
            ]
          },
          "orderId": {
            "type": [
              "string",
              "null"
            ]
          },
          "comment": {
            "type": [
              "string",
              "null"
            ]
          },
          "usdtAmount": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "270.73"
            ],
            "description": "USDT к отправке (только USDT-методы)"
          },
          "usdtRate": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "92.00"
            ],
            "description": "₽ за 1 USDT на момент создания"
          },
          "statusReason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Причина отказа / отмены"
          },
          "externalId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Хеш транзакции / номер перевода после выплаты"
          },
          "source": {
            "type": "string",
            "enum": [
              "api",
              "manual",
              "auto"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601, UTC"
          },
          "processedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "ISO 8601, UTC"
          }
        }
      },
      "PayoutList": {
        "type": "object",
        "required": [
          "items",
          "hasMore"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Payout"
            }
          },
          "nextCursor": {
            "type": [
              "string",
              "null"
            ]
          },
          "hasMore": {
            "type": "boolean"
          }
        }
      },
      "PayoutEvent": {
        "type": "object",
        "required": [
          "id",
          "event",
          "createdAt",
          "data"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "event": {
            "type": "string",
            "enum": [
              "payout.paid",
              "payout.rejected"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601, UTC"
          },
          "isTest": {
            "type": "boolean",
            "const": false
          },
          "data": {
            "type": "object",
            "required": [
              "payout"
            ],
            "properties": {
              "payout": {
                "$ref": "#/components/schemas/Payout"
              }
            }
          }
        }
      },
      "InvoiceEvent": {
        "type": "object",
        "required": [
          "id",
          "event",
          "createdAt",
          "data"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Идентификатор события; совпадает с X-Payra-Delivery-Id"
          },
          "event": {
            "type": "string",
            "enum": [
              "invoice.paid",
              "invoice.failed",
              "invoice.expired"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601, UTC"
          },
          "data": {
            "type": "object",
            "required": [
              "invoice"
            ],
            "properties": {
              "invoice": {
                "$ref": "#/components/schemas/Invoice"
              }
            }
          }
        }
      },
      "RefundEvent": {
        "type": "object",
        "required": [
          "id",
          "event",
          "createdAt",
          "data"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "event": {
            "type": "string",
            "enum": [
              "refund.succeeded",
              "refund.failed"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601, UTC"
          },
          "data": {
            "type": "object",
            "required": [
              "refund",
              "invoice"
            ],
            "properties": {
              "refund": {
                "$ref": "#/components/schemas/Refund"
              },
              "invoice": {
                "$ref": "#/components/schemas/Invoice"
              }
            }
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "validation",
                  "unauthorized",
                  "signature_invalid",
                  "timestamp_invalid",
                  "duplicate_request",
                  "project_not_active",
                  "invoice_not_found",
                  "order_id_conflict",
                  "amount_out_of_range",
                  "method_unavailable",
                  "provider_unavailable",
                  "refund_exceeds_amount",
                  "insufficient_balance",
                  "rate_limited",
                  "internal",
                  "test_mode",
                  "address_not_found",
                  "address_not_confirmed",
                  "too_many_pending",
                  "min_amount",
                  "insufficient_funds",
                  "rate_unavailable",
                  "not_found"
                ]
              },
              "message": {
                "type": "string"
              },
              "param": {
                "type": "string",
                "description": "Поле запроса, к которому относится ошибка"
              },
              "details": {
                "type": "array",
                "items": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    }
  }
}