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

Для взаимодействия сервиса Mandrill с приложением, существует API с довольно широким спектром возможностей, с основными из них нам и предстоит, познакомиться. К тому же имеется возможность использования базового (бесплатного аккаунта) позволяющего производить рассылку до 12000 писем в месяц.

Использование API


Поскольку осуществление взаимодействия с API Mandrill предполагается реализовывать на PHP, то саму библиотеку берем отсюда. Можно воспользоваться установкой через Composer или стандартной установкой (скачиваем, распаковываем и подключаем необходимые модули). Если вы выбрали второй вариант установки, то вам необходимо найти в скачанном архиве каталог “src”, в нем то и содержатся файлы и каталоги необходимой библиотеки.

Для того чтобы иметь возможность использования API, необходимо получить API ключ (имеется ввиду, что вы уже зарегистрированы). Заходим в аккаунт и слева в меню кликаем по пункту “Settings” и в разделе “API Keys” сможем увидеть свой ключ:

Mandrill - Получение API ключа


Далее потребуется создать субаккаунт. Для этого опять же в левостороннем меню кликаем по пункту “Outbound” и после открытия соответствующего раздела настроек, нажимаем кнопку “Create a Subaccount”:

Создание субаккаунта


Для отправки письма можно воспользоваться заранее созданным шаблоном, который должен быть доступен в вашем аккаунте, или же ограничиться отправкой обычного текстового сообщения. Также имеется возможность оформления письма в формате HTML. Для отправки письма, существуют следующие методы класса Mandrill_Messages:

  • array send(message [, async = false [, ip_pool = null [, send_at = null]]]) – в качестве параметра message требуется передать заполненную в виде массива структуру данных, приводимую в табл. 1. Параметр async – устанавливает фоновую пакетную отправку сообщений. В качестве параметра ip_pool требуется указывать имя выделенного пула ip адресов, как видно из описания прототипа функции этот параметр не является обязательным. И в качестве последнего параметра send_at можно указать время в формате UTC timestamp YYYY-MM-DD HH:MM:SS для запланированной отправки письма. Но для этого потребуется платный аккаунт с положительным балансом;
  • array sendTemplate(template_name, template_content, message [, async = false [, ip_pool = null [, send_at = null]]]) – данный метод отличается от предыдущего наличием двух дополнительных параметров. В первом параметре template_name указывается имя существующего шаблона. А второй параметр template_content является массивом элементов динамического контента, структура данных которых описана в табл. 2. Подробнее можно ознакомиться здесь.


Таблица 1. Описание полей структуры данных для отправки письма
Параметр Описание
html
string
Текст с содержанием HTML разметки.
text
string
Текст письма.
subject
string
Тема письма.
from_email
string
Email отправителя.
from_name
string
Имя отправителя.
to
array
Массив элементов “to”, содержащих данные об отправителе.
global_merge_vars
array
Массив элементов “global_merge_vars”, содержащих глобальные данные о метках шаблона.
merge_vars
array
Массив элементов “merge_vars”, содержащих данные о метках шаблона.
subaccount
string
Уникальный идентификатор субаккаунта.
attachments
array
Массив вложений добавляемых к письму.
images
array
Массив изображений добавляемых к письму.
Структура параметра массива to
email
string
Email адресата.
name
string
Имя адресата.
type
string
Тип заголовка. По умолчания используется “to”. Возможные варианты (to, cc, bcc).
Структура параметра массива global_merge_vars
name
string
Имя переменной, чувствительность к регистру. Имя переменной не может начинаться с символа “_”.
content
string
Значение переменной.
Структура параметра массива merge_vars
rcpt
string
Email адрес получателя, к которому буду применены локальные подстановочные метки шаблона.
vars
array
Массив элементов “vars”, содержащих локальные данные о метках шаблона. Структура параметра массива “vars” идентична структуре параметра массива “global_merge_vars”.
Структура параметра массива attachments
type
string
MIME type вложений.
name
string
Имя файла.
content
string
Содержимое файла должно быть в кодировке base64.
Структура параметра массива images.
type
string
MIME type файла – должен быть изображением.
name
string
Имя файла.
content
string
Содержимое файла должно быть в кодировке base64.

