first commit

This commit is contained in:
2025-08-02 16:30:27 +02:00
commit 23646bfcee
14851 changed files with 1750626 additions and 0 deletions

View File

@@ -0,0 +1,248 @@
<?php
namespace KupShop\GTMBundle\ServerSideGTMEvent;
use KupShop\GTMBundle\ServerSideGTMEvent\DataLoaders\Products;
use KupShop\KupShopBundle\Context\CurrencyContext;
use KupShop\KupShopBundle\Context\DomainContext;
use KupShop\KupShopBundle\Context\LanguageContext;
use KupShop\KupShopBundle\Context\PriceLevelContext;
use KupShop\KupShopBundle\Context\UserContext;
use KupShop\KupShopBundle\Util\System\AsyncQueue;
use KupShop\OrderingBundle\Event\OrderEvent;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Contracts\EventDispatcher\Event;
abstract class AbstractServerSideGTMEvent
{
use AnalyticsSessionIDsTrait;
use FetchUsersStatsOrderData;
public const ContainerConfigPath = '/_kyticka';
private $debugHash;
private $serverUrl;
protected ?Request $request = null;
public function __construct(
protected readonly DomainContext $domainContext,
protected readonly CurrencyContext $currencyContext,
protected readonly LanguageContext $languageContext,
protected readonly UserContext $userContext,
protected readonly Products $productLoader,
protected readonly RequestStack $requestStack,
protected readonly PriceLevelContext $priceLevelContext,
protected readonly LoggerInterface $logger,
protected readonly AsyncQueue $asyncQueue,
) {
$dbCfg = \Settings::getDefault();
$this->setDebugHash($dbCfg['analytics']['google_tag_manager']['debugHash'] ?? false);
$this->setServerUrl($dbCfg['analytics']['google_tag_manager']['serverUrl'] ?? false);
}
public function setDebugHash($hash)
{
$this->debugHash = $hash;
}
public function setServerUrl($url)
{
$this->serverUrl = $url;
}
public static function getContainerConfigPath(): ?string
{
global $cfg;
$hasUrl = $cfg['Addr']['print'] ?? null;
if (findModule(\Modules::GTM, \Modules::SUB_CONTAINER_CONFIG) && $hasUrl) {
return substr($cfg['Addr']['print'], 0, -1).AbstractServerSideGTMEvent::ContainerConfigPath;
}
return null;
}
public function getServerUrl(): string
{
$dbcfg = \Settings::getDefault();
if (
(($dbcfg['analytics']['google_tag_manager']['container_config_ss'] ?? false) === 'Y')
&& ($domain = self::getContainerConfigPath())
) {
return $domain;
}
return $this->serverUrl;
}
public function start(Event $event)
{
$serverUrl = $this->getServerUrl();
if (!$serverUrl) {
return;
}
$payload = $this->getPayload($event);
$payloadJson = json_encode($payload);
// Protože skripty v údržbě končí exit(), tím pádem se tam neprovede async handling
// A takovej divnej if proto, abysme si byli jisti ze to je udrzba, resp. ze to dela superadmin
if (isAdministration() && isSuperuser()) {
$this->sendData($serverUrl, $payloadJson, $this->getHeader());
} else {
$this->sendDataAsync($serverUrl, $payloadJson);
}
$this->logger->notice('[GTM-SS] Purchase payload', $payload);
}
public function setRequest(?Request $request)
{
$this->request = $request;
}
abstract protected function getPayload(Event $event): array;
final protected function sendDataAsync(string $url, string $payload)
{
$headers = $this->getHeader();
$this->asyncQueue->addHandler(fn () => $this->sendData($url, $payload, $headers), '[GTM-SS] measure event');
}
protected function sendData(string $url, string $payload, ?array $headers = null): void
{
$headers ??= $this->getHeader();
if ($this->debugHash) {
$headers[] = 'x-gtm-server-preview: '.$this->debugHash;
}
$ch = curl_init('https://'.$url.'/measure'); // example: https://m.shop.wpj.cz/measure
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_exec($ch);
curl_close($ch);
}
/**
* @return string[]
*/
public function getHeader(): array
{
return [
'Content-Type: application/json',
"Cookie: _fbp={$this->getBrowserFBPixelSessionId($this->getRequest())}",
];
}
protected function getCommonData(Event $event): array
{
$domain = $this->domainContext->getActiveId();
$request = $this->getRequest();
$referer = $request->headers->get('referer');
return [
'client_id' => $this->getClientId(),
'user_id' => $this->getUserId(),
'ga_session_id' => $this->getBrowserGASessionId($request),
'ga_session_id_2' => $this->getBrowserGASessionId($request, '_2'),
'ga_session_id_3' => $this->getBrowserGASessionId($request, '_3'),
'currency' => $this->currencyContext->getActiveId(),
'language' => $this->languageContext->getActiveId(),
'page_encoding' => 'UTF-8',
'page_hostname' => $domain,
'page_location' => "https://{$domain}",
'ip_override' => $request->getClientIp(),
'page_path' => $referer,
'page_title' => null,
'page_referrer' => $referer,
'user_agent' => $request->headers->get('User-Agent'),
'user_data' => $this->getUserData($event),
'domain' => $domain,
];
}
protected function getUserId(): string
{
return (string) \Cart::getCartID(false);
}
protected function getClientId(): string
{
return \Cart::getCartID(false).'_noCookie';
}
protected function getUserData(Event $event): ?array
{
$prefixArray = [];
if ($priceLevel = $this->priceLevelContext->getActive()) {
$prefixArray['price_level'] = $priceLevel->name;
}
if ($event instanceof OrderEvent) {
$order = $event->getOrder();
$user = $order->getUser();
$prefixArray += [
'id' => $user instanceof \User ? (string) $user->id : null,
'groups' => is_array($user?->getGroups()) ? array_values(array_map(fn ($v) => $v['name'], $user->getGroups())) : null,
];
if ($admin = getAdminUser()) {
$prefixArray['admin'] = $admin['login'];
}
$userStats = $this->fetchUsersStatsOrderData($order);
return $prefixArray + [
'lastOrderDate' => $userStats['last_date'] ?? null,
'firstOrderDate' => $userStats['first_date'] ?? null,
'countOrders' => $userStats['count'] ?? null,
'email_address' => $order->invoice_email,
'phone_number' => $order->invoice_phone,
'address' => [
'first_name' => $order->invoice_name,
'last_name' => $order->invoice_surname,
'street' => $order->invoice_street,
'city' => $order->invoice_city,
'region' => $order->invoice_state,
'postal_code' => $order->invoice_zip,
'country' => $order->invoice_country,
],
];
}
if ($user = $this->userContext->getActive()) {
return $prefixArray + [
'email_address' => $user->email ?? null,
'phone_number' => $user->phone ?? null,
'address' => [
'first_name' => $user->name ?? null,
'last_name' => $user->surname ?? null,
'street' => $user->street ?? null,
'city' => $user->city ?? null,
'region' => $user->state ?? null,
'postal_code' => $user->zip ?? null,
'country' => $user->country ?? null,
],
];
}
return null;
}
protected function getRequest(): ?Request
{
$request = $this->request ?? $this->requestStack->getMainRequest();
if (!$request && !isFrontend()) {
$request = new Request();
}
return $request;
}
}

