openapi: 3.0.3
info:
  title: Paynet UWS (Universal Web Service) API
  description: |
    API-спецификация для интеграции с платежной системой Paynet по протоколу UWS (универсальный коннектор).
    Взаимодействие осуществляется по протоколу JSON-RPC 2.0 поверх HTTP 1.1 POST с защитой TLS v1.3 (HTTPS).
    Соответствует спецификации UWS v3.4 от 07.05.2025.

    **Важные требования:**
    - Все запросы должны использовать кодировку `UTF-8` и заголовки `Content-Type: application/json`, `Accept: application/json`.
    - Формат дат строго `YYYY-MM-dd HH:mm:ss`, часовой пояс GMT+5.
      **Единственное исключение:** параметр `timestamp` в запросе `CheckTransaction` приходит в формате
      `EEE MMM dd HH:mm:ss z yyyy` (например, `Mon Jun 16 06:12:41 UZT 2021`). Формат зафиксирован
      исторически и изменению не подлежит.
    - Обязательно использование `HTTP Basic Authentication`. При отсутствии или неверных данных
      авторизации система должна возвращать `HTTP 401 Unauthorized` (а не 200 OK с JSON-RPC ошибкой).
    - SLA: обработка транзакции — не более 500 мс (допускается до 1 с, но не более 30 минут в сутки);
      при отсутствии ответа в течение 30 секунд Paynet разрывает соединение по таймауту.
      При систематическом нарушении SLA Paynet может отключить поставщика до устранения причины.
    - Суммы (`amount`) — целые числа в тийинах (1 сум = 100 тийин).
    - Состав и типы значений объекта `fields` определяются партнером в анкете
      «Порядок технического взаимодействия» (Таблицы 3/5); рекомендуются строковые значения.
  version: 3.4.0
  contact:
    name: Paynet Integration Team
    email: support@paynet.uz

servers:
  - url: https://api.partner.domain
    description: Production сервер партнера (URL предоставляется партнером)

security:
  - basicAuth: []

paths:
  /uws:
    post:
      summary: Единая точка входа для JSON-RPC запросов
      description: |
        Все запросы от Paynet к биллингу партнера отправляются на этот endpoint методом POST.
        Тело запроса должно соответствовать стандарту JSON-RPC 2.0. Метод определяется полем `method`,
        структура `params` жестко привязана к значению `method` (см. схему `RpcRequest`).
      operationId: processRpcRequest
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RpcRequest'
            examples:
              GetInformation:
                summary: Запрос справочной информации
                value:
                  jsonrpc: "2.0"
                  method: "GetInformation"
                  id: 12350
                  params:
                    serviceId: 1
                    fields:
                      client_id: "634247"
              PerformTransaction:
                summary: Проведение платежа
                value:
                  jsonrpc: "2.0"
                  method: "PerformTransaction"
                  id: 12345
                  params:
                    amount: 100000
                    serviceId: 1
                    transactionId: 12345678900
                    fields:
                      client_id: "634247"
              CheckTransaction:
                summary: Проверка состояния (обратите внимание на формат timestamp!)
                value:
                  jsonrpc: "2.0"
                  method: "CheckTransaction"
                  id: 12346
                  params:
                    serviceId: 1
                    transactionId: 12345678900
                    timestamp: "Mon Jun 16 06:12:41 UZT 2021"
              CancelTransaction:
                summary: Отмена транзакции
                value:
                  jsonrpc: "2.0"
                  method: "CancelTransaction"
                  id: 12347
                  params:
                    serviceId: 1
                    transactionId: 12345678900
                    timestamp: "2021-06-16 12:44:57"
              GetStatement:
                summary: Сверка за период
                value:
                  jsonrpc: "2.0"
                  method: "GetStatement"
                  id: 12348
                  params:
                    serviceId: 1
                    dateFrom: "2021-04-20 08:00:00"
                    dateTo: "2021-04-30 08:00:00"
              ChangePassword:
                summary: Смена пароля (необязательный метод)
                value:
                  jsonrpc: "2.0"
                  method: "ChangePassword"
                  id: 12351
                  params:
                    newPassword: "newSecurePassword"
      responses:
        '200':
          description: Успешный или неуспешный RPC ответ (result XOR error)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RpcResponse'
              examples:
                GetInformationResult:
                  summary: Ответ GetInformation
                  value:
                    jsonrpc: "2.0"
                    id: 12350
                    result:
                      status: 0
                      timestamp: "2021-04-30 08:00:00"
                      fields:
                        balance: "420000"
                        name: "Пушкин А.С."
                PerformTransactionResult:
                  summary: Ответ PerformTransaction
                  value:
                    jsonrpc: "2.0"
                    id: 12345
                    result:
                      timestamp: "2021-06-16 12:41:54"
                      providerTrnId: 2323
                      fields:
                        client_id: "634247"
                CheckTransactionResult:
                  summary: Ответ CheckTransaction
                  value:
                    jsonrpc: "2.0"
                    id: 12346
                    result:
                      transactionState: 1
                      timestamp: "2021-06-16 12:44:57"
                      providerTrnId: 2323
                GetStatementResult:
                  summary: Ответ GetStatement (только успешные транзакции)
                  value:
                    jsonrpc: "2.0"
                    id: 12348
                    result:
                      statements:
                        - amount: 120000
                          providerTrnId: 23
                          transactionId: 12345679800
                          timestamp: "2021-04-23 17:04:22"
                        - amount: 780000
                          providerTrnId: 47
                          transactionId: 12346578901
                          timestamp: "2021-04-24 13:25:02"
                ChangePasswordResult:
                  summary: Ответ ChangePassword
                  value:
                    jsonrpc: "2.0"
                    id: 12351
                    result: "success"
                BusinessError:
                  summary: Бизнес-ошибка (клиент не найден)
                  value:
                    jsonrpc: "2.0"
                    id: 12350
                    error:
                      code: 302
                      message: "Клиент не найден"
        '401':
          description: Unauthorized. Отсутствуют или неверны данные HTTP Basic Auth. Тело ответа не требуется.

