Сначала немного разглагольствований :) Рано или поздно перед любым интернет-магазином встает вопрос настройки брошенной корзины. Статистика и сосущее под ложечкой ощущение упущенных денег не щадят никого.

Источник
Процент брошенных корзин на первый квартал 2018 года в разрезе индустрии:

Источник
При этом, несмотря на общедоступную статистику, большинство интернет-магазинов не пользуются доступными возможностями и не подключают брошенную корзину. Недавнее «домашнее» исследование от EmailSoldiers наглядно показывает, что бОльшая часть магазинов вообще не замо��ачивается об этом.

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

Источник
И вот мы с камрадом Артемом Александровым начали внедрение корзины с двух сторон.
Кратко описываем суть задачи.
Задача: подключить брошенную корзину для сайта ххх.хх с помощью рассылочного сервиса Mailchimp
Выдаем все необходимые материалы.
Ключ API: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-usXX
Где взять ключ?

→ Даем ссылку на документацию
ID листа, к которому подключаем Store: XXXXXXXXXX
Где взять ID листа?

В сервисе рассылок заранее должно быть создано письмо. Как только API-запрос получен сервисом рассылок, происходит автоматическое формирование письма и добавление адресата в очередь для отправки.
Для нашего случая мы выбрали следующую логику отправки брошенной корзины:
авторизованный на сайте пользователь добавляет товары в корзину, не совершает транзакцию и не завершает заказ, корзина остается без изменений 1 час. После этого отправляется запрос в Mailchimp, в котором передается email, состав заказа пользователя, изображения товаров, цена товаров и ссылка на корзину пользователя.
При создании автоматизации для брошенной корзины мейлчимп сразу спрашивает, одно ли будет письмо или несколько и предлагает выбрать подключенный магазин.

В базовых шаблонах мч предлагает на выбор три штуки:

