This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Добро пожаловать в SHM

SHM - это открытая, бесплатная универсальная биллинговая система с действиями по событиям.

Принцип работы:

  • Вы создаете услуги, назначаете им цену, период действия и другие свойства
  • Регистрируйте своих клиентов и оказывайте им услуги
  • Биллинговая система SHM будет выполнять назначенные команды для этих услуг на ваших серверах, в зависимости от событий

SHM хорошо подходит для оказания разовых и периодических услуг, таких как:

  • Услуги хостинга
  • Услуги по продаже сервисов, таких как VPN
  • Интернет услуги и услуги связи с безлимитными (пакетными) тарифами
  • Оказание услуг по подписке

SHM не приспособлен для учёта потребленных услуг, таких как трафик, минуты, звонки и т.п..

SHM состоит из:

  • Ядро системы (API)
  • БД: MySQL
  • Личного кабинета администратора
  • Личного кабинета пользователя

1 - Установка

1.1 - Docker

Прежде чем начать, убедитесь, что на вашем сервере запущен демон синхронизации времени (NTP)

Описание

SHM разработан и адаптирован для работы в контейнерах (Docker).

Плюсы:

  • Простота. SHM очень просто запустить и крайне легко обновлять.
  • Безопасность. Контейнеризация позволяет изолировать SHM от вашей системы. Код SHM никак не может сломать/испортить вашу основную систему, так как не имеет доступа к ней (работает в контейнере).
  • Надежность. Вы можете спокойно обновлять вашу систему и библиотеки в ней. Это не затронет работу SHM, так как SHM использует свои собственные библиотеки в контейнере.
  • Совместимость. На вашем сервере дополнительно могут быть установлены любые другие программы, что никак не помешает и не повлияет на работу SHM.
  • Кросплатформенность. SHM работает на любой ОС семейства Linux, где установлен Docker (amd64). Так же можно собать под Mac, включая arm64. Можно запустить Docker и в Windows.
  • Ресурсы. Docker позволяет ограничивать потребляемые ресурсы контейнеров.
  • Переносимость. SHM легко перенести на другой сервер. Достаточно только сделать backup базы данных и импортировать его на новом сервере.
  • Kubernetes. SHM прекрасно работает в k8s, что позволяет использовать его в промышленных масштабах.
  • Современность. В наши дни почти весь серверный софт запускают в контейнерах, по выше описанным причинам.

Минусы:

  • Необходимо установить Docker на ваш сервер.

Установите базовые пакеты:

apt update
apt install curl nginx certbot python3-certbot-nginx

Установите docker:

  curl -sL https://get.docker.com | bash

Настройка и запуск SHM:

Создайте рабочую директорию SHM на сервере и перейдите в нее:

mkdir -p /opt/shm/ && cd /opt/shm

Скачайте файлы docker-compose.yml и .env

curl -sO https://raw.githubusercontent.com/danuk/shm/master/docker-compose.yml
curl -sO https://raw.githubusercontent.com/danuk/shm/master/.env

при желании вы можете изменить пароли для БД в файле .env

Так же Вы можете указать в файле .env временную зону в которой будет работать SHM. В дальнейшем эту зону менять нельзя!

Запустите SHM:

docker compose up -d

SHM будет доступен по адресу:

http://АДРЕС_СЕРВЕРА:8081

Логин: admin

Пароль: admin

Web сервер и доменные имена

Чтобы открыть SHM внешнему миру установите Nginx на ваш сервер, и настройте как написано ниже.

Настройте DNS вашего домена:

admin IN A ПУБЛИЧНЫЙ_IP_ВАШЕГО_СЕРВЕРА
bill  IN A ПУБЛИЧНЫЙ_IP_ВАШЕГО_СЕРВЕРА

Настройка nginx

Создайте файл /etc/nginx/sites-available/admin.conf на сервере:

(Замените $DOMAIN на ваш реальный домен)

server {
  listen 80;
  server_name admin.$DOMAIN;

  location / {
    proxy_pass         http://127.0.0.1:8081;
    proxy_redirect     off;
    proxy_set_header   Host             $host;
    proxy_set_header   X-Real-IP        $remote_addr;
    proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
    proxy_set_header   X-Forwarded-Proto $scheme;
  }
}

Создайте файл /etc/nginx/sites-available/bill.conf на сервере:

(Замените $DOMAIN на ваш реальный домен)

server {
  listen 80;
  server_name bill.$DOMAIN;

  location / {
    proxy_pass         http://127.0.0.1:8082;
    proxy_redirect     off;
    proxy_set_header   Host             $host;
    proxy_set_header   X-Real-IP        $remote_addr;
    proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
    proxy_set_header   X-Forwarded-Proto $scheme;
  }
}

Включите ваши сайты:

ln -s /etc/nginx/sites-available/admin.conf /etc/nginx/sites-enabled/
ln -s /etc/nginx/sites-available/bill.conf /etc/nginx/sites-enabled/
service nginx reload

Добавление SSL сертификата (https)

Создайте сертификаты для ваших доменов:

certbot --nginx -d admin.$DOMAIN
certbot --nginx -d bill.$DOMAIN

Проверьте, что всё работает:

https://admin.$DOMAIN

Логин: admin

Пароль: admin

Настройка SHM

В Административном интерфейсе необходимо прописать актуальные настройки.

  1. Зайдите в интерфейс администратора: https://admin.$DOMAIN
  2. Перейдите в раздел: “Настройки” -> “Конфигурация” и выполните соответствующие настройки, например:
  • В настройке api укажите реальный адрес, например: https://admin.$DOMAIN
  • В настройке cli укажите реальный адрес, например: https://bill.$DOMAIN
  • В настройке mail укажите ваш реальный обратный адрес from

1.2 - Kubernetes

Описание

SHM разработан и адаптирован для работы в Kubernetes.

Настройка и запуск SHM:

  • Добавьте репозиторий:

helm repo add danuk https://danuk.github.io/shm

  • Установите SHM:

helm install my-shm danuk/shm --set 'global.shm.domain=$DOMAIN'

Возможен автоматический выпуск SSL сертификатов для доменов, если в кластере Kubernetes установлен и настроен cert-manager

Основные параметры:

  • global.shm.domain - доменное имя
  • global.shm.version - версия SHM. По-умолчанию установлен latest
  • global.shm.db_user - Пользователь БД
  • global.shm.db_pass - Пароль БД
  • global.shm.db_root_pass - Пароль пользователя root БД
  • global.shm.db_name - Имя БД
  • mysql.primary.persistence.size - Размер диска для БД (по-умолчанию 1Gi)
  • helm-common.ingress.admin.acme - укажите 1 или true для выпуска SSL сертификата для админки
  • helm-common.ingress.client.acme - укажите 1 или true для выпуска SSL сертификата для клиентского кабинета

Полные настройки доступны в файле helm чарта: values.yaml

Настройка SHM

В Административном интерфейсе необходимо прописать актуальные настройки.

  1. Зайдите в интерфейс администратора: https://admin.$DOMAIN (admin:admin)
  2. Перейдите в раздел: “Настройки” -> “Конфигурация” и выполните соответствующие настройки, например:
  • В настройке api укажите реальный адрес, например: https://admin.$DOMAIN
  • В настройке cli укажите реальный адрес, например: https://bill.$DOMAIN
  • В настройке mail укажите ваш реальный обратный адрес from
  • В настройке company укажите реальное название компании в поле name

2 - Настройка системы

2.1 - Биллинг

Биллинг (автоматизированная система расчётов (АСР)) - система, програмный комплекс, для тарификации оказываемых услуг.

Обязательно настройте синхронизацию времени на вашем сервере, иначе могут быть ошибки и не точности в работе биллинга.

Функции биллинга SHM:

  • Оказание платных услуг
  • Вычисление даты окончания услуг, в зависимости от системы расчёта и периода оплаты
  • Возврат средств за преждевременно завершенные услуги
  • Генерация событий для возможности привязки внешних команд

Биллинг SHM всегда округляет деньги до сотого знака (до копеек).

SHM поддерживает несколько систем расчетов.

2.1.1 - Системы расчетов

Система расчета с фиксированным кол-вом дней в месяце

SHM использует по-умолчанию именно эту систему расчетов.

Считаем, что в месяце всегда 30 дней.

Например, услуга стоит 100р. в месяц, тогда легко подсчитать стоимость услуги за день, за час, за минуту и т.п.

Стоимость дня будет вычислена по формуле: 100р./30дней = 3.33 руб/день.

При заказе услуги дата окончания будет вычислена как текущая дата плюс 30 дней.

Дата окончания услуг плавающая из-за разного кол-ва дней в месяцах.

Этот способ расчетов самый простой и понятный для клиентов.

Календарная система расчетов

Самая сложная и самая честная система расчетов стоимости услуг.

Стоимость дня зависит от кол-ва дней в месяце.

Например, стоимость услуги за месяц 100р.:

в Январе 31 день, поэтому, стоимость услуги за день: 100р./31дней = 3.32 руб/день,

в Феврале 28 дней, поэтому, стоимость услуги за день: 100р./28дней = 3.57 руб/день,

Если клиент заказал услугу на месяц 1-ого Января, то дата окончания услуги будет 31-ого Января, тут всё ожидаемо.

Но если клиент заказал услугу на месяц 10 января, то дата окончания услуги будет 9 февраля (а не 10, как ожидалось). Это связано с тем, что стоимость услуги в январе меньше, чем в феврале (из-за разного кол-ва дней в месяцах). Однако, особо внимательным клиентам кажется, что у них украли день. Но бывают и обратные случаи, когда мы “дарим” дни: например, если клиент закажет услугу 27 февраля, то дата окончания будет 29 марта. Клиентам приходится объяснять, что “крадут/дарят” дни не мы, а календарь.

Дата окончания услуг плавающая из-за разного кол-ва дней в месяцах.

Расчет по последнему дню месяца

Данная система расчетов очень удобна для оказания услуг юридическим лицам, так как бухгалтерия выставляет закрывающие документы в конце каждого месяца.

При заказе услуги 10 января, первый платеж будет за период с 10 по 31 января. Следующий платеж будет с 1 февраля по 28 февраля, затем, с 1 марта по 31 марта и так далее.

Дата окончания услуги всегда последний день месяца.

2.1.2 - Платежи и бонусы

  • Платежи - строки в БД (pays_history) с указанием суммы, даты и типа платежа.
  • Бонусы - строки в БД (bonus_history) с указанием кол-ва бонусов, даты начисления/списания и поле комментария
  • Списания - строки в БД (withdraw_history) с указанием сколько, за что и кода было списано (оказано)
  • Баланс - разница платежей и списаний
  • Бонусный баланс - сумма бонусов в таблице бонусов

Списания

Каждый раз, при заказе или продлении услуги, биллинг вычисляет итоговую сумму списания (итоговая стоимость услуги).

Сумма списания вычисляется как произведение стоимости услуги (cost), помноженное на кол-во (qnt) за вычетом персональной скидки клиента и скидки на саму услугу.

SHM проверяет бонусный баланс, и если его достаточно, то услуга будет оказана за счет бонусов. При этом, стоимость (total) услуги будет 0, т.к. стоимость это деньги, а не бонусы. Если же бонусов не достаточно для полной оплаты услуги, то конечная стоимость услуги будет вычислена за вычетом кол-ва бонусов.

2.2 - Услуги

Услуги

Для того, чтобы оказать/продать услугу, её необходимо создать и настроить.

Создавать и управлять услугами удобно через интерфейс Администратора, либо через API. Полное описание полей и настроек услуг вы можете посмотреть на странице API.

Описание полей услуг:

Поле Тип Обязательное default Описание
name Integer Да - Название услуги. Например: "Тариф простой"
category String Да - Служит для группировки услуг. События для услуг создаются по этому полю
cost Double Да - Стоимость услуги за период
period_cost Integer Нет 1 Кол-во месяцев. Например, для продажи доменных имён обычно используется период в 12 месяцев
next Integer Нет - Идентификатор следующей услуги. `-1` - удалить услугу после истечения
children JSON Нет - Дочерние услуги. Массив из идентификаторов дочерних услуг и их кол-ва
allow_to_order Integer Нет - Установите 1, для разрешения услуги к заказу клиентом
pay_in_credit Integer Нет - Установите 1, для разрешения кредита для услуги
pay_always Integer Нет - Установите 1, для установки флага: "Всегда платная услуга"
config JSON Нет - Произвольные данные в формате JSON