Таблица 2. Структура элемента массива использующегося в параметре template_content
Параметр Описание
name
string
Имя метки.
content
string
Содержимое метки.

Метод send() позволяет произвести текстовую отправку письма, а второй метод предоставляет возможность производить отправку писем с использованием, заранее созданных шаблонов. Оба метода возвращают массив со структурой данных представленной в табл. 3.

Таблица 3. Структура ответа, для каждого адресата
Параметр Описание
email
string
Email адресата.
status
string
Статус отправки: отправлено, в очереди, запланировано, отклонено или отправка не возможна.
reject_reason
string
Причина отказа доставки письма. Может быть, одной из следующих вариантов: rejected, hard-bounce, soft-bounce, spam, unsub, custom, invalid-sender, invalid, test-mode-limit, rule
_id
string
Уникальный идентификатор отправленного сообщения.

Чтобы потом не отвлекаться, создадим на будущее сразу и шаблон. Для этого в уже знакомом нам левостороннем меню кликаем по пункту “Outbound” и после перехода в соответствующий раздел, нажимаем кнопку “Create a Template”:

Mandrill - создание шаблона


Здесь нам будет предложено ввести имя шаблона. После ввода такового, нажимаем кнопку “Start Coding”:

Mandrill - указание имени шаблона


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

Mandrill - заполнение полей шаблона


Теперь, когда уже все настроено, можно производить отправку письма. Исходный код отправки письма:

// Подключение библиотеки Mandrill
require_once ('Mandrill.php');

$mail = new Mandrill('API key');

/* Блок динамического контента */
$tpl_con = array(
	array(
		'name' => 'my-label',
		'content' => '<h2>Моя рассылка</h2>'
	)
);

$data = array(
	'html' => 'It is html text',
	'text' => 'It is simple text',
	'subject' => 'Hi friend',
	'from_email' => 'friend@example.com',
	'from_name' => 'Michael',
	'to' => array(		
		array(
			'email' => 'example@gmail.com',
			'name' => 'Lucas',
			'type' => 'to'
		)
	),
	'global_merge_vars' => array(
		array(
			'name' => 'NAME',
			'content' => 'Lucas'
		)
	),
	'merge_vars' => array(
		array(
			'rcpt' => 'example@gmail.com',
			'vars' => array(
				array(
					'name' => 'NAME',
					'content' => 'Lucas'
				)
			)
		)
	),
	'attachments' => array(
		array(
			'type' => 'application/pdf',
			'name' => 'test.pdf',
			'content' => base64_encode(file_get_contents('test.pdf'))
		)
	),
	'images' => array(
		array(
			'type' => 'image/png',
			'name' => 'Smiley.png',
			'content' => base64_encode(file_get_contents('Smiley.png'))
		)
	),
	'subaccount' => 'cust-123'
);

try {
	/* Отправка обычного письма */
	$result = $mail->messages->send($data);
	print_r($result);
	/* Отправка письма с использованием шаблона */
	$result = $mail->messages->sendTemplate('example', $tpl_con, $data);
	print_r($result);
} catch(Mandrill_Error $error) {
	echo 'Error: ' . get_class($error) . ' - ' . $error->getMessage();
}


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

Mandrill — результат отправки


Мы отправили одно и то же письмо с использованием обоих доступных нам методов. Для того чтобы можно было увидеть отличия. В качестве примера к письму были прикреплены изображение и документ в формате PDF
Также была продемонстрирована работа с динамическим контентом и шаблонами. Разница в использовании шаблона от динамического контента состоит в том, что для шаблона указывается метка.
формат написания метки
*|name|* — имя является регистронезависимым

Данная метка в будущем будет заменена на ее содержание. А для динамического контента в HTML теге указывается следующий атрибут.
Оформление динамического контента
mc:edit=«section-name», идентификатор является регистрозависимым

