Инженерлік блог

Paywall әлде AdSense? Firebase және Google Analytics көмегімен жарнаманы «киттерден» қалай динамикалық түрде жасырдық

11.08.2026
Шарафутдинов Р.

Мүдделер қақтығысы: Продакттың ашкөздігі дені сау ақылға қарсы

Мобильді және веб-қосымшалар әлемінде екі орындық бар. Біреуінде — AdSense (немесе кез келген басқа жарнама желісі): баннерлерді көрсетуден, сыйақысы бар бейнелерден және тітіркендіргіш толық экранды interstitial-дардан түсетін тұрақты, бірақ тиын-тебен табыстар. Екіншісінде — Paywall (жазылымдар/In-App Purchases): пайдаланушылар премиум-функциялар немесе жарнаманы өшіру үшін нақты ақша төлейтін алтын кеніш.

Кез келген продакт-менеджер ерте ме, кеш пе осы дилеммаға тап болады. Бір жағынан, біз ешқашан ақша төлемейтін (оларды «халявщиктер» немесе «free-riders» деп атаймыз) 95% пайдаланушыға мүмкіндігінше көп жарнама көрсеткіміз келеді. Екінші жағынан, бізде қалған 5% бар — бұл біздің «киттеріміз» (whales). Бұл бізге $50, $100 әкелуге немесе жылдық жазылым ресімдеуге дайын адамдар.

Міне, осы жерде нағыз ауру басталады. Егер біз әлеуетті «китімізге» қатарынан үшеу казино туралы толық экранды жарнамалық роликті көрсетсек, ол әзірлеушілерді сыбап, қосымшаны жай ғана жойып тастайды. Біз жарнамадан $0.02 табу үшін $50 жоғалтамыз. Бұл экономикалық суицид.

Осылайша мынадай міндет туындады: біздің алдымызда «кит» (немесе әлеуетті кит) тұрғанын нақты уақыт режимінде қалай түсінуге болады және ол премиумды өзі сатып алғанша күтпей-ақ, одан барлық жарнаманы динамикалық түрде қалай жасыруға болады? Ол өнімге ғашық болып, «Сатып алу» түймесін өзі басатындай етіп, оған барынша тегіс, элиталық UX-ті қалай жасауға болады?

Бұл мақалада (көлемі жағынан шағын диссертацияға татиды) мен ОЗАТ командасының Google Analytics 4 (GA4), BigQuery, машиналық оқыту (Vertex AI) және Firebase негізінде жарнамалық жүктемені динамикалық басқару жүйесін қалай құрғаны туралы айтып беремін. Дайындалыңыздар, инженерлік ет (мясо), кэштеу және миллисекундтар үшін күрес көп болады.

«Киттің» анатомиясы: Біз кімді іздейміз?

Код жазбас бұрын, кімнен жарнаманы жасыратынымызды анықтап алайық. Біздің жағдайда біз аудиторияны үш сегментке бөлдік:

  1. Confirmed Whales (Расталған киттер): Премиумды немесе In-App Purchase-ті сатып алып қойғандар. Мұнда бәрі қарапайым — жарнаманы әдепкі бойынша (default) өшіреміз. Бұл базалық функционал.
  2. High Propensity to Buy (Сатып алу ықтималдығы жоғары): Бұл жігіттер әлі ештеңе сатып алған жоқ, бірақ олардың мінез-құлқы: «Мен дайынмын!» деп айқайлап тұр. Олар қосымшаға күн сайын кіреді, core-фичаларды белсенді қолданады, Paywall экранына жиі кіреді, бірақ неге екені белгісіз, конвертацияланбайды. Бәлкім, оларды жарнама тітіркендіретін шығар. Егер біз оны алып тастасақ, олардың лоялдылығын арттырамыз.
  3. Free-riders (Қарапайым пайдаланушылар): Айына бір рет кіреді, бір-екі түймені басады, кетеді. Сатып алу ықтималдығы — 0.01%. Міне, осыларға біз жарнамалық баннерлерімізді толық қуатында айналдыратын боламыз.

Ең қиын категория — екіншісі. Нақты уақытта High Propensity-ді қалай есептеуге болады? Көмекке ML келеді.

Data Pipeline: Кликтен скорингке дейін

Біздің шешіміміздің архитектурасы келесідей:

App -> GA4 -> BigQuery -> Vertex AI (ML Model) -> Cloud Functions -> Firebase (Custom Claims / Firestore) -> App

