feat: track and push TeleCart Pulse events
This commit is contained in:
@@ -109,9 +109,11 @@
|
|||||||
</template>
|
</template>
|
||||||
<template #body="{ data }">
|
<template #body="{ data }">
|
||||||
<template v-if="col.field === 'id'">{{ data.id }}</template>
|
<template v-if="col.field === 'id'">{{ data.id }}</template>
|
||||||
<template v-else-if="col.field === 'telegram_user_id'">{{
|
<template v-else-if="col.field === 'telegram_user_id'">
|
||||||
data.telegram_user_id
|
{{ data.telegram_user_id }}
|
||||||
}}
|
</template>
|
||||||
|
<template v-else-if="col.field === 'tracking_id'">
|
||||||
|
<code>{{ data.tracking_id }}</code>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="col.field === 'username'">
|
<template v-else-if="col.field === 'username'">
|
||||||
<div class="tw:flex tw:items-center tw:gap-2">
|
<div class="tw:flex tw:items-center tw:gap-2">
|
||||||
@@ -180,7 +182,8 @@
|
|||||||
<InputText v-model="filterModel.value" type="text" placeholder="Поиск по фамилии"
|
<InputText v-model="filterModel.value" type="text" placeholder="Поиск по фамилии"
|
||||||
class="p-column-filter"/>
|
class="p-column-filter"/>
|
||||||
</template>
|
</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"/>
|
<DatePicker v-model="filterModel.value" dateFormat="dd.mm.yy" placeholder="dd.mm.yyyy"/>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="col.field === 'orders_count'">
|
<template v-else-if="col.field === 'orders_count'">
|
||||||
@@ -314,6 +317,14 @@ const columns = ref([
|
|||||||
filterable: true,
|
filterable: true,
|
||||||
visible: false
|
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: 'username', header: 'Имя пользователя', sortable: true, filterable: true, visible: true},
|
||||||
{field: 'first_name', 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},
|
{field: 'last_name', header: 'Фамилия', sortable: true, filterable: true, visible: true},
|
||||||
@@ -597,10 +608,10 @@ function resetFilters() {
|
|||||||
operator: FilterOperator.AND,
|
operator: FilterOperator.AND,
|
||||||
constraints: [{value: null, matchMode: FilterMatchMode.DATE_IS}]
|
constraints: [{value: null, matchMode: FilterMatchMode.DATE_IS}]
|
||||||
},
|
},
|
||||||
privacy_consented_at: {
|
privacy_consented_at: {
|
||||||
operator: FilterOperator.AND,
|
operator: FilterOperator.AND,
|
||||||
constraints: [{value: null, matchMode: FilterMatchMode.DATE_IS}]
|
constraints: [{value: null, matchMode: FilterMatchMode.DATE_IS}]
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
lazyParams.value.page = 1;
|
lazyParams.value.page = 1;
|
||||||
lazyParams.value.first = 0;
|
lazyParams.value.first = 0;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
export const TC_PULSE_EVENTS = {
|
export const TC_PULSE_EVENTS = {
|
||||||
WEBAPP_OPEN: 'WEBAPP_OPEN',
|
WEBAPP_OPEN: 'WEBAPP_OPEN',
|
||||||
|
ORDER_CREATED: 'ORDER_CREATED',
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -87,3 +87,39 @@ export function getCssVarOklchRgb(cssVarName) {
|
|||||||
return `#${toHex(r)}${toHex(g)}${toHex(b_)}`;
|
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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {useSettingsStore} from "@/stores/SettingsStore.js";
|
|||||||
import ApplicationError from "@/ApplicationError.vue";
|
import ApplicationError from "@/ApplicationError.vue";
|
||||||
import AppMetaInitializer from "@/utils/AppMetaInitializer.ts";
|
import AppMetaInitializer from "@/utils/AppMetaInitializer.ts";
|
||||||
import {injectYaMetrika} from "@/utils/yaMetrika.js";
|
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 {register} from 'swiper/element/bundle';
|
||||||
import 'swiper/element/bundle';
|
import 'swiper/element/bundle';
|
||||||
@@ -20,6 +20,7 @@ import {getCssVarOklchRgb} from "@/helpers.js";
|
|||||||
import {defaultConfig, plugin} from '@formkit/vue';
|
import {defaultConfig, plugin} from '@formkit/vue';
|
||||||
import config from './formkit.config.js';
|
import config from './formkit.config.js';
|
||||||
import {TC_PULSE_EVENTS} from "@/constants/tPulseEvents.js";
|
import {TC_PULSE_EVENTS} from "@/constants/tPulseEvents.js";
|
||||||
|
import {usePulseStore} from "@/stores/Pulse.js";
|
||||||
|
|
||||||
register();
|
register();
|
||||||
|
|
||||||
@@ -34,6 +35,7 @@ app
|
|||||||
|
|
||||||
const settings = useSettingsStore();
|
const settings = useSettingsStore();
|
||||||
const blocks = useBlocksStore();
|
const blocks = useBlocksStore();
|
||||||
|
const pulse = usePulseStore();
|
||||||
|
|
||||||
const appLoading = createApp(AppLoading);
|
const appLoading = createApp(AppLoading);
|
||||||
appLoading.mount('#app');
|
appLoading.mount('#app');
|
||||||
@@ -51,24 +53,9 @@ settings.load()
|
|||||||
throw new Error('App disabled (maintenance mode)');
|
throw new Error('App disabled (maintenance mode)');
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => pulse.initFromStartParams())
|
||||||
const webapp = window.Telegram.WebApp;
|
.then(() => pulse.catchTelegramCustomerFromInitData())
|
||||||
ingest({
|
.then(() => pulse.ingest(TC_PULSE_EVENTS.WEBAPP_OPEN))
|
||||||
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(() => {
|
.then(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ import {useCartStore} from "@/stores/CartStore.js";
|
|||||||
import {YA_METRIKA_GOAL} from "@/constants/yaMetrikaGoals.js";
|
import {YA_METRIKA_GOAL} from "@/constants/yaMetrikaGoals.js";
|
||||||
import {useYaMetrikaStore} from "@/stores/yaMetrikaStore.js";
|
import {useYaMetrikaStore} from "@/stores/yaMetrikaStore.js";
|
||||||
import {useSettingsStore} from "@/stores/SettingsStore.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', {
|
export const useCheckoutStore = defineStore('checkout', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
@@ -30,7 +33,7 @@ export const useCheckoutStore = defineStore('checkout', {
|
|||||||
|
|
||||||
console.log("Allows write to PM: ", data.user.allows_write_to_pm);
|
console.log("Allows write to PM: ", data.user.allows_write_to_pm);
|
||||||
|
|
||||||
if (! data.user.allows_write_to_pm) {
|
if (!data.user.allows_write_to_pm) {
|
||||||
console.log("Sending request");
|
console.log("Sending request");
|
||||||
const granted = await new Promise(resolve => {
|
const granted = await new Promise(resolve => {
|
||||||
window.Telegram.WebApp.requestWriteAccess((granted) => {
|
window.Telegram.WebApp.requestWriteAccess((granted) => {
|
||||||
@@ -52,35 +55,44 @@ export const useCheckoutStore = defineStore('checkout', {
|
|||||||
});
|
});
|
||||||
this.order = response.data;
|
this.order = response.data;
|
||||||
|
|
||||||
if (! this.order.id) {
|
if (!this.order.id) {
|
||||||
console.debug(response.data);
|
console.debug(response.data);
|
||||||
throw new Error('Ошибка создания заказа.');
|
throw new Error('Ошибка создания заказа.');
|
||||||
}
|
}
|
||||||
|
|
||||||
const yaMetrika = useYaMetrikaStore();
|
const yaMetrika = useYaMetrikaStore();
|
||||||
yaMetrika.reachGoal(YA_METRIKA_GOAL.ORDER_CREATED_SUCCESS, {
|
const pulse = usePulseStore();
|
||||||
price: this.order?.final_total_numeric,
|
|
||||||
currency: this.order?.currency,
|
await nextTick(() => {
|
||||||
});
|
yaMetrika.reachGoal(YA_METRIKA_GOAL.ORDER_CREATED_SUCCESS, {
|
||||||
yaMetrika.dataLayerPush({
|
price: this.order?.final_total_numeric,
|
||||||
"ecommerce": {
|
currency: this.order?.currency,
|
||||||
"currencyCode": useSettingsStore().currency_code,
|
});
|
||||||
"purchase": {
|
yaMetrika.dataLayerPush({
|
||||||
"actionField": {
|
"ecommerce": {
|
||||||
"id": this.order.id,
|
"currencyCode": useSettingsStore().currency_code,
|
||||||
'revenue': this.order?.final_total_numeric,
|
"purchase": {
|
||||||
},
|
"actionField": {
|
||||||
"products": this.order.products ? this.order.products.map((product, index) => {
|
"id": this.order.id,
|
||||||
return {
|
'revenue': this.order?.final_total_numeric,
|
||||||
id: product.product_id,
|
},
|
||||||
name: product.name,
|
"products": this.order.products ? this.order.products.map((product, index) => {
|
||||||
price: product.total_numeric,
|
return {
|
||||||
position: index,
|
id: product.product_id,
|
||||||
quantity: product.quantity,
|
name: product.name,
|
||||||
};
|
price: product.total_numeric,
|
||||||
}) : [],
|
position: index,
|
||||||
|
quantity: product.quantity,
|
||||||
|
};
|
||||||
|
}) : [],
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
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 window.Telegram.WebApp.HapticFeedback.notificationOccurred('success');
|
||||||
|
|||||||
50
frontend/spa/src/stores/Pulse.js
Normal file
50
frontend/spa/src/stores/Pulse.js
Normal 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));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import {defineStore} from "pinia";
|
import {defineStore} from "pinia";
|
||||||
import {useSettingsStore} from "@/stores/SettingsStore.js";
|
import {useSettingsStore} from "@/stores/SettingsStore.js";
|
||||||
import sha256 from 'crypto-js/sha256';
|
|
||||||
import {toRaw} from "vue";
|
import {toRaw} from "vue";
|
||||||
|
import {usePulseStore} from "@/stores/Pulse.js";
|
||||||
|
|
||||||
export const useYaMetrikaStore = defineStore('ya_metrika', {
|
export const useYaMetrikaStore = defineStore('ya_metrika', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
@@ -20,6 +20,10 @@ export const useYaMetrikaStore = defineStore('ya_metrika', {
|
|||||||
|
|
||||||
params.referer = params.referer ?? this.prevPath;
|
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) {
|
if (typeof window.ym === 'function' && window.YA_METRIKA_ID !== undefined) {
|
||||||
console.debug('[ym] Hit ', fullUrl);
|
console.debug('[ym] Hit ', fullUrl);
|
||||||
console.debug('[ym] ID ', window.YA_METRIKA_ID);
|
console.debug('[ym] ID ', window.YA_METRIKA_ID);
|
||||||
@@ -47,6 +51,10 @@ export const useYaMetrikaStore = defineStore('ya_metrika', {
|
|||||||
return;
|
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) {
|
if (typeof window.ym === 'function' && window.YA_METRIKA_ID !== undefined) {
|
||||||
console.debug('[ym] reachGoal ', target, ' params: ', params);
|
console.debug('[ym] reachGoal ', target, ' params: ', params);
|
||||||
window.ym(window.YA_METRIKA_ID, 'reachGoal', target, 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) {
|
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 = {
|
const userParams = {
|
||||||
tg_id: tgID,
|
tracking_id: usePulseStore().tracking_id,
|
||||||
language: window.Telegram?.WebApp?.initDataUnsafe?.user?.language_code || 'unknown',
|
language: window.Telegram?.WebApp?.initDataUnsafe?.user?.language_code || 'unknown',
|
||||||
platform: window.Telegram?.WebApp?.platform || 'unknown',
|
platform: window.Telegram?.WebApp?.platform || 'unknown',
|
||||||
};
|
};
|
||||||
@@ -119,6 +121,19 @@ export const useYaMetrikaStore = defineStore('ya_metrika', {
|
|||||||
return;
|
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)) {
|
if (Array.isArray(window.dataLayer)) {
|
||||||
console.debug('[ym] dataLayer push: ', object);
|
console.debug('[ym] dataLayer push: ', object);
|
||||||
window.dataLayer.push(object);
|
window.dataLayer.push(object);
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ class TelegramCustomersHandler
|
|||||||
'id',
|
'id',
|
||||||
'telegram_user_id',
|
'telegram_user_id',
|
||||||
'oc_customer_id',
|
'oc_customer_id',
|
||||||
|
'tracking_id',
|
||||||
'username',
|
'username',
|
||||||
'first_name',
|
'first_name',
|
||||||
'last_name',
|
'last_name',
|
||||||
@@ -323,6 +324,7 @@ class TelegramCustomersHandler
|
|||||||
'id' => (int) $customer['id'],
|
'id' => (int) $customer['id'],
|
||||||
'telegram_user_id' => (int) $customer['telegram_user_id'],
|
'telegram_user_id' => (int) $customer['telegram_user_id'],
|
||||||
'oc_customer_id' => (int) $customer['oc_customer_id'],
|
'oc_customer_id' => (int) $customer['oc_customer_id'],
|
||||||
|
'tracking_id' => $customer['tracking_id'],
|
||||||
'username' => $customer['username'],
|
'username' => $customer['username'],
|
||||||
'first_name' => $customer['first_name'],
|
'first_name' => $customer['first_name'],
|
||||||
'last_name' => $customer['last_name'],
|
'last_name' => $customer['last_name'],
|
||||||
|
|||||||
@@ -30,7 +30,8 @@
|
|||||||
"psr/container": "^2.0",
|
"psr/container": "^2.0",
|
||||||
"psr/log": "^1.1",
|
"psr/log": "^1.1",
|
||||||
"symfony/cache": "^5.4",
|
"symfony/cache": "^5.4",
|
||||||
"vlucas/phpdotenv": "^5.6"
|
"vlucas/phpdotenv": "^5.6",
|
||||||
|
"ramsey/uuid": "^4.2"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"doctrine/sql-formatter": "^1.3",
|
"doctrine/sql-formatter": "^1.3",
|
||||||
|
|||||||
@@ -4,8 +4,68 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "0c1bcdf986f5b31fb943e21467785c64",
|
"content-hash": "049ebb1f7c985aa2bbbe3578c203fb37",
|
||||||
"packages": [
|
"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",
|
"name": "carbonphp/carbon-doctrine-types",
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
@@ -1488,6 +1548,194 @@
|
|||||||
},
|
},
|
||||||
"time": "2019-03-08T08:55:37+00:00"
|
"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",
|
"name": "symfony/cache",
|
||||||
"version": "v5.4.46",
|
"version": "v5.4.46",
|
||||||
@@ -2063,6 +2311,86 @@
|
|||||||
],
|
],
|
||||||
"time": "2025-01-02T08:10:11+00:00"
|
"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",
|
"name": "symfony/service-contracts",
|
||||||
"version": "v1.1.2",
|
"version": "v1.1.2",
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
use Openguru\OpenCartFramework\Application;
|
use Openguru\OpenCartFramework\Application;
|
||||||
use Openguru\OpenCartFramework\Config\Settings;
|
use Openguru\OpenCartFramework\Config\Settings;
|
||||||
use Openguru\OpenCartFramework\Support\Utils;
|
use Openguru\OpenCartFramework\Support\Utils;
|
||||||
|
use Openguru\OpenCartFramework\TeleCartPulse\TeleCartPulseService;
|
||||||
|
|
||||||
if (! function_exists('table')) {
|
if (! function_exists('table')) {
|
||||||
function db_table(string $name): string
|
function db_table(string $name): string
|
||||||
|
|||||||
@@ -5,4 +5,5 @@ namespace Openguru\OpenCartFramework\TeleCartPulse;
|
|||||||
final class PulseEvents
|
final class PulseEvents
|
||||||
{
|
{
|
||||||
public const WEBAPP_OPEN = 'WEBAPP_OPEN';
|
public const WEBAPP_OPEN = 'WEBAPP_OPEN';
|
||||||
|
public const ORDER_CREATED = 'ORDER_CREATED';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ class TeleCartPulseService
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$initData = Arr::get($data, 'webapp.initData');
|
$initData = Arr::get($data, 'payload.webapp.initData');
|
||||||
if (! $initData) {
|
if (! $initData) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -46,12 +46,16 @@ class TeleCartPulseService
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
$decoded = $this->initDataDecoder->parseInitDataStringToArray($initData);
|
$decoded = $this->initDataDecoder->parseInitDataStringToArray($initData);
|
||||||
$startParam = Arr::get($decoded, 'start_param');
|
$startParam = Arr::get($decoded, 'start_param', '');
|
||||||
$deserialized = StartParamSerializer::deserialize($startParam);
|
$deserialized = StartParamSerializer::deserialize($startParam);
|
||||||
|
|
||||||
if ($event === PulseEvents::WEBAPP_OPEN) {
|
if ($event === PulseEvents::WEBAPP_OPEN) {
|
||||||
$this->handleWebAppInit($data, $deserialized);
|
$this->handleWebAppInit($data, $deserialized);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($event === PulseEvents::ORDER_CREATED) {
|
||||||
|
$this->handleOrderCreated($data, $deserialized);
|
||||||
|
}
|
||||||
} catch (ClientException $exception) {
|
} catch (ClientException $exception) {
|
||||||
$contents = (string)$exception->getResponse()->getBody();
|
$contents = (string)$exception->getResponse()->getBody();
|
||||||
$decoded = json_decode($contents, true);
|
$decoded = json_decode($contents, true);
|
||||||
@@ -108,4 +112,30 @@ class TeleCartPulseService
|
|||||||
|
|
||||||
$client->post('events', compact('json'));
|
$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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Openguru\OpenCartFramework\TeleCartPulse;
|
||||||
|
|
||||||
|
use Ramsey\Uuid\Uuid;
|
||||||
|
|
||||||
|
class TrackingIdGenerator
|
||||||
|
{
|
||||||
|
public static function generate(): string
|
||||||
|
{
|
||||||
|
return Uuid::uuid4()->toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -96,7 +96,7 @@ class ETLHandler
|
|||||||
|
|
||||||
$query
|
$query
|
||||||
->select([
|
->select([
|
||||||
new RawExpression('md5(telegram_user_id) AS tracking_id'),
|
'tracking_id',
|
||||||
'telegram_user_id' => 'tg_user_id',
|
'telegram_user_id' => 'tg_user_id',
|
||||||
'telecart_customers.oc_customer_id',
|
'telecart_customers.oc_customer_id',
|
||||||
'is_premium',
|
'is_premium',
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ use Openguru\OpenCartFramework\Http\JsonResponse;
|
|||||||
use Openguru\OpenCartFramework\Http\Request;
|
use Openguru\OpenCartFramework\Http\Request;
|
||||||
use Openguru\OpenCartFramework\Http\Response;
|
use Openguru\OpenCartFramework\Http\Response;
|
||||||
use Openguru\OpenCartFramework\Support\Arr;
|
use Openguru\OpenCartFramework\Support\Arr;
|
||||||
|
use Openguru\OpenCartFramework\TeleCartPulse\TrackingIdGenerator;
|
||||||
use Openguru\OpenCartFramework\Telegram\Enums\TelegramHeader;
|
use Openguru\OpenCartFramework\Telegram\Enums\TelegramHeader;
|
||||||
use Openguru\OpenCartFramework\Telegram\Exceptions\DecodeTelegramInitDataException;
|
use Openguru\OpenCartFramework\Telegram\Exceptions\DecodeTelegramInitDataException;
|
||||||
use Openguru\OpenCartFramework\Telegram\TelegramInitDataDecoder;
|
use Openguru\OpenCartFramework\Telegram\TelegramInitDataDecoder;
|
||||||
@@ -41,11 +42,15 @@ class TelegramCustomerHandler
|
|||||||
public function saveOrUpdate(Request $request): JsonResponse
|
public function saveOrUpdate(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->telegramCustomerService->saveOrUpdate(
|
$customer = $this->telegramCustomerService->saveOrUpdate(
|
||||||
$this->extractTelegramUserData($request)
|
$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) {
|
} catch (Throwable $e) {
|
||||||
$this->logger->error('Could not save telegram customer data', ['exception' => $e]);
|
$this->logger->error('Could not save telegram customer data', ['exception' => $e]);
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ namespace App\Models;
|
|||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Openguru\OpenCartFramework\QueryBuilder\Builder;
|
use Openguru\OpenCartFramework\QueryBuilder\Builder;
|
||||||
use Openguru\OpenCartFramework\QueryBuilder\Connections\ConnectionInterface;
|
use Openguru\OpenCartFramework\QueryBuilder\Connections\ConnectionInterface;
|
||||||
|
use Openguru\OpenCartFramework\TeleCartPulse\TrackingIdGenerator;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
|
|
||||||
class TelegramCustomer
|
class TelegramCustomer
|
||||||
@@ -81,6 +82,7 @@ class TelegramCustomer
|
|||||||
{
|
{
|
||||||
$data['created_at'] = Carbon::now()->toDateTimeString();
|
$data['created_at'] = Carbon::now()->toDateTimeString();
|
||||||
$data['updated_at'] = Carbon::now()->toDateTimeString();
|
$data['updated_at'] = Carbon::now()->toDateTimeString();
|
||||||
|
$data['tracking_id'] = TrackingIdGenerator::generate();
|
||||||
|
|
||||||
$success = $this->database->insert(self::TABLE_NAME, $data);
|
$success = $this->database->insert(self::TABLE_NAME, $data);
|
||||||
|
|
||||||
|
|||||||
@@ -23,10 +23,10 @@ class TelegramCustomerService
|
|||||||
* Сохранить или обновить Telegram-пользователя
|
* Сохранить или обновить Telegram-пользователя
|
||||||
*
|
*
|
||||||
* @param array $telegramUserData Данные пользователя из Telegram.WebApp.initDataUnsafe
|
* @param array $telegramUserData Данные пользователя из Telegram.WebApp.initDataUnsafe
|
||||||
* @return void
|
* @return array
|
||||||
* @throws RuntimeException Если данные невалидны или не удалось сохранить
|
* @throws RuntimeException Если данные невалидны или не удалось сохранить
|
||||||
*/
|
*/
|
||||||
public function saveOrUpdate(array $telegramUserData): void
|
public function saveOrUpdate(array $telegramUserData): array
|
||||||
{
|
{
|
||||||
$telegramUserId = $this->extractTelegramUserId($telegramUserData);
|
$telegramUserId = $this->extractTelegramUserId($telegramUserData);
|
||||||
$telegramCustomerData = $this->prepareCustomerData($telegramUserData, $telegramUserId);
|
$telegramCustomerData = $this->prepareCustomerData($telegramUserData, $telegramUserId);
|
||||||
@@ -38,6 +38,8 @@ class TelegramCustomerService
|
|||||||
} else {
|
} else {
|
||||||
$this->telegramCustomer->create($telegramCustomerData);
|
$this->telegramCustomer->create($telegramCustomerData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $this->telegramCustomer->findByTelegramUserId($telegramUserId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -49,7 +51,7 @@ class TelegramCustomerService
|
|||||||
*/
|
*/
|
||||||
private function extractTelegramUserId(array $telegramUserData): int
|
private function extractTelegramUserId(array $telegramUserData): int
|
||||||
{
|
{
|
||||||
$telegramUserId = (int) Arr::get($telegramUserData, 'id');
|
$telegramUserId = (int)Arr::get($telegramUserData, 'id');
|
||||||
|
|
||||||
if ($telegramUserId <= 0) {
|
if ($telegramUserId <= 0) {
|
||||||
throw new RuntimeException('Telegram user ID is required and must be positive');
|
throw new RuntimeException('Telegram user ID is required and must be positive');
|
||||||
@@ -102,7 +104,7 @@ class TelegramCustomerService
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (int) $customer['id'];
|
return (int)$customer['id'];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function increaseOrdersCount(int $telecartCustomerId): void
|
public function increaseOrdersCount(int $telecartCustomerId): void
|
||||||
|
|||||||
Reference in New Issue
Block a user