View File

@@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace KupShop\GTMBundle\ServerSideGTMEvent;
use Symfony\Component\HttpFoundation\Request;
trait AnalyticsSessionIDsTrait
{
protected function getBrowserGASessionId(Request $request, string $number = '')
{
$dbcfg = \Settings::getDefault();
$measurement_id = $dbcfg->analytics['google_tag_manager']['ga4_measurement_id'.$number] ?? '';
$cookies = $request->cookies->all();
// Cookie name example: '_ga_1YS1VWHG3V'.
$cookie_name = '_ga_'.str_replace('G-', '', $measurement_id);
if (isset($cookies[$cookie_name])) {
// Cookie value example: 'GS2.1.s1746623129$o109$g1$t1746623938$j0$l0$h0'.
// Cookie value example: 'GS1.1.1659710029.4.1.1659710504.0'.
// Session Id: ^^^^^^^^^^.
if (preg_match('/^GS\d+\.\d+\.?s?(\d+)/', $cookies[$cookie_name], $matches)) {
return $matches[1];
}
}
return '';
}
protected function getGA3SessionId(): string
{
return preg_replace("/^.+\.(.+?\..+?)$/", '\\1', $_COOKIE['_ga'] ?? '');
}
protected function getBrowserFBPixelSessionId(?Request $request)
{
$cookies = $request?->cookies->all();
if (isset($cookies['_fbp'])) {
return $cookies['_fbp'];
}
return '';
}
}