2.2.1 - Настройка

Название услуги

Любое произвольное, понятное название. Название услуги будут видеть ваши клиенты.

Категория услуги

Категория услуги это любое слово.

Категория услуг может использоваться для следующих задач:

  • связывание услуг и событий. Укажите эту же категорию в событии для связывания
  • групировка и выборка услуг, например в API, в шаблонах, и в Telegram bot-е
  • Отображение определенных Web форм в кабинете клиента

Предопределенные категории:

  • vpn-* - Для этого имени категории в Web интерфейсе пользователя будет отображаться специальная форма, где пользователь может скачать ключ VPN. Вы можете указать категорию услуги, например такую: vpn-russia, или vpn-home

Период услуги

Период, на который услуга будет зарегистрирована. По истечению этого периода, услуга будет продлена автоматически на этот же период, если не указано иного.

Период задается в формате M.DDHH, где:

  • M - месяцы
  • DD - дни
  • HH - часы

Примеры:

  • 0.01 - период 1 день
  • 0.0001 - период 1 час
  • 0.10 (0.1) - период 10 дней
  • 0.1001 - 10 дней и 1 час
  • 0.1110 (0.111) - 11 дней и 10 часов
  • 1.10 (1.1) - период 1 месяц и 10 дней
  • 1.1012 - период 1 месяц, 10 дней и 12 часов
  • 12 - период 1 год (12 месяцев)

Стоимость

Стоимость услуги за указанный период. Эта сумма списывается при заказе и продлении услуги.

При досрочном удалении услуги будет вычислена итоговая сумма исходя из периода и стоимости.

Например, стоимость услуги 300, период 1 месяц (30 дней). Если услугу удалить через 10 дней, то итоговая стоимость услуги составит: 300/30*10=100, т.е. на балнс вернется: 300-100=200

Следующая услуга

  • Можно указать: “Не изменять”. Режим по-умолчанию. Услуга будет автоматически продлеваться без изменения имени, стоимости и периода.
  • Можно указать: “Не продлевать”. В этом случае, услуга будет автоматически удалена по окончанию периода.
  • Можно указать следующую услугу. Это удобный механизм для организации тестовых периодов. Например, можно сделать услугу вида: “Тест на 10 дней” за 0 рублей, а следующей указать уже услугу с периодом 30 дней, за 300 рублей. Таким образом, когда первая услуга истечет (пройдет 10 дней), биллинг автоматически переключится на следующую услугу с указанной стоимостью. Так же удобно создавать услуги вида: “Регистрация домена”, а следующей услугой указать: “Продление домена”. Срок одинаковый, но стоимость разная. В случае, если денег на балансе будет недостаточно, услуга заблокируется.

Следующую услугу можно указать как в “Каталоге услуг” (services), так и в “Услугах пользователя” (user_services). Если услуга у пользователя уже создана, то необходимо производить эту настройку именно в услуге пользователя.

Дочерние услуги

Дочерние услуги - это такие услуги, которые будут созданы при создании основной услуги (под-услуги).

Более подробно про тип услуг можно почитать здесь.

Прайс-лист

Установите галочку “Доступно к заказу”, чтобы услуга появилась в списке доступных для регистрации пользователем.

Если установить галочку “Можно заказать только один раз”, то в случае, когда клиент уже заказывал эту услугу ранее, она будет исключена из списка доступных для регистрации услуг.

2.2.2 - Виды услуг

Дочерние услуги

Бывают случаи, когда для одной платной услуги, нужно создать несколько услуг, на различных серверах.

Например:

  • Услуга “Виртуальный хостинг”, подразумевает создание таких услуг как: “Web хостинг”, “Хостинг БД” и “Хостинг почты”. Это три совершенно разные услуги, которые оказываются как дочерние к основной.

  • Услуга “DNS хостинг”, подразумевает под собой первычный и вторичный DNS-ы. Это тоже удобно оформить ввиде дочерних услуг.

Если для дочерней услуги НЕ установлен флаг “Всегда платная услуга” (pay_always), то такая услуга не будет тарифицироваться

Составные услуги

Составные услуги это такой режим работы биллинга, когда стоимость родительской услуги суммируется со стоимостью дочерних. Таким образом, услуга будет оплачена только в том случае, если хватает средств как на основную услугу, так и на все её дочерние. Изменяя параметр кол-во дочерних услуг, изменяется общая стоимость услуги.

Этот режим удобно использовать для продажи таких услуг, как VPS, где в качестве дочерних создаются такие услуги как CPU, RAM, HDD и т.п. Например, установив стоимость для 1 CPU, и указав кол-во этих услуг, будет автоматически вычислена итоговая стоимость.

Одноразовые услуги

Если услугу пометить как “одноразовая”, то такую услугу клиент сможет заказать только единожды. Этот режим хорошо подходит для создания “пробных” периодов.

Например: для того, чтобы дать клиенту тестовый, бесплатный (или акционный) период, можно создать одноразовую услугу с акционной стоимостью, а в качестве следующей услуги установить уже обычную услугу, с обычной ценой. Когда тестовая услуга закончится, биллинг автоматически переключится на базовую услугу (при наличии средств на счёте).

Пользовательские услуги

Пользовательская услуга - это такая услуга, которая уже оказана пользователю.

Пользовательская услуга характеризуется статусом и датой истечения, так же стоимость пользовательской услуги может отличаться от услуги, т.к. могут быть применены различные скидки для конкретного пользователя и его услуги.

2.2.3 - События

События генерируются SHM, для возможности привязки команд.

Событие происходит между переходом услуги пользователя из одного статуса в другой.

Список cобытий

Event Статус ДО Статус ПОСЛЕ Описание
create INIT ACTIVE Услуга создана и оплачена (впервые)
not_enough_money INIT NOT PAID Не хватает денег для создания услуги
prolongate ACTIVE ACTIVE Услуга продлена (хватило денег для продления активной услуги)
block ACTIVE BLOCK Услуга заблокирована (нехватка денег для продления, либо вручную)
activate BLOCK ACTIVE Услуга активирована (возобновлена после блокировки)
remove BLOCK REMOVED Услуга удалена
changed * * Событие вызывается каждый раз, когда меняется статус услуги

Если для события настроена команда, то пользовательской услуге будет присвоен промежуточный статус: PROGRESS, а после выполнения команды пользовательской услуге будет присвоен результирующий статус.

Схема создания новой услуги:

flowchart LR
    classDef white fill:white
    classDef green fill:green,color:#fff
    classDef red fill:red,color:#fff
    classDef gray fill:gray,color:#fff
    classDef yellow fill:yellow

    A[[INIT\nсоздание услуги]]:::white --> B{Оплата}
    B -- Оплата не прошла --> E[[NOT PAID]]:::yellow --> CH{{CHANGED}}
    B -- Оплачена --> C{{CREATE}}
    C -->|События нет| TS
    C -->|Событие есть|TC[[PROGRESS\nвыполняем событие]]:::gray
    TC -- Успех --> TS[[ACTIVE]]:::green --> CH
    TC -- Ошибка --> TSS[[STUCK]]:::red

Схема продления услуги:

flowchart LR
    classDef green fill:green,color:#fff
    classDef red fill:red,color:#fff
    A[[ACTIVE]]:::green -->|продление| B{Оплата}
    B -- Оплата не прошла --> E[[BLOCK]]:::red --> CH{{CHANGED}}
    B -- Оплачена --> C[[ACTIVE]]:::green --> D{{PROLONGATE}}

событие PROLONGATE не переводит услугу в статус PROGRESS, и событие CHANGED не вызывается

Схема разблокировки услуги:

flowchart LR
    classDef green fill:green,color:#fff
    classDef red fill:red,color:#fff
    classDef gray fill:gray,color:#fff

    A[[BLOCK]]:::red --> B{Оплата}
    B -- Оплата не прошла --> E[[BLOCK]]:::red
    B -- Оплачена --> C{{ACTIVATE}}
    C -->|События нет| TS
    C -->|Событие есть|TC[[PROGRESS\nвыполняем событие]]:::gray
    TC -- Успех --> TS[[ACTIVE]]:::green --> CH{{CHANGED}}
    TC -- Ошибка --> TSS[[STUCK]]:::red

Настройка событий

Для события необходимо указать “Категорию” услуги и “Группу серверов”, для выполнения команд.

Категория события должна соответствовать категории услуги, для которой создается это событие и может быть указана с маской, например: vpn-*, где * заменяет любые символы.

В зависимости от выбранной группы серверов, могут быть использованы дополнительные настройки, зависящие от Транспорта группы.

Например, если выбрана группа серверов, транспорт которой определен как “SSH”, то будет предложено выбрать шаблон в качестве скрипта, который должен быть выполнен на конечном сервере. Используйте Шаблоны для написания команд.

А если выбрана группа серверов, транспорт которой определен как “Mail”, то можно настроить поле “Шаблон” (template_id), по которому будет сформировано письмо, а так-же другие поля, такие как “Тема письма” (subject) и прочие.

Настройки серверов можно переопределять настройками событий.

2.3 - Серверы

SHM использует Ваши серверы для управления услугами. Способ взаимодействия с Вашими серверами мы называем “Транспорт”.

Для удобства, все серверы добавляются в “Группы Серверов”. Каждой группе серверов назначается транспорт.

Все доступные настройки для серверов можно увидеть в интерфейсе API.

Группы серверов

Группы серверов используются для объединения однотипных серверов в группу.

При первом создании услуги, сервер выбирается из группы серверов по определенным правилам, и сохраняется как server_id в раздел settings как настройки для услуги пользователя (us.settings.server_id).

Все последущие команды для этой услуги будут выполняться на выбранном сервере.

Все доступные настройки для групп серверов можно увидеть в API.

2.3.1 - Транспорт

Транспорт - способ (протокол) взаимодействия с Вашими серверами.

2.3.1.1 - HTTP

HTTP (HTTPS) - самый распространенный протокол в сети Интернет.

В этом разделе описывается способ настройки транспорта HTTP.

Настройки

Хост (host)
адрес сервера, например: https://domain.com

Можно использовать шаблоны, например: https://api.telegram.org/bot{{ config.telegram.token }}/sendMessage

Метод (method)
Метод HTTP: GET, POST, PUT, DELETE. По-умолчанию используется POST.
Шаблон (template_id)
Шаблон для формирования PAYLOAD DATA (context). Используется для POST и PUT методов. Для остальных можно указать любой.
content_type
По-умолчанию установлен в значение: application/json; charset=utf-8
headers
Заголовки, например: {"Authorization":"Basic YWRtaW46YWRtaW4","Cache-Control":"no-cache"}
timeout
Таймаут HTTP сервера в секундах. По-умолчанию 10 сек
verify_hostname
Установите этот параметр в значение 0, если не требуется проверка SSL сертификата на валидность (самоподписный сертификат)

Примеры

Отправка сообщения в Telegram (sendMessage)

https://core.telegram.org/bots/api#sendmessage

  1. Настройте Telegram уведомления по инструкции
  2. Создайте шаблон для формирования уведомления в Telegram через HTTP:
{{
  toJson({
    chat_id = user.settings.telegram.chat_id
    text = "test message"
  })
}}
  1. Создайте группу серверов с транспортом HTTP
  2. Создайте сервер в этой группе, укажите Хост: https://api.telegram.org/bot{{ config.telegram.token }}/sendMessage и укажите созданый шаблон на 2 шаге.
  3. Привяжите необходимое событие к этой группе серверов (созданную на шаге 3)

Теперь, когда событие наступит, SHM выполнит вот такую команду:

