<?php declare(strict_types=1);
namespace App\Service\Affiliate;
use App\Entity\User;
use App\RabbitMq\Writer\LandingPageWriter;
use App\Transliterator\Transliterator;
use Doctrine\Persistence\ManagerRegistry;
use Psr\Log\LoggerInterface;
use RuntimeException;
use Sindrive\RabbitMqTaskBundle\Service\TaskHandler;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
class AffiliateIdTracker implements EventSubscriberInterface
{
public const AFFILIATE_KEY = 'atid';
public const AFFILIATE_HASH = 'htid';
private TaskHandler $taskHandler;
private LoggerInterface $logger;
private ReferralTokenManager $referralTokenManager;
private ManagerRegistry $doctrine;
public function __construct(
LoggerInterface $logger,
ReferralTokenManager $referralTokenManager,
ManagerRegistry $doctrine,
TaskHandler $taskHandler
)
{
$this->logger = $logger;
$this->referralTokenManager = $referralTokenManager;
$this->doctrine = $doctrine;
$this->taskHandler = $taskHandler;
}
public function onKernelResponse(ResponseEvent $event): void
{
$request = $event->getRequest();
if (!$request->query->has(self::AFFILIATE_KEY)) {
return;
}
$requestToken = $request->query->get(self::AFFILIATE_KEY);
$cookieToken = $request->cookies->get(self::AFFILIATE_KEY);
if ($cookieToken === $requestToken) {
return;
}
$trackingHash = hash('sha512', date('y-m-d-h-i-s') . $requestToken);
$this->taskHandler->sendTask(
LandingPageWriter::QUEUE,
serialize([
'new' => true,
'referrer' => $request->headers->get('referer'),
'landedOn' => $request->getUri(),
'trackingHash' => $trackingHash,
'referee_key' => $this->referralTokenManager->resolveId($requestToken),
])
);
$response = $event->getResponse();
$response->headers->setCookie(Cookie::create(self::AFFILIATE_KEY, $requestToken, time() + 24 * 60 * 60, '/'));
$response->headers->setCookie(Cookie::create(self::AFFILIATE_HASH, $trackingHash, time() + 30 * 24 * 60 * 60, '/'));
}
public function setUserAffiliate(Request $request, User $user): void
{
if (null === $user->getEmail()) {
throw new RuntimeException('User must have an email to be tracked.');
}
if (!$request->cookies->has(self::AFFILIATE_KEY) || !$request->cookies->has(self::AFFILIATE_HASH)) {
return;
}
$cookieToken = $request->cookies->get(self::AFFILIATE_KEY);
$referrerId = $this->referralTokenManager->resolveId($cookieToken);
if (null === $referrerId) {
$this->logger->info(
'AffiliateIdTracker: Not numeric token ' . $cookieToken
);
return;
}
if (null !== $user->getReferral()) {
$this->logger->info(
'AffiliateIdTracker: User ' . $user->getId() . ' already have a referral.',
);
return;
}
$referee = $this->doctrine->getRepository(User::class)->find($referrerId);
if (null === $referee || $referee->isLocked()) {
$this->logger->info(
'AffiliateIdTracker: Referral is not enabled or locked ' . ($referee !== null ? $referee->getId() : ''),
);
return;
}
$this->logger->info(
'AffiliateIdTracker: Assigning referral ' . $referee->getId() . ' to user ' . Transliterator::hideEmail($user->getEmail()),
);
$referral = $user->addReferral($referee);
$this->doctrine->getManager()->persist($referral);
$trackingHash = $request->cookies->get(self::AFFILIATE_HASH);
$user->setTrackingHash($trackingHash);
$this->logger->info(
'AffiliateIdTracker::setUserAffiliate conversion with hash: ' . $user->getTrackingHash(),
);
$msg = [
'new' => false,
'trackingHash' => $user->getTrackingHash(),
'user' => $user->getId(),
];
$this->taskHandler->sendTask(LandingPageWriter::QUEUE, serialize($msg));
}
/**
* @return array<string, array<int|string, array<int|string, int|string>|int|string>|string>
*/
public static function getSubscribedEvents(): array
{
return [
KernelEvents::RESPONSE => 'onKernelResponse',
];
}
}