В лучших традициях, если у вас есть время, можно заверстать корзину самостоятельно.
Динамические товары без стилей выглядят примерно так (забудем про отступы, они некрасивенько здесь отображаются):
Казалось бы, если изменить цифру в переменной *|ABANDONED_CART:[$total=3]|*, то в письме будет отображаться другое количество товаров, но нет, поставьте хоть 5, хоть 100, мч отказывается показывать другое количество.
И, что тоже немного странно, переменная *|PRODUCT:PRICE|* заменяется на значения формата RUB288, и поменять это тоже почему-то нельзя, но об этом позже.
Для разнообразия мы пытались подставить еще и переменные с количеством игр и с общей стоимостью заказа, которые передаем по api, но мейлчимп и тут сказал «нет». Что ж, да будет так.
С мэйлчимпом был исторический опыт интеграции с их сторонним сервисом mandrill, там все просто и понятно. Очень дружелюбная документация (конечно же, на английском), но не возникло никаких шероховатостей и заработало с первого пинка.
Также у нас на сайте был внедрен механизм подписки через специальные формы. Вот там мы в полной мере ощутили недосказанность и двусмысленность документации мэйлчимпа. Английский не является проблемой для опытного разработчика, а вот знание клингонского диалекта не подразумевается по умолчанию.
Исходные данные такие: язык php7 и фрэймворк yii2, который сильно оброс уже своей экосистемой. Т.е. у нас уже 6 небольших проектов, которые стараются использовать общие компоненты как на бэкенде, так и на фронтенде. Соответственно, реализация любой задачи требует решать ее проектонезависимо, но это не подразум��вает фрэймворконезависимость, т.к. за это приходится платить человекочасами, коих всегда дефицит.
Получив задачу на интеграцию, надо первым делом осмотреться. Что нам дано? Во-первых, сервис мэйлчимп, с которым надо подружиться. Идем на гитхаб и видим, что там достаточно много реализаций. Но выбор прост — у самого популярного пакета 1.5к звезд (drewm/mailchimp-api).
Пакет дает простую обертку над rest-взаимодействием с мэйлчимпом. Нам остается только обрастить это своей логикой.
Во-вторых, нам дана документация. Исходя из документации, у нас есть ресурс Store с вложенными ресурсами: Cart, Customer, Order, Product, Promo rule. Для брошенной корзины без рекомендованных товаров нам понадобятся только Product, Cart и Customer. Cart в свою очередь состоит из набора Cart line, а Product содержит Product variants.
Мы декомпозировали задачу следующим образом:
Ок, поехали. Первым делом беремся за сущность «магазин». Мы решили сразу использовать тестовый и боевой вариант магазина и, в зависимости от переменной окружения, отвечающей за дев/прод режим, мы работали либо с одним магазином, либо с другим.
Чтобы загрузить данные по магазину, мы стучимся post-запросом по адресу /ecommerce/stores со следующим набором параметров:
Параметров несколько больше, но все зависит от потребностей. Т.к. мы не собирались использовать контактные данные магазина в письмах, то не заполнили поля phone, address, timezone и т. п.
Но нас ожидал небольшой сюрприз. Поле money_format вроде специально создано для возможности представить цену в удобном нам формате. Но при построении шаблона брошенной корзины мэйлчимп упорно подставляет RUB перед числом. Мэйлчимп, перестань!
После загрузки мы можем проверить данные с помощью get-запроса по адресу /ecommerce/stores, чтобы увидеть все загруженные магазины, либо /ecommerce/stores/{id} для получения данных по конкретному магазину.
Теперь можно добавлять все остальные данные, т. к. Store является корневой точкой в нашем дереве данных, т.к. все остальные данные мы будем загружать в определенный магазин.
Так-с, чтобы МЧ мог подставлять товары в брошенную корзину, надо ему скормить эти товары. Для этого у нас есть адрес /ecommerce/stores/{store_id}/products, куда мы отправляем post-запросы на создание продуктов в системе.
Что тут примечательного? Ну во-первых, каждый товар должен состоять хотя бы из одного товарного предложения. По сути Product — это некий контейнер для загрузки товарных предложений. Причем, id товара и товарного предложения могут пересекаться, т. к. это разные ресурсы в api МЧ.
И вот тут началась недосказанность документации МЧ. Приходилось догадываться, пускай это было несложно, но ведь можно было сделать нормально, а не как всегда.
Поле handle было описано как «the handle of a product». Ок, посовещавшись мы решили, что это часть урла, относящегося к самому продукту (чпу). Но это подтвердилось только в ходе тестов.
Также к товару можно прикрепить массив картинок, но мы решили, что нам не пригодится и поэтому грузили только главное изображение как к товару, так и к каждому товарному предложению.
И тут у нас возникла проблема, товары почему-то не отображались в темплейтах мейлчимпа.
Начали рыться в доке по Product. И нашли поле visibility с роскошным описанием:

Ну ок, тип String! А что туда можно передавать? Почему нельзя описать все возможные значения?! Я ведь могу туда отправить, например, «show me pls!».
Благо есть пример запроса!