View File

@@ -0,0 +1,78 @@
<?php
namespace KupShop\GTMBundle\ServerSideGTMEvent;
use KupShop\GTMBundle\Utils\DataLoaders\PurchaseState;
use KupShop\OrderingBundle\Event\CartItemEvent;
use Symfony\Contracts\EventDispatcher\Event;
use Symfony\Contracts\Service\Attribute\Required;
class CartAddItemEvent extends AbstractServerSideGTMEvent
{
#[Required]
public PurchaseState $purchaseState;
public function getPayload(Event $event): array
{
if (!($event instanceof CartItemEvent)) {
return [];
}
$product = $event->getProduct();
$product->fetchVariations();
if (empty($product->producer)) {
$product->fetchProducer();
}
$product->fetchImages('product_gallery');
$item = $this->productLoader->loadProductObject($product);
$item['quantity'] = abs($event->getDifferences()['pieces_diff'] ?? 1);
$item['index'] = 1;
if (($soldOut = $this->purchaseState->getSoldOut($product)) !== null) {
$item['soldOut'] = $soldOut;
}
$params = $this->getCommonData($event);
$params['items'] = [$item];
return [
'events' => [
[
'name' => $this->getEventName(),
'user_id' => $this->getUserId(),
'client_id' => $this->getClientId(),
'ga3_session_id' => $this->getGA3SessionId(),
'params' => $params,
],
],
];
}
protected function getCommonData(Event $event): array
{
$dbcfg = \Settings::getDefault();
$product = $event->getProduct();
$request = $this->getRequest();
return array_merge(parent::getCommonData($event), [
'page_title' => $product->meta_title ?: $product->title.' - '.$dbcfg['shop_title'],
'value' => $this->getEventPrice($event)->asFloat(),
'event_id' => $request->cookies->get('gtm_event_uid'),
]);
}
private function getEventPrice(Event $event): \Decimal
{
/** @var CartItemEvent $event */
$price = $event->getProduct()->getProductPrice()->getPriceWithVat();
$pieces = (int) abs($event->getDifferences()['pieces_diff'] ?? 1);
return $price->mul(toDecimal($pieces));
}
protected function getEventName(): string
{
return 'add_to_cart';
}
}

View File

@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace KupShop\GTMBundle\ServerSideGTMEvent;
use KupShop\OrderingBundle\Event\CartItemEvent;
use Symfony\Contracts\EventDispatcher\Event;
class CartRemoveItemEvent extends CartAddItemEvent
{
public function getPayload(Event|CartItemEvent $event): array
{
if (!$event->getProduct()) {
return [];
}
return parent::getPayload($event);
}
protected function getEventName(): string
{
return 'remove_from_cart';
}
}

View File

