Deep links для SuperApp уровня Halyk: WebView-сервисы, cross-auth и UTM/MMP-атрибуция без потерь
Dilshat Rakhimov
May 2026 · 15 min read
У SuperApp есть неприятная техническая проблема, которую часто недооценивают, пока маркетинг не начинает продвигать отдельные продукты. Приложение может быть огромным: депозиты, кредиты, переводы, коммунальные платежи, мобильная связь, билеты, страховки, eGov-like сценарии, партнерские витрины и десятки WebView-сервисов. Но рекламная ссылка все равно открывает либо главную страницу, либо последний открытый экран, либо WebView без авторизации, либо правильный сервис, но без UTM, MMP payload и campaign context.
На уровне пользователя это выглядит как маленькая UX-проблема: человек нажал на рекламу депозита, а попал в общий экран приложения. На уровне performance-маркетинга это уже системная поломка. Кампания покупает intent на конкретный продукт, но приложение не доводит пользователя до этого продукта. Алгоритм получает слабый post-click сигнал. Атрибуция видит install или open, но теряет service-level outcome. Продуктовая команда говорит, что реклама не работает. Маркетинг отвечает, что события есть. На самом деле сломан маршрут.
Deep link в SuperApp - это не просто ссылка. Это продуктовый API.

Почему это особенно больно в банковском SuperApp
В ecommerce deep link обычно ведет в товар, категорию или checkout. В банковском SuperApp link target сложнее:
- Пользователь может быть не установлен, установлен, но не авторизован, авторизован, но без KYC, KYC-approved, но не eligible для продукта.
- Нужный экран может быть native module, embedded WebView, партнерский домен, mini-app или legacy web service.
- WebView может требовать отдельную авторизацию или cross-token.
- Кампания может прийти через Google Ads, Meta, TikTok, push, SMS, QR, email или MMP link.
- Атрибуционные параметры могут жить в URL, MMP callback, Android install referrer, SKAN/postback, backend click context или CRM event.
Если архитектура построена как "поймали ссылку, открыли приложение", она не выдержит такого числа переходов. Нужен link contract: стабильные шаблоны, проверенные домены, context ledger, auth handoff и правила, какие параметры можно переносить между native и WebView.
Наблюдение по Halyk/Homebank: проблема не только в assetlinks.json
Я бы не писал "у Halyk нет App Links" в таком виде. Быстрая публичная проверка на 4 мая 2026 года показывает более точную картину:
| Endpoint | Что видно публично |
|---|---|
https://homebank.kz/.well-known/assetlinks.json | Возвращает JSON и содержит Android package kz.kkb.homebank |
https://homebank.kz/.well-known/apple-app-site-association | Возвращает AASA-файл с applinks и broad paths *, / |
https://halykbank.kz/.well-known/assetlinks.json | Возвращает HTML-страницу, а не Digital Asset Links JSON |
https://www.halykbank.kz/.well-known/assetlinks.json | Возвращает HTML-страницу |
https://www.homebank.kz/.well-known/assetlinks.json | Возвращает 404 |
https://halykbank.kz/.well-known/apple-app-site-association | Возвращает HTML-страницу, а не AASA JSON |
Это не полноценный reverse-engineering audit приложения. Для него нужно смотреть Android manifest, iOS associated domains entitlement, MMP configuration, runtime routing и actual device behavior. Но как публичный симптом этого достаточно: у link estate есть неравномерность. Один домен может быть правильно ассоциирован с приложением, а другие маркетинговые или корпоративные домены - нет. Если рекламные, лендинговые, SMS, QR или WebView-ссылки гуляют между такими доменами, часть маршрутов будет открываться через браузер, часть через приложение, часть через redirect, а часть будет терять параметры.
И даже когда verified host есть, это не решает второй слой проблемы: нужны шаблонные ссылки на конкретные продукты и сервисы. Ссылка вида https://homebank.kz/ технически может открыть приложение. Но она не говорит приложению: "открой категорию депозитов в KZT", "открой оплату мобильной связи с provider_id", "открой WebView страховок и сохрани campaign_id".
Verified links - это layer 0, не вся система
На Android App Links работают через intent filters и Digital Asset Links. Android проверяет хосты из intent filters и ищет файл по адресу https://hostname/.well-known/assetlinks.json. Если host не прошел verification, система возвращается к обычному intent resolution, и ссылка может уйти в браузер или показать chooser.
На iOS Universal Links требуют Associated Domains entitlement и файл apple-app-site-association на каждом домене или субдомене, который приложение заявляет как applinks:. Apple отдельно предупреждает, что для разных subdomains нужны свои association files, а redirects для AASA hosting не поддерживаются. Это важно для банков: example.kz, www.example.kz, go.example.kz, online.example.kz, pay.example.kz - это разные hosts, и нельзя надеяться, что один файл магически закроет все.
Минимальный Android contract:
Минимальный assetlinks.json:
Минимальный AASA:
Но в SuperApp этого недостаточно. Если go.bank.kz открывает приложение, а дальше приложение не умеет разобрать deposit_category, provider_id, service_id, campaign_id, ctx и auth state, verified link просто превращается в красивый вход на главную.
Link contract: продуктовые шаблоны вместо хаоса URL
SuperApp с сотнями сервисов не должен поддерживать сотни случайных deep links. Нужна небольшая, версионированная грамматика:

Хороший набор шаблонов:
Примеры для маркетинга:
Правила:
- Path описывает destination: продукт, категорию, service id, merchant id.
- Query описывает campaign context, фильтры и безопасные UI hints.
- Нельзя класть в URL ИИН, номер карты, номер счета, номер договора, телефон, сумму кредита, приватный account id или любые PII.
- Для чувствительных деталей используется
ctxилиstate: короткий opaque ID, который резолвится сервером после авторизации. - У каждого шаблона есть owner, схема параметров, fallback, analytics event и QA status.
Deep-link registry должен жить не в Notion и не в голове одного mobile developer. Это должен быть управляемый конфиг:
Cross-authorization: где чаще всего теряются параметры
Самая частая поломка в SuperApp выглядит так:
- Пользователь кликает рекламу:
...?utm_campaign=deposit_kzt&deep_link_value=deposit_term. - App Link открывает приложение.
- Пользователь не авторизован.
- Приложение отправляет его в login или KYC.
- После login auth service возвращает пользователя на default home.
utm_*,deep_link_value,service_id,return_toи MMP attributes уже потеряны.
Или другой вариант:
- App открывает WebView сервиса.
- WebView требует отдельный token.
- Cross-auth endpoint делает redirect.
- Redirect не прокидывает query params.
- Сервис открывается, но campaign context не доходит до purchase event.
Решение - не пытаться таскать все параметры через каждый redirect. Для этого нужен context ledger.

