Добавление собственного SMS шлюза

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

1. Как добавить свой собственный смс сервис (sms шлюз, смс шлюз) в модуль СМС Оповещений?

Во время инициализации, модуль помимо собственной директории  с смс сервисами проверяет и дополнительные директории:
/bitrix/php_interface/bxamaker.smsnotice/
или
/local/php_interface/bxamaker.smsnotice
Соответственно, если в папке есть скрипты, то они будут подгружаться. Описание и пример такого файла будет описан ниже

2. Оповещения о статусе смс сообщения от смс шлюаза

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

Обработчки располагается по адресу - http:// домен /bitrix/tools/bmaker.smsnotice/notice.php. Причем чтобывсе работало верно, обработчик должен иметь соответствующие парамтеры, по которым определяется статус какого именно сообщения изменился и сам статус. Все зависит от конкретного шшлюза. 

Для примера, смс шлюз, работа с которым описна ниже в соответствующем классе, будет делать запросы по такому адресу:
http:// домен /bitrix/tools/bxmaker.smsnotice/notice.php?smsId=1022565&status=1&serviceId=2
при запросе этого ресурса, будет подключен соответствующий сервис обработки, при этом smsId - обязательный параметр значение которого соответствует идентификатору записи о смс сообщении в истории отправленных смс. 

Соответственно будет запрошена информация об этом сообщение, подклчюен автоматически класс для работы с смс сервисом через который отправлялось это смс. После этого сама обработка остальных парамтеров будет передана методу notice() класса соответствующего смс сервиса.

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

3. Описание класса смс сервиса

<?

// файл /local/php_interface/bxamaker.smsnotice/example.php


namespace Bxmaker\SmsNotice\Service;

use Bitrix\Main\Config\Option;
use Bitrix\Main\Loader;
use Bitrix\Main\Localization\Loc;
use Bxmaker\SmsNotice\Error;
use Bxmaker\SmsNotice\Manager;
use Bxmaker\SmsNotice\ManagerTable;
use Bxmaker\SmsNotice\Result;
use Bxmaker\SmsNotice\Service;

Loc::loadMessages(__FILE__);


