openapi: 3.1.0
info:
  title: MEGOGO Terminals API (номер телефона)
  version: 1.0.0
  summary: Подключение и отключение подписок MEGOGO для ваших пользователей по номеру телефона.
  description: "Terminals API для партнеров, которые продают подписки MEGOGO своим\nпользователям, например\
    \ мобильных операторов: запросы `/terminals/{partnerId}/`,\nкоторые идентифицируют пользователя по\
    \ номеру телефона.\n\n## Доступ\n\n- Каждый запрос должен содержать параметр `sign`. См. «Подпись».\n\
    - MEGOGO принимает запросы только с IP-адресов (IPv4) ваших серверов.\n  Передайте адреса своему менеджеру\
    \ MEGOGO. Сообщите MEGOGO до смены\n  адресов серверов: запросы с других адресов получают HTTP 401.\n\
    - Кнопка «Try it out» отключена. Отправляйте запросы со своего сервера.\n\n## Подпись\n\n1. Запишите\
    \ приватный ключ, затем `GET`, затем путь запроса\n   (например, `/terminals/example_operator/user/phone`).\n\
    2. Отсортируйте query-параметры по названию (по возрастанию). Допишите\n   каждый параметр как `название=значение`\
    \ без разделителей. Используйте\n   значения до URL-кодирования (`+380…`, а не `%2B380…`). `sign`\
    \ не\n   включайте.\n3. Вычислите хеш SHA-256 от строки (UTF-8) и запишите его как 64\n   шестнадцатеричных\
    \ символа в нижнем регистре.\n4. Закодируйте эти 64 символа в Base64. Удалите `=`. Замените `/` и\
    \ `+`\n   на `-`.\n5. Допишите `_` и публичный ключ.\n\nВсе query-параметры, которые вы отправляете,\
    \ должны быть в подписи.\n\n## Лимиты\n\n1000 запросов в минуту для каждого ID партнера. Сверх лимита\
    \ можно\nполучить HTTP 429 Too Many Requests.\n\n## Тестовая среда\n\nОтдельной тестовой среды нет.\
    \ Ваш менеджер MEGOGO выдает тестовый ID\nпартнера, тестовые ключи и тестовые ID услуг на рабочих\
    \ серверах.\n\n## Ответы и ошибки\n\nРезультат операции возвращается с HTTP 200:\n\n```json\n{\"successful\"\
    : true}\n```\n\n```json\n{\"successful\": false, \"message\": \"Exception while trying to buy object,\
    \ code 2023\"}\n```\n\nЕсли `message` заканчивается словом `code` и числом, ориентируйтесь на\nчисло,\
    \ остальной текст не анализируйте. Ошибки доступа и формата запроса\nвозвращаются с другим HTTP-статусом\
    \ и таким телом:\n\n```json\n{\"message\": \"Not Authorized\", \"code\": 401, \"result\": \"error\"\
    }\n```\n\n| Код в `message` | Значение | Что делать |\n|---|---|---|\n| 2006 | ID услуги — не подписка\
    \ (например, это тариф на отдельное видео) | Используйте ID услуги подписки от менеджера MEGOGO |\n\
    | 2009 | Подписка не продается | Обратитесь к менеджеру MEGOGO |\n| 2010 | Подписка не найдена | Обратитесь\
    \ к менеджеру MEGOGO |\n| 2012 | Неправильные настройки подписки или тарифа | Обратитесь к менеджеру\
    \ MEGOGO |\n| 2013 | Подписка недоступна в регионе | Обратитесь к менеджеру MEGOGO |\n| 2016 | MEGOGO\
    \ не может создать услугу пользователя | Повторите позже. Если ошибка не исчезает, напишите в поддержку\
    \ MEGOGO |\n| 2023 | Услуга уже активна, и продление запрещено, или дата окончания вышла бы дальше\
    \ чем 15 лет от текущего момента | Ничего делать не нужно |\n| 40009 | Подписка не продается через\
    \ партнеров | Обратитесь к менеджеру MEGOGO |\n\nКоды 2023, 2006 и 40009 получены в тестах на рабочих\
    \ серверах. Остальные\nкоды взяты из списка ошибок биллинга MEGOGO.\n\nСообщения без кода:\n\n| `message`\
    \ | Значение |\n|---|---|\n| `Service is not configured for this partner` | ID услуги не настроен\
    \ для вашего ID партнера |\n| `Invalid operation passed` | Неизвестная операция в `subscription/{operation}`\
    \ |\n| `Invalid phone number format` | В номере телефона есть не только цифры (`user/phone`) |\n|\
    \ `General error` | В номере телефона есть не только цифры (`subscriptions`, `subscription/{operation}`),\
    \ или другая ошибка |\n| `User doesn't not exist` | Номер телефона не зарегистрирован в MEGOGO (`user/phone`).\
    \ Текст приведен так, как его возвращает API |\n\n## Язык ответов\n\nЯзык названий и описаний услуг\
    \ MEGOGO задает для каждого ID партнера. По\nумолчанию это язык региона партнера. Чтобы изменить его,\
    \ обратитесь к\nменеджеру MEGOGO. Сообщения об ошибках всегда на английском.\n"
  contact:
    name: MEGOGO partner support
    email: supportpartners@megogo.net