components:
  securitySchemes:
    basicAuth:
      type: http
      scheme: basic
      description: Логин и пароль предоставляются партнером для авторизации запросов Paynet.

  schemas:
    RpcId:
      description: Идентификатор запроса. Может быть любого типа (число или строка); ответ обязан вернуть то же значение.
      oneOf:
        - type: integer
          format: int64
        - type: string

    RpcRequest:
      description: JSON-RPC 2.0 запрос. Структура `params` определяется значением `method`.
      oneOf:
        - $ref: '#/components/schemas/GetInformationRequest'
        - $ref: '#/components/schemas/PerformTransactionRequest'
        - $ref: '#/components/schemas/CheckTransactionRequest'
        - $ref: '#/components/schemas/CancelTransactionRequest'
        - $ref: '#/components/schemas/GetStatementRequest'
        - $ref: '#/components/schemas/ChangePasswordRequest'

    GetInformationRequest:
      type: object
      additionalProperties: false
      required: [jsonrpc, method, id, params]
      properties:
        jsonrpc:
          type: string
          enum: ["2.0"]
        method:
          type: string
          enum: [GetInformation]
        id:
          $ref: '#/components/schemas/RpcId'
        params:
          $ref: '#/components/schemas/GetInformationParams'

    PerformTransactionRequest:
      type: object
      additionalProperties: false
      required: [jsonrpc, method, id, params]
      properties:
        jsonrpc:
          type: string
          enum: ["2.0"]
        method:
          type: string
          enum: [PerformTransaction]
        id:
          $ref: '#/components/schemas/RpcId'
        params:
          $ref: '#/components/schemas/PerformTransactionParams'

    CheckTransactionRequest:
      type: object
      additionalProperties: false
      required: [jsonrpc, method, id, params]
      properties:
        jsonrpc:
          type: string
          enum: ["2.0"]
        method:
          type: string
          enum: [CheckTransaction]
        id:
          $ref: '#/components/schemas/RpcId'
        params:
          $ref: '#/components/schemas/CheckTransactionParams'

    CancelTransactionRequest:
      type: object
      additionalProperties: false
      required: [jsonrpc, method, id, params]
      properties:
        jsonrpc:
          type: string
          enum: ["2.0"]
        method:
          type: string
          enum: [CancelTransaction]
        id:
          $ref: '#/components/schemas/RpcId'
        params:
          $ref: '#/components/schemas/CancelTransactionParams'

    GetStatementRequest:
      type: object
      additionalProperties: false
      required: [jsonrpc, method, id, params]
      properties:
        jsonrpc:
          type: string
          enum: ["2.0"]
        method:
          type: string
          enum: [GetStatement]
        id:
          $ref: '#/components/schemas/RpcId'
        params:
          $ref: '#/components/schemas/GetStatementParams'

    ChangePasswordRequest:
      type: object
      additionalProperties: false
      required: [jsonrpc, method, id, params]
      properties:
        jsonrpc:
          type: string
          enum: ["2.0"]
        method:
          type: string
          enum: [ChangePassword]
        id:
          $ref: '#/components/schemas/RpcId'
        params:
          $ref: '#/components/schemas/ChangePasswordParams'

    GetInformationParams:
      type: object
      additionalProperties: false
      required: [serviceId, fields]
      properties:
        serviceId:
          type: integer
          description: Идентификатор сервиса на стороне поставщика (статический, указывается в «Порядке технического взаимодействия»)
        fields:
          type: object
          description: Перечень заполненных полей сервиса (например, номер телефона, лицевой счет). Состав и типы значений — по анкете партнера.

    PerformTransactionParams:
      type: object
      additionalProperties: false
      required: [amount, serviceId, transactionId, fields]
      properties:
        amount:
          type: integer
          format: int64
          minimum: 1
          description: Сумма операции в тийинах (целое число, 1 сум = 100 тийин)
        serviceId:
          type: integer
          description: Идентификатор сервиса на стороне поставщика
        transactionId:
          type: integer
          format: int64
          description: Уникальный идентификатор транзакции PAYNET (ключ идемпотентности)
        fields:
          type: object
          description: Перечень заполненных полей сервиса. Состав и типы значений — по анкете партнера.

    CheckTransactionParams:
      type: object
      additionalProperties: false
      required: [serviceId, transactionId, timestamp]
      properties:
        serviceId:
          type: integer
        transactionId:
          type: integer
          format: int64
        timestamp:
          type: string
          pattern: '^[A-Z][a-z]{2} [A-Z][a-z]{2} \d{2} \d{2}:\d{2}:\d{2} [A-Z]{2,5} \d{4}$'
          description: |
            Дата и время обработки запроса на стороне PAYNET (GMT+5).
            **ВНИМАНИЕ: исключение из общего правила.** Формат — `EEE MMM dd HH:mm:ss z yyyy`
            (например, `Mon Jun 16 06:12:41 UZT 2021`). Формат зафиксирован исторически и не изменится.
            Месяц — строго в сокращенной форме `MMM` («Jun»).
          example: "Mon Jun 16 06:12:41 UZT 2021"

    CancelTransactionParams:
      type: object
      additionalProperties: false
      required: [serviceId, transactionId, timestamp]
      properties:
        serviceId:
          type: integer
        transactionId:
          type: integer
          format: int64
        timestamp:
          type: string
          pattern: '^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$'
          description: |
            Дата и время обработки запроса на стороне PAYNET (GMT+5).
            Подтверждено core-командой: и запрос, и ответ CancelTransaction используют стандартный
            формат `YYYY-MM-dd HH:mm:ss` (в отличие от CheckTransaction). В легаси-примерах
            спецификации встречался формат `dd.MM.yyyy HH:mm:ss` — это опечатка источника.
          example: "2021-06-16 12:44:57"

    GetStatementParams:
      type: object
      additionalProperties: false
      required: [serviceId, dateFrom, dateTo]
      properties:
        serviceId:
          type: integer
        dateFrom:
          type: string
          pattern: '^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$'
          description: Дата и время начала периода (GMT+5)
        dateTo:
          type: string
          pattern: '^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$'
          description: Дата и время окончания периода (GMT+5)

    ChangePasswordParams:
      type: object
      additionalProperties: false
      required: [newPassword]
      properties:
        newPassword:
          type: string
          minLength: 8
          description: Новый пароль (минимальная длина согласовывается с Paynet)

    RpcResponse:
      description: JSON-RPC 2.0 ответ. Содержит либо `result` (успех), либо `error` (неуспех) — никогда оба сразу.
      oneOf:
        - $ref: '#/components/schemas/RpcSuccessResponse'
        - $ref: '#/components/schemas/RpcErrorResponse'

    RpcSuccessResponse:
      type: object
      additionalProperties: false
      required: [jsonrpc, id, result]
      properties:
        jsonrpc:
          type: string
          enum: ["2.0"]
        id:
          $ref: '#/components/schemas/RpcId'
        result:
          $ref: '#/components/schemas/RpcResult'

    RpcErrorResponse:
      type: object
      additionalProperties: false
      required: [jsonrpc, id, error]
      properties:
        jsonrpc:
          type: string
          enum: ["2.0"]
        id:
          description: Совпадает с id запроса; для ошибки парсинга (-32700) допускается null.
          oneOf:
            - type: integer
              format: int64
            - type: string
              nullable: true
        error:
          $ref: '#/components/schemas/RpcError'

    RpcResult:
      oneOf:
        - $ref: '#/components/schemas/GetInformationResult'
        - $ref: '#/components/schemas/PerformTransactionResult'
        - $ref: '#/components/schemas/CheckCancelTransactionResult'
        - $ref: '#/components/schemas/GetStatementResult'
        - $ref: '#/components/schemas/ChangePasswordResult'

    GetInformationResult:
      type: object
      additionalProperties: false
      required: [status, timestamp, fields]
      properties:
        status:
          type: integer
          description: Состояние запроса (0 — успешно). В легаси-примерах встречалась строка "0" — возвращайте целое число.
        timestamp:
          type: string
          pattern: '^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$'
          description: Дата и время обработки запроса на стороне поставщика (GMT+5)
        fields:
          type: object
          description: Перечень дополнительных параметров (например, баланс и имя клиента)

    PerformTransactionResult:
      type: object
      additionalProperties: false
      required: [providerTrnId, fields, timestamp]
      properties:
        providerTrnId:
          type: integer
          format: int64
          description: Идентификатор транзакции поставщика
        fields:
          type: object
          description: |
            Перечень заполненных полей сервиса. Если это согласовано в анкете (Таблица 4),
            здесь возвращается баланс плательщика после проведения транзакции (в тийинах).
        timestamp:
          type: string
          pattern: '^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$'
          description: Дата и время обработки запроса на стороне поставщика (GMT+5)

    CheckCancelTransactionResult:
      type: object
      additionalProperties: false
      required: [providerTrnId, timestamp, transactionState]
      properties:
        providerTrnId:
          type: integer
          format: int64
        timestamp:
          type: string
          pattern: '^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$'
          description: Дата и время обработки запроса на стороне поставщика (GMT+5)
        transactionState:
          $ref: '#/components/schemas/TransactionStateEnum'

    GetStatementResult:
      type: object
      additionalProperties: false
      required: [statements]
      properties:
        statements:
          type: array
          description: Массив транзакций за период. Включаются ТОЛЬКО успешные транзакции (state 1); отмененные не включаются.
          items:
            $ref: '#/components/schemas/StatementItem'

    StatementItem:
      type: object
      additionalProperties: false
      required: [amount, transactionId, providerTrnId, timestamp]
      properties:
        amount:
          type: integer
          format: int64
          minimum: 1
          description: Сумма финансовой операции в тийинах
        transactionId:
          type: integer
          format: int64
        providerTrnId:
          type: integer
          format: int64
        timestamp:
          type: string
          pattern: '^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$'
          description: Дата и время обработки транзакции на стороне поставщика (GMT+5)

    ChangePasswordResult:
      type: string
      description: Статус обработки запроса (строка, например "success")
      example: "success"

    TransactionStateEnum:
      type: integer
      description: |
        1 – Успешно проведенная транзакция, 2 – Отмененная транзакция, 3 – Транзакция не найдена.
        Для CheckTransaction по неизвестной транзакции возвращайте state 3 (успешный конверт), а не ошибку 203.
      enum: [1, 2, 3]

    RpcError:
      type: object
      additionalProperties: false
      required:
        - code
        - message
      properties:
        code:
          type: integer
          description: |
            Код ошибки Paynet — код уровня JSON-RPC, возвращается внутри объекта error (HTTP-статус ответа при этом остаётся 200 OK; это НЕ HTTP-код, их часто путают). Полный справочник:
            0: Проведено успешно (значение статуса; внутри объекта error не возвращается)
            77: Недостаточно средств на счету клиента для отмены платежа
            100: Услуга временно не поддерживается
            101: Квота исчерпана
            102, 103: Системная/Неизвестная ошибка
            113: Кошелёк не идентифицирован
            140: Превышен ежемесячный лимит для данного аккаунта
            141: Превышен дневной лимит для данного аккаунта
            201, 202: Транзакция существует/отменена
            203: Транзакция не найдена (используется в CancelTransaction; для CheckTransaction — state 3)
            301, 302, 304, 305: Сущность не найдена
            306: Допустимое время отмены транзакции истекло (поставщик отказывает в возврате по своим бизнес-правилам — закрывающие документы сформированы, отчетный период закрыт; например, оплата в январе, попытка возврата в марте)
            401-410: Ошибки валидации параметров 1-10 (распределение по параметрам — сервис-специфично)
            411: Не заданы один или несколько обязательных параметров (бизнес-уровень; ср. -32602 на уровне протокола)
            412: Неверный логин или пароль (легаси; аутентификация выполняется через HTTP 401)
            413: Неверная сумма
            414: Неверный формат даты и времени
            415: Сумма превышает максимальный лимит
            501, 601, 603: Ограничения доступа/команд
            JSON-RPC: -32300, -32700, -32600, -32601, -32602, -32603
        message:
          type: string
          description: Описание ошибки