/**
* Название класса должно быть уникальным и должно соответствовать названию файла ./example.php
* Class example
* @package Bxmaker\SmsNotice\Service
*/
Class example
{
private $module_id = 'bxmaker.smsnotice';

private $login = '';
private $pass = '';
private $notice_path = '';
private $oHttp = null;

/**
* Инициализация сервиса
* example constructor.
* @param array $arParams - параметры из настроек сервиса в админке
*/
public function __construct($arParams = array())
{
if (is_null($this->oHttp)) {
$this->oHttp = new \Bitrix\Main\Web\HttpClient();
}

$this->login = $arParams['LOGIN'];
$this->pass = $arParams['PASS'];

// site.ru - ваш сайт, на который могут приходить оповещения от смс шлюза
//$this->notice_path = 'http://site.ru/bitrix/tools/bxmaker.smsnotice/notice.php';

$this->notice_path = 'http://' . Option::get('main', 'server_name', '') . '/bitrix/tools/' . $this->module_id . '/notice.php';
}


/**
* Отправка сообщения, этот метод вызывается уже после добавления смс в локальную базe
* @param $phone - телефон получателя
* @param $text - текст
* @param array $arParams - smsId - ID записи смс, service - данные о сервисе через который отправляется
* @return Result - объект класса Result c результатом или ошибкой
*/
public function send($phone, $text, $arParams = array())
{
//делаем запрос для отправки
//notice_url - передаем арес по которому нас надо оповещать о статусе смс, в данном примере
// у сервис эт делает (но не все смс сервисы так делают)
$res = $this->oHttp->get('http:// домен /api/send_sms.php?' . http_build_query(array(
'to' => $phone,
'msg' => $text,
'notice_url' => $this->getNoticeUrl($arParams)
)));

// сервис возвращает ответы в виде цифр
// 1 - доставлено
// 8 - отправлено
// иначе ошибка

if ($res == 1) {
// \Bxmaker\SmsNotice\SMS_STATUS_DELIVERED - соответствует статусу сообщения доставлено,
// а админке в списке отправленных смс для текущего смс будет устанволен статус доставлено
// а в setMore устанавливаются доп парамтеры, номер телефона и текст, для анализа ошибок
$result = new Result(\Bxmaker\SmsNotice\SMS_STATUS_DELIVERED);
$result->setMore('phone', $phone);
$result->setMore('text', $text);

// если возвращается идентификатор сообшений от смс шлюза
// $result->setMore('params', array(
// 'messageId' => (isset($match[1]) ? trim($match[1]) : '')
// ));

} else {

if ($res == 8) {
// в данном случае отправлено
$result = new Result(\Bxmaker\SmsNotice\SMS_STATUS_SENT);
$result->setMore('phone', $phone);
$result->setMore('text', $text);

// если возвращается идентификатор сообшений от смс шлюза
// $result->setMore('params', array(
// 'messageId' => (isset($match[1]) ? trim($match[1]) : '')
// ));

} else {
// здесь мы фиксируем ошибку
// в $res будет текст ошибки от сервиса к примеру: не достаточно средств
// BXMAKER_SMSNOTICE_SERVICE_SEND_ERROR - это идентификатор типа ошибки
// ну и доп параметры для анализа причин ошибок - кому оптравляли. какой текст отправляли
$result = new Result(new Error($res, 'BXMAKER_SMSNOTICE_SERVICE_SEND_ERROR', array(
'phone' => $phone,
'text' => $text
)));
}
}
return $result;
}
public function sendPack($arPack) { // PackItem - array( // "phone" => $arItem['PHONE'], // "clientId" => $id, // "text" => $arItem['TEXT'], // "sender" => $arItem['SENDER'] // ) $result = new Result(); $arMsg = array(); foreach ($arPack['messages'] as $msg) { $arMsg[$msg['clientId']] = $this->send($msg['phone'], $msg['text']); } $result->setResult(array('messages' => $arMsg)); return $result; }
/**
* для конкретного сервиса возвращает путь с парамтерами дялоповещения сайта о статус конкретной смс от смс шлюза
* @param array $arParams
* @return string
*/
private function getNoticeUrl($arParams = array())
{
return $this->notice_path . '?' . http_build_query(array(
'smsId' => $arParams['smsId'],
'serviceId' => $arParams['service']['ID'],
'status' => '%d'
));
}


/**
* Проверка баланса
* @return Result
*/
public function getBalance()
{
$res = $this->oHttp->get('http:// домен /api/get_balance2.php?' . http_build_query(array(
'login' => $this->login,
'pass' => $this->pass
)));
// мсервис в ответ пришлет сумму на балансе в цифровом виде
if (intval($res) > 0) {
$result = new Result($res . ' руб.');
} else {
// в $res текст ошибки от сервиса, например - не верный логин или пароль
$result = new Result(new Error($res, 'BXMAKER_SMSNOTICE_SERVICE_GETBALANCE_ERROR'));
}
return $result;
}

/**
* Прием оповещений от смс сервиса по http о статусе соообщения
* @return Result объект класса Result
* @throws \Bitrix\Main\ArgumentException
*/
public function notice()
{

/**
* Не все сервисы поддерживают такие оповещения
* СМС шлюз, после отправки смс сообщения может прислать нам статус сообщения,
* в данном случае запрос от сервиса придет по такому url адресу -
* http:// домен /bitrix/tools/bxmaker.smsnotice/notice.php?smsId=1022565&status=1&serviceId=2
*/

$req = \Bitrix\Main\Application::getInstance()->getContext()->getRequest();
$result = new Result();
$oManagerTable = new ManagerTable();
$dbr = $oManagerTable->getList(array(
'filter' => array(
'ID' => (int)$req->getQuery('smsId')
)
));

if ($ar = $dbr->fetch()) {
// раз найдено то проверим статус
switch ($req->getQuery('status')) {
case 1: {
$result->setResult(\Bxmaker\SmsNotice\SMS_STATUS_DELIVERED);
break;
}
case 8: {
$result->setResult(\Bxmaker\SmsNotice\SMS_STATUS_SENT);
break;
}
default: {
$result->setError(new Error(GetMessage($this->module_id . '.SERVICE.SMS48.NOTICE.BXMAKER_SMSNOTICE_SERVICE_NOTICE_ERROR_UNKNOWN_SMS_STATUS'), 'BXMAKER_SMSNOTICE_SERVICE_NOTICE_ERROR_UNKNOWN_SMS_STATUS', array(
'params' => $req->getQueryList()
)));
}
}
} else {
$result->setError(new Error(GetMessage($this->module_id . '.SERVICE.SMS48.NOTICE.NOT_FOUND_SMS_BY_ID'), 'BXMAKER_SMSNOTICE_SERVICE_NOTICE_ERROR_NOT_FOUN_SMS_BY_ID'));
}

$result->setMore('smsId', (int)$req->getQuery('smsId'));
return $result;
}


/**
* Агент конкретного сервиса вызывается с данными о нескольких смс, для запроса к смс шлюзу и получения статусов
* @param $arSms
* @return Result
*/
public function agent($arSms)
{
$result = new Result(true);
$arResult = array();
$arError = array();


foreach ($arSms as $smsId => $arSmsMore) {
if (isset($arSmsMore['params']['messageId'])) {

$res = $this->oHttp->get('http:// домен /api/check_sms.php?' . http_build_query(array(
'id' => $arSmsMore['params']['messageId']
)));

if ($res == 1) {
$arResult[$smsId] = new Result(\Bxmaker\SmsNotice\SMS_STATUS_DELIVERED);
} else {

if ($res == 8) {
$arResult[$smsId] = new Result(\Bxmaker\SmsNotice\SMS_STATUS_SENT);
} else {
$arResult[$smsId] = new Result(new Error('UNKNOWN_MESSAGE_ID', \Bxmaker\SmsNotice\SMS_STATUS_ERROR, array(
'response' => $res
)));
}
}
} else {
$arError[] = new Error('UNKNOWN_MESSAGE_ID', \Bxmaker\SmsNotice\SMS_STATUS_ERROR);
$arResult[$smsId] = new Result(new Error('UNKNOWN_MESSAGE_ID', \Bxmaker\SmsNotice\SMS_STATUS_ERROR));
}
}

$result->setMore('results', $arResult);
$result->setMore('errors', (!empty($arError) ? $arError : null)); // ошибок нет

return $result;
}

/**
* Параметры выводимые в админке при выборе текущего смс шлюза
* @return array
*/
public function getParams()
{

return array(
'LOGIN' => array(
'NAME' => 'Логин',
'NAME_HINT' => 'Пояснение к полю',
'TYPE' => 'STRING',
'VALUE' => ''
),
'PASS' => array(
'NAME' => 'Пароль',
'NAME_HINT' => 'Пояснение к полю',
'TYPE' => 'STRING',
'VALUE' => ''
)
);
}

public function getDescription()
{
return 'Здесь описание выподимое над полями, с информацие и ссылками где, что и как заполнять';
} }

В результате при добавлении  нового  смс-сервиса или изменении добавленного ранее вы увидите в списке сервис с названием = названию класса (и файла),  пример класса выше на скриншоте

Скриншот добавленного смс-шлюза