servers:
- url: https://billing.megogo.net
  description: 'Рабочая среда (production)

    '
tags:
- name: Users
- name: Subscriptions
paths:
  /terminals/{partnerId}/user/phone:
    get:
      tags:
      - Users
      operationId: checkUserByPhone
      summary: Проверить, зарегистрирован ли номер телефона в MEGOGO
      description: 'Сообщает, есть ли в MEGOGO пользователь с этим номером телефона. Запрос не

        создает пользователя. Отправляйте его перед `subscriptions` для номера,

        который может быть новым для MEGOGO.

        '
      parameters:
      - $ref: '#/components/parameters/partnerId'
      - $ref: '#/components/parameters/phone'
      - $ref: '#/components/parameters/sign'
      responses:
        '200':
          description: '`"successful": true` — пользователь существует. `"successful": false` —

            пользователя нет или формат номера неправильный (см. `message`).

            '
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Result'
              examples:
                exists:
                  summary: The user exists
                  value:
                    successful: true
                notFound:
                  summary: The phone number is not registered
                  value:
                    successful: false
                    message: User doesn't not exist
                badFormat:
                  summary: The phone number has characters that are not digits
                  value:
                    successful: false
                    message: Invalid phone number format
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /terminals/{partnerId}/subscriptions:
    get:
      tags:
      - Subscriptions
      operationId: listSubscriptions
      summary: Получить ваши услуги или активные услуги пользователя
      description: "- Без `phone`: услуги, которые MEGOGO настроил для вашего ID партнера и\n  показывает\
        \ вам. По этому списку проверяйте ID услуг, полученные от\n  менеджера MEGOGO. Список может быть\
        \ короче вашего перечня ID услуг:\n  если ID услуги в нем нет, обратитесь к менеджеру MEGOGO.\n\
        \  `dateFinish` равен `-1`.\n- С `phone`: активные услуги пользователя, которые вы можете продавать.\n\
        \  Приостановленной услуги в списке нет.\n\nВНИМАНИЕ: с `phone` запрос создает пользователя MEGOGO,\
        \ если номер не\nзарегистрирован. Сначала отправьте `user/phone`.\n\nДва ID услуг одной подписки\
        \ MEGOGO дают одну запись: периоды\nскладываются, а запись имеет ID услуги последнего запроса.\
        \ Используйте\nэтот ID услуги в `unsubscribe`.\n"
      parameters:
      - $ref: '#/components/parameters/partnerId'
      - name: phone
        in: query
        required: false
        description: 'Номер телефона пользователя. См. параметр `phone` запроса `user/phone`.

          '
        schema:
          type: string
          pattern: ^\+?[0-9]+$
          examples:
          - '380000000001'
      - $ref: '#/components/parameters/sign'
      responses:
        '200':
          description: 'Список услуг. Пустой список (`[]`), если у пользователя нет активных услуг.

            '
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Service'
              examples:
                userServices:
                  summary: Active service of a user
                  value:
                  - serviceId: example_light_30d
                    serviceName: Light (partner)
                    serviceDescription: TV channels and a movie collection
                    tariff:
                      tariffId: 12345
                      tariffName: 30 days
                      price: 0
                      tryAndBuy: false
                      dateFinish: 1796221882
                      dateFinishString: '2026-12-02 14:31:22'
                    dateFinish: 1796221882
                    dateFinishString: '2026-12-02 14:31:22'
                noServices:
                  summary: The user has no active services
                  value: []
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /terminals/{partnerId}/subscription/{operation}:
    get:
      tags:
      - Subscriptions
      operationId: changeSubscription
      summary: Подключить, отключить, приостановить или возобновить
      description: "Изменяет услугу пользователя. Если в MEGOGO нет пользователя с этим\nномером телефона,\
        \ `subscribe` его создает. MEGOGO не отправляет\nпользователю сообщений: сообщите ему сами, как\
        \ войти.\n\n- `subscribe` — подключить услугу: один период от текущего момента.\n  Для активной\
        \ услуги результат зависит от режима продления:\n  продление партнером → еще один период после\
        \ текущей даты окончания;\n  автоматическое продление → код 2023, без изменений.\n- `unsubscribe`\
        \ — отключить услугу сразу. Отключает и приостановленную\n  услугу: приостановленный остаток теряется.\n\
        - `suspend` — прекратить доступ до `resume`. До `resume` услуги нет в\n  `subscriptions`.\n- `resume`\
        \ — возобновить приостановленную услугу. Дата окончания\n  сдвигается на время приостановки.\n\
        \nВНИМАНИЕ: отправляйте `resume` только после собственного `suspend`. Если\nприостановленной услуги\
        \ нет, `resume` работает как `subscribe`: добавляет\nпериод или подключает новую услугу. Не отправляйте\
        \ `subscribe`, пока\nуслуга приостановлена: после этого приостановленный остаток не вернется.\n\
        \n`unsubscribe` и `suspend` возвращают `\"successful\": true`, даже если\nуслуга не активна.\n"
      parameters:
      - $ref: '#/components/parameters/partnerId'
      - name: operation
        in: path
        required: true
        schema:
          type: string
          enum:
          - subscribe
          - unsubscribe
          - suspend
          - resume
      - $ref: '#/components/parameters/phone'
      - $ref: '#/components/parameters/serviceId'
      - $ref: '#/components/parameters/sign'
      responses:
        '200':
          description: 'Результат.

            '
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Result'
              examples:
                ok:
                  summary: Successful
                  value:
                    successful: true
                alreadyActive:
                  summary: 'Code 2023: the service is already active'
                  value:
                    successful: false
                    message: Exception while trying to buy object, code 2023
                unknownService:
                  summary: The service ID is not configured for your partner ID
                  value:
                    successful: false
                    message: Service is not configured for this partner
                unknownOperation:
                  summary: The operation is not known
                  value:
                    successful: false
                    message: Invalid operation passed
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/ServerError'
components:
  parameters:
    partnerId:
      name: partnerId
      in: path
      required: true
      description: 'Ваш ID партнера.

        '
      schema:
        type: string
        examples:
        - example_operator
    phone:
      name: phone
      in: query
      required: true
      description: 'Номер телефона пользователя в международном формате: код страны и

        номер, только цифры, например `380000000001`. В начале допускается `+`: в

        URL кодируйте его как `%2B`. Не используйте пробелы, скобки или локальный

        формат (`0…`). MEGOGO не проверяет длину номера: проверяйте ее на своей

        стороне.

        '
      schema:
        type: string
        pattern: ^\+?[0-9]+$
        examples:
        - '380000000001'
    serviceId:
      name: serviceId
      in: query
      required: true
      description: 'ID услуги. Его создает и выдает вам MEGOGO.

        '
      schema:
        type: string
        examples:
        - example_light_30d
    sign:
      name: sign
      in: query
      required: true
      description: 'Подпись запроса. См. «Подпись» в описании этого API.

        '
      schema:
        type: string
  responses:
    BadRequest:
      description: 'Обязательного параметра нет в запросе, или он пустой.

        '
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            message: Invalid request format
            code: 400
            result: error
    Unauthorized:
      description: 'Подпись неправильная или отсутствует, ID партнера или публичный ключ

        неизвестны, или IP-адреса нет в списке вашего ID партнера. Ответ

        одинаковый для всех этих причин.

        '
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            message: Not Authorized
            code: 401
            result: error
    TooManyRequests:
      description: 'Больше 1000 запросов в минуту для вашего ID партнера.

        '
    ServerError:
      description: 'Временная ошибка. Прежде чем повторно отправить `subscribe` или `resume`,

        получите `subscriptions` для пользователя: эти операции не идемпотентны.

        '
  schemas:
    Result:
      type: object
      required:
      - successful
      properties:
        successful:
          type: boolean
          description: '`true` — операция выполнена. `false` — операция не выполнена, см. `message`.

            '
        message:
          type: string
    Error:
      type: object
      properties:
        message:
          type: string
        code:
          type: integer
          description: 'HTTP-статус.

            '
        result:
          type: string
          const: error
    Service:
      type: object
      properties:
        serviceId:
          type: string
          description: 'Ваш ID услуги.

            '
        serviceName:
          type: string
          description: 'Название подписки MEGOGO на языке ответов вашего ID партнера.

            '
        serviceDescription:
          type: string
          description: 'Описание подписки.

            '
        tariff:
          type: object
          properties:
            tariffId:
              type: integer
              description: 'ID тарифа MEGOGO.

                '
            tariffName:
              type: string
              description: 'Период тарифа, например `30 days`.

                '
            price:
              type: number
              description: 'Цена тарифа MEGOGO. Не используйте ее: цену для своих пользователей

                устанавливаете вы.

                '
            tryAndBuy:
              type: boolean
              description: 'Не используйте.

                '
            dateFinish:
              type: integer
              description: 'Только с `phone`. То же значение, что `dateFinish` услуги.

                '
            dateFinishString:
              type: string
              description: 'Только с `phone`. То же значение, что `dateFinishString` услуги.

                '
        dateFinish:
          type: integer
          description: 'Конец оплаченного периода, Unix-время (секунды). `-1` в списке без `phone`.

            '
        dateFinishString:
          type: string
          description: 'То же время, `YYYY-MM-DD hh:mm:ss`, UTC. `1969-12-31 23:59:59` в списке без `phone`.

            '
