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:
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
В Административном интерфейсе необходимо прописать актуальные настройки.
- Зайдите в интерфейс администратора:
https://admin.$DOMAIN
- Перейдите в раздел: “Настройки” -> “Конфигурация” и выполните соответствующие настройки, например:
- В настройке
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
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
В Административном интерфейсе необходимо прописать актуальные настройки.
- Зайдите в интерфейс администратора:
https://admin.$DOMAIN
(admin:admin)
- Перейдите в раздел: “Настройки” -> “Конфигурация” и выполните соответствующие настройки, например:
- В настройке
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
- Настройте Telegram уведомления по инструкции
- Создайте шаблон для формирования уведомления в Telegram через HTTP:
{{
toJson({
chat_id = user.settings.telegram.chat_id
text = "test message"
})
}}
- Создайте группу серверов с транспортом HTTP
- Создайте сервер в этой группе, укажите Хост:
https://api.telegram.org/bot{{ config.telegram.token }}/sendMessage
и укажите созданый шаблон на 2 шаге.
- Привяжите необходимое событие к этой группе серверов (созданную на шаге 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 мог отправлять сообщения Вашим пользователям, необходимо:
- Создать Telegram Bot-а, с помощью бота @BotFather (https://telegram.me/BotFather)
- В админке, в “Настройки” -> “Конфигурация”, необходимо сохранить Telegram Token, полученный на предыдущем шаге
- Создайте шаблон сообщения в админке, которое вы хотите отправлять своим пользователям (“Настройки” -> “Шаблоны”)
- Создайте нужное событие. Привяжите Ваш шаблон к нужному событию. В качестве группы серверов необходимо указать “Telegram уведомления”, или любую другую группу, транспорт которой “telegram”
- Дайте Вашему пользователю ссылку на вашего бота, чтобы он мог его себе добавить. После добавления бота Ваш клиент сможет получать от него уведомления
В случаях, когда Ваш клиент регистрировался в 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 позволяет использовать шаблоны для внешнего использования
Поддерживаемые методы:
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 }}
Ниже приведен список специальных аргументов:
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.
- Настройте одну или несколько платежных систем
- Скачайте шаблон и сохраните в SHM под названием
tg_payments
- В Шаблоне своего бота используйте конструкцию вида:
<% 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.
Данные в хранилище сохраняются под произвольными именами. Имена должны быть уникальными для каждого пользователя.
Типы хранимой информации:
Максимальный объем записи для одного ключа: 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 для использованиях их при выполнении Шаблона (при необходимости)
Использование промокодов
Для того, чтобы использовать промокод необходимо выполнить следующий код:
Использование промокодов с помощью внешней ссылки
Создайте Шаблон для публичного использования с именем: 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"
}
}
}
Ваш секретный ключ можно посмотреть в Настройках.
Пример ссылки для создания платежа:
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
}
}
}
Пример ссылки для создания платежа:
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 перед обновлением
- Перейдите в директорию с Вашим файлом:
docker-compose.yml
- Загрузите новые образы:
docker compose pull
- Перезапустите контейнеры:
docker compose down && docker compose up -d
3.3 - Разное
Разные полезные штуки
Просмотр логов
Если что-то пошло не так и есть необходимость посмотреть логи, то:
- Перейдите в директорию с Вашим файлом:
docker-compose.yml
- Выполните команду:
docker compose logs -f
Сброс пароля пользователя admin
Если Вы забыли пароль администратора, то можно его сбросить следующим образом:
- Перейдите в директорию с Вашим файлом:
docker-compose.yml
- Выполните команду:
docker compose exec core scripts/reset_admin_pass.cgi
Редактирование стилей (CSS) клиентского кабинета
- Перейдите в директорию с Вашим файлом:
docker-compose.yml
- Скачайте текущий файл стилей:
wget https://raw.githubusercontent.com/danuk/shm-client/master/app/assets/css/styles-alternative.css
- Раскоментируйте строки в файле
docker-compose.yml
:
# volumes:
# - ./styles-alternative.css:/app/assets/css/styles-alternative.css
- Перезапустите SHM:
docker compose down
docker compose up -d
Теперь Вы можете править файл стилей styles-alternative.css
и наблюдать результат в клиентском интерфейсе пользователя (обновляя страницу браузера).
Перенос SHM на другой сервер
Если возникла необходимость в переносе SHM на другой сервер, то выполните следующие шаги:
- Сделайте архивную копию БД на текущем сервере
- Установите и запустите SHM на новом сервере
- Восстановите БД нового 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
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 сделайте следующее:
- Установите на свой компьютер:
git
и docker
- Создайте отдельную директорию
shm-dev
и перейдите в неё
- Скачайте
docker-compose.yml
файл для разработки:
wget https://raw.githubusercontent.com/danuk/shm/master/contributing/docker-compose.yml
- Авторизуйтесь (или зарегистрируйтесь) на https://github.com и сделайте Fork для следующих репозиториев (этот шаг можно пропустить, если не планируете делиться вашими наработками):
- Выполните следующие команды для клонирования (загрузки) основных репозиториев (вместо
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 код, либо по ссылке. Спасибо.
Банковская карта
https://www.tinkoff.ru/rm/firsov.daniil1/yX53F64253
Tether (TRC20) - USDT
Мой публичный адрес для получения 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… либо используйте более свежую версию, либо пересоздайте БД.