где, section-name – уникальный идентификатор, означающий, что данные указанного идентификатора будут вставлены в содержащий его HTML тег. Так же стоит учесть, что элементы шаблона указанные в поле “global_merge_vars” (см. табл. 1), будут заменены на соответствующие элементы, указанные в поле “vars” структуры “merge_vars” для указанных email адресов в поле “rcpt” структуры “merge_vars”.

Рассмотрим следующий пример:
'to' => array(	
	array(
		'email' => 'example_1@gmail.com',
		'name' => 'Lucas 2',
		'type' => 'to'
	),
	array(
		'email' => 'example_2@gmail.com',
		'name' => 'Lucas 1',
		'type' => 'to'
	)
),
'global_merge_vars' => array(
	array(
		'name' => 'NAME',
		'content' => 'Lucas'
	)
),
'merge_vars' => array(
	array(
		'rcpt' => 'example_1@gmail.com',
		'vars' => array(
			array(
				'name' => 'NAME',
				'content' => 'Alex'
			)
		)
	)
)

Здесь переопределение метки произойдет только для получателя с адресом “example_1@gmail.com”. Соответственно метка в шаблоне с именем “NAME”, будет заменена на имя “Alex”, для остальных получателей содержание метки останется “Lucas”.
При использовании обоих методов для отправки писем необходимо учитывать следующие факторы:
  • При использовании метода send():
    • Если в структуре данных (см. табл. 1) указано поле “html” и оно содержит некие данные, то поле “text” будет им перекрыто.

  • При использовании метода sendTemplate():
    • Если в редакторе шаблонов (см. выше) шаблон имеет формат “HTML”, то ни одно из двух полей: “html” и “text” не сможет перекрыть текст шаблона;
    • Если в редакторе шаблонов, шаблон имеет формат “Text”, то для его автоматической подстановки в шаблон письма при отправке, запрещено указывать в структуре данных поля: “html” и “text”. Вернее указывать можно, но они не должны содержать никаких данных, иначе в тело письма пойдут данные указанные в одном из двух полей в порядке приоритета (html, text);
    • Если в шаблоне не заполнить поле “subject”, то приоритет перейдет к одноименному полю (при наличии такового) в структуре данных (см. табл. 1).


Перечень возможных ошибок содержится в табл. 4.

Таблица 4. Перечень возможных ошибок, при неудавшейся отправке письма
Invalid_Key Неверный API ключ.
Unknown_Message Идентификатор сообщения не существует.
ValidationError Параметры, переданные при вызове, являются некорректными.
GeneralError Произошла ошибка во время запроса.