@@ -0,0 +1,151 @@
<?php
namespace KupShop\GTMBundle\ServerSideGTMEvent\DataLoaders;
use KupShop\GTMBundle\Utils\PriceComputer;
use KupShop\KupShopBundle\Context\ContextManager;
use KupShop\KupShopBundle\Context\LanguageContext;
use KupShop\KupShopBundle\Util\StringUtil;
use KupShop\KupShopBundle\Util\System\UrlFinder;
use Symfony\Component\Routing\Router;
use Symfony\Contracts\Service\Attribute\Required;
class Products
{
/**
* @var ContextManager
*/
protected $contextManager;
/**
* @var PriceComputer
*/
protected $priceComputer;
/**
* @var LanguageContext
*/
protected $languageContext;
protected UrlFinder $urlFinder;
#[Required]
final public function setUrlFinder(UrlFinder $urlFinder): void
{
$this->urlFinder = $urlFinder;
}
/**
* @param \Product|\Variation $product
*/
public function loadProductObject($product): array
{
// TODO: tmp hotfix - pompo.sk pada na pameti, velky pocet sekci a tady se loaduje SK strom a pak i CZ strom, takze toho je fakt hooodne :)
// TODO: v jednu chvili se do pameti dokaze asi nacist cca 10k sekci, protoze APCu + memcached x2 protoze jeste strom v default jazyce
increaseMemoryLimit(300);
$data = [
'item_id' => join('_', array_filter([$product->id, $product->variationId ?? ''])),
'product_code' => $product->productCode,
'product_id' => $product->id,
'variation_code' => $product->variationCode ?? '',
'variation_id' => $product->variationId ?? '',
'ean' => $product->ean,
'item_name' => $product->title,
'discount' => $product->discount->asFloat() ?? 0.0,
'item_brand' => $product->producer['title'] ?? '',
'item_variant' => $product->variationTitle ?? '',
'price' => $this->priceComputer->getPrice($product->getProductPrice()),
'price_with_vat' => $this->priceComputer->getPrice($product->getProductPrice(), true),
'price_without_vat' => $this->priceComputer->getPrice($product->getProductPrice(), false),
'price_vat' => $this->priceComputer->getPrice($product->getProductPrice()->getVatValue(), false, false),
'vat_rate' => $product->getProductPrice()->getVat()->asInteger(),
'description' => $product->descr,
'url' => path('products', ['id' => $product->id, 'name' => StringUtil::slugify($product->title)], Router::ABSOLUTE_URL),
];
if ($img_url = $product->image['src_big'] ?? null) {
$data['img_url'] = $this->urlFinder->staticUrlAbsolute($img_url);
}
$categories = $product->fetchSections();
$category = $categories[0] ?? [];
foreach ($this->getCategoryPath($category) as $i => $value) {
$data['item_category'.(($i) ? $i + 1 : '')] = $value;
}
if (findModule(\Modules::PRODUCTS, \Modules::SUB_PRICE_BUY)) {
$data['price_buy'] = $this->priceComputer->getPrice($product->price_buy);
}
return array_merge($this->getDataInDefaultLanguage($product), $data);
}
public function getCategoryPath($category)
{
if (!$category) {
return [];
}
$categoryNames = [];
do {
$categoryNames[] = $category->getNameShort() ?? '';
} while (($category = $category->getParent()) && count($categoryNames) < 5);
return array_reverse($categoryNames);
}
/**
* @param \Product|\Variation $product
*
* @return array
*/
protected function getDataInDefaultLanguage($product)
{
$array = [];
$this->contextManager->activateContexts([LanguageContext::class => $this->languageContext->getDefaultId()], function () use ($product, &$array) {
$langProduct = new \Variation();
$product = $langProduct->createProductOrVariation($product->id, $product->variationId ?? null);
$product->createFromDB(); // in default language
$product->fetchProducer();
$array = [
'default_item_name' => $product->title,
'default_item_variant' => $product->variationTitle ?? null,
'default_item_brand' => $product->producer['name'] ?? '',
];
$categories = $product->fetchSections();
$category = $categories[0] ?? [];
foreach ($this->getCategoryPath($category) as $i => $value) {
$array['default_item_category'.(($i) ? $i + 1 : '')] = $value;
}
});
return $array;
}
/**
* @required
*/
public function setPriceComputer(PriceComputer $priceComputer): void
{
$this->priceComputer = $priceComputer;
}
/**
* @required
*/
public function setContextManager(ContextManager $contextManager): void
{
$this->contextManager = $contextManager;
}
/**
* @required
*/
public function setLanguageContext(LanguageContext $languageContext): void
{
$this->languageContext = $languageContext;
}
}

View File

@@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace KupShop\GTMBundle\ServerSideGTMEvent;
use Query\Operator;
trait FetchUsersStatsOrderData
{
public function fetchUsersStatsOrderData(\Order $order): array
{
return sqlQueryBuilder()
->select('DATE(max(date_created)) last_date, DATE(min(date_created)) first_date, count(date_created) count')
->from('orders')
->where(Operator::like(['invoice_email' => $order->invoice_email]))
->andWhere(Operator::not(Operator::equals(['id' => $order->id])))
->execute()->fetchAssociative();
}
}

View File