curl https://api.telegram.org/bot2836119681:AAEyvDasDFC-Y98xmYOhLni8p2bhshjkhio/sendMessage \
    -X POST \
    -H 'Content-Type: application/json; charset=utf-8' \
    -d '{"chat_id":1234567890,"text":"test message"}'

Формирование QueryString для GET запросов

Используйте ф-ию toQueryString() для формирования аргументов для GET запросов.

Пример:

{{
  toQueryString(
    A = 1
    text = "test message"
    qaz = "Привет Мир "
  )
}} 

Вернет строку вида: text=test%20message&A=1&qaz=%D0%9F%D1%80%D0%B8%D0%B2%D0%B5%D1%82%20%D0%9C%D0%B8%D1%80%20, которая будет автоматически добавлена к URI.

2.3.1.2 - SSH

SSH (Secure Shell) - выполнение команд на Ваших серверах по средствам протокола SSH.

В этом разделе описывается способ настройки транспорта SSH.

Настройки

Хост (host)
адрес сервера SSH, например: root@1.2.3.4
port
порт SSH сервера. По-умолчанию 22
timeout
Таймаут SSH сервера в секундах. По-умолчанию 10 сек
Ключ (key_id)
SSH ключ для доступа на сервер
Команда (cmd)
Произвольная shell команда. Используется как команда по-умолчанию и для тестирования работы SSH. Реальную команду удобно прописывать в События услуги
proxy_jump
Если ваш сервер находится внутри другого сервера (виртуальный сервер: OpenVZ, LXC, KVM…), то эту настройку можно использовать для указания реального, внешнего сервера. Сначала SHM подключиться к этому серверу, после чего, подключиться к Хост (host).

2.3.1.3 - Mail

Транспорт “Mail” служит для отправки писем.

Письма формируются с помощью Шаблонов, а шаблоны привязываются к Событиям услуг

Настройки

Хост (host)
адрес сервера, например: smtp.mailgun.org:587
user
Имя пользователя для авторизации на почтовом сервере (не обязательное)
password
Пароль для авторизации на почтовом сервере (не обязательное)
from
Адрес отправителя письма. По-умолчанию берется из config->mail->from
from_name
Поле “От-кого”. По-умолчанию берется из config->mail->from_name или “SHM”
subject
Тема письма. По-умолчанию берется из config->mail->subject или “SHM”
to
email адресата. Пытается получить это поле автоматически из логина клиента или его профиля
bcc
email адрес скрытой копии. Можно использовать в отладочных целях, для контроля отправляемых писем (не обязательное)
template_id
Идентификатор шаблона, по которому формируется тело письма
message
Простое текстовое сообщение для отправки. Используется только, если template_id не задан

2.3.1.4 - Telegram