Комментарии (12)


  1. HunterNNm
    25.04.2015 21:43
    +1

    Давно работаю с Mandrill, удобный сервис и API, буквально утром настраивал один проект. До этого поправлял несколько других. Хотелось бы отметить то, о чём забывают многие при передаче файлов: при кодировании в base64 размер файла увеличивается примерно на треть, после чего возникают ошибки отправки… У них вроде как помечено, но не уверен.


  1. IDVsbruck
    26.04.2015 14:25
    +2

    Не так давно создавали аналогичный сервис, правда, для внутреннего использования компании. Как ориентир, использовали готовые почтовые сервисы, аналогичные рассматриваемому. У нас был список из 14 сервисов. Наиболее удобным и совершенным выглядит в этом списке Mailgun. Mandrill занимает только вторую строчку — тоже весьма удобен и совершенен, но до лидера не дотягивает.
    Недостатки всех без исключения — достаточно слабая поддержка в работе с атачментами, отсутствие или слабая поддержка почтовых конверторов (хотя не исключаю, что они просто надежно спрятаны в коммерческих версиях), полное игнорирование алгоритмов шифрования писем типа PGP/GPG (это очень востребованный инструментарий, но его почему-то неактивно внедряют), негибкая авторизация — сомнительная возможность имплементировать свой алгоритм авторизации.


    1. prishelec Автор
      26.04.2015 19:18

      Хорошее примечание. Когда есть из чего выбрать это здорово.


    1. vma
      27.04.2015 12:00

      Спасибо за наводку на Mailgun.
      В области защиты/подписи электронных писем S/MIME несколько более распространён, нежели PGP.
      А какие «свои» алгоритмы авторизации хочется внедрять? Ведь лучше использовать стандартные проверенные, а не свои самопальные.


      1. IDVsbruck
        27.04.2015 14:28

        Ну да, насчет «самопальный» — это несколько огульно. Речь, скорее, об альтернативах существующей токенной авторизации, когда онлайн или через АПИ получаешь рабочий токен и можешь с ним работать. Если процесс авторизации не прямой (к примеру, у нас централизованный сервис авторизации для всех продуктов компании), то это не очень удобно (я сейчас не буду вдаваться в подробности, но сделать быстро и удобно не получилось). Второе ограничение — используя один аккаунт на сервисе, используем токен(ы) для всего акка, а хотелось бы разграничения ролей при доступе к одному акку, что в обозреваемых сервисах не было доступно. Весьма заманчивой также выглядела бы реализация OAuth2 в сервисах (уверен, на сегодняшний день она есть у кого-то, но на момент обзора год назад этого не было ни у кого из рассматриваемых).


  1. t1gor
    30.04.2015 00:32

    Все круто, сервис действительно удобный. Не понятно только зачем документацию переводить. Хоть тег поставьте, что ли.


  1. prishelec Автор
    30.04.2015 00:43

    Наиболее подходящие теги вроде бы добавил. Еще есть еще предложения у вас?
    Зачем переводить документацию? Отвечу. На удивление статей в «рунете» по mandrill не было (а может просто не нашел). У меня изначально были вопросы по использованию шаблонов (разница между динамическим контентом и заменяемыми метками). Некоторые называют работу с шаблонами — эта работа с динамическими блоками, а некоторые на оборот. Поэтому возникла идея написать статью для внесения ясности. Возможно кому ни будь поможет сэкономить время.


  1. NeonSunlight
    02.05.2015 14:46

    Сервис действительно замечательный, однако есть одно важное «но», которое перечеркивает все его достоинства для нашего проекта — письма доходят медленно.
    Отправляем через smtp. В логах API письмо появляется сразу, а в логах Outbound оно может появиться только через несколько минут, а реально идти может вплоть до получаса.
    На stackoverflow есть подобный вопрос, но ответа на него до сих пор нету.


    1. t1gor
      03.05.2015 12:14

      Так в этом же вся суть асинхронной отправки


      1. NeonSunlight
        03.05.2015 15:23

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


        1. t1gor
          03.05.2015 15:56

          Мне кажется что тут ответа вам никто и не подскажет, просто надо пробовать и выбрать то, что подходит именно вам. Судя по Google-у, вам может подойти MailGun.


        1. IDVsbruck
          03.05.2015 23:04

          Уверен, у него есть нотификаторы, стандартный перечень которых извещает о получении новой почты, изменении папок и т.д. Другое дело, что это часть АПИ, с которым обычно работает собственное приложение или адаптированный софт. Внешние готовые решения типа Outbound могут иметь таковую… а могут не иметь.
          На то это и не почтовый сервер, а почтовый сервис, который ориентирован на использование своего АПИ.
          К примеру, в своем продукте у нас был нотификатор, который немедленно извещал клиент о получении нового письма (если он подписан на получение извещений). Соответственно, клиент либо показывал сообщение, что письмо пришло — пользователь должен обновить окно, либо клиент автоматически посылает запрос на получение новых писем (обычное поведение онлайн-клиентов). В любом случае, этот процесс не происходит автоматом.
          В твоем случае пауза перед получением новой порции писем вызвана тем, что обычно клиенты имею функцтонал опроса родительского сервиса на предмет поддержки сессии/токена, получения новой почты и прочего сопутствующего функционала. Вот и получается, что если опрос стоит на полчаса, то реально пауза может быть от 0 до 30 минут, смотря в какой момент задержки таймера прийдет письмо. Аналогия — старые версии MS Outlook, у которых по дефолту стояло 2 минуты для поллинга — новые письма были доступны либо после рефреша, либо по таймеру.