Ну, это не отменяет проблему. Я ведь так и не знаю, какие еще могут быть значения, которые могут оказаться полезными.
Все, с этим справились! Теперь email-маркетолог может убедиться в наличии товаров в системе через построение шаблона с участием товаров или все через те же get-запросы с помощью консоли.
Дальше перед нами стоит задача загрузки брошенных корзин в МЧ. Изначально в голову пришло 2 варианта:
Для начала делаем запрос в нашу базу данных (далее БД) за нашими данными в окне от 1 часа до 3. Почему 3? Через час после последнего изменения мы отправляем корзину в систему. В МЧ настроен минимально возможный интервал отправки корзины — 1 час. Поэтому в теории через 2 часа ± 5 минут произойдет отправка письма. Так что 3 часа — это величина даже с запасом.
Получив данные из БД, мы делаем get-запрос по адресу /ecommerce/stores/{store_id}/carts. Таким образом мы получаем все корзины, которые сидят в системе e-commerce и ждут своей очереди на отправку (либо уже отправлены). Для чего нам это нужно? Нужно для синхронизации с нашими данными. Мы отправим все корзины, полученные из БД, но нам нужно удалить те, которые уже не находятся в промежутке 1-3 часа. После 3х — уже неактуальные данные. До часа — корзины, к��торые могли опять возобновить, либо оформить заказ.
Для удаления нам надо просто найти разницу между двумя массивами/коллекциями корзин.
Получив корзины, которые необходимо удалить, мы отправляем delete-запрос /ecommerce/stores/{store_id}/carts/{cart_id}.
Дальше берем корзины для загрузки и циклом отправляем их post-запросами в систему.
Параметры корзины выглядят как-то так:
И опять наша любимая рубрика «догадайся, как работают эти поля». Например, методом научного тыка было выявлено, что можно не создавать покупателя отдельным запросом. Надо передать минимально требуемый набор полей, и он автоматически создастся, если его не было в системе. В нашем случае мы ограничились id, email, opt_in_status. Последний параметр отвечает за состояние подписки юзера в нашем листе. Если он true, то это означает состояние subscribed, в противном случае transactional.
Список товаров без проблем загружается через массив Cart Lines, который в свою очередь является ресурсом сущности Cart. Т.е. мы можем отдельно управлять этим набором с помощью rest-запросов.
Ну вот вроде бы и все? А вот и нет.
При тестировании мы обратили внимание, что отправив одну и ту же корзину, она отправляется лишь раз. Хотя мы ее удаляли из системы и загружали заново. Нигде ничего не сказано, ни единого слова! В итоге опытным путем, с помощью какой-то матери, мы приняли за основу гипотезу, что корзина с одним и тем же id может быть отправлена только один раз.
Тут я полностью согласен с создателями, что это ограничение уберегает нас от заспамливания пользователей. Но напишите об этом! Не нужны нам квесты, нам хватает своих мистических ошибок в коде, который мы пишем.
После того, как мы все это проделали, МЧ начал присылать нам красивые письма о брошенной корзине. И тут появился второй вопрос. Если юзер, бросил корзину и вернулся из письма в свой же аккаунт, т. е. он был авторизован в момент перехода по ссылке, то он попадет в свою корзину без проблем. А так получается, что письмо говорит тебе «на! возьми свою корзину назад!», а мы при переходе ему говорим «упс, чего-то потерялося все! Мы ничего не трогали! Оно само!»
Было решено кодировать состав корзины в строку и передавать в checkout_url при отправке корзины в МЧ. А при переходе на сайт ловить эту строку, декодировать и накидывать все товары в корзину, не забыв перед этим ее полностью обнулить.
Таким образом, в какой бы браузер мы не отправили юзера, он получает свою корзину, как мы и обещали. Единственный минус, что ему остается только авторизоваться. Но авторизовывать через ссылку — это моветон, да и вообще дело опасное, в первую очередь для наших клиентов.
Что в итоге? В принципе, проблем особых не было при реализации, так как очень часто выручали ответы МЧ при ошибках, связанных с валидацией переданных полей. Но их было бы еще меньше, если бы они нормально по-человечьи описали все эти тонкости работы МЧ и более подробно описали бы поля.
Чтобы отслеживать успех всей операции, придется настроить и периодически посматривать отчет в аналитике. Представим, что у нас у всех, как у взрослых, подключен ecommerce, иначе чуда не получится :)
Чтобы собрать новый отчет под брошенную корзину, идем в «Мои отчеты»:

Дальше «Добавить отчет»:

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

That’s all forks!
Теперь у вас настроена отправка брошенной корзины и отчет, по которому вы можете смотреть выхлоп с нее. И тестируйте, тестируйте, тестируйте! ;)
Процент брошенных корзин с 2006 по 2017

Источник
Процент брошенных корзин на первый квартал 2018 года в разрезе индустрии:

Источник
При этом, несмотря на общедоступную статистику, большинство интернет-магазинов не пользуются доступными возможностями и не подключают брошенную корзину. Недавнее «домашнее» исследование от EmailSoldiers наглядно показывает, что бОльшая часть магазинов вообще не замо��ачивается об этом.
Текущая статистика по подключенным брошенным корзинам