Архитектура:
Смысл ctx_id: он должен пережить все переходы. Raw UTM может потеряться, MMP callback может прийти позже, install referrer может появиться только на first open, WebView может стартовать после login. Но ctx_id связывает все это в одну запись.
Пример записи:
Если пользователь уже авторизован, app router сразу открывает destination. Если нет, он сохраняет ctx_id, отправляет пользователя в login/KYC, а после success возвращается не на home, а в тот же route target.
MMP и UTM: не смешивайте routing и attribution
Одна из причин, почему SuperApp-команды ломают deep links, - они пытаются одним URL решить две разные задачи:
- Routing: куда открыть пользователя.
- Attribution: откуда пользователь пришел.
Эти слои связаны, но не равны. deep_link_value=deposit_term может быть routing signal. utm_campaign=deposit_kzt_may - campaign reporting. af_sub1=deposit - MMP sub parameter. gclid или gbraid - ad click identifier. provider_id=mobile-kcell - product target. Все это нельзя бездумно передавать во все сервисы и нельзя терять на login.
Для AppsFlyer OneLink installed-user flow UDL может вернуть deep_link_value, deep_link_sub1-10 и параметры из link. Но для new users privacy protection ограничивает payload: нельзя рассчитывать, что после install вы получите все campaign fields вроде media source и campaign в deep-link callback. Adjust аналогично разделяет link parameters, deep_link и custom label/redirect логику. Поэтому app router должен уметь объединять несколько источников:
| Источник | Что дает | Как использовать |
|---|---|---|
| Open URL | Path, query, ctx, UTM, click ids | Primary routing для installed users |
| MMP callback | deep_link_value, sub params, attribution metadata | Deferred routing и attribution enrichment |
| Android Install Referrer | Referrer URL из Google Play | Android install attribution fallback |
| Backend click context | Normalized campaign params and destination | Источник истины для cross-auth и WebView |
| Firebase/GA4 events | App open, screen, product event | Measurement and audience building |
Правило merge:
Отдельно: не начинайте новые проекты на Firebase Dynamic Links. Firebase официально deprecated Dynamic Links, а shutdown был назначен на 25 августа 2025 года. Для installed-user deep linking используйте App Links и Universal Links напрямую. Для deferred deep linking и paid attribution используйте MMP или собственный link gateway с понятным fallback, но не стройте новую систему на FDL.
WebView-сервисы: как не терять контекст в mini-app
WebView в SuperApp часто живет как отдельный продукт внутри продукта. У него может быть свой backend, свои sessions, партнерский order id и собственный analytics script. Если native app просто открывает:
то контекст почти гарантированно потеряется. Нужен handoff:
Требования к handoff token:
- Short TTL: минуты, не дни.
- Audience-bound: только конкретный service id.
- Return path signed: WebView не должен сам решать, куда вернуть пользователя.
- No raw PII: token может указывать на server-side subject, но не раскрывать ИИН, телефон или номер счета.
- Event correlation: token содержит или позволяет получить
event_id/ctx_id.
Когда WebView завершает действие, он должен вернуть событие в native/backend:
Но финальное measurement-событие лучше подтверждать backend-ом:
Иначе покупка может улететь в аналитику до того, как платеж реально подтвержден.
Как должны выглядеть ссылки для депозитов, платежей и сервисов
Плохая ссылка:
Она не addressable. Рекламная система не знает, какой продукт продвигался. Приложение не знает, куда вести пользователя. BI потом пытается угадать intent по campaign name.
Слабая ссылка:
Она лучше, но не хватает категории, currency, offer id, route fallback и attribution context.
Нормальная ссылка:
Для платежей:
Для WebView-сервиса:
Для MMP OneLink можно хранить route в deep_link_value, а параметры - в sub fields:
Но приложение все равно должно нормализовать это в собственный route:
Что делать с неустановленным приложением
Если app установлен, verified link должен открыть приложение. Если не установлен, ссылка должна открыть fallback: App Store, Google Play или web landing page. Здесь MMP обычно полезен, потому что умеет route-by-platform и deferred deep linking.
Но нельзя рассчитывать, что App Store или Play Store сохранят полный URL как есть. На Android есть Install Referrer API, через который можно получить referrer content из Google Play. На iOS прямой referrer через install flow принципиально ограничен, поэтому deferred deep link чаще держится на MMP attribution match и privacy-safe payload.
Правильная модель:
- Для installed users - App Links / Universal Links.
- Для not installed - MMP link или link gateway с store fallback.
- Для deferred route -
deep_link_value+ sub params, а не надежда на полный raw URL после install. - Для paid attribution - MMP/ads integration, а не самодельная UTM-таблица.
- Для bank compliance - consent-aware storage and deletion of click context.
QA matrix: как понять, что deep links реально работают
Тестировать нужно не "ссылка открывает приложение", а весь route.
| Сценарий | Ожидаемый результат |
|---|---|
| Installed + logged in | Открывается точный продуктовый экран |
| Installed + logged out | Login, затем тот же продуктовый экран |
| Installed + KYC missing | KYC flow, затем return_to или понятный eligibility fallback |
| Not installed Android | Store install, first open, deferred route |
| Not installed iOS | Store install, first open, MMP deferred route where available |
| WebView service | Cross-auth token есть, ctx не потерян, service event возвращается |
| Expired WebView token | Refresh/handoff retry, не home screen |
| Invalid provider_id | Controlled fallback to category screen |
utm_* + MMP params | Context ledger содержит оба слоя |
| Sensitive params in URL | Link rejected or sanitized |
Команды для технического QA:
Проверки:
- Status 200.
- No redirect for association files.
- Correct JSON body.
- Correct content type where platform expects it.
- Every claimed host has its own file.
- Production app id/package and signing certificate are present.
- Dev/staging app ids do not accidentally shadow production behavior.
Android test:
iOS test:
- Add the URL to Notes.
- Long-press it and verify that iOS offers to open in the app.
- Use Associated Domains diagnostics in Developer settings.
- Check that direct Safari address-bar navigation is not treated as proof of Universal Link behavior. Apple documents that entering a URL directly into the address bar does not open the app as a Universal Link.
Measurement events
Deep-link success should be measured separately from product success.
В BigQuery нужно уметь ответить на вопросы:
- Какие кампании чаще всего теряют route после auth?
- Какие WebView-сервисы открываются, но не возвращают callback?
- Где больше всего invalid provider ids?
- Какие
utm_campaignприводят кdeeplink_received, но не кdeeplink_opened? - Сколько product purchases имеют
ctx_id, а сколько выглядят как organic because context was dropped?
Что бы я заставил SuperApp-команду сделать за 30 дней
- Выбрать canonical link domain: например
go.bank.kzили verifiedhomebank.kz, но без смешивания с неассоциированными host variants. - Прописать App Links / Universal Links для production, staging и dev раздельно.
- Убрать marketing links с доменов, где association endpoints возвращают HTML/404.
- Создать registry deep-link templates для deposits, payments, transfers, loans, cards, marketplace, services.
- Добавить
ctx_idledger и перестать носить чувствительный контекст в query string. - Переделать login/KYC flow так, чтобы
state=ctx_idпереживал auth. - Сделать WebView handoff token и return_to contract.
- Нормализовать MMP payload в тот же route contract.
- Логировать
deeplink_received,deeplink_resolved,deeplink_opened,deeplink_failed. - Собрать QA dashboard по route loss, auth loss, WebView callback loss и attribution loss.
Главная мысль
Если SuperApp не умеет открывать конкретный депозит, платеж, кредит или WebView-сервис по стабильной ссылке, маркетинг обречен оптимизировать не продукт, а абстрактный app open. Если cross-authorization теряет URL parameters, платная кампания может привести правильного пользователя, но событие покупки будет выглядеть как organic или generic app usage. Если MMP payload живет отдельно от native router, deferred deep linking будет случайностью.
Deep-linking в SuperApp нужно проектировать как инфраструктуру роста: verified hosts, template registry, context ledger, auth-safe return path, WebView handoff, MMP merge rules and measurement events. Тогда ссылка из рекламы, push, SMS или QR ведет не "в приложение", а в нужный продукт с сохраненным attribution context.
Sources
- Android Developers: Add Android App Links
- Android Developers: Verify Android App Links
- Apple Developer: Supporting Associated Domains
- Apple Developer: Debugging Universal Links
- Firebase: Dynamic Links Deprecation FAQ
- AppsFlyer: Tracking link structure and parameters
- AppsFlyer: iOS Unified Deep Linking
- Adjust: Link parameters
- Android Developers: Google Play Install Referrer