@@ -0,0 +1,144 @@
<?php
namespace KupShop\GTMBundle\ServerSideGTMEvent;
use KupShop\GTMBundle\Utils\DataLoaders\PurchaseState;
use KupShop\GTMBundle\Utils\PriceComputer;
use KupShop\OrderingBundle\Event\OrderEvent;
use KupShop\OrderingBundle\Util\Order\DeliveryInfo;
use KupShop\OrderingBundle\Util\Order\OrderInfo;
use Symfony\Contracts\EventDispatcher\Event;
use Symfony\Contracts\Service\Attribute\Required;
class OrderComplete extends AbstractServerSideGTMEvent
{
private PriceComputer $priceComputer;
private DeliveryInfo $deliveryInfo;
private PurchaseState $purchaseState;
public function getPayload(Event $event): array
{
if (!($event instanceof OrderEvent)) {
return [];
}
$dbcfg = \Settings::getDefault();
$order = $event->getOrder();
$items = [];
$index = 1;
foreach ($order->fetchItems() as $item) {
if (!isset($item['product'])) {
continue;
}
/** @var \Product|\Variation $product */
$product = $item['product'];
$product->fetchProducer();
$product->fetchVariations();
$newItem = $this->productLoader->loadProductObject($product);
$newItem['index'] = $index++;
$newItem['quantity'] = $item['pieces'];
$prices = $this->purchaseState->getPrices($item);
$newItem['price'] = $prices['price'];
$newItem['price_with_vat'] = $prices['priceWithVat'];
$newItem['price_without_vat'] = $prices['priceWithoutVat'];
$newItem['price_vat'] = $prices['priceVat'];
$newItem['vat_rate'] = $prices['vatRate'];
$newItem['discount'] = $product->discount->asFloat();
if (($soldOut = $this->purchaseState->getSoldOut($product)) !== null) {
$newItem['soldOut'] = $soldOut;
}
$items[] = $newItem;
}
$deliveryType = $order->getDeliveryType();
$shippingPrice = $this->getDeliveryPrice($order);
$shippinPrice = $this->priceComputer->getPrice($shippingPrice ?? toDecimal(0));
if (($dbcfg->analytics['google_tag_manager']['withoutDelivery'] ?? 'N') == 'Y') {
// Tohle je kvuli tomu, ze podle nastaveni nevime jestli ma byt bez nebo s dph, proto nechame rozhodnout funkce uvnitr a pak to akorat proste odectu
$tp = toDecimal($this->priceComputer->getPrice($order->getTotalPrice()));
$shipp = toDecimal($shippinPrice);
$totalPrice = $tp->sub($shipp);
} else {
$totalPrice = $order->getTotalPrice();
}
$commonData = $this->getCommonData($event);
if ($coupons = OrderInfo::getUsedCoupons($order)) {
$coupons = join(',', $coupons);
}
$params = $commonData + [
'currency' => $order->currency,
'transaction_id' => $order->order_no,
'value' => $this->priceComputer->getPrice($totalPrice),
'vat' => $this->priceComputer->getPrice($order->total_price_array['value_vat'], null, false),
'delivery_type' => $deliveryType->getDelivery()->getClassName(),
'payment_type' => $deliveryType->getPayment()->getClassName(),
'payment' => $this->purchaseState->getPaymentObject($order),
'items' => $items,
'shipping' => $this->priceComputer->getPrice($order->getDeliveryPrice()),
'discounts' => $this->purchaseState->getDiscounts($order->getPurchaseState(true)),
'coupon' => $coupons ?: '',
'charges' => $this->purchaseState->getCharges($order),
'cjevent' => $order->getData('cjevent'),
];
return [
'user_id' => $commonData['user_id'],
'client_id' => $commonData['client_id'],
'ga3_session_id' => $this->getGA3SessionId(),
'events' => [
[
'name' => 'purchase',
'params' => $params,
],
],
];
}
protected function getCommonData(Event $event): array
{
/** @var \Order $order */
$order = $event->getOrder();
return array_merge(parent::getCommonData($event), [
'user_id' => $order->getData('cart_id'),
'client_id' => $order->getData('client_id') ?: $order->getData('cart_id').'_noCookie',
'currency' => $order->getCurrency(),
'ip_override' => $order->getData('ip_address'),
'language' => $order->getLanguage(),
'page_path' => path('kupshop_content_orders_order', ['id' => $order->id]),
'page_title' => str_replace('%ORDERNO', $order->order_no, translate_shop('title', 'orderView')),
'page_referrer' => $order->getData('referer'),
'user_agent' => $order->getData('user_agent'),
]);
}
public function getDeliveryPrice(\Order $order)
{
$delivery_item = $this->deliveryInfo->getDeliveryItem($order);
return $delivery_item ? $order->fetchItems()[$delivery_item]['total_price'] : null;
}
#[Required]
public function setPriceComputer(PriceComputer $priceComputer): void
{
$this->priceComputer = $priceComputer;
}
#[Required]
public function setDeliveryInfo(DeliveryInfo $deliveryInfo): void
{
$this->deliveryInfo = $deliveryInfo;
}
#[Required]
public function setPurchaseState(PurchaseState $purchaseState): void
{
$this->purchaseState = $purchaseState;
}
}

View File

@@ -0,0 +1,103 @@
<?php
namespace KupShop\GTMBundle\ServerSideGTMEvent;
use KupShop\GTMBundle\Utils\DataLoaders\PurchaseState;
use KupShop\GTMBundle\Utils\PriceComputer;
use KupShop\OrderingBundle\Util\Order\DeliveryInfo;
use KupShop\OrderingBundle\Util\Order\OrderInfo;
use Symfony\Contracts\EventDispatcher\Event;
use Symfony\Contracts\Service\Attribute\Required;
class OrderRefundBaseEvent extends AbstractServerSideGTMEvent
{
public string $refundType;
#[Required]
public PriceComputer $priceComputer;
#[Required]
public DeliveryInfo $deliveryInfo;
#[Required]
public PurchaseState $purchaseState;
public function getPayload(Event $event): array
{
$dbcfg = \Settings::getDefault();
$order = $event->getOrder();
$items = [];
$index = 1;
foreach ($order->fetchItems() as $item) {
if (empty($item['product'])) {
continue;
}
/** @var \Product|\Variation $product */
$product = $item['product'];
$product->fetchProducer();
$newItem = $this->productLoader->loadProductObject($product);
$newItem['index'] = $index++;
$newItem['discount'] = $product->discount->asFloat();
$newItem['quantity'] = $item['pieces'];
$prices = $this->purchaseState->getPrices($item);
$newItem['price'] = $prices['price'];
$newItem['price_with_vat'] = $prices['priceWithVat'];
$newItem['price_without_vat'] = $prices['priceWithoutVat'];
$newItem['price_vat'] = $prices['priceVat'];
$newItem['vat_rate'] = $prices['vatRate'];
$items[] = $newItem;
}
$deliveryType = $order->getDeliveryType();
$shippingPrice = $this->getDeliveryPrice($order);
$shippinPrice = $this->priceComputer->getPrice($shippingPrice ?? toDecimal(0));
if (($dbcfg->analytics['google_tag_manager']['withoutDelivery'] ?? 'N') == 'Y') {
$tp = toDecimal($this->priceComputer->getPrice($order->getTotalPrice()));
$shipp = toDecimal($shippinPrice);
$totalPrice = $tp->sub($shipp);
} else {
$totalPrice = $order->getTotalPrice();
}
if ($coupons = OrderInfo::getUsedCoupons($order)) {
$coupons = join(',', $coupons);
}
$params = [
'currency' => $order->currency,
'transaction_id' => $order->order_no,
'value' => $this->priceComputer->getPrice($totalPrice),
'coupon' => $coupons ?: '',
'shipping' => $this->priceComputer->getPrice($order->getDeliveryPrice()),
'tax' => $this->priceComputer->getPrice($order->total_price_array['value_vat'], null, false),
'delivery_type' => $deliveryType->getDelivery()?->getClassName() ?? '',
'payment_type' => $deliveryType->getPayment()?->getClassName() ?? '',
'refund_type' => $this->refundType,
'items' => $items,
'user_data' => array_merge(['sha_email' => hash('sha256', $order->invoice_email.($dbcfg->analytics['google_tag_manager']['sha_salt'] ?? false ?: ''))], $this->getUserData($event)),
'cjevent' => $order->getData('cjevent'),
];
return [
'events' => [
[
'name' => 'refund',
'params' => $params,
],
],
];
}
public function getDeliveryPrice(\Order $order)
{
$delivery_item = $this->deliveryInfo->getDeliveryItem($order);
return $delivery_item ? $order->fetchItems()[$delivery_item]['total_price'] : null;
}
}

View File

@@ -0,0 +1,8 @@
<?php
namespace KupShop\GTMBundle\ServerSideGTMEvent;
class OrderStornoEvent extends OrderRefundBaseEvent
{
public string $refundType = 'storno';
}

View File

@@ -0,0 +1,95 @@
<?php
namespace KupShop\GTMBundle\ServerSideGTMEvent;
use KupShop\GTMBundle\Utils\PriceComputer;
use KupShop\ReservationBundle\Entity\Reservation;
use Symfony\Contracts\EventDispatcher\Event;
use Symfony\Contracts\Service\Attribute\Required;
class ReservationCreatedEvent extends AbstractServerSideGTMEvent
{
public PriceComputer $priceComputer;
public function getPayload(Event $event): array
{
/** @var Reservation $reservation */
$reservation = $event->reservation;
$dbcfg = \Settings::getDefault();
$items = [
$this->productLoader->loadProductObject($reservation->getProduct())
+ ['index' => 1, 'quantity' => 1],
];
$commonData = $this->getCommonData($event);
$allDeliveryTypes = \DeliveryType::getAll(true);
$deliveryType = $allDeliveryTypes[$dbcfg->reservations['delivery_type']];
$params = $commonData + [
'currency' => $this->currencyContext->getActiveId(),
'transaction_id' => 'RES'.$reservation->id,
'value' => $this->priceComputer->getPrice($reservation->getPrice()),
'vat' => $this->priceComputer->getPrice($reservation->getPrice()->getVatPrice(), null, false),
'delivery_type' => $deliveryType ? $deliveryType->getDelivery()->getName() : '',
'payment_type' => $deliveryType ? $deliveryType->getPayment()->getName() : '',
'items' => $items,
'shipping' => \DecimalConstants::zero(),
];
return [
'user_id' => $commonData['user_id'],
'client_id' => $commonData['client_id'],
'ga3_session_id' => $this->getGA3SessionId(),
'events' => [
[
'name' => 'purchase',
'params' => $params,
],
],
];
}
protected function getCommonData(Event $event): array
{
$dbcfg = \Settings::getDefault();
/** @var Reservation $Reservation */
$Reservation = $event->reservation;
$request = $this->requestStack->getCurrentRequest();
return array_merge(parent::getCommonData($event), [
'ip_override' => $request->getClientIp(),
'page_path' => createScriptURL(['s' => 'product', 'IDproduct' => $Reservation->getProduct()->id, 'TITLE' => $Reservation->getProduct()->title]),
'page_title' => $Reservation->getProduct()->title,
'page_referrer' => $request->getSession()->get('referer'),
'user_agent' => $request->server->get('HTTP_USER_AGENT'),
'shaEmail' => hash('sha256', $Reservation->email.($dbcfg->analytics['google_tag_manager']['sha_salt'] ?? false ?: '')),
]);
}
protected function getUserData(Event $event): array
{
return [
'email_address' => $event->reservation['email'] ?? '',
'phone_number' => $event->reservation['phone'] ?? '',
'address' => [
'first_name' => $event->reservation['name'] ?? '',
'last_name' => $event->reservation['surname'] ?? '',
'street' => null,
'city' => null,
'region' => null,
'postal_code' => null,
'country' => null,
],
];
}
#[Required]
public function setPriceComputer(PriceComputer $priceComputer): void
{
$this->priceComputer = $priceComputer;
}
}

View File

@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace KupShop\GTMBundle\ServerSideGTMEvent;
use KupShop\GTMBundle\Utils\DataLoaders\PurchaseState;
use KupShop\UserBundle\Event\UserRegisteredEvent;
use Symfony\Contracts\EventDispatcher\Event;
class UserSignUpEvent extends AbstractServerSideGTMEvent
{
protected function getPayload(Event $event): array
{
if (!($event instanceof UserRegisteredEvent)) {
return [];
}
return $this->getCommonData($event) +
[
'events' => [
'name' => 'sign_up',
'params' => [
'method' => $event->getMethod(),
'email' => $event->getUser()->email,
'shaEmail' => PurchaseState::shaEmail($event->getUser()->email),
],
],
];
}
}

View File

@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace KupShop\GTMBundle\ServerSideGTMEvent;
use KupShop\GTMBundle\Utils\DataLoaders\PurchaseState;
use KupShop\UserBundle\Event\UserNewsletterEvent;
use Symfony\Contracts\EventDispatcher\Event;
class UserSubscribeEvent extends AbstractServerSideGTMEvent
{
protected function getPayload(Event $event): array
{
if (!($event instanceof UserNewsletterEvent)) {
return [];
}
return $this->getCommonData($event) +
[
'events' => [
'name' => $this->getEventName(),
'params' => [
'email' => $event->getEmail(),
'shaEmail' => PurchaseState::shaEmail($event->getEmail()),
],
],
];
}
public function getEventName(): string
{
return 'success_newsletter_subscribe';
}
}

View File

@@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
namespace KupShop\GTMBundle\ServerSideGTMEvent;
class UserUnsubscribeEvent extends UserSubscribeEvent
{
public function getEventName(): string
{
return 'success_newsletter_unsubscribe';
}
}