Источник
При этом все (и мы тоже не святые) нагоняют трафик, докручивают рекламу и креативы, но даже не пытаются вернуть человека, который сорвался в последний момент.
А ведь и в первой итерации можно получить прирост заказов, используя письмо без динамического контента, нужно только настроить его. Всего один раз приложить усилия, чтобы в фоновом режиме это приносило деньги — это ли не сказка?
Естественно, письмо с динамическим контентом, в которое подтягиваются товары из корзины, может сработать лучше. А, может, на вашу аудиторию круче подействует очередной котенок с грустными глазами. Или вы подтянете рекомендуемые товары к товарам в корзине и увидите, что из писем их покупают чаще и повышают средний чек. Или у вас дойдут руки сделать серию писем, из которой конверсия будет еще больше.
Можно улучшать и тестировать письма брошенной корзины до недостижимого идеала, но любое единожды настроенное письмо уже несравнимо лучше, чем ничего.
Конверсия для брошенной корзины по данным RetailRocket

Источник
И вот мы с камрадом Артемом Александровым начали внедрение корзины с двух сторон.
Техническая реализация
ТЗ на интеграцию
Кратко описываем суть задачи.
Задача: подключить брошенную корзину для сайта ххх.хх с помощью рассылочного сервиса Mailchimp
Выдаем все необходимые материалы.
Ключ API: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-usXX
Где взять ключ?

→ Даем ссылку на документацию
ID листа, к которому подключаем Store: XXXXXXXXXX
Где взять ID листа?

В сервисе рассылок заранее должно быть создано письмо. Как только API-запрос получен сервисом рассылок, происходит автоматическое формирование письма и добавление адресата в очередь для отправки.
Для нашего случая мы выбрали следующую логику отправки брошенной корзины:
авторизованный на сайте пользователь добавляет товары в корзину, не совершает транзакцию и не завершает заказ, корзина остается без изменений 1 час. После этого отправляется запрос в Mailchimp, в котором передается email, состав заказа пользователя, изображения товаров, цена товаров и ссылка на корзину пользователя.
Верстка шаблона
При создании автоматизации для брошенной корзины мейлчимп сразу спрашивает, одно ли будет письмо или несколько и предлагает выбрать подключенный магазин.

В базовых шаблонах мч предлагает на выбор три штуки:

- Брошенная корзина с динамическими товарами
- Брошенная корзина с продуктовыми рекомендациями (нужно настраивать отдельно)
- Брошенная корзина без товаров (просто текстовое письмо)
В лучших традициях, если у вас есть время, можно заверстать корзину самостоятельно.
Динамические товары без стилей выглядят примерно так (забудем про отступы, они некрасивенько здесь отображаются):
<table>
<tbody>
*|ABANDONED_CART:[$total=3]|*
<table>
<tbody>
<tr>
<td>
<a href="*|CART:URL|*" title="*|PRODUCT:TITLE|*" target="_blank">
<img src="*|PRODUCT:IMAGE_URL|*">
</a>
</td>
<td>
*|PRODUCT:TITLE|* — *|PRODUCT:PRICE|*
</td>
</tr>
</tbody>
</table>
*|END:ABANDONED_CART|*
</tbody>
</table>
*|END:ABANDONED_CART|*
</tbody>
</table>
Казалось бы, если изменить цифру в переменной *|ABANDONED_CART:[$total=3]|*, то в письме будет отображаться другое количество товаров, но нет, поставьте хоть 5, хоть 100, мч отказывается показывать другое количество.
И, что тоже немного странно, переменная *|PRODUCT:PRICE|* заменяется на значения формата RUB288, и поменять это тоже почему-то нельзя, но об этом позже.
Для разнообразия мы пытались подставить еще и переменные с количеством игр и с общей стоимостью заказа, которые передаем по api, но мейлчимп и тут сказал «нет». Что ж, да будет так.
Слово программисту :)
С мэйлчимпом был исторический опыт интеграции с их сторонним сервисом mandrill, там все просто и понятно. Очень дружелюбная документация (конечно же, на английском), но не возникло никаких шероховатостей и заработало с первого пинка.
Также у нас на сайте был внедрен механизм подписки через специальные формы. Вот там мы в полной мере ощутили недосказанность и двусмысленность документации мэйлчимпа. Английский не является проблемой для опытного разработчика, а вот знание клингонского диалекта не подразумевается по умолчанию.
Исходные данные такие: язык php7 и фрэймворк yii2, который сильно оброс уже своей экосистемой. Т.е. у нас уже 6 небольших проектов, которые стараются использовать общие компоненты как на бэкенде, так и на фронтенде. Соответственно, реализация любой задачи требует решать ее проектонезависимо, но это не подразум��вает фрэймворконезависимость, т.к. за это приходится платить человекочасами, коих всегда дефицит.
Получив задачу на интеграцию, надо первым делом осмотреться. Что нам дано? Во-первых, сервис мэйлчимп, с которым надо подружиться. Идем на гитхаб и видим, что там достаточно много реализаций. Но выбор прост — у самого популярного пакета 1.5к звезд (drewm/mailchimp-api).
Пакет дает простую обертку над rest-взаимодействием с мэйлчимпом. Нам остается только обрастить это своей логикой.
Во-вторых, нам дана документация. Исходя из документации, у нас есть ресурс Store с вложенными ресурсами: Cart, Customer, Order, Product, Promo rule. Для брошенной корзины без рекомендованных товаров нам понадобятся только Product, Cart и Customer. Cart в свою очередь состоит из набора Cart line, а Product содержит Product variants.
Мы декомпозировали задачу следующим образом:
- Загрузить данные по магазину в ресурс Stores
- Загрузить все доступные к покупке товары в Products
- Настроить загрузку корзин с пользователями по расписанию
Ок, поехали. Первым делом беремся за сущность «магазин». Мы решили сразу использовать тестовый и боевой вариант магазина и, в зависимости от переменной окружения, отвечающей за дев/прод режим, мы работали либо с одним магазином, либо с другим.
Чтобы загрузить данные по магазину, мы стучимся post-запросом по адресу /ecommerce/stores со следующим набором параметров:
[
'id' => 'dev.***.ru',
'list_id' => '****',
'name' => '*** - test',
'domain' => 'dev.***.ru',
'email_address' => 'admin@***.ru',
'currency_code' => 'RUB',
'primary_locale' => 'ru',
'money_format' => '₽',
]Параметров несколько больше, но все зависит от потребностей. Т.к. мы не собирались использовать контактные данные магазина в письмах, то не заполнили поля phone, address, timezone и т. п.
Но нас ожидал небольшой сюрприз. Поле money_format вроде специально создано для возможности представить цену в удобном нам формате. Но при построении шаблона брошенной корзины мэйлчимп упорно подставляет RUB перед числом. Мэйлчимп, перестань!
После загрузки мы можем проверить данные с помощью get-запроса по адресу /ecommerce/stores, чтобы увидеть все загруженные магазины, либо /ecommerce/stores/{id} для получения данных по конкретному магазину.
Теперь можно добавлять все остальные данные, т. к. Store является корневой точкой в нашем дереве данных, т.к. все остальные данные мы будем загружать в определенный магазин.
Так-с, чтобы МЧ мог подставлять товары в брошенную корзину, надо ему скормить эти товары. Для этого у нас есть адрес /ecommerce/stores/{store_id}/products, куда мы отправляем post-запросы на создание продуктов в системе.
[
'id' => '742',
'title' => 'Кастрюля',
'handle' => 'kastrulya',
'url' => 'http://***.ru/catalog/kastrulya/',
'description' => 'Кастрюля — незаменимая вещь на кухне. Купив кастрюлю, вы измените свою жизнь в лучшую сторону. Вы поймете, что невозможно прожить без этой вещи и дня! В каждый дом по кастрюле и пусть никто не уйдет обиженным!',
'type' => 'Посуда',
'vendor' => 'Рога и Копыта',
'image_url' => 'http://***.ru/images/742/product.png',
'variants' => [
[
'id' => '742',
'title' => 'Кастрюля',
'url' => 'http://***.ru/catalog/kastrulya/',
'price' => 890,
'sku' => 'KA453',
'inventory_quantity' => 1000,
'image_url' => 'http://***.ru/images/742/product.png',
'visibility' => 'visible',
],
],
]
Что тут примечательного? Ну во-первых, каждый товар должен состоять хотя бы из одного товарного предложения. По сути Product — это некий контейнер для загрузки товарных предложений. Причем, id товара и товарного предложения могут пересекаться, т. к. это разные ресурсы в api МЧ.
И вот тут началась недосказанность документации МЧ. Приходилось догадываться, пускай это было несложно, но ведь можно было сделать нормально, а не как всегда.
Поле handle было описано как «the handle of a product». Ок, посовещавшись мы решили, что это часть урла, относящегося к самому продукту (чпу). Но это подтвердилось только в ходе тестов.
Также к товару можно прикрепить массив картинок, но мы решили, что нам не пригодится и поэтому грузили только главное изображение как к товару, так и к каждому товарному предложению.
И тут у нас возникла проблема, товары почему-то не отображались в темплейтах мейлчимпа.
Начали рыться в доке по Product. И нашли поле visibility с роскошным описанием:

Ну ок, тип String! А что туда можно передавать? Почему нельзя описать все возможные значения?! Я ведь могу туда отправить, например, «show me pls!».
Благо есть пример запроса!

Ну, это не отменяет проблему. Я ведь так и не знаю, какие еще могут быть значения, которые могут оказаться полезными.
Все, с этим справились! Теперь email-маркетолог может убедиться в наличии товаров в системе через построение шаблона с участием товаров или все через те же get-запросы с помощью консоли.
Дальше перед нами стоит задача загрузки брошенных корзин в МЧ. Изначально в голову пришло 2 варианта:
- При каждом изменении корзины (добавление/удаление товара), мы повторяем это действие в МЧ. Из минусов — сразу напрашивается огромное количество запросов к внешнему сервису.
- Раз в n минут смотреть корзины, которые не менялись более часа назад. После чего отправляем их в МЧ. Проблема только одна — следить за корзинами, которые были возобновлены после того, как отправились в МЧ.
Для начала делаем запрос в нашу базу данных (далее БД) за нашими данными в окне от 1 часа до 3. Почему 3? Через час после последнего изменения мы отправляем корзину в систему. В МЧ настроен минимально возможный интервал отправки корзины — 1 час. Поэтому в теории через 2 часа ± 5 минут произойдет отправка письма. Так что 3 часа — это величина даже с запасом.
Получив данные из БД, мы делаем get-запрос по адресу /ecommerce/stores/{store_id}/carts. Таким образом мы получаем все корзины, которые сидят в системе e-commerce и ждут своей очереди на отправку (либо уже отправлены). Для чего нам это нужно? Нужно для синхронизации с нашими данными. Мы отправим все корзины, полученные из БД, но нам нужно удалить те, которые уже не находятся в промежутке 1-3 часа. После 3х — уже неактуальные данные. До часа — корзины, к��торые могли опять возобновить, либо оформить заказ.
Для удаления нам надо просто найти разницу между двумя массивами/коллекциями корзин.
Получив корзины, которые необходимо удалить, мы отправляем delete-запрос /ecommerce/stores/{store_id}/carts/{cart_id}.
Дальше берем корзины для загрузки и циклом отправляем их post-запросами в систему.
Параметры корзины выглядят как-то так:
[
'id' => '1207',
'customer' =>
[
'id' => '25',
'email_address' => 'email@example.com',
'opt_in_status' => false,
],
'currency_code' => 'RUB',
'order_total' => 1597,
'checkout_url' => 'http://***.ru/cart/abandoned/?cart=eyJpdGVtcyI6eyI1OTgwIjoxLCIzNDA0IjoxLCI3NzMiOjEsIjkwNTgiOjEsIjkwOTEiOjEsIjE4ODciOjEsIjc4NCI6MSwiNTExMSI6MSwiODA1MyI6MSwiMTk0MSI6MSwiNTQ0NSI6MSwiNzk1NCI6MywiOTA2NyI6NCwiOTA2NSI6NCwiNzg0MyI6MSwiOTA2NiI6M30sInByb21vY29kZSI6bnVsbH0%253D',
'lines' => [
0 => [
'id' => '123',
'product_id' => '5980',
'product_variant_id' => '5980',
'quantity' => 1,
'price' => 841,
],
1 => [
'id' => '124',
'product_id' => '3404',
'product_variant_id' => '3404',
'quantity' => 1,
'price' => 756,
],
],
]
И опять наша любимая рубрика «догадайся, как работают эти поля». Например, методом научного тыка было выявлено, что можно не создавать покупателя отдельным запросом. Надо передать минимально требуемый набор полей, и он автоматически создастся, если его не было в системе. В нашем случае мы ограничились id, email, opt_in_status. Последний параметр отвечает за состояние подписки юзера в нашем листе. Если он true, то это означает состояние subscribed, в противном случае transactional.
Список товаров без проблем загружается через массив Cart Lines, который в свою очередь является ресурсом сущности Cart. Т.е. мы можем отдельно управлять этим набором с помощью rest-запросов.
Ну вот вроде бы и все? А вот и нет.
При тестировании мы обратили внимание, что отправив одну и ту же корзину, она отправляется лишь раз. Хотя мы ее удаляли из системы и загружали заново. Нигде ничего не сказано, ни единого слова! В итоге опытным путем, с помощью какой-то матери, мы приняли за основу гипотезу, что корзина с одним и тем же id может быть отправлена только один раз.
Тут я полностью согласен с создателями, что это ограничение уберегает нас от заспамливания пользователей. Но напишите об этом! Не нужны нам квесты, нам хватает своих мистических ошибок в коде, который мы пишем.
После того, как мы все это проделали, МЧ начал присылать нам красивые письма о брошенной корзине. И тут появился второй вопрос. Если юзер, бросил корзину и вернулся из письма в свой же аккаунт, т. е. он был авторизован в момент перехода по ссылке, то он попадет в свою корзину без проблем. А так получается, что письмо говорит тебе «на! возьми свою корзину назад!», а мы при переходе ему говорим «упс, чего-то потерялося все! Мы ничего не трогали! Оно само!»
Было решено кодировать состав корзины в строку и передавать в checkout_url при отправке корзины в МЧ. А при переходе на сайт ловить эту строку, декодировать и накидывать все товары в корзину, не забыв перед этим ее полностью обнулить.
Таким образом, в какой бы браузер мы не отправили юзера, он получает свою корзину, как мы и обещали. Единственный минус, что ему остается только авторизоваться. Но авторизовывать через ссылку — это моветон, да и вообще дело опасное, в первую очередь для наших клиентов.
Что в итоге? В принципе, проблем особых не было при реализации, так как очень часто выручали ответы МЧ при ошибках, связанных с валидацией переданных полей. Но их было бы еще меньше, если бы они нормально по-человечьи описали все эти тонкости работы МЧ и более подробно описали бы поля.
Настройка отчета в Google.Analytics
Чтобы отслеживать успех всей операции, придется настроить и периодически посматривать отчет в аналитике. Представим, что у нас у всех, как у взрослых, подключен ecommerce, иначе чуда не получится :)
Чтобы собрать новый отчет под брошенную корзину, идем в «Мои отчеты»:

Дальше «Добавить отчет»:

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

That’s all forks!
Теперь у вас настроена отправка брошенной корзины и отчет, по которому вы можете смотреть выхлоп с нее. И тестируйте, тестируйте, тестируйте! ;)
