feat: track and push TeleCart Pulse events

This commit is contained in:
2025-11-30 16:52:32 +03:00
parent fc8044484e
commit ef785654b9
19 changed files with 583 additions and 70 deletions

View File

@@ -109,9 +109,11 @@
</template>
<template #body="{ data }">
<template v-if="col.field === 'id'">{{ data.id }}</template>
<template v-else-if="col.field === 'telegram_user_id'">{{
data.telegram_user_id
}}
<template v-else-if="col.field === 'telegram_user_id'">
{{ data.telegram_user_id }}
</template>
<template v-else-if="col.field === 'tracking_id'">
<code>{{ data.tracking_id }}</code>
</template>
<template v-else-if="col.field === 'username'">
<div class="tw:flex tw:items-center tw:gap-2">
@@ -180,7 +182,8 @@
<InputText v-model="filterModel.value" type="text" placeholder="Поиск по фамилии"
class="p-column-filter"/>
</template>
<template v-else-if="['last_seen_at', 'created_at', 'privacy_consented_at'].includes(col.field)">
<template
v-else-if="['last_seen_at', 'created_at', 'privacy_consented_at'].includes(col.field)">
<DatePicker v-model="filterModel.value" dateFormat="dd.mm.yy" placeholder="dd.mm.yyyy"/>
</template>
<template v-else-if="col.field === 'orders_count'">
@@ -314,6 +317,14 @@ const columns = ref([
filterable: true,
visible: false
},
{
field: 'tracking_id',
header: 'Tracking ID',
sortable: false,
filterable: true,
visible: false,
help: 'Tracking ID это публичный уникальный идентификатор покупателя, используется в рекламных кампаниях для отслеживания активности.',
},
{field: 'username', header: 'Имя пользователя', sortable: true, filterable: true, visible: true},
{field: 'first_name', header: 'Имя', sortable: true, filterable: true, visible: true},
{field: 'last_name', header: 'Фамилия', sortable: true, filterable: true, visible: true},

View File

@@ -1,3 +1,4 @@
export const TC_PULSE_EVENTS = {
WEBAPP_OPEN: 'WEBAPP_OPEN',
ORDER_CREATED: 'ORDER_CREATED',
};

View File

@@ -87,3 +87,39 @@ export function getCssVarOklchRgb(cssVarName) {
return `#${toHex(r)}${toHex(g)}${toHex(b_)}`;
}
export function deserializeStartParams(serialized) {
if (!serialized) {
return {};
}
// Восстанавливаем стандартные base64 символы
let encoded = serialized.replace(/-/g, '+').replace(/_/g, '/');
// Добавляем padding, если нужно
const padding = encoded.length % 4;
if (padding !== 0) {
encoded += '='.repeat(4 - padding);
}
// Декодируем из base64
let json;
try {
json = atob(encoded); // btoa / atob стандартные в браузере
} catch (e) {
throw new Error('Failed to decode base64 string');
}
// Парсим JSON
let parameters;
try {
parameters = JSON.parse(json);
} catch (e) {
throw new Error('Failed to decode JSON: ' + e.message);
}
if (typeof parameters !== 'object' || parameters === null || Array.isArray(parameters) && !Array.isArray(parameters)) {
throw new Error('Decoded value is not an object');
}
return parameters;
}

View File

@@ -8,7 +8,7 @@ import {useSettingsStore} from "@/stores/SettingsStore.js";
import ApplicationError from "@/ApplicationError.vue";
import AppMetaInitializer from "@/utils/AppMetaInitializer.ts";
import {injectYaMetrika} from "@/utils/yaMetrika.js";
import {checkIsUserPrivacyConsented, ingest, saveTelegramCustomer} from "@/utils/ftch.js";
import {checkIsUserPrivacyConsented} from "@/utils/ftch.js";
import {register} from 'swiper/element/bundle';
import 'swiper/element/bundle';
@@ -20,6 +20,7 @@ import {getCssVarOklchRgb} from "@/helpers.js";
import {defaultConfig, plugin} from '@formkit/vue';
import config from './formkit.config.js';
import {TC_PULSE_EVENTS} from "@/constants/tPulseEvents.js";
import {usePulseStore} from "@/stores/Pulse.js";
register();
@@ -34,6 +35,7 @@ app
const settings = useSettingsStore();
const blocks = useBlocksStore();
const pulse = usePulseStore();
const appLoading = createApp(AppLoading);
appLoading.mount('#app');
@@ -51,24 +53,9 @@ settings.load()
throw new Error('App disabled (maintenance mode)');
}
})
.then(() => {
const webapp = window.Telegram.WebApp;
ingest({
event: TC_PULSE_EVENTS.WEBAPP_OPEN,
webapp,
})
.catch(err => console.error('Ingest failed:', err));
})
.then(() => {
// Сохраняем данные Telegram-пользователя в базу данных
const userData = window.Telegram?.WebApp?.initDataUnsafe?.user;
if (userData) {
console.debug('[Init] Saving Telegram customer data');
saveTelegramCustomer(userData)
.then(() => console.debug('[Init] Telegram customer data saved successfully'))
.catch(() => console.warn('[Init] Failed to save Telegram customer data:', error));
}
})
.then(() => pulse.initFromStartParams())
.then(() => pulse.catchTelegramCustomerFromInitData())
.then(() => pulse.ingest(TC_PULSE_EVENTS.WEBAPP_OPEN))
.then(() => {
(async () => {
try {

View File

@@ -5,6 +5,9 @@ import {useCartStore} from "@/stores/CartStore.js";
import {YA_METRIKA_GOAL} from "@/constants/yaMetrikaGoals.js";
import {useYaMetrikaStore} from "@/stores/yaMetrikaStore.js";
import {useSettingsStore} from "@/stores/SettingsStore.js";
import {usePulseStore} from "@/stores/Pulse.js";
import {TC_PULSE_EVENTS} from "@/constants/tPulseEvents.js";
import {nextTick} from "vue";
export const useCheckoutStore = defineStore('checkout', {
state: () => ({
@@ -58,6 +61,9 @@ export const useCheckoutStore = defineStore('checkout', {
}
const yaMetrika = useYaMetrikaStore();
const pulse = usePulseStore();
await nextTick(() => {
yaMetrika.reachGoal(YA_METRIKA_GOAL.ORDER_CREATED_SUCCESS, {
price: this.order?.final_total_numeric,
currency: this.order?.currency,
@@ -82,6 +88,12 @@ export const useCheckoutStore = defineStore('checkout', {
}
}
});
pulse.ingest(TC_PULSE_EVENTS.ORDER_CREATED, {
order_id: this.order.id,
revenue: this.order?.final_total_numeric,
currency: this.order?.currency,
});
});
await window.Telegram.WebApp.HapticFeedback.notificationOccurred('success');
await useCartStore().getProducts();

View File

@@ -0,0 +1,50 @@
import {defineStore} from "pinia";
import {ingest, saveTelegramCustomer} from "@/utils/ftch.js";
import {toRaw} from "vue";
import {deserializeStartParams} from "@/helpers.js";
export const usePulseStore = defineStore('pulse', {
state: () => ({
tracking_id: null,
campaign_id: null,
}),
actions: {
initFromStartParams() {
const webapp = window.Telegram.WebApp;
const startParam = webapp.initDataUnsafe.start_param;
const deserialized = deserializeStartParams(startParam);
this.tracking_id = deserialized?.tracking_id;
this.campaign_id = deserialized?.campaign_id;
console.debug('[Pulse] Init with start parameters: ', deserialized);
},
ingest(event, eventData = {}) {
ingest({
event: event,
payload: {
webapp: window.Telegram.WebApp,
eventData: eventData,
},
})
.then(() => console.debug('[Pulse] Event Ingested', event, eventData))
.catch(err => console.error('Ingest failed:', err));
},
catchTelegramCustomerFromInitData() {
const userData = window.Telegram?.WebApp?.initDataUnsafe?.user;
if (userData) {
console.debug('[Pulse] Saving Telegram customer data');
saveTelegramCustomer(userData)
.then((response) => {
this.tracking_id = this.tracking_id || response?.data?.tracking_id || null;
console.debug(
'[Pulse] Telegram customer data saved successfully. Tracking ID: ',
toRaw(this.tracking_id)
);
})
.catch(() => console.warn('[Pulse] Failed to save Telegram customer data:', error));
}
},
},
});

View File

@@ -1,7 +1,7 @@
import {defineStore} from "pinia";
import {useSettingsStore} from "@/stores/SettingsStore.js";
import sha256 from 'crypto-js/sha256';
import {toRaw} from "vue";
import {usePulseStore} from "@/stores/Pulse.js";
export const useYaMetrikaStore = defineStore('ya_metrika', {
state: () => ({
@@ -20,6 +20,10 @@ export const useYaMetrikaStore = defineStore('ya_metrika', {
params.referer = params.referer ?? this.prevPath;
const pulse = usePulseStore();
params.campaign_id = params.campaign_id || pulse.campaign_id || null;
params.tracking_id = params.tracking_id || pulse.tracking_id || null;
if (typeof window.ym === 'function' && window.YA_METRIKA_ID !== undefined) {
console.debug('[ym] Hit ', fullUrl);
console.debug('[ym] ID ', window.YA_METRIKA_ID);
@@ -47,6 +51,10 @@ export const useYaMetrikaStore = defineStore('ya_metrika', {
return;
}
const pulse = usePulseStore();
params.campaign_id = params.campaign_id || pulse.campaign_id || null;
params.tracking_id = params.tracking_id || pulse.tracking_id || null;
if (typeof window.ym === 'function' && window.YA_METRIKA_ID !== undefined) {
console.debug('[ym] reachGoal ', target, ' params: ', params);
window.ym(window.YA_METRIKA_ID, 'reachGoal', target, params);
@@ -69,14 +77,8 @@ export const useYaMetrikaStore = defineStore('ya_metrika', {
}
if (typeof window.ym === 'function' && window.YA_METRIKA_ID !== undefined) {
let tgID = null;
if (window?.Telegram?.WebApp?.initDataUnsafe?.user?.id) {
tgID = sha256(window.Telegram.WebApp.initDataUnsafe.user.id).toString();
}
const userParams = {
tg_id: tgID,
tracking_id: usePulseStore().tracking_id,
language: window.Telegram?.WebApp?.initDataUnsafe?.user?.language_code || 'unknown',
platform: window.Telegram?.WebApp?.platform || 'unknown',
};
@@ -119,6 +121,19 @@ export const useYaMetrikaStore = defineStore('ya_metrika', {
return;
}
const pulse = usePulseStore();
const campaignId = pulse.campaign_id || null;
object.ecommerce = object.ecommerce || {};
if (campaignId) {
object.ecommerce.promotions = object.ecommerce.promotions || [];
object.ecommerce.promotions.push({ id: campaignId });
}
// Всегда добавляем ключи на верхнем уровне
object.campaign_id = campaignId;
object.tracking_id = pulse.tracking_id || null;
if (Array.isArray(window.dataLayer)) {
console.debug('[ym] dataLayer push: ', object);
window.dataLayer.push(object);

View File

@@ -79,6 +79,7 @@ class TelegramCustomersHandler
'id',
'telegram_user_id',
'oc_customer_id',
'tracking_id',
'username',
'first_name',
'last_name',
@@ -323,6 +324,7 @@ class TelegramCustomersHandler
'id' => (int) $customer['id'],
'telegram_user_id' => (int) $customer['telegram_user_id'],
'oc_customer_id' => (int) $customer['oc_customer_id'],
'tracking_id' => $customer['tracking_id'],
'username' => $customer['username'],
'first_name' => $customer['first_name'],
'last_name' => $customer['last_name'],

View File

@@ -30,7 +30,8 @@
"psr/container": "^2.0",
"psr/log": "^1.1",
"symfony/cache": "^5.4",
"vlucas/phpdotenv": "^5.6"
"vlucas/phpdotenv": "^5.6",
"ramsey/uuid": "^4.2"
},
"require-dev": {
"doctrine/sql-formatter": "^1.3",

View File

@@ -4,8 +4,68 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "0c1bcdf986f5b31fb943e21467785c64",
"content-hash": "049ebb1f7c985aa2bbbe3578c203fb37",
"packages": [
{
"name": "brick/math",
"version": "0.9.3",
"source": {
"type": "git",
"url": "https://github.com/brick/math.git",
"reference": "ca57d18f028f84f777b2168cd1911b0dee2343ae"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/brick/math/zipball/ca57d18f028f84f777b2168cd1911b0dee2343ae",
"reference": "ca57d18f028f84f777b2168cd1911b0dee2343ae",
"shasum": ""
},
"require": {
"ext-json": "*",
"php": "^7.1 || ^8.0"
},
"require-dev": {
"php-coveralls/php-coveralls": "^2.2",
"phpunit/phpunit": "^7.5.15 || ^8.5 || ^9.0",
"vimeo/psalm": "4.9.2"
},
"type": "library",
"autoload": {
"psr-4": {
"Brick\\Math\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Arbitrary-precision arithmetic library",
"keywords": [
"Arbitrary-precision",
"BigInteger",
"BigRational",
"arithmetic",
"bigdecimal",
"bignum",
"brick",
"math"
],
"support": {
"issues": "https://github.com/brick/math/issues",
"source": "https://github.com/brick/math/tree/0.9.3"
},
"funding": [
{
"url": "https://github.com/BenMorel",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/brick/math",
"type": "tidelift"
}
],
"time": "2021-08-15T20:50:18+00:00"
},
{
"name": "carbonphp/carbon-doctrine-types",
"version": "2.1.0",
@@ -1488,6 +1548,194 @@
},
"time": "2019-03-08T08:55:37+00:00"
},
{
"name": "ramsey/collection",
"version": "1.3.0",
"source": {
"type": "git",
"url": "https://github.com/ramsey/collection.git",
"reference": "ad7475d1c9e70b190ecffc58f2d989416af339b4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ramsey/collection/zipball/ad7475d1c9e70b190ecffc58f2d989416af339b4",
"reference": "ad7475d1c9e70b190ecffc58f2d989416af339b4",
"shasum": ""
},
"require": {
"php": "^7.4 || ^8.0",
"symfony/polyfill-php81": "^1.23"
},
"require-dev": {
"captainhook/plugin-composer": "^5.3",
"ergebnis/composer-normalize": "^2.28.3",
"fakerphp/faker": "^1.21",
"hamcrest/hamcrest-php": "^2.0",
"jangregor/phpstan-prophecy": "^1.0",
"mockery/mockery": "^1.5",
"php-parallel-lint/php-console-highlighter": "^1.0",
"php-parallel-lint/php-parallel-lint": "^1.3",
"phpcsstandards/phpcsutils": "^1.0.0-rc1",
"phpspec/prophecy-phpunit": "^2.0",
"phpstan/extension-installer": "^1.2",
"phpstan/phpstan": "^1.9",
"phpstan/phpstan-mockery": "^1.1",
"phpstan/phpstan-phpunit": "^1.3",
"phpunit/phpunit": "^9.5",
"psalm/plugin-mockery": "^1.1",
"psalm/plugin-phpunit": "^0.18.4",
"ramsey/coding-standard": "^2.0.3",
"ramsey/conventional-commits": "^1.3",
"vimeo/psalm": "^5.4"
},
"type": "library",
"extra": {
"captainhook": {
"force-install": true
},
"ramsey/conventional-commits": {
"configFile": "conventional-commits.json"
}
},
"autoload": {
"psr-4": {
"Ramsey\\Collection\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Ben Ramsey",
"email": "ben@benramsey.com",
"homepage": "https://benramsey.com"
}
],
"description": "A PHP library for representing and manipulating collections.",
"keywords": [
"array",
"collection",
"hash",
"map",
"queue",
"set"
],
"support": {
"issues": "https://github.com/ramsey/collection/issues",
"source": "https://github.com/ramsey/collection/tree/1.3.0"
},
"funding": [
{
"url": "https://github.com/ramsey",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/ramsey/collection",
"type": "tidelift"
}
],
"time": "2022-12-27T19:12:24+00:00"
},
{
"name": "ramsey/uuid",
"version": "4.2.3",
"source": {
"type": "git",
"url": "https://github.com/ramsey/uuid.git",
"reference": "fc9bb7fb5388691fd7373cd44dcb4d63bbcf24df"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ramsey/uuid/zipball/fc9bb7fb5388691fd7373cd44dcb4d63bbcf24df",
"reference": "fc9bb7fb5388691fd7373cd44dcb4d63bbcf24df",
"shasum": ""
},
"require": {
"brick/math": "^0.8 || ^0.9",
"ext-json": "*",
"php": "^7.2 || ^8.0",
"ramsey/collection": "^1.0",
"symfony/polyfill-ctype": "^1.8",
"symfony/polyfill-php80": "^1.14"
},
"replace": {
"rhumsaa/uuid": "self.version"
},
"require-dev": {
"captainhook/captainhook": "^5.10",
"captainhook/plugin-composer": "^5.3",
"dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
"doctrine/annotations": "^1.8",
"ergebnis/composer-normalize": "^2.15",
"mockery/mockery": "^1.3",
"moontoast/math": "^1.1",
"paragonie/random-lib": "^2",
"php-mock/php-mock": "^2.2",
"php-mock/php-mock-mockery": "^1.3",
"php-parallel-lint/php-parallel-lint": "^1.1",
"phpbench/phpbench": "^1.0",
"phpstan/extension-installer": "^1.0",
"phpstan/phpstan": "^0.12",
"phpstan/phpstan-mockery": "^0.12",
"phpstan/phpstan-phpunit": "^0.12",
"phpunit/phpunit": "^8.5 || ^9",
"slevomat/coding-standard": "^7.0",
"squizlabs/php_codesniffer": "^3.5",
"vimeo/psalm": "^4.9"
},
"suggest": {
"ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.",
"ext-ctype": "Enables faster processing of character classification using ctype functions.",
"ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.",
"ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.",
"paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter",
"ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type."
},
"type": "library",
"extra": {
"captainhook": {
"force-install": true
},
"branch-alias": {
"dev-main": "4.x-dev"
}
},
"autoload": {
"files": [
"src/functions.php"
],
"psr-4": {
"Ramsey\\Uuid\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "A PHP library for generating and working with universally unique identifiers (UUIDs).",
"keywords": [
"guid",
"identifier",
"uuid"
],
"support": {
"issues": "https://github.com/ramsey/uuid/issues",
"source": "https://github.com/ramsey/uuid/tree/4.2.3"
},
"funding": [
{
"url": "https://github.com/ramsey",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/ramsey/uuid",
"type": "tidelift"
}
],
"time": "2021-09-25T23:10:38+00:00"
},
{
"name": "symfony/cache",
"version": "v5.4.46",
@@ -2063,6 +2311,86 @@
],
"time": "2025-01-02T08:10:11+00:00"
},
{
"name": "symfony/polyfill-php81",
"version": "v1.33.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php81.git",
"reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c",
"reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c",
"shasum": ""
},
"require": {
"php": ">=7.2"
},
"type": "library",
"extra": {
"thanks": {
"url": "https://github.com/symfony/polyfill",
"name": "symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Php81\\": ""
},
"classmap": [
"Resources/stubs"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php81/tree/v1.33.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-09-09T11:45:10+00:00"
},
{
"name": "symfony/service-contracts",
"version": "v1.1.2",

View File

@@ -0,0 +1,16 @@
<?php
use Openguru\OpenCartFramework\Migrations\Migration;
return new class extends Migration {
public function up(): void
{
$sql = <<<SQL
ALTER TABLE `telecart_customers`
ADD COLUMN `tracking_id` VARCHAR(64) NOT NULL AFTER `oc_customer_id`;
SQL;
$this->database->statement($sql);
}
};

View File

@@ -3,6 +3,7 @@
use Openguru\OpenCartFramework\Application;
use Openguru\OpenCartFramework\Config\Settings;
use Openguru\OpenCartFramework\Support\Utils;
use Openguru\OpenCartFramework\TeleCartPulse\TeleCartPulseService;
if (! function_exists('table')) {
function db_table(string $name): string

View File

@@ -5,4 +5,5 @@ namespace Openguru\OpenCartFramework\TeleCartPulse;
final class PulseEvents
{
public const WEBAPP_OPEN = 'WEBAPP_OPEN';
public const ORDER_CREATED = 'ORDER_CREATED';
}

View File

@@ -33,7 +33,7 @@ class TeleCartPulseService
return;
}
$initData = Arr::get($data, 'webapp.initData');
$initData = Arr::get($data, 'payload.webapp.initData');
if (! $initData) {
return;
}
@@ -46,12 +46,16 @@ class TeleCartPulseService
try {
$decoded = $this->initDataDecoder->parseInitDataStringToArray($initData);
$startParam = Arr::get($decoded, 'start_param');
$startParam = Arr::get($decoded, 'start_param', '');
$deserialized = StartParamSerializer::deserialize($startParam);
if ($event === PulseEvents::WEBAPP_OPEN) {
$this->handleWebAppInit($data, $deserialized);
}
if ($event === PulseEvents::ORDER_CREATED) {
$this->handleOrderCreated($data, $deserialized);
}
} catch (ClientException $exception) {
$contents = (string)$exception->getResponse()->getBody();
$decoded = json_decode($contents, true);
@@ -108,4 +112,30 @@ class TeleCartPulseService
$client->post('events', compact('json'));
}
private function handleOrderCreated(array $data, array $deserialized): void
{
if (isset($deserialized['campaign_id'], $deserialized['tracking_id'])) {
$payload = [
'event' => PulseEvents::ORDER_CREATED,
'campaign_id' => $deserialized['campaign_id'],
'tracking_id' => $deserialized['tracking_id'],
'meta' => [
'domain' => Utils::getCurrentDomain(),
'version' => Arr::get($data, 'webapp.version'),
'platform' => Arr::get($data, 'webapp.platform'),
'order_id' => Arr::get($data, 'eventData.order_id'),
'currency' => Arr::get($data, 'eventData.currency'),
],
'timestamp' => Carbon::now('UTC')->toJSON(),
];
$dataToSend = [
'payload' => $payload,
'signature' => $this->payloadSigner->sign($payload),
];
$this->pushEvent($dataToSend);
}
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace Openguru\OpenCartFramework\TeleCartPulse;
use Ramsey\Uuid\Uuid;
class TrackingIdGenerator
{
public static function generate(): string
{
return Uuid::uuid4()->toString();
}
}

View File

@@ -96,7 +96,7 @@ class ETLHandler
$query
->select([
new RawExpression('md5(telegram_user_id) AS tracking_id'),
'tracking_id',
'telegram_user_id' => 'tg_user_id',
'telecart_customers.oc_customer_id',
'is_premium',

View File

@@ -9,6 +9,7 @@ use Openguru\OpenCartFramework\Http\JsonResponse;
use Openguru\OpenCartFramework\Http\Request;
use Openguru\OpenCartFramework\Http\Response;
use Openguru\OpenCartFramework\Support\Arr;
use Openguru\OpenCartFramework\TeleCartPulse\TrackingIdGenerator;
use Openguru\OpenCartFramework\Telegram\Enums\TelegramHeader;
use Openguru\OpenCartFramework\Telegram\Exceptions\DecodeTelegramInitDataException;
use Openguru\OpenCartFramework\Telegram\TelegramInitDataDecoder;
@@ -41,11 +42,15 @@ class TelegramCustomerHandler
public function saveOrUpdate(Request $request): JsonResponse
{
try {
$this->telegramCustomerService->saveOrUpdate(
$customer = $this->telegramCustomerService->saveOrUpdate(
$this->extractTelegramUserData($request)
);
return new JsonResponse([], Response::HTTP_NO_CONTENT);
return new JsonResponse([
'data' => [
'tracking_id' => Arr::get($customer, 'tracking_id'),
],
], Response::HTTP_OK);
} catch (Throwable $e) {
$this->logger->error('Could not save telegram customer data', ['exception' => $e]);

View File

@@ -7,6 +7,7 @@ namespace App\Models;
use Carbon\Carbon;
use Openguru\OpenCartFramework\QueryBuilder\Builder;
use Openguru\OpenCartFramework\QueryBuilder\Connections\ConnectionInterface;
use Openguru\OpenCartFramework\TeleCartPulse\TrackingIdGenerator;
use RuntimeException;
class TelegramCustomer
@@ -81,6 +82,7 @@ class TelegramCustomer
{
$data['created_at'] = Carbon::now()->toDateTimeString();
$data['updated_at'] = Carbon::now()->toDateTimeString();
$data['tracking_id'] = TrackingIdGenerator::generate();
$success = $this->database->insert(self::TABLE_NAME, $data);

View File

@@ -23,10 +23,10 @@ class TelegramCustomerService
* Сохранить или обновить Telegram-пользователя
*
* @param array $telegramUserData Данные пользователя из Telegram.WebApp.initDataUnsafe
* @return void
* @return array
* @throws RuntimeException Если данные невалидны или не удалось сохранить
*/
public function saveOrUpdate(array $telegramUserData): void
public function saveOrUpdate(array $telegramUserData): array
{
$telegramUserId = $this->extractTelegramUserId($telegramUserData);
$telegramCustomerData = $this->prepareCustomerData($telegramUserData, $telegramUserId);
@@ -38,6 +38,8 @@ class TelegramCustomerService
} else {
$this->telegramCustomer->create($telegramCustomerData);
}
return $this->telegramCustomer->findByTelegramUserId($telegramUserId);
}
/**