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

Неліктен GA4 Kaspi арқылы төлем жасағанда 30% өтірік айтады? Server-Side GTM және Cloud Functions көмегімен транзакциялар атрибуциясын жөндейміз

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

Қазақстандық e-commerce-тің басты бас ауруы

Егер сіз Қазақстанда e-commerce-пен айналыссаңыз, сізде Kaspi арқылы төлем 100% қосылған. Бұл де-факто стандарт, монополия және юзер үшін жай ғана ыңғайлы. Бірақ маркетологтар мен веб-аналитиктер үшін Kaspi Pay — бұл жердегі тозақтың жеке филиалы.

Көз алдыңызға елестетіңіз: сіз Google Ads-ке бюджет құясыз, таргетолог мінсіз кампанияларды баптайды, адамдар басады, сатып алады. Сіз ROAS-ты (жарнаманың өтелуі) көру және таргетологқа сыйақы жазу үшін Google Analytics 4 (GA4)-ке кіресіз. Ал онда — тосынсый! Барлық транзакциялардың 30%-ының көзі Direct / (none) немесе одан да күлкілісі Referral / pay.kaspi.kz болып тұрады. Google Ads өзінің тиын әкелгенін көрсетеді, ал шын мәнінде сатылымдар дәл сол жерден келіп жатыр.

Таргетолог жылайды, маркетолог валерьян ішеді, бизнес жарнамаға кететін шығындарды қысқартады (ал ол шын мәнінде жұмыс істеп тұрған еді). Бұл неліктен орын алады? Және ең бастысы — бэкендтің жартысын қайта жазбай, мұны қалай жөндеуге болады?

Сессияның үзілу анатомиясы: Атрибуция қайда жоғалады?

Мобильді телефоннан сатып алу кезіндегі типтік User Journey-ді (пайдаланушы жолын) қадам бойынша талдап көрейік:

  1. Пайдаланушы Google-дан жарнаманы көреді (онда gclid және жарнама көзі бар). Басады, сайтқа өтеді. GA4-те сессия басталады, client_id (браузердің бірегей идентификаторы) және session_id тіркеледі.
  2. Юзер тауарды себетке салады, жеткізу мәліметтерін толтырады және «Kaspi арқылы төлеу» түймесін басады.
  3. Кері қайтпайтын нүкте: Сайт пайдаланушыны Kaspi мобильді қосымшасына редирект жасайды (бағыттайды). Браузер жиналады (немесе жабылады).
  4. Пайдаланушы төлемді Face ID арқылы растайды, Kaspi қосымшасында жасыл құсбелгіні көреді.
  5. Ал одан кейін айырылыс:
    • А сценарийі (Оптимистік, 70% жағдайда): Юзер «Дүкенге оралу» түймесін басады. Kaspi қосымшасы браузерді ашады, юзер /checkout/success бетіনে түседі. Бірақ браузер жаңа қойынды (вкладка) ашуы мүмкін немесе контекст жоғалады және GA4 бұл өтуді көзі pay.kaspi.kz (реферал) болатын жаңа сессия ретінде санайды. Иә, kaspi.kz-ті Referral Exclusion List-ке қосуға болады, бірақ бұл әрдайым құтқара бермейді.
    • Б сценарийі (Пессимистік, 30% жағдайда): Юзер жай ғана Kaspi қосымшасын сырғытып жауып, телефонын қалтасына салады. Тауар сатып алынды ғой, сайтқа оралудың қажеті қанша? Нәтижесінде сіздің дерекқорыңызда транзакция бар, ақша шотта, ал GA4-те purchase оқиғасы мүлдем жоқ.

Дәл осы Б сценарийінің кесірінен сіздің аналитикаңыз үштен бір бөлікке өтірік айтады. Сіз ең ыстық лидтерді жоғалтасыз және Google Ads-тің машиналық оқыту алгоритмдерін өлтіресіз, өйткені олар сәтті конверсиялар туралы сигналдар алмайды.

Жұмыс істемейтін қостыльдар (уақытша шешімдер)

Әдетте диджитал-агенттіктердің «сеньорлары» екі шешім ұсынады:

  • Тікелей бэкендтен Measurement Protocol (MP). Логикалық тұрғыдан дұрыс сияқты: сәтті төлемнен кейін бэкенд GA4-ке POST-сұрау жібереді. Бірақ мәселе мынада, бэкенд әдетте client_id мен session_id-ді білмейді. Нәтижесінде ол транзакцияны «жаңа» пайдаланушыдан жібереді де, ол Direct-ке түседі.
  • Webhooks + Zapier/Make. Қымбат, баяу және қауіпсіз емес. Транзакциялар туралы деректерді (сомалар мен тауарлармен бірге) бөгде no-code сервистері арқылы беру — энтерпрайз үшін онша жақсы идея емес.

Мінсіз архитектура: Webhooks + Cloud Functions + sGTM

Бұл мәселені біржола шешу үшін бізге пайдаланушының сайтқа оралған-оралмағанына тәуелсіз жұмыс істейтін асинхронды пайплайн құру қажет. Транзакция GA4-ке сервер арқылы ақша нақты түскен кезде (Kaspi-ден келген вебхук бойынша) жіберілуі тиіс.

Архитектура мынадай болады:

  1. Фронтенд (Тапсырыс жасау): Юзер «Тапсырысты рәсімдеу» түймесін басқанда, фронтенд бэкендке тек тауарларды ғана емес, сонымен қатар аналитика кукилерін де жібереді: _ga (ішінде client_id бар) және session_id. Бэкенд оларды дерекқордағы тапсырыстар кестесінде (Orders) сақтайды.
  2. Бэкенд (Күту): Бэкенд күйі pending болатын тапсырыс жасайды және төлемге сілтеме береді.
  3. Kaspi (Төлем): Юзер қосымшада төлейді. Kaspi біздің серверге қорғалған HTTP Webhook жібереді: «#12345 тапсырыс сәтті төленді».
  4. Cloud Function (Байыту): Бэкендтің негізгі монолитіне аналитикамен жүктеме түсірмеу үшін біз Google Cloud Functions-те микросервис көтереміз. Ол вебхукты қабылдайды, дерекқорға кіреді, #12345 тапсырысты табады, одан client_id мен session_id-ді шығарып алады, тауарлар туралы деректері бар JSON қалыптастырады.
  5. Server-Side GTM (Жіберу): Cloud Function бұл JSON-ды біздің серверлік Google Tag Manager-ге (sGTM) жібереді. sGTM дұрыс Measurement Protocol сұрауын ұқыпты қалыптастырып, оны GA4-ке итереді (push).

1-қадам: Фронтендте Client ID және Session ID-ді ұстап алу

Осы екі параметрсіз GA4 транзакцияны жарнаманы басумен (кликпен) жабыстыра алмайды. Тапсырысты бэкендке жібермес бұрын оларды кукиден және localStorage-тен (GA4 баптауларына байланысты) шығарып аламыз.

// _ga кукиінен client_id шығарып алу функциясы
function getGAClientId() {
  const match = document.cookie.match(/(?:^|;)s*_ga=([^;]*)/);
  if (match) {
    // Куки мынадай болады: GA1.1.123456789.1600000000
    // Бізге соңғы екі бөлігі қажет
    const parts = match[1].split('.');
    return parts.length === 4 ? `${parts[2]}.${parts[3]}` : null;
  }
  return null;
}

// Тапсырысты бэкендке жібереміз
async function createOrder(cartItems) {
  const payload = {
    items: cartItems,
    ga_client_id: getGAClientId(),
    ga_session_id: sessionStorage.getItem('ga_session_id') // Немесе gtag('get', ...) ішінен аламыз
  };
  
  await api.post('/orders', payload);
}

Кеңес: Егер сіз жаңа gtag.js пайдалансаңыз, параметрлерді асинхронды gtag('get', 'G-XXXXXXX', 'client_id', (id) => {...}) әдісі арқылы алу ең сенімді жол.

2-қадам: Вебхукты өңдеуге арналған Google Cloud Function

Біз Serverless-функциясын қолданамыз. Ол нөлге дейін масштабталады, құны тиын тұрады және егер Kaspi бізге секундына 1000 вебхук жіберуді ұйғарса да (ақаулардан кейін осындай болып тұрады) құламайды.

// Google Cloud Functions (Node.js) үшін index.js
const fetch = require('node-fetch');
const { Firestore } = require('@google-cloud/firestore');
const db = new Firestore();

const SGTM_URL = 'https://sgtm.yourdomain.kz/kaspi-webhook'; // Сіздің Server-Side GTM URL-іңіз