Әр қадамды егжей-тегжейлі талдайық.

1-қадам: GA4 және BigQuery (Шикі деректерді жинау)

Қосымшадағы барлық оқиғалар (кликтер, экрандарды қарау, сессиялар) Firebase/GA4-ке түседі. Біз GA4-тен BigQuery-ге шикі деректерді күнделікті экспорттауды (Daily Export) баптаймыз. BigQuery-де біз бағанағы events_YYYYMMDD кестелерін аламыз.

Бірақ шикі деректер ML-моделі үшін жарамайды. Бізге Feature Engineering қажет. Біз пайдаланушылардың (user_pseudo_id немесе user_id бойынша) соңғы 14 күндегі мінез-құлқын күн сайын агрегациялайтын dbt-модельдерін жаздық.

Біз қандай фичаларды жинаймыз:

  • RFM (Recency, Frequency, Monetary): Соңғы кіргеніне неше күн болды, барлығы қанша сессия, қанша ақша жұмсады (әзірге 0).
  • Engagement: Сессияның орташа ұзақтығы, бір сессиядағы экрандар саны.
  • Paywall Intents: Пайдаланушы сатып алу экранын қанша рет ашты.
  • Ad Tolerance: Ол қанша жарнамалық роликті соңына дейін көрді, ал қаншасын өткізіп жіберді (skip).

2-қадам: Vertex AI-да машиналық оқыту

Агрегацияланған деректерде біз классификация моделін (мысалы, XGBoost) оқытамыз. Таргет (Y) — пайдаланушы келесі 7 күнде сатып алу жасады ма (1 немесе 0).

Біз Vertex AI AutoML немесе Python-дағы кастомды пайплайндарды қолданамыз. Модель аптасына бір рет жаттығады. Ал Batch Prediction (барлық ағымдағы белсенді пайдаланушыларды скорингтеу) біз күн сайын түнде іске қосамыз.

Vertex AI жұмысының нәтижесі BigQuery-дегі мынадай кесте болып табылады:

user_id | propensity_score | segment
------------------------------------
user_A  | 0.89             | High
user_B  | 0.05             | Low
user_C  | 0.45             | Medium

3-қадам: Firebase-пен синхрондау (The Tricky Part)

Окей, бізде BigQuery-де пайдаланушы сегменттері бар кесте бар. Енді қосымша бұл туралы кешігусіз (delay) қалай біле алады? Мобильді қосымшадан BigQuery-ге SQL-сұрау жасау керек пе? Құдай сақтасын! Бұл баяу, қымбат әрі қауіпсіз емес.

Бұл статусты клиентке жеткізуіміз керек. Мұндай стейтті сақтауға арналған мінсіз орын — Firebase. Бірақ Firebase-тің нақты қай сервисін таңдау керек?

Бізде үш нұсқа бар:

  1. Firestore (Дерекқор): segment: "High" өрісі бар users/{userId} құжатын сақтау.
  2. Firebase Remote Config: Google Analytics Audiences бойынша шарттарды (Conditions) пайдалану.
  3. Firebase Auth Custom Claims: Статусты тікелей пайдаланушының JWT-токеніне тігіп тастау.

Неге Firestore емес?

Firestore — тамаша таңдау. Мобилка құжатқа жазылады (Snapshot Listener). Cloud Function күн сайын түнде BigQuery-ден кестені оқиды және Firestore-ға батч-жаңартулар (batch writes) жасайды.
Минус: Дерекқордан қосымша оқулар (reads) = қосымша шығындар. Сонымен қатар, біз SDK инициализациясын және базадан бірінші жауапты күтуіміз керек, бұл суық старт кезінде 300-500мс алуы мүмкін. Бұл "Flickering" (жарнаманың жыпылықтауы) эффектісіне әкеледі.

Неге Remote Config + GA Audiences емес?

Бұл ең "no-code" жол сияқты көрінеді. GA4-те аудитория құрамыз, ол Remote Config-ке жеткенше күтеміз де, show_ads = false параметрін өзгертеміз.
Минус: Синхрондау кешігуі. GA4-тегі аудиториялар Firebase-ке 24 сағатқа дейін жетеді. Бұл өте баяу. Біз "дайын тұрған" пайдаланушыны жоғалтып аламыз.

Біздің таңдауымыз: Firebase Auth Custom Claims