Telegram - популярный мессенджер (https://telegram.org/)

В этом разделе описывается способ настройки транспорта Telegram.

Транспорт Telegram умеет как просто отправлять уведомления, так и работать в качестве полноценного бота: регистрировать клиентов, услуги, пополнять баланс и т.п.

Telegram уведомления

flowchart LR
    A([SHM]) --> B(Событие) -->С(Шаблон) --> D(Telegram API) --> E(Telegram client)

Для того, чтобы SHM мог отправлять сообщения Вашим пользователям, необходимо:

  1. Создать Telegram Bot-а, с помощью бота @BotFather (https://telegram.me/BotFather)
  2. В админке, в “Настройки” -> “Конфигурация”, необходимо сохранить Telegram Token, полученный на предыдущем шаге
  3. Создайте шаблон сообщения в админке, которое вы хотите отправлять своим пользователям (“Настройки” -> “Шаблоны”)
  4. Создайте нужное событие. Привяжите Ваш шаблон к нужному событию. В качестве группы серверов необходимо указать “Telegram уведомления”, или любую другую группу, транспорт которой “telegram”
  5. Дайте Вашему пользователю ссылку на вашего бота, чтобы он мог его себе добавить. После добавления бота Ваш клиент сможет получать от него уведомления

В случаях, когда Ваш клиент регистрировался в SHM НЕ через Telegram bot-а, необходимо указать его логин telegram в его профиле (кабинете)

Telegram bot

flowchart LR
    A(Telegram client) <--> B(Telegram API) <--> С([SHM\ntelegram_bot])

Для работы полноценного бота нужно:

  • Выполнить шаги 1 и 2 из предыдущего раздела (Telegram уведомления), если еще не выполнены.
  • Настроить Telegram API, сообщить ему адрес, куда отправлять запросы от клиента (от бота). Для этого скачайте bash скрипт. Перед запуском скрипта необходимо его отредактировать, записать в него свой token и HTTP адрес SHM. Выполните скрипт.
  • Проверьте наличие шаблона telegram_bot. Внесите в него изменения по своему усмотрению.
  • Запустите бота (/start). Если всё настроено верно, вы увидите приветствие.

Telegram bot - использует шаблон telegram_bot. В шаблоне заложена вся логика бота.

Более подробно о шаблоне Telegram читайте здесь: Шаблон Telegram bot

2.3.1.5 - LOCAL

LOCAL - локальный транспорт: код выполняется на сервере SHM.

Этот транспорт удобно использовать когда требуется выполнить шаблон средствами самого SHM. Примеры можно найти здесь.

2.4 - Шаблоны

Шаблоны позволяют генерировать как однострочные команды, так и целые блоки текста. Эти механизмы являются основой построения взаимодействий SHM.

Шаблоны рендерятся локально (сервером SHM), результат рендера может быть отправлен/выполнен с помощью Транспорта.

Шаблоны служат:

Введение

Синтаксис: {{ ОБЪЕКТ.ДАННЫЕ }}

Например, с помощью объекта user можно получить доступ к данным пользователя:

  • user.id - идентификатор пользователя
  • user.login - логин пользователя
  • user.balance - баланс пользователя

Используя эти функции мы можем написать такой шаблон:

Уважаемый клиент.
Ваш логин: {{ user.login }}
Ваш баланс: {{ user.balance }} руб.

В шаблонах поддерживаются условия и циклы, примеры использования вы можете увидеть здесь: Прогноз оплаты

Больше информации о шаблонизаторе Вы можете узнать здесь

Объекты и функции

Ниже приведен список методов для работы с SHM через шаблоны.

Достуность тех или иных методов зависит от контекста применения шаблона.

Подробнее о доступности методов описано здесь.

Пользователь

Метод Описание
user.id() Идентификатор пользователя (получить/установить)
user.switch( USER_ID ) Переключение пользователя на указанного (смена контекста)
user.login Логин пользователя
user.balance Баланс пользователя
user.credit Кредитный лимит пользователя
user.dogovor Договор пользователя
user.full_name ФИО пользователя
user.settings Получить settings пользователя
user.get_bonus Получить кол-во бонусов
user.income_percent Получить процент партнерских бонусов
user.add_bonus( КОЛ-ВО, КОММЕНТ ) Начисление бонусов
user.set_settings({ 'foo' => 1 }) Сохранить в settings пользователя произвольные данные
user.services Ссылка на услуги пользователя
user.gen_session.id Специальная функция для генерации идентификатора сессии
user.set_new_passwd Смена пароля пользователя. Вернет новый пароль
user.pays. Ссылка на платежи пользователя
user.delete Удаление пользователя (с нулевым балансом, без услуг)
user.list_for_api( 'admin', 1) Получить всех пользователей

Услуги пользователя

Метод Описание
us.id() Идентификатор пользовательской услуги (получить/установить)
us.name Имя пользовательской услуги
us.created Дата создания пользовательской услуги
us.expire Дата истечения пользовательской услуги
us.status Статус пользовательской услуги
us.settings Получить параметры пользовательской услуги
us.set_settings({ 'foo' => 1 }) Сохранить в settings услуги пользователя произвольные данные
us.set(FIELD, VALUE) Установка поля FIELD в значение VALUE. Пример: us.set('next', 123)
us.child_by_category(CATEGORY). Ссылка на дочернюю услугу определенной категории
us.finish( 'money_back', 1) Завершение услуги с возвратом средств (биллинг продлит или заблокирует услугу в зависимости от наличия средств)
us.block Принудительная блокировка услуги пользователя
us.activate Активация услуги пользователя после блокировки
us.delete Удаление заблокированной услуги пользователя
us.gen_store_pass Специальная функция для генерации и сохранения пароля в settings
us.parent. Ссылка на родительскую услугу пользователя
us.top_parent. Ссылка на самую верхнюю услугу пользователя
us.service. Ссылка на каталог услуг
us.withdraw. Ссылка на списание услуги
us.list_for_api() Получение списка услуг пользователя

Каталог услуг

Метод Описание
service.id() Идентификатор пользовательской услуги (получить/установить)
service.name Название услуги
service.cost Базовая стоимость услуги
service.period Период услуги
service.category Категория услуги
service.server Ссылка на сервер услуги
service.id( N ).name Получение имени услуги c идентификатором N
service.id( N ). Получение произвольного поля услуги c идентификатором N
service.api_price_list() Возвращает массив услуг из каталога
service.settings Получить settings услуги
service.set_settings({ 'foo' => 1 }) Сохранить в settings услуги произвольные данные
service.withdraw. Ссылка на объект списания
service.create_for_api( 'service_id', N, 'check_allow_to_order',1 ) Регистрирует услугу клиенту с идентификатором N
service.list_for_api() Получение списка услуг из каталога

Платежи

Метод Описание
pay.id() Получить/установить id платежа
pay.date Дата и время платежа
pay.money Cумма платежа
pay.pay_system_id Имя платежной системы
pay.comment Данные платежа
pay.last Получить ссылку на последний платеж
pay.forecast Возвращает JSON прогноза оплат услуг
pay.paysystems Получить список платежных систем
pay.list_for_api() Получение списка платежей

Бонусы

Метод Описание
bonus.list_for_api() Получение списка бонусов

Списания

Метод Описание
wd.id() Получить/установить id списания
wd.create_date Дата создания списания
wd.withdraw_date Дата списания списания
wd.cost Сумма
wd.discount Скидка
wd.bonus Кол-во бонусов
wd.months Период услуги
wd.total Итоговая стоимость
wd.service_id идентификатор каталога услуг
wd.user_service_id идентификатор услуги пользователя
wd.qnt Кол-во единиц товара
wd.list_for_api() Получение списка списаний

Сервера

Метод Описание
server.id() Получить/установить id сервера
server.name Имя сервера
server.host Host сервера
server.transport Транспорт сервера
server.settings Получение settings текущего сервера
server.set_settings({ 'foo' => 1 }) Сохранить в settings сервера произвольные данные
server.group. Ссылка на группу сервера
server.servers_by_group_id( N ) Получение списка серверов из группы N
server.list_for_api() Получение списка серверов

Группы серверов

Метод Описание
sg.id() Получить/установить id группы серверов
sg.name Имя группы серверов
sg.type Способ выбора серверов (random,by-one,evenly)
sg.transport Транспорт группы (local,ssh,http…)
sg.settings settings группы серверов
sg.set_settings({ 'foo' => 1 }) Сохранить в settings группы серверов произвольные данные
sg.list_for_api() Получение списка групп серверов

Шаблоны

Метод Описание
tpl.id Получить/установить id шаблона
tpl.id( NAME ).parse( 'usi', 123 ) Выполнить шаблон с именем NAME для пользовательской услуги с идентификаторм 123
tpl.data Данные шаблона
tpl.settings Получить settings шаблона

Хранилище

Метод Описание
storage.save( NAME, DATA ) Сохранить данные DATA в хранилище с ключом NAME
storage.load( NAME ) Получить данные из хранилища с ключом NAME
storage.del( NAME ) Удалить данные из хранилища с ключом NAME
storage.list_for_api() Получение списка данных

Конфигурация

Метод Описание
config.NAME Получить данные NAME из конфигурации

Telegram

Метод Описание
telegram.bot(TEMPLATE, CMD, [ARGS]) Выполнить CMD с аргументами ARGS из шаблона TEMPLATE

Задачи

Метод Описание
task Работа с задачами

Вспомогательные ф-ии

Метод Описание
params Аргументы вызова шаблона (http query string)
event_name Переменная содержит название текущего события
toJson() Функция преобразования объекта в JSON
toQueryString() Функция преобразования объектов в Query string
ref() Функция для преобразования данных в массив
list_for_api() Функция для получения списков данных из объекта

Примеры

2.4.1 - Использование методов

Ряд методов, описанных в разделе Шаблоны не будут работать в ряде случаев.

Работа с идентификаторами

Практически все методы поддерживают работу с идентификаторами (id).

Например, user.id вернет идентификатор текущего пользователя, а us.id вернет идентификатор текущей пользовательской услуги.

Если метод не возвращает свой id, то значит он небыл инициализирован, и следует “добираться” до метода либо через услугу пользователя, либо через явное указание идентификатора: id( N ). Например, в ряде случаев, server.id не вернет свой идентификатор, однако: us.service.server.id - вернет.

Помимо получения идентификатора, его можно и установить, пример: user.id(123) - вернет объект для пользователя с идентификатором 123.

  • user.login - вернет login текущего пользователя
  • user.id(123).login - вернет login пользователя 123
  • us.name - вернет название текущей услуги пользователя (если определена)
  • us.id(99).name - вернет название услуги пользователя с идентификатором 99

Контекст применения шаблонов

Метод Контекст
us.* События
us.id( N ).* Везде
us.withdraw Для платных услуг
us.service.server Для услуг, у которых определен сервер (server_id в settings услуги пользователя)
server.* События, Задачи, INIT сервера
server.id( N ) Везде
task События, Задачи

2.4.2 - Платежные системы

Описание

SHM имеет встроенный модуль paysystems, позволяющий получать список доступных платежных систем.

Получить список платежных систем можно:

  • Через Web: /shm/v1/user/pay/paysystems
  • Из шаблонов: pay.paysystems( АРГУМЕНТЫ )

Аргументы

Параметр Описание
user_id Идентификатор пользователя
amount Сумма платежа. По-умолчанию в неё записывается значение total из “Прогноз оплаты”
pp Proposed Payment (Предлагаемый платеж). Установите этот параметр в 1, если хотите, чтобы сумма платежа (amount) была установлена в ссылку shm_url
paysystem Укажите платежную систему, если хотите получить ссылку только для одной конкретной платежной системы

Описание полей

Пример ответа:

{
    "TZ": "Europe/Moscow",
        "data": [
        {
            "amount": 123,
            "forecast": 0,
            "name": "yookassa",
            "paysystem": "yookassa",
            "shm_url": "/shm/pay_systems/yookassa.cgi?action=create&user_id=1&ts=1706539331&amount=",
            "user_id": 1,
            "weight": 10
        },
        {
            "amount": 123,
            "forecast": 0,
            "name": "ЮMoney",
            "paysystem": "yoomoney",
            "shm_url": "/shm/pay_systems/yoomoney.cgi?action=create&user_id=1&ts=1706539331&amount=",
            "user_id": 1,
            "weight": 0
        }
        ],
        "date": "Mon Jan 29 17:42:11 2024",
        "items": 0,
        "limit": 25,
        "offset": 0,
        "version": "0.10.0"
}

Где:

  • paysystem - Платежная система
  • name - Произвольное название платежной системы (для отображения)
  • amount - Сумма платежа. По-умолчанию заполняется из “Прогноз оплаты”, если он положительный, или из переданного аргумента amount. В остальных случаях - пустой.
  • forecast - “Прогноз оплаты”
  • weight - “вес”. Используется для сортировки платежных систем
  • shm_url - Ссылка для выставления счета в платежной системе. Если флаг pp не установлен, то amount в ссылке остается пустой. Это сделано для удобства использования ссылки.

2.4.3 - Функции

toJson()

Преобразует объект в JSON.

Данную ф-ию удобно использовать для отладки запросов, когда не очевидно, что вернет тот или иной метод, для просмотра полей и т.п.

Пример 1:

Смотрим, что вернет метод user.list_for_api:

{{ toJson( user.list_for_api ) }}

Результат:

{
  "balance": 123.45,
  "block": 0,
  "bonus": 0,
  "can_overdraft": 0,
  "created": "2024-01-08 15:18:16",
  "credit": 0,
  "discount": 0,
  "full_name": "Admin",
  "last_login": "2024-01-22 20:52:53",
  "login": "admin",
  "user_id": 1
}

Пример 2:

Строим JSON объект. Удобно для использования в Telegram bot-е, в HTTP запросах и т.п.:

{{
  toJson(
    a = user.id
    b = 2
    c = [3,4,5]
  )
}}

Результат:

{"a":1,"b":2,"c":[3,4,5]}

toQueryString()

Преобразование объекта в Query String:

Пример:

{{
  toQueryString(
    a = user.id
    b = 2
    c = "hello world"
  )
}}

Результат:

a=1&b=2&c=hello%20world

list_for_api()

Метод для получения списка данных объекта.

Без аргументов выдаст первые 25 строк данных.

Аргументы:

Параметр Описание
admin Установка этого параметра в 1 позволяет получить данные всех клиентов
limit Кол-во отдаваемых данных. По-умолчанию: 25. (0 - без лимитов)
offset Индекс начала смещения списка. По-умолчанию: 0
filter Используется для поиска данных по определенным полям
sort_field Поле для сортировки (по-умолчанию ключевое поле)
sort_direction Порядок сортировки: asc, desc (по-умолчанию desc)

Пример 1:

Выведем всех пользователей:

{{ arr = ref(user.list_for_api('admin', 1)) }}
{{ FOR item IN arr }}
User id: {{ item.user_id }}, Login: {{ item.login }}, Balance: {{ item.balance }}
{{ END }}

Результат:

User id: 1, Login: admin, Balance: 0
User id: 22, Login: danuk, Balance: 224.44
User id: 34, Login: Dima, Balance: 0
User id: 117, Login: xims, Balance: 200

Пример 2:

Выведем список всех услуг с категорией начинающиеся на web, и период которых равен 1 месяцу:

{{ arr = ref(service.list_for_api('filter', { 'category' => 'web%', period => 1 } )) }}
{{ FOR item IN arr }}
Service id: {{ item.service_id }}, Name: {{ item.name }}, Cost: {{ item.cost }}
{{ END }}

Результат:

Service id: 111, Name: Web хостинг, Cost: 0
Service id: 110, Name: Тариф X-MAX, Cost: 300
Service id: 5, Name: Web хостинг LITE, Cost: 0

Сортировка

Существует два типа сортировки:

  • Сортировка на уровне запросов
  • Сортировка на уровне шаблона

При использовании пагинации важно сортировать результаты на уровне запросов.

Сортировка на уровне запросов

Сортировка на уровне запросов осуществляется непосредственно в БД.

Пример выдачи результатов отсортированных по возрастанию:

{{ arr = ref(service.list_for_api('sort_direction','asc')) }}

Пример выдачи результатов отсортированных по возрастанию по полю name:

{{ arr = ref(service.list_for_api('sort_direction','asc', 'sort_field','name')) }}

Сортировка в шаблонах

Сортировка в шаблонах используется для сортировки полученных данных.

Для сортировки используете функции: sort (для алфавитной сортировки), nsort (для числовой сортировки) и reverse (обратный порядок данных).

Пример сортировки по полю category:

{{ arr = ref(service.list_for_api().sort('cateogry')) }}

Пример сортировки по полю cost, в убывающем порядке:

{{ arr = ref(service.list_for_api().nsort('cost').reverse) }}

2.4.4 - Автоматизации

2.4.4.1 - Подсчет доходов за месяц

Ниже приведен пример шаблона для подсчета поступления средств за Январь 2024 года:

{{ sum = 0 }}
{{ arr = ref(user.pays.list_for_api( 'admin', 1, 'limit', 0,  'filter', { 'date' => '2024-01-%'} )) }}
{{ FOR item IN arr }}
{{ sum = sum + item.money }}
{{ END }}

Итого за Январь: {{ sum }} руб.

2.4.4.2 - Рассылки

SHM умеет делать рассылки по всем пользователям.

Для того, чтобы сделать рассылку, необходимо:

  • В кабинете Администратора выбрать пункт меню: “Задачи -> Текущие задачи”, далее нажать кнопку “ADD”.
  • Выберите для кого сделать рассылку: для одного пользователя или для всех (при тестировании используйте одного конкретного пользователя)
  • Выберите группу серверов, которая будет использоваться для рассылки
  • Выберите необходимый шаблон
  • Создайте задачу
  • Контролируйте исполнение задчи в “Текущие задачи”

В случае, если после рендера шаблон получается пустой, то такое сообщение отправлено не будет. Этот свойство используется для выборочной отправки сообщений.

Примеры шаблонов

Шаблон для всех клиентов

Уважаемый {{ user.full_name }}!
Это тестовое сообщение.
Ваш личный кабинет находится по адресу: {{ config.cli.url }}

Шаблон для пользователей Telegram

Если рассылку делать с помощью транспорта Телеграм, то данная проверка в шаблоне смысла не имеет, так как транспорт Телеграм сам проверяет это поле.

{{ IF user.settings.telegram.login }}
Вы получили это сообщение потому, что у Вас есть Telegram.
Ваш Telegram логин: {{ user.settings.telegram.login }}
{{ END }}

Шаблон для услуг в определенной категории

Данный шаблон будет отправлен только клиентам, у которых есть услуги в категории test. Категорию услуг можно указать и через маску, например так: te%.

{{ IF user.services.list_for_api('category','test') }}
Уважаемый {{ user.full_name }}!
Вы получили это сообщение потому, что у Вас есть услуга в категории test.
{{ END }}

Информация об услугах в определенной категории

Показываем сообщение для каждой услуги в категории test.

{{ user_services = ref(user.services.list_for_api('category','test')) }}
{{ IF user_services }}
Услуги в категории test найдены в количестве: {{ user_services.size }} штук.

{{ FOR item IN user_services }}
Услуга: {{ item.name }}
Категория: {{ item.category }}
Статус: {{ item.status }}
Действует до: {{ item.expire }}

Цена: {{ item.withdraws.cost }}
Кол-во: {{ item.withdraws.qnt }}
Cкидка: {{ item.withdraws.discount }}%
Итого: {{ item.cost }}
{{ END }}

{{ END }}

Активная услуга в определенной категории

Перебираем (ищем) все услуги пользователя с категорией test, и проверяем, что статус активный. Как только активная услуга будет найдена, то пишем сообщение и прекращаем перебор (LAST). Таким образом, будет выведено только одно сообщение, даже если услуг несколько.

Если нужно выводить сообщения для каждой услуги, необходимо убрать LAST.

{{ FOR item IN ref(user.services.list_for_api('category','test')) }}

{{ IF item.status == 'ACTIVE' }}
Вы видите это сообщение, потому что у вас есть активная услуга в категории test
{{ LAST }}
{{ END }}

{{ END }}

Уведомление клиентам без услуг в определенной категории

{{ IF ref(user.services.list_for_api('category','test')).empty }}
Вы видите это сообщение, потому что у вас нет ниодной услуги в категории test
{{ END }}

Подробнее о создании и настройке шаблонов можно прочитать здесь

2.4.4.3 - Удаление услуг при блокировке

Для удаления услуг при блокировке создайте шаблон вида:

{{ IF us.status == 'BLOCK' }}
{{ us.delete }}
{{ END }}

и добавьте его к событию CHANGED для нужной категории услуг.

Каждый раз, когда услуга будет переходить в статус BLOCK, SHM будет удалять её.

2.4.4.4 - Массовое начисление бонусов

Бывают случаи, когда необходимо начислить бонусы всем клиентам. Здесь приведены примеры, как это сделать.

Массовое начисление бонусов клиентам с указанной активной услугой:

Следующий код начислит всем клиентам с активной услугой 5 по 100 бонусов:

{{ arr = ref(user.services.list_for_api( 'admin',1, 'limit',0, 'filter',{ 'service_id' => 5, 'status' => 'ACTIVE' } )) }}
{{ FOR item IN arr }}
{{ user = user.switch( item.user_id ) }}
{{ user.add_bonus( 100, 'Акция' ) }}
{{ END }}

2.4.4.5 - Массовое обновление тарифов

Бывают случаи, когда нужно обновить тарифы всем клиентам. Здесь приведены примеры, как это сделать.

Обновление стоимости текущей услуги (тарифа)

Просто изменить стоимость услуги в Каталоге не достаточно. Необходимо для таких услуг пользователя установить “следующую” услугу в “текущую”:

{{ arr = ref(user.services.list_for_api( 'admin',1, 'limit',0, 'filter',{} )) }}
{{ FOR item IN arr }}
{{ user = user.switch( item.user_id ) }}
{{ us.id( item.user_service_id ).set('next', item.service_id) }}
{{ END }}

Массовая смена услуг (тарифов)

Например, мы хотим сменить (со следующего учетного периода) услугу 5 на 6, делается это так:

{{ arr = ref(user.services.list_for_api( 'admin',1, 'limit',0, 'filter',{ 'service_id' => 5 } )) }}
{{ FOR item IN arr }}
{{ user = user.switch( item.user_id ) }}
{{ us.id( item.user_service_id ).set('next', 6) }}
{{ END }}

2.4.5 - Уведомления клиентам

Для отправки уведомлений клиентам создайте соответствующее событие и привяжите к нему шаблон.

Уведомления будут отправлены с помощью выбранного транспорта. Например, Вы можете отправить уведомление EMAIL, и/или Telegram (настраивается в событии).

2.4.5.1 - Прогноз оплаты

Описание

SHM имеет встроенный модуль forecast, позволяющий получать информацию о прогнозе оплаты услуг.

forecast находит все услуги (кроме уже заблокированных), дата истечения которых менее чем 3 дня (days), и ещё неоплаченные услуги, и вычисляет стоимость их продления.

Получить прогноз оплаты можно следующими способами:

  • Через Web для текущего авторизованного пользователя: /shm/v1/user/pay/forecast
  • Через Web для пользователя с ID 123: /shm/v1/user/pay/forecast?user_id=123 (нужно быть авторизованным под администратором)
  • Из шаблонов: user.pays.forecast

Настройки

Параметр Описание
days Кол-во дней для прогноза (3 дня по-умолчанию)
blocked Учитывать заблокированные услуги (0 - по-умолчанию НЕТ)

Пример команды в шаблонах:

{{ forecast = user.pays.forecast('days', 10, 'blocked', 1) }}

Пример шаблона

Метод user.pays.forecast возвращает JSON вида:

{
  "items": [
        {
            "name": "Регистрация домена в зоне .RU",
            "service_id": 11,
            "user_service_id": 2949,
            "usi": 2949,
            "cost": 590,
            "discount": 0,
            "months": 12,
            "qnt": 1,
            "total": 590,
            "expire": "2017-07-29 12:39:46",
            "status": "ACTIVE",
            "next": {
                "name": "Продление домена в зоне .RU",
                "service_id": 12,
                "cost": 890,
                "discount": 0,
                "months": 12,
                "qnt": 1,
                "total": 890
            }
        }
    ],
    "balance": -21.56,
    "bonuses": 100,
    "dept": 21.56,
    "total": 768.44
}

Мы можем построить шаблон письма о прогнозе опаты услуг следующим образом:

Уважаемый {{ user.full_name }}

Уведомляем Вас о сроках действия услуг:

{{ FOR item IN ref(user.pays.forecast.items) }}
- Услуга: {{ item.name }}
  {{ IF item.expire }}
  Истекает: {{ item.expire }}
  {{ IF item.service_id != item.next.service_id }}
  Следующая услуга: {{ item.next.name }}
  Стоимость: {{ item.next.total }}
  {{ ELSE }}
  Стоимость продления: {{ item.next.total }}
  {{ END }}
  {{ ELSE }}
  Стоимость: {{ item.total }}
  {{ END }}

{{ END }}

{{ IF user.pays.forecast.dept }}
Погашение задолженности: {{ user.pays.forecast.dept }}
{{ END }}

Итого к оплате: {{ user.pays.forecast.total }} руб.

Подробнее о создании и настройке шаблонов можно прочитать здесь

2.4.5.2 - Уведомление о зачислении платежа

Описание

Создайте шаблон со следующим содержимым и привяжите его к событию PAYMENT:

Ваш платеж на сумму {{ user.pays.last.money }} зачислен.

Баланс: {{ user.balance }}

2.4.6 - HTTP (API)

SHM позволяет использовать шаблоны для внешнего использования

Поддерживаемые методы:

  • GET
  • POST

HTTP адрес

Доступ к шаблону с именем my_template можно получить следующим способами:

Для пользователей

Доступ с авторизацией:

/shm/v1/template/my_template?format=html&foo=1&bar=hello

Пример для curl

curl -s -u 'admin:admin' http://127.0.0.1:8081/shm/v1/template/my_template

Более детально о вариантах аутентификации можно почитать здесь

Для публичного использования

/shm/v1/public/my_template?format=html&foo=1&bar=hello

Для того, чтобы шаблон работал без авторизации необходимо прописать в его settings параметр:

allow_public: true

В публичных шаблонах переменная user не устанавливается автоматически, но её можно установить самостоятельно:

  • Указать идентификатор вручную (статически):

    в шаблоне: {{ user = user.switch( 123 ) }}

  • Указать идентификатор через HTTP запрос:

    /shm/v1/public/my_template?uid=123

    в шаблоне: {{ user = user.switch( request.params.uid ) }}

Пример для curl

curl -s http://127.0.0.1:8081/shm/v1/public/my_template?uid=123

Пример шаблона для проверки существования пользователя:

{{ IF user.switch( request.params.uid ).id }}
...
{{ END }}

Аргументы

В строку запроса можно добавить любые аргументы. Внутри самого шаблона к ним можно обратиться через переменную request.params

{{ foo = request.params.foo }}

Ниже приведен список специальных аргументов:

format - формат отдаваемого контента (MIME types)

  • plain (text/plain)
  • html (text/html)
  • json (application/json)
  • other (application/octet-stream)
  • qrcode

user_id - зарезервирован

В случае работы с шаблоном из под прав администратора можно явно указать идентификатор пользователя

HTTP заголовки

В шаблонах можно читать заголовки. Их удобно использовать для различных проверок

{{ headers = request.headers }}

Пример шаблона для проверки заголовка x-webhook-secret:

{{ IF request.headers.x-webhook-secret == 'something-very-very-secret' }}
...
{{ END }}

Примеры

2.4.6.1 - Вывод данных в формате HTML

Пример шаблона (my_template) для формирования данных в HTML:

<table border=1>
{{ FOR u IN ref( user.list_for_api('admin',1,'limit',0)) }}
<tr>
    <td>{{ u.user_id }}</td>
    <td>{{ u.login }}</td>
    <td>{{ u.balance }}</td>
</tr>
{{ END }}
</table>

Пример вызова шаблона для curl:

curl -s http://127.0.0.1:8081/shm/v1/public/my_template?format=html

Необходимо прописать в settings шаблона параметр: allow_public: true

Результат работы HTML шаблона лучше всего смотреть в браузере:

http://127.0.0.1:8081/shm/v1/public/my_template?format=html

2.4.6.2 - Вывод данных в формате JSON

Для формирования JSON объекта удобно использовать следующий синтаксис:

Пример шаблона (my_template) для формирования данных в JSON:

{{ u = user.id( request.params.uid ) }}
{{
  toJson({
    user_id = u.id
    login = u.login
    balance = u.balance
    last_payment = u.pays.last
    forecast = u.pays.forecast.total
  })
}}

Пример вызова шаблона для curl:

curl -s http://127.0.0.1:8081/shm/v1/public/my_template?format=json&uid=123

Необходимо прописать в settings шаблона параметр: allow_public: true

Пример результата:

{
    "user_id": "123",
    "login": "danuk",
    "balance": -21.56,
    "last_payment": {
        "comment": null,
        "date": "2016-01-04 20:33:35",
        "id": 2,
        "money": 455,
        "pay_system_id": "manual",
        "user_id": 123
    },
    "forecast": 1013.45
}

2.4.7 - Выполнение скриптов

Шаблоны позволяют генерировать как однострочные команды, так и целые блоки текста. Эти механизмы являются основой построения взаимодействий SHM.

2.4.8 - Telegram bot

Шаблон для Telegram Bot-а

Это двух-уровневый шаблон. Сначала используются теги <% ... %> для нахождения нужной секции шаблона, соответствующей команды. После нахождения нужной секции шаблонизатор будет использовать теги вида: {{ ... }}.

Для сопоставления команды пользователя/бота используется внутренняя переменная cmd. Так, при наборе команды /balance будет найдена секция: <% CASE '/balance' %>.

Команда USER_NOT_FOUND является встроенной в SHM, и вызывается автоматичеки, когда SHM не может найти у себя этого пользователя.

В каждой секции мы можем писать реальные команды Telegram. Например, команда sendMessage отправляет сообщение в Telegram. Вы можете использовать эту команду в соответсвии с документацией Telegram.

В каждой секции можно писать множество команд, через запятую (см. пример в секции /balance).

Для того, чтобы лучше понять, как строятся всевозможные кнопочки, читайте документацию Telegram. В этом шаблоне всего-лишь описаны вызовы этих методов.

Пример шаблона:

<% SWITCH cmd %>
<% CASE 'USER_NOT_FOUND' %>
{
    "shmRegister": {
        "callback_data": "/menu",
        "error": "ОШИБКА"
    }
}
<% CASE ['/start', '/menu'] %>
{
    "sendMessage": {
        "text": "Я Ваш тестовый Telegram Bot",
        "reply_markup": {
            "inline_keyboard": [
                [
                    {
                        "text": "Баланс",
                        "callback_data": "/balance"
                    }
                ]
            ]
        }
    }
}
<% CASE '/balance' %>
{
    "deleteMessage": { "message_id": {{ message.message_id }} }
},
{
    "sendMessage": {
        "text": "Баланс: {{ user.balance }}",
        "reply_markup": {
            "inline_keyboard": [
                [
                    {
                        "text": "Назад",
                        "callback_data": "/menu"
                    }
                ]
            ]
        }
    }
}

Стандартные методы Telegram

В SHM можно использовать любые методы Telegram.

Полный перечень доступных методов и синтаксис их использования можно посмотреть на официальном сайте документации Telegram https://core.telegram.org/bots/api#available-methods

chat_id - заполняется автоматически, но при необходимости его можно указать

Встроенные переменные SHM

  • cmd - специальная переменная SHM в которую записывается значение, полученное из Telegram, в зависимости от типа: message.text для команд введеных пользователем, и callback_query.data для callback_data. Если команда пользователя начинается с символа /, то такая команда будет усечена до первого слова.
  • args - массив аргументов команды, разделитель - пробел.
  • start_args - именованный массив аргументов для команды start
  • message - полное сообщение от Telegram (json объект)

Примеры парсинга cmd и args:

Источник Команда cmd args
Пользователь /test 1 2 3 /test [1,2,3]
Пользователь test 1 2 3 test 1 2 3 [1,2,3]
callback_data /test 1 2 3 /test [1,2,3]
callback_data test 1 2 3 test [1,2,3]

args - это массив. Для получения значений из него используйте синтаксис: args.N, где N - индекс элемента. Например, получить первый элемент массива можно так: args.0

Передача дополнительных параметров при старте бота

При старте бота может понадобится передача каких-либо данных, например для отслеживания рекламных акций (utm метрики), или для создания партнерской ссылки.

ВНИМАНИЕ: суммарная длина параметров не может превышать 64 символа

Генерация партнерской ссылки

Для генерации партнерской ссылки можно использовать следующий шаблон:

https://t.me/myshm_bot?start={{ toBase64Url(toQueryString( pid = user.id )) }}

где myshm_bot - имя бота (замените на свой)

Генерация ссылки с произвольными параметрами

https://t.me/myshm_bot?start={{ toBase64Url(toQueryString(
    utm_source = 'google'
    utm_medium = 'telegram'
    foo = 'bar'
))
}}

