openapi: 3.0.3
info:
  title: ProxyShark User API Billing
  version: "1.0.0"
  description: |
    Все методы требуют заголовок Authorization: Bearer <API_KEY> для путей, начинающихся с /user/api/.
tags:
  - name: Datacenter & ISP Proxies
    description: Операции для датацентровых и ISP прокси
  - name: Residential Proxies
    description: Операции для резидентских прокси
  - name: General
    description: Общие операции (профиль, заказы, справочники)
servers:
  - url: https://user-api.proxyshard.com

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
  schemas:
    ApiSuccess:
      type: object
      properties:
        success: { type: boolean }
      required: [success]

    ApiError:
      type: object
      properties:
        success: { type: boolean, example: false }
        message: { type: string }
      required: [success, message]

    PurchaseRequest:
      type: object
      properties:
        product: { type: string, example: "Datacenter Proxy" }
        location: { type: string, nullable: true, example: "DE" }
        cycle: { type: string, example: "monthly" }
        quantity: { type: integer, minimum: 1, default: 1, example: 10 }
        promo_code: { type: string, nullable: true, example: "SAVE10" }
        auto_renewal: { type: boolean, default: false }
      required: [product, cycle]
    PurchaseRequestDcIsp:
      type: object
      description: Покупка для Datacenter/ISP — поле location обязательно.
      properties:
        product:
          type: string
          enum: ["Datacenter Proxy", "ISP Proxy"]
          example: "Datacenter Proxy"
        location: { type: string, example: "DE" }
        cycle: { type: string, example: "monthly" }
        quantity: { type: integer, minimum: 1, default: 1, example: 10 }
        promo_code: { type: string, nullable: true }
        auto_renewal: { type: boolean, default: false }
      required: [product, location, cycle]
    PurchaseRequestResidentialStandard:
      type: object
      description: Покупка для Standart Residential Proxy — location не требуется; цикл фиксирован (lifetime).
      properties:
        product:
          type: string
          enum: ["Standart Residential Proxy"]
          example: "Standart Residential Proxy"
        quantity: { type: integer, minimum: 1, default: 1, example: 1 }
        promo_code: { type: string, nullable: true }
      required: [product]
    PurchaseRequestResidentialPremium:
      type: object
      description: Покупка для Premium Residential Proxy — location не требуется; цикл фиксирован (lifetime).
      properties:
        product:
          type: string
          enum: ["Premium Residential Proxy"]
          example: "Premium Residential Proxy"
        quantity: { type: integer, minimum: 1, default: 1, example: 1 }
        promo_code: { type: string, nullable: true }
      required: [product]
    PurchaseRequestResidentialUnmetered:
      type: object
      description: Покупка для Unmetered Residential proxy — поддерживает авто-продление.
      properties:
        product:
          type: string
          enum: ["Unmetered Residential proxy"]
          example: "Unmetered Residential proxy"
        cycle: { type: string, example: "monthly" }
        promo_code: { type: string, nullable: true }
        auto_renewal: { type: boolean, default: false }
      required: [product, cycle]
    PurchaseResponse:
      type: object
      properties:
        success: { type: boolean, example: true }
        message: { type: string, example: "Order paid successfully" }
        order_id: { type: integer }
      required: [success, message, order_id]

    ActiveProxyItem:
      type: object
      properties:
        ip: { type: string }
        username: { type: string }
        password: { type: string }
        http_port: { type: integer }
        socks_port: { type: integer }
        until: { type: string, description: ISO8601/string date }
        status: { type: string }
      required: [ip, username, password, http_port, socks_port, until, status]

    AvailableCountRequest:
      type: object
      properties:
        country: { type: string, example: "DE" }
        product: { type: string, example: "dc" }
      required: [country, product]
    AvailableCountResponse:
      type: object
      properties:
        country: { type: string }
        product: { type: string }
        amount: { type: integer }
      required: [country, product, amount]

    ApiMeResponse:
      type: object
      properties:
        email: { type: string }
        active_orders: { type: integer }
        wallet_balance: { type: integer, description: "в центах" }
      required: [email, active_orders, wallet_balance]

    ProductWithCycles:
      type: object
      properties:
        name: { type: string }
        description: { type: string, nullable: true }
        cycles:
          type: array
          items: { type: string }
      required: [name, cycles]

    AddBandwidthRequest:
      type: object
      properties:
        amount: { type: integer, example: 5, description: "Объём в ГБ" }
        promo_code: { type: string, nullable: true }
      required: [amount]

    GetSubUsersRequest:
      type: object
      properties:
        offset: { type: integer, example: 0 }
        limit: { type: integer, example: 20 }
      required: [offset, limit]

    EditSubUserPayload:
      type: object
      properties:
        proxy_type: { type: string, example: "standart", description: "standart | premium" }
        name: { type: string }
      required: [proxy_type, name]

    CreateSubUserPayload:
      type: object
      properties:
        proxy_type: { type: string, example: "standart" }
        name: { type: string }
        data: { type: integer, example: 5, description: "Объём в ГБ" }
      required: [proxy_type, name, data]

    ChangeSubUserPasswordPayload:
      type: object
      properties:
        proxy_type: { type: string, example: "standart" }
      required: [proxy_type]

    ModifyTrafficPayload:
      type: object
      properties:
        proxy_type: { type: string, example: "standart" }
        data: { type: integer, example: 5, description: "Объём в ГБ" }
      required: [proxy_type, data]