Custom Claims (пайдаланушы бекітулері) — бұл сиқыр. Сіз пайдаланушының авторизациялық токеніне (JWT) тікелей кез келген кастомды ақпаратты қоса аласыз. Пайдаланушы қосымшаны ашқанда, токен құрылғының локальді кэшінде жатады. Клиентке өзінің сегментін білу үшін желілік сұраулар жасаудың қажеті жоқ!

// Cloud Function: BigQuery -> Firebase Auth синхрондау
import * as admin from 'firebase-admin';
import { BigQuery } from '@google-cloud/bigquery';

const bq = new BigQuery();

exports.syncWhalesToAuth = functions.pubsub.schedule('every 24 hours').onRun(async (context) => {
    const query = `SELECT user_id, segment FROM `my_project.marts.user_segments` WHERE segment = 'High'`;
    const [rows] = await bq.query(query);

    for (const row of rows) {
        // Custom Claim 'is_whale' = true орнатамыз
        await admin.auth().setCustomUserClaims(row.user_id, { is_whale: true });
    }
    console.log(`${rows.length} кит синхрондалды.`);
});

Миллисекундтар үшін күрес: Клиенттік реализация (React)

Егер біз жарнамалық баннерді рендеринг жасап, содан кейін жарты секундтан соң оны жасырсақ (өйткені бұл кит деген статус бізге енді жетті) — бұл өте нашар көрінеді. Верстка секіреді (Cumulative Layout Shift), пайдаланушы кездейсоқ бос жерге (немесе, одан да жаманы, біз жасырғымыз келген жарнамаға) басып қалады.

Firebase Auth көмегімен біз қосымша іске қосылған кезде токенді синхронды түрде тексере аламыз.

// React: Пайдаланушы статусын тексеру
import { useEffect, useState } from 'react';
import { getAuth, onAuthStateChanged, getIdTokenResult } from 'firebase/auth';

export const useAdStrategy = () => {
    const [showAds, setShowAds] = useState<boolean | null>(null); // null = жүктелуде

    useEffect(() => {
        const auth = getAuth();
        const unsubscribe = onAuthStateChanged(auth, async (user) => {
            if (user) {
                // claims аламыз. Жылдамдық үшін ForceRefresh = false!
                const tokenResult = await getIdTokenResult(user, false);
                const isWhale = tokenResult.claims.is_whale === true;
                setShowAds(!isWhale);
            } else {
                // Логин жасамағандарға - жарнаманы толықтай көрсетеміз
                setShowAds(true);
            }
        });
        return () => unsubscribe();
    }, []);

    return showAds;
};

Антипаттерн: "Әзірге ілініп тұра тұрсын, кейін көреміз"

Тексеру жүріп жатқанда, ешқашан әдепкі бойынша showAds: true қолданбаңыз. Интерфейсті секірткенше, 100мс-қа лоадер немесе жарнама блогының бос skeleton-ын (скелетонын) көрсеткеніңіз жөн. Әдепкі мән null немесе "pending state" болуы тиіс.

Қу трюк: Динамикалық Paywall (Soft Paywall)

Окей, біз «киттерден» жарнаманы жасырдық. Ары қарай не істейміз? Адамдардың жынына тимегенімізге қуанамыз ба?

Жоқ, продакт-менеджер әлі де ақша қалайды. Егер біз жай ғана жарнаманы алып тастасақ, «кит» бізде ақылы жазылым бар екенін мүлде ұмытып кетуі мүмкін. Оған онсыз да жақсы ғой!

Сондықтан біз жарнама блоктарын Soft Paywall Banners-ке ауыстырдық. AdSense-тің казино туралы жарнамасының орнына, сол орындарда біз өзіміздің премиум-фичаларымыздың баннерлерін ұқыпты әрі нативті түрде көрсетеміз.

«Сізге біздің қосымшамыз жарнамасыз ұнап жатыр ма? Аналитика мен есептерді экспорттауды ашу үшін Premium-ді қазір 50% жеңілдікпен ресімдеңіз!»

Шын мәнінде, біз жарнама инвентарын өзіміздің апселл (Upsell) құралымызға айналдырдық.

A/B Тестілеу және Аналитика: Сандар өтірік айтпайды

Әрине, біз бұл логиканы 100% пайдаланушыларға тексерусіз шығара алмас едік. Біз аудиторияны (High Propensity тобын) екіге бөліп, Firebase A/B Testing арқылы А/В тест бастадық.

  • Бақылау тобы (А тобы): Қалыпты AdSense жарнамасын алуды жалғастырды.
  • Тест тобы (В тобы): Жарнама өшірілді (Soft Paywall-ға ауыстырылды).

Шокинг (және аса емес) нәтижелер:

  1. В тобындағы Ad Revenue (Жарнамалық табыстар) қисынды түрде 12%-ға төмендеді. Бұл киттерге баннерлер көрсетпегеніміз үшін біз алмаған сол баяғы тиындар.
  2. В тобындағы Conversion Rate to Premium (Жазылымға конверсия) 47%-ҒА ӨСТІ. Таза UX және нативті апселл адамдарды тітіркендіру арқылы агрессивті мәжбүрлеуге қарағанда әлдеқайда тиімдірек жұмыс істейтіні белгілі болды.
  3. High Propensity пайдаланушылары арасындағы Retention Rate (7-ші күннің ұстап қалуы) 15%-ға артты. Олар ашуланып, қосымшаны жоюды доғарды.

А/В тест нәтижелері: AdSense vs Soft Paywall (High Propensity сегменті үшін)

Стандартты жарнаманы толық өшіріп, оны ішкі апселлге ауыстырған кездегі негізгі бизнес-метрикаларды салыстыру.

Масштабтау: Vertex AI Endpoint-пен Real-time ML

Жоғарыда сипатталған пайплайн пакеттік (Batch) режимде жұмыс істейді. Біз сегменттерді түнде қайта есептейміз. Бұл қосымшалардың 90% үшін жеткілікті.

Бірақ пайдаланушы бір сессия ішінде "китке" айналса ше? Мысалы, ол жаңа ғана несие картасын байлады немесе себетке 10 тауар қосты. Біз жарнаманы ертең таңертең емес, дәл ҚАЗІР алып тастағымыз келеді.

Бұл үшін біз модельді Vertex AI Endpoint (Нақты уақыттағы API) ретінде орналастырдық (deploy). Пайдаланушы негізгі әрекетті (Key Event) жасағанда, мобильді қосымша біздің Cloud Function-ға HTTP-сұрау жібереді. Функция Endpoint-ті тартады, жаңа скор алады, және егер скор > 0.9 болса, дереу admin.auth().setCustomUserClaims(...) жасайды және клиенттен токенді мәжбүрлеп жаңартуды сұрайды (getIdToken(true)).

Жарнама дәл сессия барысында, көз алдымызда жоғалады. Бұл вау-эффект тудырады.

Қорытынды: Data-архитектураға инвестиция салыңыз

Бизнес көбінесе монетизацияға қосқыш ретінде қарайды: "Кімге болса да жарнама қоса салайық". Қысқа мерзімді перспективада бұл ақша әкеледі. Ал ұзақ мерзімді перспективада — лояльды аудиторияны өртеп жібереді және LTV-ді (Lifetime Value) өлтіреді.

Google Analytics 4, BigQuery, Vertex AI-дағы ML-модельдер және Firebase Auth Custom Claims арқылы стейтті лезде жеткізу байламының көмегімен біз монетизацияны балтадан скальпельге айналдырдық. Біз LTV-де жеңіске жетеміз, UX-ті жақсартамыз және пайдаланушыларды да, қаржы директорын да бақытты етеміз.

Мұндай жүйені енгізу инженерлік мәдениетті және Data Science пен Frontend-әзірлеушілер арасындағы тығыз өзара әрекеттесуді талап етеді. Бірақ конверсияның өсу графиктерін көрген бойда, сіз мұның оған тұрарлық екенін түсінесіз.

Ал сіз өз өнімдеріңізде жарнама мен жазылымдар арасындағы балансты қалай сақтайсыз? Түсініктемелерде бөлісіңіздер (немесе бізге ОЗАТ-қа жазыңыздар, біз мұны қалай емдеу керектігін білеміз).

Рустам Шарафутдинов

Рустам Шарафутдинов

Инженерлік блог авторы

Google Cloud архитектурасы саласындағы сарапшы және 15 жылдан астам тәжірибесі бар Senior Full-Stack әзірлеушісі. Ақауға төзімді архитектураларға, жоғары жүктемелі жобаларды оңтайландыруға және AI (Vertex AI) интеграциясына маманданған.

Сараптама: GCP, Kubernetes, Микросервистер, React, Node.js

Пікірлер (0)