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`).\n2. Відсортуйте\
    \ query-параметри за назвою (за зростанням). Допишіть кожен\n   параметр як `назва=значення` без роздільників.\
    \ Використовуйте значення\n   до URL-кодування (`+380…`, а не `%2B380…`). `sign` не включайте.\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 видає тестовий\nID партнера, тестові ключі й тестові 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 послуги останнього\
        \ запиту. Використовуйте цей\nID послуги в `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`.

            '