paths:
  /user/api/orders/purchase:
    post:
      tags: ["General"]
      security: [{ bearerAuth: [] }]
      summary: Создать и оплатить заказ
      description: |
        Покупка по имени продукта, локации и циклу.
        Для Datacenter/ISP — location обязателен.
        Для Standart Residential Proxy — location не требуется.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              oneOf:
                - $ref: '#/components/schemas/PurchaseRequestDcIsp'
                - $ref: '#/components/schemas/PurchaseRequestResidentialStandard'
                - $ref: '#/components/schemas/PurchaseRequestResidentialPremium'
                - $ref: '#/components/schemas/PurchaseRequestResidentialUnmetered'
                - $ref: '#/components/schemas/PurchaseRequest'
            examples:
              datacenter:
                value: { product: "Datacenter Proxy", location: "DE", cycle: "monthly", quantity: 10, auto_renewal: true }
              isp:
                value: { product: "ISP Proxy", location: "US", cycle: "monthly", quantity: 5 }
              residentialStandard:
                value: { product: "Standart Residential Proxy", quantity: 1 }
              residentialPremium:
                value: { product: "Premium Residential Proxy", quantity: 1 }
              residentialNew:
                value: { product: "Residential Proxy NEW", cycle: "monthly", quantity: 1 }
              unmeteredResidential:
                value: { product: "Unmetered Residential proxy", cycle: "monthly", auto_renewal: true }
              mobile:
                value: { product: "Mobile Proxy Plan Name", cycle: "day", quantity: 1 }
      responses:
        '200':
          description: Успешно
          content:
            application/json:
              schema: { $ref: '#/components/schemas/PurchaseResponse' }
        '400':
          description: Ошибка валидации/баланса/недостаток стока
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ApiError' }
        '401':
          description: Неавторизован
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ApiError' }

  /user/api/proxies/active:
    get:
      tags: ["Datacenter & ISP Proxies"]
      security: [{ bearerAuth: [] }]
      summary: Получить активные прокси по заказу
      parameters:
        - in: query
          name: order_id
          required: true
          schema: { type: integer }
      responses:
        '200':
          description: Список прокси
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  data:
                    type: array
                    items: { $ref: '#/components/schemas/ActiveProxyItem' }
                  order_tag: { type: string, nullable: true }
                required: [success, data]
        '403':
          description: Метод недоступен для этого заказа
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ApiError' }

  /user/api/proxies/products:
    get:
      tags: ["General"]
      security: [{ bearerAuth: [] }]
      summary: Получить продукты и их циклы
      description: Возвращает продукты для API с названиями доступных циклов.
      responses:
        '200':
          description: Список продуктов
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  products:
                    type: array
                    items: { $ref: '#/components/schemas/ProductWithCycles' }
                required: [success, products]

  /user/api/proxies/available-count:
    get:
      tags: ["General"]
      security: [{ bearerAuth: [] }]
      summary: Узнать доступное количество прокси
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/AvailableCountRequest' }
      responses:
        '200':
          description: Успешно
          content:
            application/json:
              schema: { $ref: '#/components/schemas/AvailableCountResponse' }

  /user/api/me:
    get:
      tags: ["General"]
      security: [{ bearerAuth: [] }]
      summary: Информация о пользователе
      responses:
        '200':
          description: Данные профиля
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ApiMeResponse' }

  /user/api/orders:
    get:
      tags: ["General"]
      security: [{ bearerAuth: [] }]
      summary: Список заказов
      parameters:
        - in: query
          name: status
          required: true
          description: active | on-hold | cancelled | all
          schema: { type: string }
        - in: query
          name: offset
          required: false
          description: Смещение выборки (по умолчанию 0)
          schema: { type: integer, minimum: 0, example: 0 }
        - in: query
          name: limit
          required: false
          description: Размер страницы (1..100, по умолчанию 20)
          schema: { type: integer, minimum: 1, maximum: 100, example: 20 }
      responses:
        '200':
          description: Список заказов
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  orders:
                    type: array
                    items:
                      type: object
                      properties:
                        order_id: { type: integer }
                        product_id: { type: integer }
                        product_name: { type: string }
                        cycle_id: { type: integer }
                        cycle_name: { type: string }
                        expires_at: { type: string, nullable: true, description: "ISO8601 время истечения" }
                        auto_renewal: { type: boolean, nullable: true }
                        tag: { type: string, nullable: true }
                      required: [order_id, product_id, product_name, cycle_id, cycle_name]
                  count: { type: integer, description: "Количество заказов в текущей выборке" }
                  prev: { type: string, nullable: true, description: "URI предыдущей страницы" }
                  next: { type: string, nullable: true, description: "URI следующей страницы" }
                required: [success, orders]

  /user/api/orders/{id}:
    get:
      tags: ["General"]
      security: [{ bearerAuth: [] }]
      summary: Получить информацию о заказе
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: integer }
      responses:
        '200':
          description: Информация о заказе
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  order:
                    type: object
                    properties:
                      order_id: { type: integer }
                      product_name: { type: string }
                      cycle_name: { type: string }
                      quantity: { type: integer }
                      price_paid: { type: integer }
                      status: { type: string }
                      created_at: { type: string, nullable: true }
                      expires_at: { type: string, nullable: true }
                      promo_code: { type: string, nullable: true }
                      auto_renewal: { type: boolean, nullable: true }
                      tag: { type: string, nullable: true }
                required: [success, order]

  /user/api/orders/{id}/renew:
    post:
      tags: ["General"]
      security: [{ bearerAuth: [] }]
      summary: Продлить заказ
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: integer }
      responses:
        '200':
          description: Успешно
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ApiSuccess' }
        '400':
          description: Недостаточный баланс/валидация
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ApiError' }

  /user/api/orders/{id}/add-bandwidth:
    post:
      tags: ["Residential Proxies"]
      security: [{ bearerAuth: [] }]
      summary: Купить дополнительный трафик (Residential)
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: integer }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/AddBandwidthRequest' }
      responses:
        '200':
          description: Успешно
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ApiSuccess' }

  /user/api/orders/{id}/tag:
    post:
      tags: ["General"]
      security: [{ bearerAuth: [] }]
      summary: Изменить тег заказа
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: integer }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                tag: { type: string, example: "none" }
              required: [tag]
      responses:
        '200':
          description: Успешно
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ApiSuccess' }

  /user/api/proxies/sub-users:
    get:
      tags: ["Residential Proxies"]
      security: [{ bearerAuth: [] }]
      summary: Список саб-пользователей
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/GetSubUsersRequest' }
      responses:
        '200':
          description: Успешно
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  data: { type: object }

  /user/api/proxies/sub-user/{user_id}:
    get:
      tags: ["Residential Proxies"]
      security: [{ bearerAuth: [] }]
      summary: Получить саб-пользователя по ID
      parameters:
        - in: path
          name: user_id
          required: true
          schema: { type: string }
      responses:
        '200':
          description: Успешно
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  data: { type: object }
    patch:
      tags: ["Residential Proxies"]
      security: [{ bearerAuth: [] }]
      summary: Редактировать саб-пользователя (имя/пакет)
      parameters:
        - in: path
          name: user_id
          required: true
          schema: { type: string }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/EditSubUserPayload' }
      responses:
        '200':
          description: Успешно
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  data: { type: object }
    delete:
      tags: ["Residential Proxies"]
      security: [{ bearerAuth: [] }]
      summary: Удалить саб-пользователя
      parameters:
        - in: path
          name: user_id
          required: true
          schema: { type: string }
      responses:
        '200':
          description: Успешно
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  data: { type: object }

  /user/api/proxies/sub-user:
    post:
      tags: ["Residential Proxies"]
      security: [{ bearerAuth: [] }]
      summary: Создать саб-пользователя
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/CreateSubUserPayload' }
      responses:
        '200':
          description: Успешно
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  data: { type: object }

  /user/api/proxies/sub-user/{user_id}/data/add:
    post:
      tags: ["Residential Proxies"]
      security: [{ bearerAuth: [] }]
      summary: Добавить трафик саб-пользователю
      parameters:
        - in: path
          name: user_id
          required: true
          schema: { type: string }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ModifyTrafficPayload' }
      responses:
        '200':
          description: Успешно
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  data: { type: object }

  /user/api/proxies/sub-user/{user_id}/data/subtract:
    post:
      tags: ["Residential Proxies"]
      security: [{ bearerAuth: [] }]
      summary: Списать трафик у саб-пользователя
      parameters:
        - in: path
          name: user_id
          required: true
          schema: { type: string }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ModifyTrafficPayload' }
      responses:
        '200':
          description: Успешно
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  data: { type: object }

  /user/api/proxies/sub-user/{user_id}/password:
    post:
      tags: ["Residential Proxies"]
      security: [{ bearerAuth: [] }]
      summary: Сменить пароль саб-пользователя
      parameters:
        - in: path
          name: user_id
          required: true
          schema: { type: string }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ChangeSubUserPasswordPayload' }
      responses:
        '200':
          description: Успешно
          content:
            application/json:
              schema:
                type: object
                properties:
                  success: { type: boolean }
                  data: { type: object }