Интерфейс Прикладного Программирования.

API (application programming interface).

Для того, чтобы иметь возможность внешним приложением (возможно, даже работающим на другом домене и хостере) производить какие-то элементарные операции над подписчиками, имеет смысл оснастить Почтовую Нану так называемым API. Кому это надо, тот в курсе, что сиё есть.

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

URL API и ключ для подписывания запросов.

Опубликованы на морде админки, там их и ищите.

Функции для работы с API для внешнего скрипта.

Пусть будут такими:

<?php /* Выше этой строки в файле ничего не должно быть */

   
$url='URL API впишите сюда';
   
$key='Ключ для подписывания запросов впишите сюда';
   
   
/* Тут код для работы с API */

function postman_api_transport($url,$data) {
   if (
function_exists('add_to_csp'))
      
add_to_csp(
         
'form-action',
         
parse_url($url,PHP_URL_SCHEME).'://'.
         
parse_url($url,PHP_URL_HOST).'/'
      
);
   
$curl=curl_init();
   
curl_setopt($curl,CURLOPT_URL$url);
   
curl_setopt($curl,CURLOPT_POST1);
   
curl_setopt($curl,CURLOPT_POSTFIELDS,urldecode(http_build_query($data)));
   
curl_setopt($curl,CURLOPT_RETURNTRANSFERtrue);
   
curl_setopt($curl,CURLOPT_SSL_VERIFYPEERfalse);
   
$answer=curl_exec($curl);
   
curl_close($curl);
   return 
unserialize(trim($answer));
}
   
function 
postman_api_sign($key,$data) {
   
$sign='';
   
$num=0;
   foreach (array(
'ksort','krsort') as $sort) {
      if (
$sort=='ksort')  ksort ($data,SORT_STRING);
      if (
$sort=='krsort'krsort($data,SORT_STRING);
      foreach (
$data as $k => $v) {
         if (
$k!='sign') {
            
$num++;
            
$sign.=is_array($v) ?
               
postman_api_sign($key,$v):
               
md5($num.$k.$key.$v);
         }
      }
   }
   return 
md5($sign);
}

/* Ниже этой строки в файле ничего не должно быть. */ ?>

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

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

На место комментария /* Тут код для работы с API */ можно писать собственно запросы, после чего анализировать ответы. Далее приведены примеры, как это может выглядеть.

1) Подписка на рассылку.

Сформируем массив данных на конкретном примере.

   $data=array(
      
'instruction' => 'add user',      # Команда подписки юзера
      
'email'       => 'user@host.ru',  # e-mail подписчика
      
'name'        => 'User Name',     # Имя подписчика
      
'dispatch'    => 'dispatch',      # Идентификатор рассылки
      
'letters'     => 0,               # Число "полученных" писем
   
);
   
$data['sign']=postman_api_sign($key,$data);
   
$answer=postman_api_transport($url,$data);
   
$answer['sign']=postman_api_sign($key,$answer) ? truefalse;

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

Три дальнейшие строчки кода делают очевидные вещи:

  1. Подписывание данных ключом API.
  2. Передача данных Почтовой Нане по URL-у API, и получение ответа.
  3. Верифицирование данных по их подписи.
    Любая из сторон обмена данными подписывает их одинаковым ключом.
    Эту подпись, естественно, надо проверять в связке с самими данными.

В итоге получаем массив $answer такого содержания:

   Array
(
    [
answer] => user@host.ru User Name добавлен в службу dispatch с числом полученный писем 0
    
[ok] => ok
    
[sign] => 1
)

Элемент массива answer рассказывает на понятном человеческом языке, какие именно действия исполнила Почтовая Нана в ответ на поступившую команду.

Элемент ok присутствует всегда, когда команда благополучно исполнена.
Можете использовать его в качестве флага успешного выполнения задания.

Элемент sign принимает значение true, если ответ Почтовой Наны сопровождён подписью, валидной для переданных ею данных. В противном случае значение будет false, и ответу доверять нельзя.

Обратите внимание:

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

В любом случае, когда что-то пошло не так, элемент ok будет отсутствовать, и, кроме того, появится элемент error с подробной расшифровкой ошибки на человеческом языке. Три примера того, как это работает:

 # По ошибке вместо команды 'add user' указана команда 'add_user'
Array
(
    [
error] => Array
        (
            [
0] => Получена незнакомая командане поддерживаемая API.
        )

    [
sign] => 1
)
 
# Мы указали идентификатор несуществующей службы Dispatch вместо dispatch
Array
(
    [
error] => Array
        (
            [
0] => Служба с идентификатором Dispatch не найдена.
        )

    [
sign] => 1
)
 
# И в дополнение к этому намудрили с именем подписчика и его адресом:
Array
(
    [
error] => Array
        (
            [
0] => e-mail пользователя @host.ru некорректен.
            [
1] => Не задано имя пользователялибо оно не валидно.
            [
2] => Служба с идентификатором Dispatch не найдена.
        )

    [
sign] => 1
)

2) Отписка от рассылки.

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

   $data=array(
      
'instruction' => 'delete user',   # Команда отписки юзера
      
'email'       => 'user@host.ru',  # e-mail подписчика
      
'dispatch'    => 'dispatch',      # Идентификатор рассылки
   
);

Ответные данные в массиве $answer будут такими:

Array
(
    [
answer] => Адрес user@host.ru удалён из службы dispatch
    
[ok] => ok
    
[sign] => 1
)

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

 # Запрос:
   
$data=array(
      
'instruction' => 'delete user',
      
'email'       => 'user@host.ru,other@user.com',
      
'dispatch'    => 'dispatch',
   );
 
# Ответ:
Array
(
    [
answer] => Адреса user@host.ruother@user.com удалены из службы dispatch
    
[ok] => ok
    
[sign] => 1
)

Обратите внимание:

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

Фактической проверки, есть ли пользователь в данной службе, не производится. Как проверяются такие вещи, рассказано далее.

3) Информация о подписчике.

Массив данных для получения информации о пользователе минимально содержит команду, адрес подписчика, и идентификатор службы опционально:

   $data=array(
      
'instruction' => 'user info',     # Команда получения информации.
      
'email'       => 'user@host.ru',  # E-mail подписчика.
      
'dispatch'    => 'dispatch',      # Идентификатор службы. Может отсутствовать.
   
);

Ответные данные в массиве $answer будут такими:

 # Пользователь успешно найден:
Array
(
    [
answer] => Array
        (
            [
0] => Array
                (
                    [
0] => user@host.ru # E-mail пользователя.
                    
[1] => User Name    # Имя пользователя.
                    
[2] => 0            # Число полученных писем.
                    
[3] => dispatch     # Идентификатор службы.
                    
[4] => 1481188724   # Таймстамп даты подписки.
                    
[5] => 0            # Таймстамп последнего полученного письма.
                                        # Или ноль, если письма ещё не получались.
                
)
        )
    [
ok] => ok
    
[sign] => 1
)
 
# Пользователь не найден;
Array
(
    [
error] => Array
        (
            [
0] => Пользователь с e-mail user@host.ru не найден в службе dispatch
        
)

    [
sign] => 1
)

Если идентификатор службы в запросе не упоминать:

   $data=array(
      
'instruction' => 'user info',     # Команда получения информации.
      
'email'       => 'user@host.ru',  # E-mail подписчика.
   
);

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

 # Пользователь успешно найден:
Array
(
    [
answer] => Array
        (
            [
0] => Array
                (
                    [
0] => user@host.ru
                    
[1] => User Name
                    
[2] => 0
                    
[3] => dispatch
                    
[4] => 1481188724
                    
[5] => 0
                
)
            [
1] => Array
                (
                    [
0] => user@host.ru
                    
[1] => User Name
                    
[2] => 1
                    
[3] => desman
                    
[4] => 1481101115
                    
[5] => 1481102239
                
)
        )

    [
ok] => ok
    
[sign] => 1
)
 
# Пользователь не найден;
Array
(
    [
error] => Array
        (
            [
0] => Пользователь с e-mail user@host.ru не найден ни в одной службе.
        )

    [
sign] => 1
)

Примечание:

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