Mailchimp dump basket: a guide for the lazy

    First, a little ranting :) Sooner or later, before any online store there is a question of setting up an abandoned basket. Statistics and the feeling of missing money sucking under the spoon do not spare anyone.

    Percentage of abandoned baskets from 2006 to 2017

    Percentage of abandoned baskets from 2006 to 2017
    Source The

    percentage of abandoned baskets for the first quarter of 2018 by industry:
    Percentage of abandoned baskets for the first quarter of 2018 by industry

    At the same time, despite publicly available statistics, most online stores do not use the available opportunities and do not connect an abandoned basket. A recent “homemade” survey from EmailSoldiers clearly shows that most of the stores do not bother about it at all.

    Current statistics for connected abandoned baskets

    EmailSoldiers Research
    The source

    At the same time, everyone (and we are not saints too) gains traffic, completes advertising and creatives, but does not even try to return a person who broke at the last moment.

    But in the first iteration, you can get an increase in orders, using a letter without dynamic content, you only need to configure it. Only once to make an effort to make money in the background - is this not a fairy tale?

    Naturally, a letter with dynamic content, which pulls goods from the basket, can work better. Or maybe your next kitten with sad eyes will have a cooler effect on your audience. Or you pull the recommended goods to the goods in the basket and see that from the letters they buy more often and increase the average bill. Or you will have hands to make a series of letters, from which the conversion will be even more.

    It is possible to improve and test letters of an abandoned basket to an unattainable ideal, but any once customized letter is incomparably better than nothing.

    Conversion for an abandoned basket according to RetailRocket

    RetailRocket abandoned basket conversion

    And here we are with comrade Artem Aleksandrov, we began the introduction of the basket from two sides.

    Technical implementation

    Integration specification

    Briefly describe the essence of the problem.

    Task: connect an abandoned shopping cart to the xxx.xx site using the Mailchimp mailing service. We

    issue all the necessary materials.


    Where can I get the key?

    Where to get the key?

    Give a link to the

    sheet ID documentation to which we connect the Store: XXXXXXXXXX

    Where can I get the sheet ID?

    Where to get ID sheet?

    A letter must be created in the mailing service in advance. As soon as the API request is received by the mailing service, the letter is automatically generated and the addressee is added to the queue for sending.

    For our case, we chose the following logic for sending an abandoned basket:
    An authorized user adds the goods to the cart, does not complete the transaction and does not complete the order, the cart remains unchanged for 1 hour. After that, a request is sent to Mailchimp, in which the email, the composition of the user's order, the images of the goods, the price of the goods and the link to the user's basket are transmitted.

    Template layout

    When creating automation for an abandoned basket, mailmailimp immediately asks if there will be one letter or several and offers to select a connected store.

    Creating automation for an abandoned cart in Mailchimp

    In the basic templates mch offers a choice of three pieces:

    Letters of choice for Mailchimp abandoned basket
    1. Abandoned basket with dynamic goods
    2. Abandoned basket with grocery recommendations (to be configured separately)
    3. Abandoned basket without goods (just a text letter)

    In the best traditions, if you have time, you can complete the basket yourself.

    Dynamic products without styles look something like this (forget about indents, they are ugly displayed here):


    It would seem that if you change the number in the variable * | ABANDONED_CART: [$ total = 3] | * , then the letter will display a different quantity of goods, but no, put at least 5, at least 100, mch refuses to show a different quantity.

    And, which is also a bit strange, the variable * | PRODUCT: PRICE | * is replaced by the values ​​of the RUB288 format, and for some reason this cannot be changed, but more on that later.

    For a change, we tried to substitute also variables with the number of games and with the total cost of the order, which we transmit via api, but the email and then said “no”. Well, so be it.

    A word to the programmer :)

    Mailchimp was a historical experience of integration with their third-party service mandrill, everything is simple and clear. Very friendly documentation (of course, in English), but there were no rough edges and it worked from the first kick.

    Also, we have implemented a subscription mechanism through special forms on our website. It was there that we fully felt the understatement and ambiguity of mailchim documentation. English is not a problem for an experienced developer, but knowledge of the Klingon dialect is not implied by default.

    The initial data are: the php7 language and the yii2 framework, which has already been heavily grown with its ecosystem. Those. we already have 6 small projects that are trying to use common components both on the back end and on the frontend. Accordingly, the implementation of any task requires solving it project-independent, but this does not imply a framework-independence, since for this one has to pay man-hours, of which there is always a shortage.

    Having received the integration task, it is necessary first of all to look around. What is given to us? First, the mailchimp service that you need to make friends with. We go to the githab and see that there are quite a lot of implementations. But the choice is simple - with the most popular package 1.5k stars ( drewm / mailchimp-api ).

    The packet gives a simple wrapper over the rest interaction with mailchimp. We can only grow on its own logic.

    Secondly, we are given documentation . Based on the documentation, we have a Store resource with embedded resources: Cart, Customer, Order, Product, Promo rule. For an abandoned basket without the recommended goods, we need only Product, Cart and Customer. Cart in turn consists of the Cart line set, and Product contains Product variants.

    We decompose the problem as follows:

    1. Download store data to the resource Stores
    2. Download all products available for purchase in Products
    3. Customize the loading of baskets with users on a schedule

    Ok, let's go. First of all, we take on the essence of "shop". We decided to immediately use the test and combat version of the store and, depending on the environment variable responsible for the dev / prod mode, we worked either with one store or with another.

    To download the data for the store, we knock a post-request to the address / ecommerce / stores with the following set of parameters:

       'id' => 'dev.***.ru',
       'list_id' => '****',
       'name' => '*** - test',
       'domain' => 'dev.***.ru',
       'email_address' => 'admin@***.ru',
       'currency_code' => 'RUB',
       'primary_locale' => 'ru',
       'money_format' => '₽',

    There are a few more parameters, but it all depends on your needs. Since we didn’t intend to use the store’s contact details in letters, we didn’t fill in the phone, address, timezone, etc. fields.
    But we were in for a little surprise. The money_format field seems to be specially created for the opportunity to present the price in a convenient format. But when building a template for an abandoned basket, mailmail impersonates the RUB before the number. Mailchimp, stop it!

    After downloading, we can check the data using a get request to the address / ecommerce / stores to see all the stores that are uploaded, or / ecommerce / stores / {id} to get data for a particular store.

    Now you can add all other data, because the Store is the root point in our data tree, because all other data we will upload to a particular store.

    So, so that the MCH could put the goods in an abandoned basket, he needs to feed these goods. To do this, we have the address / ecommerce / stores / {store_id} / products, where we send post-requests for the creation of products in the system.

       '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',

    What's so remarkable? Well, firstly, each product must consist of at least one product offer. In essence, a Product is a container for loading product offerings. Moreover, product id and product offer can overlap, since these are different resources in api MCH.

    And here began the understatement of the documentation MCH. I had to guess, let it was easy, but you could do it normally, and not as always.

    The handle field was described as “the handle of a product”. Ok, after consulting we decided that this is part of the URL related to the product itself (cnc). But this was confirmed only during the tests.

    Also, an array of images can be attached to the product, but we decided that it would not be useful to us and therefore we only loaded the main image both to the product and to each product offer.

    And here we had a problem, the products for some reason were not displayed in mailchimb templates.

    Started rummaging through the dock for Product. And they found a field of visibility with a luxurious description:

    field visibility description

    Well, ok, type String! And what can be transferred there? Why it is impossible to describe all possible values? I can send there, for example, “show me pls!”.

    Fortunately there is an example of the request!

    sample request

    Well, that doesn't cancel the problem. I don’t know what other values ​​might be that might be useful.

    Everyone coped with it! Now email-marketer can verify the availability of goods in the system through the construction of a template with the participation of goods or all through the same get-requests using the console.

    Next we face the task of loading abandoned baskets in the MCH. Initially 2 options came to mind:

    1. With each change of the basket (add / delete goods), we repeat this action in the MCH. From minuses - a huge amount of requests to an external service immediately suggests itself.
    2. Once in n minutes to watch baskets that have not changed more than an hour ago. Then send them to the MCH. The problem is only one - to monitor the baskets, which were resumed after they went to the MCH.

    To begin with, we make a request to our database (hereinafter “DB”) for our data in the window from 1 hour to 3. Why 3? An hour after the last change, we send the cart to the system. In the MCH, the minimum possible interval for sending the basket is 1 hour. Therefore, in theory, in 2 hours ± 5 minutes, the letter will be sent. So 3 hours is a value even with a margin.

    After receiving the data from the database, we make a get request to the address / ecommerce / stores / {store_id} / carts. Thus, we receive all the baskets that are sitting in the e-commerce system and are waiting for their turn to be shipped (or have already been sent). Why do we need it? It is necessary to synchronize with our data. We will send all baskets received from the database, but we need to remove those that are no longer in the range of 1-3 hours. After 3x - already irrelevant data. Up to an hour - baskets, which could resume again, or place an order.

    To delete, we just need to find the difference between the two arrays / collections of baskets.
    After receiving the baskets that need to be deleted, we send a delete request / ecommerce / stores / {store_id} / carts / {cart_id}.

    Then we take the baskets for loading and cycle them by post-requests to the system.

    Basket options look something like this:

       'id' => '1207',
       'customer' =>
               'id' => '25',
               'email_address' => '',
               '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,

    And again our favorite rubric is “guess how these fields work”. For example, the method of scientific tyke revealed that it is possible not to create a buyer by a separate request. It is necessary to transfer the minimum required set of fields, and it will automatically be created if it was not in the system. In our case, we are limited to id, email, opt_in_status. The last parameter is responsible for the subscription status of the user in our list. If it is true, then this means the state is subscribed, otherwise transactional.

    The product list is easily loaded via the Cart Lines array, which in turn is the Cart entity resource. Those. we can separately manage this set using rest requests.

    Well, that seems to be all? And no.

    When testing, we noticed that by sending the same basket, it is sent only once. Although we removed it from the system and downloaded it again. Nothing is said anywhere, not a single word! As a result, empirically, with the help of some mother, we accepted the hypothesis that the basket with the same id can be sent only once.

    Here I fully agree with the creators that this restriction protects us from spamming users. But write about it! We do not need quests, we have enough of mystical errors in the code that we write.

    After we did all this, MCH began to send us beautiful letters about an abandoned basket. And then the second question appeared. If the user dropped the basket and returned from the letter to his account, i.e., he was authorized at the moment of clicking on the link, he will get into his basket without any problems. And so it turns out that the letter says to you "on! Take your basket back! ”, and when we go to him we say“ Oops, something has lost everything! We did not touch anything! It is self! ”

    It was decided to encode the composition of the basket into a string and pass it to checkout_url when sending the basket to the MCH. And when you go to the site to catch this line, decode and throw all the goods in the basket, not forgetting to completely reset it.

    Thus, no matter what browser we send the user to, he gets his cart, as we promised. The only negative that he can only log in. But authorizing through a link is moveton, and indeed it’s a dangerous business, first of all for our clients.
    What is the result? In principle, there were no special problems in the implementation, since the MCH very often helped out with errors associated with the validation of the transmitted fields. But there would be even fewer if they normally described all these subtleties of the MCH in a human way and described the fields in more detail.

    Setting up a report in Google. Analytics

    To track the success of the entire operation, you will have to set up and periodically look at the report in the analytics. Imagine that all of us, like adults, have ecommerce connected, otherwise a miracle will not work :)

    To collect a new report for an abandoned basket, go to “My Reports”:

    My reports

    Next, “Add Report”:

    Add report

    And then add the parameters that will be monitored . We decided that we would look at an abandoned basket in the context of cities, you may have another vision.

    At mailchimp, the standard campaign for an abandoned basket is ABANDONED_CART_EMAIL, we substitute it into the filter and get a report.


    That's all forks!

    Now you are configured to send an abandoned basket and a report on which you can watch the exhaust from it. And test, test, test! ;)

    Also popular now: