Паблишинг приложений и реклама в яндексе
Сейчас будем делать сложновое. Будем связывать AppMetrica, Яндекс Метрику, RuStore, Google Play и прямое скачивание скачивание apk в одну цельную систему. Все это нужно, чтобы рекламировать наше приложение в разных магазинах с помощью одного лендинга и одной кнопки на скачивание.
Если вас заинтересует описанные мною сервисы и вы захотите их попробовать их у себя, то не стесняйтесь, пишите t.me/akovardin
Важно: обратите внимание, что Яндекс Метрика и AppMetrica это разные сервисы Яндекса и, к сожалению, они не очень дружат друг с другом.
Зачем?
Казалось бы, что сложного в рекламе мобильного приложения через Директ? Наверняка, все уже давно сделано за нас? На самом деле, это не так. Например, если вы загрузили приложение в RuStore и хотите завести рекламную кампанию в Директе, то это будет выглядеть вот так:
Яндекс Директ не собирается рекламировать приложение, он создает рекламную кампанию на сайт rustore.ru. И у вас не получится запустить рекламу с оптимизацией на установку
Надо отметить, что директ умеет работать с альтернативными магазинами приложений, но не с RuStore. Для магазина GetApps от Xiaomi у Директа даже свой лендинг есть
Для сравнения, вот так будет выглядеть рекламная кампания для любого приложения в Google Play
Хотя Директ должен сам понимать, что мы указываем ссылку на приложение, в случае с RuStore это явно не похоже на рекламу мобильного приложения. Для RuStore Яндекс умеет делать только рекламу на страницу приложения и просто так не свяжет установку с конверсией рекламной кампании. А очень хотелось бы оптимизироваться хотя бы на установку приложения, не говоря уже о действиях внутри самого приложения.
Директ умеет работать с конверсиями в Яндекс Метрике. К сожалению, она никак не связана с AppMetrica, которая используется в приложении. Эту проблему нам нужно будет решить самостоятельно.
И последняя, но не менее важная проблема. Когда мы указываем ссылку на приложение, то пользователя из рекламного материала сразу отправляет на страницу с установкой приложения. Мне это не очень нравится, потому что в таком виде у пользователя слишком мало информации о самом приложении. Да, посадочная страница добавляет лишний переход, который, казалось бы, не очень хорошо скажется на конверсии. Но с посадочной страницей мы будем ловить более релевантные конверсии, у таких пользователей будет значительно больше ретеншен и мы не будем платить за мусорные конверсии.
Минисервисы
Небольшое лирическое отступление. Меня вдохновила статья про “самодельное ПО” - когда вы сами реализуете простые приложения под ваши нужны собирая его из готовы кусочков и/или используя ИИ. А это прям то что нужно для пет проектов. Поэтому, решил по такому принципу “готовить” свои проекты. Назову такой подход минисервисами(не путать микро)
Для рекламы приложений я написал пару минисервисов:
- land - сервис для создания простых лендингов приложений. Бонусом сразу создаются странички terms и privacy, которые можно указать в настройках магазина приложений
- tracker - а этот сервис сохраняет событие установки из AppMetrica в базу и потом передает их в Яндекс Метрику
Все минисервисы сделаны с помощью PocketBase. Очень рекомендую этот простой фреймворк с админкой, который работает с SQLite и с кучей готовых плюшек. Есть готовый API под flutter. Если у вас не самое большое приложение, то вам этого хватит с головой.
Принцип работы
Сначала разберемся что куда будет передаваться. Самое главное, что нам нужно сделать - это передать параметр yclid от клика в рекламном материале до трекера в Яндекс Метрике
Хорошая схема лучше абзаца текста. Постараюсь максимально понятно объяснить что мы будем делать
Трекинг ссылка в объявлении
Первое, что вам нужно сделать, после добавления в AppMetrica - создать трекинг ссылку и добавить ее в рекламное объявление
Чтобы директ передавал все нужные параметры на лендинг, настройте “Параметры в URL” для ваших баннеров. Я указывал все возможные параметры
utm_content={banner_id}&utm_medium=cpa&utm_source=yadirect&utm_campaign={campaign_id}&yclid={yclid}
Но для учета установок будет достаточно указать yclid={yclid}
. Подробно про проброс параметров из Директа описано в документации
Трекинг ссылка будет вести на наш лендинг, реализованный в минисервисе land.
Пример создания трекинг ссылки для моего приложения:
Добавляя целевую ссылку, нужно обязательно указать проброс параметра yclid. Это параметр будет пробрасывать в постбеке. При его настройке также нужно указать параметр yclid
Постбек - это механизм передачи определенных событий из аналитики на ваш сервер через запросы от рекламной сетки
Подробнее про настройку трекер ссылок и постбеков
Лендинг с Яндекс Метрикой
Для создания лендинга нам нужен минисервис land. В этом сервисе нет совершенно ничего сложного, он позволяет очень просто завести страничку приложения и подключить к ней трекер яндекс метрики
Сервис постоен на базе PocketBase - это очень простая и легкая админка. Сама логика странички приложения реализована на Go
Для добавления лендинга нужно заполнить несколько полей:
- appname - название приложения
- title - заголовок страницы для тега
<title>
- description - описание для тега
<description>
- keywords - ключевые слова для тега
<keywords>
- slug - URL, по которому будет доступен лендинг
- yandex_counter - счетчик Яндекс Метрики
В поле yandex_counter нужно указать правильный идентификатор Яндекс Метрики, который нужно взять в настройках
И отдельно нужно настроить ссылки на магазины приложений для универсальной кнопки на главной странице самого лендинга
- title - текст, который будет отображаться на странице лендинга
- description - текст, который будет отображаться на странице лендинга
- huawei_link - ссылка на приложение в магазине huawei
- android_link - ссылка на приложение в Google Play или на скачивание прямой apk
- rustore_link - ссылка на приложение в RuStore
- cta - текст на кнопке для скачивания приложения
- image - картинка, которая будет отображаться на лендинге
Так много ссылок нужно, чтобы сделать на лендинге универсальную кнопку, которая будет вести на приложение в доступном магазине приложений
Как работает универсальная кнопка? Используется несколько важных уловок. Через User Agent определяем на каком девайсе пользователь смотрит лендинг: huawei или обычный android:
1if (/huawei/i.test(userAgent)) {
2 document.getElementById("download-button").href = huaweiLink;
3} else if (/android/i.test(userAgent)) {
4 document.getElementById("download-button").href = androidLink;
5} else {
6 document.getElementById("download-button").href = rustoreLink;
7}
Если не смогли определить тип девайса, то просто отправляем пользователя на установку приложения из RuStore
Второй важный принцип - использование ссылки на интент и параметра S.browser_fallback_url=
1<a href="intent://apps.rustore.ru/app/com.roblox.client#Intent;scheme=rustore;package=ru.vk.store;S.browser_fallback_url=https%3A%2F%2Fplay.google.com%2Fstore%2Fapps%2Fdetails%3Fid%3Dcom.roblox.client%26hl%3Den;end">Скачать приложение</a>
Выглядит сложно, но давайте разберемся. Сначала нужно нырнуть в доку по chrome: developer.chrome.com/docs/android/intents. Тут нас учат как строить ссылки, которые на андроиде будут открывать интент в приложении. Чтобы понять, как формировать ссылку на экран нужного приложения в RuStore - читаем доку www.rustore.ru/help/sdk/rustore-deeplinks/. Этих двух док достаточно, чтобы научиться правильно строить ссылки на свое приложение
Теперь переходим к фоллбеку S.browser_fallback_url
. Сюда нужно указать заэнкоженную ссылку на приложение в Google Play или AppGallery, для примера это https://www.rustore.ru/catalog/app/com.roblox.client
которая энкодиться с помощью www.urlencoder.org и вставляется как https%3A%2F%2Fplay.google.com%2Fstore%2Fapps%2Fdetails%3Fid%3Dcom.roblox.client%26hl%3Den
Но это еще не все. Бегло погуглив про наших братьев huawei, обнаружил что там это тоже, вероятно, работает - цинк. Должно работать даже в дефолтном браузере хуавея и в любом на базе webkit
1var rustoreLink = "{{.rustoreLink}}?referrerId=" + escape(referrer)
2var bundleName = "{{.bundleName}}" // application package
3
4var intent = "intent://apps.rustore.ru/app/" + bundleName + "?referrerId=" + escape(referrer) + "#Intent;scheme=rustore;package=ru.vk.store;"
5var androidFallback = "S.browser_fallback_url=" + escape("{{.androidLink}}?referrer="+escape(referrer)) + ";end"
6var huaweiFallback = "S.browser_fallback_url=" + escape("{{.huaweiLink}}?referrer="+escape(referrer)) + ";end"
7
8var androidLink = intent + androidFallback
9var huaweiLink = intent + androidFallback
Таким образом, мы отправляем пользователя в RuStore, но если магазин не установлен на девайсе, то будет срабатывать S.browser_fallback_url
и, в зависимости от того на каком девайсе пользователь смотрит ленд, он отправится в Google Play или AppGallery.
Вместо ссылки на Google Play можно указать ссылку на скачивание apk и это тоже будет работать. Очень удобно если вашего приложения нет в Google Play
Осталось собрать параметры, чтобы пробросить их в приложение. Пользователь может не перейти на скачивание приложения сразу после перехода на ленд. Поэтому, нужно сохранить полученные гет параметры в куку и при формировании ссылки брать эти параметры уже из куки
1function setCookie(name, value, days){
2 var date = new Date();
3 date.setTime(date.getTime() + (days*24*60*60*1000));
4 var expires = "; expires=" + date.toGMTString();
5 document.cookie = name + "=" + value + expires + ";path=/";
6}
7
8function getParam(p){
9 var match = RegExp('[?&]' + p + '=([^&]*)').exec(window.location.search);
10 return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
11}
12
13var yclid = getParam('yclid');
14var rbclickid = getParam('rb_clickid');
15
16if(yclid){
17 setCookie('yclid', yclid, 90);
18}
19
20if(rbclickid){
21 setCookie('rbclickid', rbclickid, 90);
22}
23
24function readCookie(name) {
25 var n = name + "=";
26 var cookie = document.cookie.split(';');
27 for(var i=0;i < cookie.length;i++) {
28 var c = cookie[i];
29 while (c.charAt(0)==' '){c = c.substring(1,c.length);}
30 if (c.indexOf(n) == 0){return c.substring(n.length,c.length);}
31 }
32 return "";
33}
Если вас смущает параметр rb_clickid, то пока не обращайте на него внимания. Он нужен для учета конверсий от ВК Рекламы, но про это будет в отдельной статье
Весь код нужно поместить в коллбек Яндекс Метрики getClientID
в котором у нас есть доступ к clientid. Это не обязательно, конверсии могут учитываться и по clientid, и по yclid. В будущем добавлю запись статы по показу ленда с учетом параметра clientid:
1ym({{.yandexCounter}}, "getClientID", function(clientid) {
2 var yclid = readCookie('yclid'); // реклама yandex
3 var rbclickid = readCookie('rbclickid'); // реклама vk
4
5 var clickid = "";
6 if (yclid != "") {
7 clickid = yclid;
8 } else if (rbclickid != "") {
9 clickid = rbclickid;
10 }
11
12 var userAgent = navigator.userAgent || navigator.vendor || window.opera;
13 var referrer = "client_id=" + clientid + "&yclid=" + yclid + "&rb_clickid=" + rbclickid + "&click_id=" + clickid
14
15 var rustoreLink = "{{.rustoreLink}}?referrerId=" + escape(referrer)
16 var bundleName = "{{.bundleName}}" // application package
17
18 var intent = "intent://apps.rustore.ru/app/" + bundleName + "?referrerId=" + escape(referrer) + "#Intent;scheme=rustore;package=ru.vk.store;"
19 var androidFallback = "S.browser_fallback_url=" + escape("{{.androidLink}}?referrer="+escape(referrer)) + ";end"
20 var huaweiFallback = "S.browser_fallback_url=" + escape("{{.huaweiLink}}?referrer="+escape(referrer)) + ";end"
21
22 var androidLink = intent + androidFallback
23 var huaweiLink = intent + androidFallback
24
25 if (/huawei/i.test(userAgent)) {
26 document.getElementById("download-button").href = huaweiLink;
27 } else if (/android/i.test(userAgent)) {
28 document.getElementById("download-button").href = androidLink;
29 } else {
30 document.getElementById("download-button").href = rustoreLink;
31 }
32});
Пример моего лендинга можно посмотреть по ссылке land.kovardin.ru/l/downloader
Цели
На этом шаге важно добавить новую цель в Яндекс Метрику. Переходим в цели, жмакаем добавить новую цель и указываем настройки
Настройки цели:
- Название - указываем какое хоти, но оно нам понадобиться при загрузке конверсий. В моем случае это
app_install
- Тип условия - выбираем JavaScript-событие
Подробнее про цели в документации Яндекс Метрики
После добавления цели для счетчика, эта цель станет доступной в настройках рекламной кампании в Директе. Это именно то, что нам нужно
Теперь рекламные кампании будет оптимизироваться на конверсии и деньги будут с вас брать только за реальные установки.
Таким образом можно передавать не только установки, но и другие целевые действия в приложении, например регистрации пользователя
Интеграция AppMetrica в приложение
Это самый простой и понятный шаг. Делаем все по инструкции
1class YourApplication : Application() {
2 override fun onCreate() {
3 super.onCreate()
4 // Creating an extended library configuration.
5 val config = AppMetricaConfig.newConfigBuilder(API_KEY).build()
6 // Initializing the AppMetrica SDK.
7 AppMetrica.activate(this, config)
8 }
9}
После добавления SDK к нам начнут прилетать постбеки. Из этих постбеков нужно выцепить параметр yclid
Обработка постбеков
Я уже писал выше про настройку постбека, теперь посмотрим как его обработать. AppMetrica будет отправлять GET запрос на указанный нами URL https://tracker.kovardin.ru/downloader/fire
Обрабатывать постбек будем в отдельном сервисе tracker. Для начала нужно его настроить:
- name - название трекера
- network - название рекламной сетки с которой работаем (пока только yandex)
- yaurl - это URL по которому мы будем загружать конверсии в Яндекс Метрику
- yatoken - токен авторизации для загрузки конверсий, который можно получить в настройках
Сначала разберемся как мы обрабатываем постбек. AppMetrica присылает постбеки через GET запрос. Например, вот такой:
https://tracker.kovardin.ru/downloader/fire?client_id={client_id}&yclid={yclid}&install_timestamp={install_timestamp}&appmetrica_device_id={appmetrica_device_id}&click_id={click_id}&transaction_id={transaction_id}&match_type={match_type}&tracker=appmetrica_821509867285037527&rb_clickid={rb_clickid}
В сервисе реализована ручка /{:name}/fire
, в которой сохраняются постбеки в коллекцию conversions
1yclid := c.QueryParam("yclid")
2rbclickid := c.QueryParam("rb_clickid")
3key := yclid + rbclickid
4
5if key == "" {
6 return nil
7}
8
9network := networkVK
10if yclid != "" {
11 network = networkYandex
12}
13
14record.Set("yclid", yclid)
15record.Set("rb_clickid", rbclickid)
16record.Set("key", key)
17record.Set("uploaded", false)
18record.Set("network", network)
19record.Set("tracker", tracker.Id)
20
21if err := h.app.Dao().SaveRecord(record); err != nil {
22 h.app.Logger().Error("error on save conversions", "error", err)
23}
uploaded
- этот параметр нужен для отметки, что конверсии уже загружены в Яндекс Метрику. Поэтому устанавливаем его в false и сохраняем
rb_clickid
- задел на работу с ВК Рекламой, но пока это не важно
Чтобы протестировать постбек, нужно на девайсе перейти по вашей трекинг ссылке https://redirect.appmetrica.yandex.com/serve/xxxxx?yclid=123
yclid указываем любой. Устанавливаем приложение через лендинг и ждем пока к нам прилетит постбек:
Jul 18 09:03:30 land[510662]: /downloader/fire?client_id=&yclid=123&install_timestamp=1721291374&appmetrica_device_id=11918280336705624214&click_id=&transaction_id=cpi17188142678303156033&match_type=fingerprint&tracker=appmetrica_821509867285037527&rb_clickid=
Все постбеки сохраняются в коллекцию conversions
Загрузка конверсий
Чтобы передать конверсии в Яндекс Метрику, нужно подготовить csv файл, указать там все конверсии и загрузить его на специальный URL https://api-metrika.yandex.net/management/v1/counter/97880624/offline_conversions/upload?client_id_type=YCLID
- 97880624 - идентификатор счетчика Яндекс Метрики, который используется на лендинге
- client_id_type=YCLID - параметр, который указывает как Метрика будет привязывать конверсии. В моем случае - по параметру yclid
Для загрузки конверсий в сервисе реализована периодическая задача Uploader
. Он получает список конверсий, формирует csv и отправляет его в Яндекс Метрику
1func (t *Task) Do() {
2 // upload fired conversions
3 t.app.Logger().Info("start upload conversions")
4
5 trackers, err := t.app.Dao().FindRecordsByFilter(
6 "tracker",
7 "enabled = true && network = 'yandex'",
8 "-created",
9 100,
10 0,
11 )
12
13 if err != nil {
14 t.app.Logger().Error("error on get trackers")
15
16 return
17 }
18
19 for _, tracker := range trackers {
20 if err := t.yandex(tracker); err != nil {
21 t.app.Logger().Error("error on upload yandex conversions", "error", err)
22 }
23
24 if err := t.vk(tracker); err != nil {
25 t.app.Logger().Error("error on upload vk conversions", "error", err)
26 }
27 }
28
29 t.app.Logger().Info("finish upload conversions")
30}
В методе yandex
создается csv файл, который отправим в Метрику
1data := &bytes.Buffer{}
2file := csv.NewWriter(data)
3if err := file.Write([]string{
4 //"ClientId",
5 "Yclid",
6 "Target",
7 "DateTime",
8}); err != nil {
9 return err
10}
11
12for _, record := range records {
13 if err := file.Write([]string{
14 record.GetString("yclid"),
15 "app_install",
16 strconv.Itoa(int(record.Created.Time().In(t.loc).Unix())),
17 }); err != nil {
18 t.app.Logger().Warn("error on write csv row", "error", err)
19 continue
20 }
21}
22
23file.Flush()
24
25body := &bytes.Buffer{}
26writer := multipart.NewWriter(body)
27
28part, _ := writer.CreateFormFile("file", "file.csv")
29if _, err := io.Copy(part, data); err != nil {
30 return err
31}
32if err := writer.Close(); err != nil {
33 return err
34}
В этом csv файле в отдельном столбце Target
мы указываем значение app_install
. Это значение должно совпадать с названием цели, которое указали на шаге настройки лендинга
Осталось только загрузить csv файл на нужный URL. Подробнее про загрузку конверсий читаем в документации
1request, _ := http.NewRequest("POST", yaurl, body) // yaurl - url указанный в настройках
2request.Header.Add("Authorization", "OAuth "+yatoken) // yatoken - токен авторизации указанный в настройках
3request.Header.Add("Content-Type", writer.FormDataContentType())
4
5resp, err := t.client.Do(request)
6if err != nil {
7 return err
8}
На этом круг замкнулся. Конверсии передаются в Яндекс Метрику и их можно увидеть в интерфейсе Яндекс Метрики
Таким образом можно запускать кампании не только на RuStore или другие альтернативные магазины, но и на прямое скачивание apk. Вся магия в трекинг ссылках AppMetrica, которые каким-то хитрым образом могут матчить переход по ссылке с установкой приложения. Подробнее про это расписано в документации Яндекса
Че по ВК Рекламе
Обязательно запущу и протестирую работу с ВК Рекламой, но в другой раз. Там все чуть проще - конверсии можно передавать а риалтайме