где myshm_bot - имя бота (замените на свой)

Переменные utm_ автоматически сохраняются в settings пользователя

Чтение переданных параметров

Вы можете прочитать переданный при старте бота параметр с помощью специальной переменной: start_args, пример:

Источник: {{ start_args.utm_source }}

Встроенные методы SHM

shmRegister

Метод позволяет зарегистрировать нового клиента

"shmRegister": {
    "callback_data": "/menu",
    "error": "ОШИБКА"
}

shmServiceOrder

Метод для регистрации новых услуг. Пример использования:

"shmServiceOrder": {
    "service_id": "{{ args.0 }}",
    "expire": "2014-09-25 10:11:12",
    "parent": 123,
    "check_exists": 1,
    "check_exists_unpaid": 1,
    "check_category": "test-%",
    "callback_data": "/menu",
    "cb_not_enough_money": "/pay",
    "error": "ОШИБКА"
 }

где: service_id - ID услуги, expire - установка произвольной даты истечения услуги (опционально), parent - ID родительской услуги (опционально), check_exists - проверка существования услуги (опционально), check_exists_unpaid - проверка существования неоплаченной услуги (опционально), check_category - проверка существования услуги по категории (опционально), callback_data - команда для случая успешного заказа услуги, cb_not_enough_money - команда для случая нехватки средств для активации услуги.

Если используется один из флагов: check_exists, check_exists_unpaid, check_category и услуга найдена, то регистрация новой услуги не осуществляется, а метод вернет первую найденную услугу (callback_data или cb_not_enough_money).

shmServiceDelete

Метод для удаления услуг пользователя. Пример использования:

"shmServiceDelete": {
    "usi": "{{ args.0 }}",
    "callback_data": "/menu",
    "error": "ОШИБКА"
 }

где: usi - ID услуги пользователя

uploadDocumentFromStorage

Метод загружает данные из Storage и отправляет их в виде файла

"uploadDocumentFromStorage": {
    "name": "{{ args.0 }}",
    "filename": "{{ args.0 }}.conf"
}

uploadPhotoFromStorage

Метод загружает данные из Storage и отправляет их в виде картинки (QR code)

"uploadPhotoFromStorage": {
    "name": "{{ args.0 }}",
    "format": "qr_code_png"
}

shmRedirectCallback

Метод вызывает указанную команду (cmd)

"shmRedirectCallback": {
    "callback_data": "/help"
}

Приём платежей

Для приёма платежей в Telegram удобно использовать дополнительный шаблон (web_app).

Шаблон используется для возможности ввода произвольной суммы и выбора настроенных платежных систем SHM.

  1. Настройте одну или несколько платежных систем
  2. Скачайте шаблон и сохраните в SHM под названием tg_payments
  3. В Шаблоне своего бота используйте конструкцию вида:
<% CASE '/payment' %>
{
  "sendMessage": {
    "text": "Оплата покупки",
    "reply_markup": {
      "inline_keyboard": [
        [
          {
            "text": "Оплатить...",
            "web_app": {
              "url": "{{ config.api.url }}/shm/v1/template/tg_payments?format=html&session_id={{ user.gen_session.id }}"
            }
          }
        ]
      ]
    }
  }
}

Авторизация пользователей

SHM автоматически авторизует пользователя. Для связки пользователя SHM и пользователя Telegram используется user_id из Telegram.

Для отправки сообщений пользователю в Telegram необходимо знать:

  • user_id - идентификатор пользователя Telegram
  • chat_id - идентификатор чата Telegram

Если пользователь хоть раз взаимодействовал с ботом, подключенным к SHM, то его user_id и chat_id автоматически сохраняются в settings пользователя.

chat_id определяется в следующем порядке:

  • Из сообщения Telegram (в случае, если клиент отправил команду боту)
  • Из settings шаблона (telegram.chat_id)
  • Из settings пользователя SHM (telegram.chat_id)

Отправка системных сообщений

В случае, если Вы хотите отправлять системные сообщения себе в телеграм, то для этого Вы можете создать отдельный шаблон, и в его settings прописать:

{
    "telegram": {
        "chat_id": ID_вашего_чата
    }
}

Пример шаблона:

Событие:  {{ event_name }}

Пользователь: {{ user.login }} ({{ user.id }})

{{ IF us.id }}
Услуга: [{{ us.id }}] {{ us.name }}
{{ END }}

{{ IF event_name == "PAYMENT" }}
Платеж на сумму: {{ user.pays.last.money }} зачислен для пользователя: {{ user.login }}
{{ END }}

Привяжите этот шаблон к нужным Вам событиями.

2.4.8.1 - Пагинация

Пример шаблона для постраничного вывода списка услуг

{{
  limit = 5
  data = []
}}
<% SWITCH cmd %>
<% CASE '/list' %>
{{
  offset = args.0 || 0
  items = ref( user.services.list_for_api('limit',limit, 'offset',offset) )
}}
{{ FOR item IN items }}
{{ data.push(
    [{
      "text" = 'Услуга: ' _ item.name _ ' ID: ' _  item.user_service_id
      "callback_data" = '/service ' _ item.user_service_id
    }]
  )
}}
{{ END }}

{{ data.push(
    [{
      "text" = 'Еще...'
      "callback_data" = '/list ' _ (limit + offset)
    }]
  ) IF items.size == limit
}}

{
  "sendMessage": {
    "text": "Список услуг",
    "reply_markup" : {
      "inline_keyboard": {{ toJson( data ) }}
    }
  }
}
<% END %>

2.5 - Хранилище

Хранилище в SHM предназначено для хранения произвольных данных в БД SHM.

Данные в хранилище сохраняются под произвольными именами. Имена должны быть уникальными для каждого пользователя.

Типы хранимой информации:

  • JSON
  • Binary

Максимальный объем записи для одного ключа: 16 Mb.

К данным, сохраненным в формате JSON можно обращаться по ключам (см. примеры ниже).

Работать с данными можно как через HTTP, так и с помощью Шаблонов.

Работа через HTTP

Для работы с хранилищем используйте следующие данные:

URL

/shm/v1/storage/manage/NAME, где NAME - имя записи

Методы

  • GET - чтение записи
  • PUT - создание новой записи
  • POST - редактирование существующией записи
  • DELETE - удаление записи

Заголовок Content-Type

  • application/json для сохранения данных JSON
  • text/html - для хранения текстовых данных
  • application/octet-stream - для хранения двоичных данных

Примеры

Сохранение данных в хранилище

Сохраним содержимое файла: my_file.txt в SHM с ключом my_file для пользователя: user:

curl -s -u "user:pass" \
      -X "PUT" \
      -H "Content-Type: text/html; charset=utf-8" \
      https://admin.DOMAIN/shm/v1/storage/manage/my_file \
      --data-binary @my_file.txt

Сохраним JSON данные в SHM с ключом data для пользователя: user:

curl -s -u "user:pass" \
      -X "PUT" \
      -H "Content-Type: application/json; charset=utf-8" \
      https://admin.DOMAIN/shm/v1/storage/manage/data \
      -d '{"user_key":123}'

Чтение данных

Чтение файла my_file из SHM и сохранение его в my_file.txt:

curl -s -u 'user:pass' https://admin.DOMAIN/shm/v1/storage/manage/my_file > my_file.txt

Чтение JSON данных, получение значения ключа user_key:

curl -s -u 'user:pass' https://admin.DOMAIN/shm/v1/storage/manage/data | jq .user_key

2.6 - Интеграции

В данном разделе описываются интеграции с различными системами и примеры решения различных задач на базе SHM

2.6.1 - WireGuard

Процесс интеграции WireGuard и SHM подробно описан здесь: habr.com

2.6.2 - Marzban

В данном разделе описывается процесс интеграции SHM и Marzban.

Marzban - проект с открытым исходным кодом (https://github.com/Gozargah/Marzban)

Это инструмент управления прокси, который предоставляет простой и удобный пользовательский интерфейс для управления сотнями учетных записей прокси, работающий на базе Xray-core.

Marzban удобен в использовании, многофункциональен и надежен. Он позволяет вам создавать разные прокси для ваших пользователей без какой-либо сложной настройки. Используя встроенный веб-интерфейс, вы можете отслеживать, изменять и ограничивать пользователей.

Шаблон Marzban работает в SHM начиная с версии 0.5.6. Обновите SHM, если ваша версия меньше.

Принцип взаимодействия SHM и Marzban

flowchart LR
    subgraph SHM
        E[Событие] --> TPL(Шаблон)
        API --> DB[(Хранилище\nключей)]
    end
    subgraph Marzban
        MZ(API)
        TPL --> MZ
        API --- MZ
    end

Интеграция

  • Создайте в SHM шаблон с именем marzban (можно любое другое имя) и поместите туда содержимое из этой ссылки.
  • В конфигурации SHM создайте параметр с именем acme, после чего добавьте туда ключ email_for_certificate_issue и укажите Ваш реальный email. Он нужен для выпуска SSL сертификата для вашего домена.
  • Создайте отдельную группу серверов, например: VPN Marzban group
  • Создайте новый сервер и включте его в группу VPN Marzban group. В качестве транспорта укажите SSH и укажите наш шаблон marzban.
  • Добавьте ключ SSH на ваш сервер, проверьте, что сервер работает (кнопка TEST).
  • В settings сервера пропишите переменную: subscription_domain. Укажите ваш домен, который указывает на Ваш сервер. Этот домен крайне важен для выпуска SSL сертификата.
  • Создайте все нужные Cобытия в SHM, и укажите там нашу группу серверов (VPN Marzban group). Укажите категорию услуг: vpn-mz-test
  • Создайте и настройте Услуги. Укажите категорию услуг: vpn-mz-test.

Инсталляция сервера Marzban

На сервере куда вы планируете инсталлировать Marzban должен быть свободным https порт (443).

  • Вы можете инсталлировать сервер Marzban с помощью SHM, выбрав этот шаблон (marzban) и нажав кнопку INIT. Предварительно укажите адрес сервера (host_name) в settings сервера.
  • Вы можете вручную инсталлировать сервер Marzban, следуя официальным инструкциям со страницы проекта Marzban.

Управление ключами

SHM управляет ключами Marzban используя API. Для авторизации убедитесь, чтобы на сервере c Marzban в файле /opt/marzban/.env были прописаны SUDO_USERNAME и SUDO_PASSWORD. Если инсталляция Marzban производилась с помощью SHM, то все нужные переменные уже прописаны.

Marzban Dashboard (Web UI)

Для доступа к Web панели Marzban используйте ссылку вида: http://IP:8000/dashboard

Логин и пароль вы можете узнать зайдя на сервер с Marzban (по SSH), и выполнить следующие команды:

  • Логин: grep '^SUDO_USERNAME=' /opt/marzban/.env | tr -d 'SUDO_USERNAME='
  • Пароль: grep '^SUDO_PASSWORD=' /opt/marzban/.env | tr -d 'SUDO_PASSWORD='

Доступ к данным (ключам) пользователей

После создания подписки “ключей” данные сохраняются в Хранилище SHM.

Получить данные можно разными способами.

Получить данные через API SHM:

  • URL для пользователя: /shm/v1/storage/manage/vpn_mrzb_$USI
  • URL для Админа: /shm/v1/storage/manage/vpn_mrzb_$USI?user_id=$USER_ID

Где $USI - идентификатор услуги пользователя, $USER_ID - идентификатор пользователя

Получение данных в Шаблонах SHM:

  • Пример получения ключа (начинающегося на ss:):
    {{ storage.read('name','vpn_mrzb_' _ us.id ).links.grep('^ss:').first }}
  • Пример получения ссылки для подписки:
    {{ storage.read('name','vpn_mrzb_' _ us.id ).subscription_url }}

В Telegram bot используйте args.0 вместо us.id.

Telegram bot

Настройте Telegram bot, если хотите продавать услуги через Telegram.

Вы можете использовать готовый шаблон для Telegram. Для этого, создайте шаблон telegram и поместите туда код.

2.6.3 - Telegram bot

С помощью SHM легко и удобно создавать Telegram bot-ов.

Telegram bot реализуется с помощью транспорта Telegram

2.7 - Партнерские программы

2.7.1 - Реферальная система

Получение бонусов за приведенных клиентов по ссылке.

В SHM реализована двух-степенчатая реферальная программа.

В конфигурации биллинга в секции billing->partner->income_percent можно указать значение партнерского начисления в процентах.

Клиенты могут получать пожизненные бонусы за приведенных клиентов (рефералов) по следующей схеме:

  • Для Web, клиент распространяет ссылку вида:

https://bill.DOMAIN/#!/?partner_id=USER_ID

где USER_ID это идентификатор клиента

партнерскую ссылку клиент может получить в своём личном кабинете в профиле

  • Для Telegram, клиент распространяет ссылку вида:

https://t.me/Name_bot?start=USER_ID

где USER_ID это идентификатор клиента

  • Со всех платежей рефералов, которые зарегистрировались в SHM по партнерской ссылке, клиенту будут начилсяться бонусы в размере установленного процента income_percent.

2.7.2 - Промокоды

Промокоды генерируются и раздаются клиентам. Промокоды в SHM привязываются к произвольным Шаблонам. Каждый раз, когда клиент использует промокод, вызывыается привязанный Шаблон, который выполняет какие-то действия в системе SHM.

Таким образом, Промокод может выполнять практически любые задачи:

  • Зачислять бонусы
  • Регистрировать тарифы
  • Выполнять какие-то внешние задачи, например отправлять HTTP запросы

Генерация промокодов

Создайте и выполните этот шаблон для генерации промокодов:

{{
  codes = promo.generate(
    template_id = 'template_promo'
    count = 1
    length = 10
    prefix = 'TEST_'
    settings = { amount = 123, tariff = 1, foo = 'bar' }
  )
}}

{{ FOR code IN codes }}
{{ code }}

{{ END }}

где:

  • template_id - название шаблона для выполнения при применении промокода
  • count - кол-во прокомодов, которое нужно сгенерировать
  • length - длина промокода без учета длины префикса
  • prefix - префикс для промокода, если нужен
  • settings - любые данные в формате JSON для использованиях их при выполнении Шаблона (при необходимости)

Использование промокодов

Для того, чтобы использовать промокод необходимо выполнить следующий код:

promo.apply( ПРОМОКОД )

Использование промокодов с помощью внешней ссылки

Создайте Шаблон для публичного использования с именем: my_promo:

Запишем туда следующий код:

{{ IF user.switch( request.params.uid ).id }}
    {{ IF promo.apply( request.params.code ) }}
        Ваш промокод успешно применен
    {{ ELSE }}
        Ошибка: промокод не найден
    {{ END }}
{{ ELSE }}
Ошибка: пользователь не найден
{{ END }}

Теперь, для использования промокода, мы можем давать клиентам ссылки вида: https://myshm.ru/shm/v1/public/my_promo?uid=123&code=XXXXXX

где:

  • my_promo - имя шаблона
  • uid - идентификатор пользователя
  • code - промокод

Примеры шаблонов для промокодов

Шаблон для зачисления пользователю бонусов

Создайте шаблон с именем указанным при создании промокода и поместите туда следуюший код:

{{ user.add_bonus( promo.settings.amount, 'Зачисление бонусов по промокоду: ' _ promo.id ) }}

При использовании промокода, созданного для этого шаблона, будет прочитано кол-во бонусов для зачисления (amount), и бонусы будут начислены.

Шаблон для регистрации тарифа

Создайте шаблон с именем указанным при создании промокода и поместите туда следуюший код:

{{
  service.create_for_api(
    service_id = promo.settings.tariff
    check_allow_to_order = 0
  )
}}

При использовании промокода, созданного для этого шаблона, будет зарегистрирован тариф с идентификатором promo.settings.tariff.

Тариф будет зарегистрирован даже, если у него не стоит галка “Доступен к заказу”, из-за того, что мы используем флаг check_allow_to_order = 0.

2.8 - Приём Платежей

SHM поддерживает различные платежные системы.

Настройка платежных систем осуществляется в интерфейсе администратора.

В самих платежных системах необходимо указать адрес, на который будут посылаться оповещения о платеже.

Вы можете воспользоваться платежными системами из списка или написать свой скрипт, который будет принимать информацию от платежной системы и зачислять этот платеж вашему клиенту в SHM по средствам API.

2.8.1 - Crypto Bot

Настройка платежной системы (https://t.me/CryptoBot) или так же известный как (https://t.me/send)

SHM не умеет конвертировать валюты, поэтому в биллинге следует установить все цены для услуг в той валюте какую указываете в fiat

  • Валюту для приёма платежа можно выбрать из списка доступнх: USD, EUR, RUB, BYN, UAH, GBP, CNY, KZT, UZS, GEL, TRY, AMD, THB, INR, BRL, IDR, AZN, AED, PLN и ILS

  • Сохраните в конфиг SHM следующие данные:

{
  "pay_systems": {
    "cryptopay": {
      "api_key":"ВАШ_Секретный_ключ",
      "description":"укажите_наименования_товара_для_чека",
      "fiat":"валюта_USD_EUR_RUB",
      "name":"CryptoPay",
      "paid_btn_name":"openBot",
      "paid_btn_url":"укажите_url_бота_для_возврата_после_удачного_платежа",
      "show_for_client":"true"
    }
  }
}
  • В телеграм боте (https://t.me/CryptoBot) “Crypto Pay” -> “Мои приложения” -> “Название приложения” -> “Вебхуки” нажмите включть вебхука, на сообщение Пришлите URL укажите URL для уведомлений вида:

    https://admin.ВАШ_ДОМЕН/shm/pay_systems/cryptopay.cgi

Ваш секретный ключ можно посмотреть в “Crypto Pay” -> “Мои приложения” -> “API-токен”

Если вы хотите попробовать и используете API-токен из Тестового Бота то SHM не сможет обработать платеж.

Пример ссылки для создания платежа:

https://admin.ВАШ_ДОМЕН/shm/pay_systems/cryptopay.cgi?action=create&amount=123

2.8.2 - Wallet

Настройка платежной системы Telegram Wallet

SHM не умеет конвертировать валюты, поэтому в биллинге следует установить все цены для услуг в той валюте какую указываете в currencyCode

  • Сохраните в конфиг SHM следующие данные:
{
  "pay_systems": {
    "wallet": {
      "api_key":"ВАШ_Секретный_ключ",
      "autoConversionCurrency":"Перечисление_на_ваш_счёт_TON_BTC_USDT",
      "currencyCode":"валюта_RUB_USD_EUR_USDT_BTC_TON",
      "description":"укажите_наименования_товара_для_чека",
      "failReturnUrl":"укажите_url_для_возврата_после_неудачного_платежа",
      "name":"Wallet",
      "returnUrl":"укажите_url_для_возврата_после_платежа",
      "show_for_client":"true"
    }
  }
}

Ваш секретный ключ можно посмотреть в Настройках.

  • В настройках вашего магазина включите тумблер Включить Webhooks в появившемся поле укажите:

    https://admin.ВАШ_ДОМЕН/shm/pay_systems/wallet.cgi

Пример ссылки для создания платежа:

https://admin.ВАШ_ДОМЕН/shm/pay_systems/wallet.cgi?action=create&amount=123

2.8.3 - ЮMoney

Настройка платежной системы ЮMoney

  • Сохраните в конфиг SHM следующие данные:
{
  "pay_systems": {
    "yoomoney": {
      "account":"АККАУНТ_ЮMONEY",
      "secret":"СЕКРЕТ_ЮMONEY"
    }
  }
}

Аккаунт можно посмотреть в платежной форме. Если форма еще не создана, введите любое значение в поле “Назначение платежа”, и платежная форма появится. Найдите в ней значение вашего аккаунта (account=). Секрет можно посмотреть здесь.

Пример ссылки для создания платежа:

https://admin.ВАШ_ДОМЕН/shm/pay_systems/yoomoney.cgi?action=create&amount=123

2.8.4 - ЮKassa

Сервис позволяет принимать оплату самозанятым, ИП и Юридическим лицам.

Настройка платежной системы ЮKassa

{
  "pay_systems": {
    "yookassa": {
      "name": "ЮKassa"
      "account_id": ВАШ_shopId
      "api_key": ВАШ_Секретный_ключ
      "customer_email": укажите_email_для_получения_чеков
      "description": укажите_наименования_товара_для_чека
      "return_url": укажите_url_для_возврата_после_платежа
      "show_for_client": true
    }
  }
}

Пример ссылки для создания платежа:

https://ВАШ_ДОМЕН/shm/pay_systems/yookassa.cgi?action=create&amount=123

2.8.5 - FreeKassa

Настройка платежной системы FreeKassa

{
  "pay_systems": {
    "freekassa": {
      "name": "FreeKassa"
      "show_for_client": true
      "merchant_id": ID_магазина_сверху_страницы
      "secret_word_1": СЕКРЕТНОЕ_СЛОВО_1
      "secret_word_2": СЕКРЕТНОЕ_СЛОВО_2
    }
  }
}
  • В разделе “Ссылки и методы” укажите URL ОПОВЕЩЕНИЯ и МЕТОД POST:

    https://admin.ВАШ-ДОМЕН/shm/pay_systems/freekassa.cgi

  • Нажмите кнопку “ПРОВЕРИТЬ СТАТУС”. Вы должны увидеть статус 200, а в Админке SHM, в платежах, Вы увидите тестовый платеж.

  • Остальные поля заполните по вашему усмотрению

Пример ссылки для создания платежа:

https://ВАШ_ДОМЕН/shm/pay_systems/freekassa.cgi?action=create&amount=123

2.8.6 - CryptoCloud.plus

Сервис позволяет принимать крипто-валюту от ваших клиентов.

SHM не умеет конвертировать валюты, поэтому в биллинге следует установить все цены для услуг в долларах, и принимать только крипто-валюту: USDT.

Настройка платежной системы CryptoCloud.plus

{
  "pay_systems": {
    "cryptocloud": {
      "name": "CryptoCloud"
      "api_key":"API_KEY"
      "shop_id":"Идентификтор магазина"
      "show_for_client": true
    }
  }
}
  • Настройте URL для уведомлений:

    https://ВАШ_ДОМЕН/shm/pay_systems/cryptocloud.cgi

Пример ссылки для создания платежа:

https://ВАШ_ДОМЕН/shm/pay_systems/cryptocloud.cgi?action=create&amount=123

3 - Администрирование

3.1 - MySQL

Бэкапы и восстановление

Создание архивной копии (Backup)

docker compose exec -T mysql /bin/bash -c 'MYSQL_PWD=${MYSQL_ROOT_PASSWORD} mysqldump -u root shm' > shm_backup.sql

Восстановление БД из архива (Restore)

docker compose exec -T mysql /bin/bash -c 'MYSQL_PWD=${MYSQL_ROOT_PASSWORD} mysql -u root shm' < shm_backup.sql

Периодические бэкапы

mysql_backup.sh

DOCKER_COMPOSE_PATH="/opt/shm"
BACKUP_DIR="/opt/shm/backups"

mkdir -p ${BACKUP_DIR}
cd ${DOCKER_COMPOSE_PATH}
docker compose exec -T mysql /bin/bash -c 'MYSQL_PWD=${MYSQL_ROOT_PASSWORD} mysqldump -u root shm' > ${BACKUP_DIR}/shm_$(date +%d%m%Y-%H%M%S).sql

3.2 - Обновление SHM

Как обновить версию SHM

Обновление версии SHM

Рекомендуем делать резервную копию БД SHM перед обновлением

  1. Перейдите в директорию с Вашим файлом: docker-compose.yml
  2. Загрузите новые образы: docker compose pull
  3. Перезапустите контейнеры: docker compose down && docker compose up -d

3.3 - Разное

Разные полезные штуки

Просмотр логов

Если что-то пошло не так и есть необходимость посмотреть логи, то:

  1. Перейдите в директорию с Вашим файлом: docker-compose.yml
  2. Выполните команду: docker compose logs -f

Сброс пароля пользователя admin

Если Вы забыли пароль администратора, то можно его сбросить следующим образом:

  1. Перейдите в директорию с Вашим файлом: docker-compose.yml
  2. Выполните команду: docker compose exec core scripts/reset_admin_pass.cgi

Редактирование стилей (CSS) клиентского кабинета

  1. Перейдите в директорию с Вашим файлом: docker-compose.yml
  2. Скачайте текущий файл стилей: wget https://raw.githubusercontent.com/danuk/shm-client/master/app/assets/css/styles-alternative.css
  3. Раскоментируйте строки в файле docker-compose.yml:
#    volumes:
#      - ./styles-alternative.css:/app/assets/css/styles-alternative.css
  1. Перезапустите SHM:
docker compose down
docker compose up -d

Теперь Вы можете править файл стилей styles-alternative.css и наблюдать результат в клиентском интерфейсе пользователя (обновляя страницу браузера).

Перенос SHM на другой сервер

Если возникла необходимость в переносе SHM на другой сервер, то выполните следующие шаги:

  1. Сделайте архивную копию БД на текущем сервере
  2. Установите и запустите SHM на новом сервере
  3. Восстановите БД нового SHM из архивной копии (с первого шага)

4 - API

Все настройки SHM и всё управление системой осуществляется с помощью вызовов API.

Web интерфейс администратора и кабинет клиента работают на основе API.

HTTP Методы

SHM API использует стандартные методы протокола HTTP:

Метод Назначение
GET Чтение данных
POST Изменение данных
PUT Добавление данных
DELETE Удаление данных

SHM API v1 отвечает в следующем формате:

{
    data: [
        {
            item: 1
        },
        {
            item: 2
        }
    ],
    items: 2
}

HTTP коды ответов

При работе с SHM API рекомендуется проверять коды ответов HTTP.

200 - УСПЕХ для GET, POST, PUT запросов

201 - УСПЕХ для DELETE запросов (эти запросы не возвращают ответа в теле)

400 - ошибка запроса, неверные или недостающие аргументы

403 - не авторизован, требуется авторизация

404 - объект не найден

5xx - ошибки сервера

Аутентификация

Аутентификация через Cookies (session_id)

Получаем session_id:

curl -H "Content-Type: application/json" \
     -X POST http://$SERVER/shm/user/auth.cgi \
     -d '{"login": "admin", "password": "admin"}'

пример ответа:

{ "session_id": "3f928c835ff78f836c4066c112b292c5" }

Запрос с использованием cookies (session_id):

curl --cookie "session_id=3f928c835ff78f836c4066c112b292c5" http://$SERVER/shm/v1/user

Basic-аутентификация

curl -u "admin:admin" http://$SERVER/shm/v1/user

Аутентификация через header-ы

curl -H "session-id: 3f928c835ff78f836c4066c112b292c5" http://$SERVER/shm/v1/user
curl -H "login: admin" -H "password: admin" http://$SERVER/shm/v1/user

Аутентификация через Query string

curl http://$SERVER/shm/v1/user?session_id=3f928c835ff78f836c4066c112b292c5

Список ресурсов

Ресурс URL
Услуги /v1/admin/service
Услуги дочерние /v1/admin/service/children
События услуг /v1/admin/service/event
Пользователи /v1/admin/user
Смена пароля пользователя /v1/admin/user/passwd
Платежи пользователя /v1/admin/user/pay
Зачисление платежей /v1/admin/user/payment
Профиль пользователя /v1/admin/user/profile
Услуги пользователя /v1/admin/user/service
Услуги пользователя - списания /v1/admin/user/service/withdraw
Блокировка услуги пользователя /v1/admin/user/service/stop
Список текущих задач для услуги /v1/admin/user/service/spool

4.1 - Конфигурация

4.2 - Приём платежей

Пример зачисления платежа клиенту

Платеж возможно зачислить только из под прав администратора

curl -u "login:password" \
     -H "Content-Type: application/json" \
     -X PUT http://$SHM_URL/shm/v1/admin/user/payment \
     -d '{"user_id": 1, "money": 123.45, "pay_system_id": "manual"}'

Где:

  • user_id - идентификатор пользователя для зачисления
  • money - сумма для зачисления
  • pay_system_id - идентификатор платежной системы. Произвольное слово, длиной не более 16 символов

4.3 - Шаблоны

Пример обновления шаблона wg_manager

curl -u "admin:admin" \
     -X "POST" \
     -H "Content-Type: text/html; charset=utf-8" \
     http://$SHM_URL/shm/v1/admin/template/wg_manager \
     --data-binary @shm_actions_script.sh

5 - Сотрудничество

5.1 - Контакты

Документация

Группа в Telegram

Все основные вопросы Вы можете задать в Telegram канале: https://t.me/shm_billing

В группе есть множество людей, готовых Вам помочь. Я тоже отвечаю в группе Telegram, и более охотно, чем в “личке”, потому что эта информация может пригодится и другим людям.

Пожалуйста, не пишите мне в “личку”, если не готовы оплатить моё время.

В группе Telegram запрещается:

  • Реклама любого рода
  • Высокомерное общение с участниками, хамство, угрозы и т.п.
  • Общение на отвлеченные темы, не касающиеся тематики SHM

Личное обращение в Telegram

Вы можете написать автору проекта в личку https://t.me/danuk в следующих случаях:

  • Коммерческое предложение, платная доработка SHM под Ваши нужды
  • Платная установка/настройка SHM
  • Другие вопросы на платной основе

5.2 - Разработка

Для разработки SHM необходимо скачать все файлы из репозитория на свой компьютер.

База данных (БД) для разработчиков уже наполнена некоторыми тестовыми данными. Каждый раз, при запуске SHM в режимие разработки, содержимое БД сбрасывается в значения по-умолчанию.

Установка

Для разработки SHM сделайте следующее:

  1. Установите на свой компьютер: git и docker
  2. Создайте отдельную директорию shm-dev и перейдите в неё
  3. Скачайте docker-compose.yml файл для разработки:
    wget https://raw.githubusercontent.com/danuk/shm/master/contributing/docker-compose.yml
    
  4. Авторизуйтесь (или зарегистрируйтесь) на https://github.com и сделайте Fork для следующих репозиториев (этот шаг можно пропустить, если не планируете делиться вашими наработками):
  1. Выполните следующие команды для клонирования (загрузки) основных репозиториев (вместо danuk используйте ваш логин на github, если делали Fork):
  • git clone https://github.com/danuk/shm.git
  • git clone https://github.com/danuk/shm-admin.git
  • git clone https://github.com/danuk/shm-client.git

Сборка

Для сборки контейнеров docker используйте следующую команду:

docker compose build

Запуск и остановка SHM

Запуск и остановка SHM осуществляется из директории shm-dev

Для запуска SHM выполните команду: docker compose up -d

После запуска всех контейнеров Вы можете использовать следующие URL:

  • http://127.0.0.1:8081 - Админский интерфейс SHM. Логин: admin, Пароль: admin
  • http://127.0.0.1:8082 - Интерфейс пользовелей SHM

Для остановки SHM выполните команду: docker compose down

Редактирование файлов (разработка)

Ядро системы, файлы Админки и файлы Кабинета пользователей располагаются в отдельных репозиториях. На шаге “Установка” мы склонировали нужные репозитории. Таким образом:

  • Файлы ядра SHM находятся в: shm-dev/shm
  • Файлы Админки находятся в: shm-dev/shm-admin
  • Файлы Кабинета клиента находятся в: shm-dev/shm-client

Меняя нужные файлы в нужных директориях мы будем видеть соответствующий результат, без перезапуска docker контейнеров.

Например, изменив файл в директории shm-admin можно увидеть результат обновив страницу админки (http://127.0.0.1:8081).

Тестирование

Если Вы внесли изменения в ядро SHM, то обязательно запустите тесты, чтобы убедиться, что не сломан основной функционал:

docker compose exec core bash -c 'prove t/*'

Делитесь Вашими наработками

Если Вы сделали что-то, чем хотели бы поделиться с другими людьми, сделайте коммит Ваших изменений и откройте Pull Request, чтобы внести Ваш код в основные репозитории SHM.

5.3 - Финансовая помощь

Если Вы хотите помочь проекту и поблагодарить разработчика, Вы можете сделать это путем перевода произвольной суммы через QR код, либо по ссылке. Спасибо.

Банковская карта

QR-code

https://www.tinkoff.ru/rm/firsov.daniil1/yX53F64253

Tether (TRC20) - USDT

QR-code

Мой публичный адрес для получения USDT: TYxksdjm3ji7q69ZPDcBLh7X7F7Lgds4m6

Ссылка для Trust Wallet

6 - Платная версия SHM (Enterprise Edition)

Платная версия SHM: Enterprise Edition (EE) создана для подписчиков платной группы SHM.

Все новые функции сначала появляются в платной версии (EE), а спустя некоторое время появляются и в бесплатной версии.

Некоторые функции доступны только в EE версии.

Установка платной версии SHM

Установка платной версии подразумевает, что у Вас уже установлена бесплатная версия SHM.

При переключении на платную версию все настройки и данные сохраняются. Обратное переключение на бесплатную версию возможно, но некоторые функции платной версии перестанут работать.

  • Оплатите подписку в группу платной поддержки биллинга SHM
  • Вы получите файл key.json. Скачайте его и сохраните на свой сервер с SHM
  • Выполните следующую команду на вашем сервере SHM:
    cat key.json | docker login \
      --username json_key \
      --password-stdin \
      cr.yandex
    
  • Замените Ваш файл docker-compose.yml. Перейдите в директорию с текущим файлом и выполните команду:
    curl -sO https://raw.githubusercontent.com/danuk/shm/master/enterprise/docker-compose.yml
    
  • Обновите Ваш SHM и перезапустите:
     docker compose pull
    
     docker compose down
     docker compose up -d
    

7 - Часто задаваемые вопросы

Ошибки запуска SHM

Ошибка: 502 Bad Gateway

Такую ошибку получают, если контейнеры SHM не запущены:

  • Нужно проверить статус контейнеров docker. Перейдите в директорию где находится файл docker-compose.yml и выполните команду: docker compose ps. Все контейнеры должны быть запущены, в состоянии UP. Если контейнеры не запущены, то нужно их запустить: docker compose up -d. Если видите, что контейнер перезапускается/падает, то нужно посмотреть его логи: docker compose logs ИМЯ_КОНТЕЙНЕРА, например: docker compose logs mysql.
  • Если все контейнеры запущены, то проблема в настройках nginx. Проверьте настройки nginx в соответствии с документацией. Проверьте, запущен ли nginx. Проверьте, не запущен ли другой web сервер на сервере, и если запущены, то отключите их и запустите nginx.

Контейнеры core и spool не запускаются

  • Посмотрите логи: docker compose logs core, скорее всего, вы увидите проблемы коннекта к БД mysql. Проверьте, запущен ли контейнер mysql. Если mysql работает, но вы продолжаете видеть ошибки коннекта к mysql, скорее всего вы забыли положить файл .env в папку, где располагается docker-compose.yml, или положили его после того, как запустили SHM. Положите этот файл и пересоздайте контейнер mysql (ВНИМАНИЕ! ВСЕ данные в БД будут удалены). Для остановки SHM и удаления всех данных из БД, используйте команду: docker compose down -v. Далее, запустите SHM обычным способом и проверьте его работоспособность: docker compose up -d.

Контейнер mysql не запускается

Смотрите логи контейнера mysql: docker compose logs mysql, если вы видите:

  • ошибка запуска из-за неподдерживаемой архитектуры, то попробуйте заменить строку image в файле docker-compose.yml с mysql:8.0 на mysql:8.0-oraclelinux8.
  • ошибка битых данных: пересоздайте контейнер mysql с удалением данных: docker compose down -v, после чего запустите: docker compose up -d.
  • ошибка версии Mysql: видимо ранее, Вы запускали более свежую версию Mysql… либо используйте более свежую версию, либо пересоздайте БД.