exports.handleKaspiWebhook = async (req, res) => {
  try {
    // 1. Kaspi-ден келген вебхук қолтаңбасын тексереміз (Security First!)
    if (!verifyKaspiSignature(req)) {
      return res.status(403).send('Forbidden');
    }

    const { order_id, status, amount } = req.body;

    // 2. Тек сәтті төлемдерді ғана өңдейміз
    if (status !== 'PAID') {
      return res.status(200).send('OK');
    }

    // 3. Тапсырысты дерекқордан аламыз
    const orderDoc = await db.collection('orders').doc(order_id).get();
    if (!orderDoc.exists) return res.status(404).send('Order not found');
    
    const orderData = orderDoc.data();

    // 4. Қайталанудан қорғаныс (purchase екі рет кетіп қалмауы үшін)
    if (orderData.ga_purchase_sent) {
       return res.status(200).send('Already processed');
    }

    // 5. sGTM үшін Payload қалыптастырамыз
    const sgtmPayload = {
      event_name: 'purchase',
      client_id: orderData.ga_client_id,
      session_id: orderData.ga_session_id,
      transaction_id: order_id,
      value: amount,
      currency: 'KZT',
      items: orderData.items // GA4 e-commerce стандарты бойынша тауарлар массиві
    };

    // 6. sGTM-ге жібереміз
    await fetch(SGTM_URL, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(sgtmPayload)
    });

    // 7. Қайталануларды болдырмау үшін тапсырысқа белгі қоямыз
    await db.collection('orders').doc(order_id).update({ ga_purchase_sent: true });

    res.status(200).send('Success');
  } catch (error) {
    console.error('Webhook error:', error);
    res.status(500).send('Internal Error');
  }
};

3-қадам: Server-Side GTM-дегі сиқыр

Неліктен біз сұрауды Cloud Function-нан тікелей GA4-ке жібермейміз? Өйткені GA4-тегі Measurement Protocol — бір қызық нәрсе. Онда тақырыптарға (User-Agent, IP-мекенжай) қойылатын талаптар үнемі өзгеріп отырады, оған қоса sGTM бізге бір нүктеден осы транзакцияны тек GA4-ке ғана емес, сонымен қатар Facebook CAPI, TikTok API, Яндекс.Метрика және т.б. жіберуге мүмкіндік береді.

sGTM-де біз /kaspi-webhook эндпойнтін тыңдайтын кастомды Клиент (Client) жасаймыз. Ол кіріс JSON-ды парсингтеп, Event Data Object генерациялайды.

Бұдан әрі біз өзіміздің кастомды клиент жасаған purchase оқиғасына триггерленетін стандартты GA4 (Google Analytics 4) тегін баптаймыз. Тегтің өзі Google серверлеріне дұрыс HTTP-сұрауын қалыптастырады, қажетті құпия кілттерді (API Secret) қосады және client_id-ді лақтырады.

Транзакция көздерін бөлу (Дейін және Кейін)

Server-Side GTM енгізілгеннен кейін атрибуция қалай өзгерді.

Нәтижелер: ROAS-тың қайта тірілуі

Осы архитектураны деплой жасағаннан кейін не болды?

  • Конверсиялар 28%-ға өсті (шын мәнінде олар өскен жоқ, жай ғана біз оларды жоғалтуды тоқтаттық).
  • Direct трафиктің үлесі төмендеді. Бұрын аспаннан түсетін транзакциялар енді Google Ads (CPC), органика (Organic Search) және әлеуметтік желілерден келген кликтерге дұрыс жабысатын болды.
  • Жарнамалық кампанияларды оқыту шарықтап кетті. Google Ads конверсиялар туралы сигналдарды 1.5 есе көп ала бастады. Target ROAS және Maximize Conversions алгоритмдері ақыры жай ғана басып қоймай, нақты ақша әкелетін қандай аудиториялар екенін түсінді. Екі апта ішінде CPO (Cost Per Order) 15%-ға төмендеді.

Google Ads алгоритмдерін оқыту

Апталар бойынша CPO (Cost Per Order) динамикасы теңгемен.

Инженерлер мен маркетологтарға арналған түйіндеме

Аналитиканың жүйелік мәселелерін фронтендтегі қостыльдармен шешуге тырыспаңыз. Егер сіздің флоуыңызда бөгде қосымшаға (Kaspi, банк-клиент, төлем шлюзі) редирект болса — фронтенд-аналитика әрдайым деректерді жоғалтады.

Ақша туралы шындықтың жалғыз сенімді көзі — бұл сіздің бэкендіңіз бен дерекқорыңыз. Cloud Functions + Server-Side GTM байламы сізге аналитиканың Enterprise-деңгейін береді. Сіз деректердің әрбір байтын басқарасыз, AdBlocker-лерге, браузерлік шектеулерге (Apple-дің ITP) және жабылған қойындыларға (вкладкаларға) тәуелді болмайсыз.

client_id жинаңыз, серверлік аналитиканы жақсы көріңіз және сіздің ROAS-ыңыз әрдайым жасыл болсын!

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

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

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

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

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

Пікірлер (0)