<?php
namespace App\EventSubscriber;
use App\DependencyInjection\Framework\EntityManagerDI;
use App\DependencyInjection\Framework\EnvironmentDI;
use App\DependencyInjection\Framework\EventDispatcherDI;
use App\DependencyInjection\Framework\LoggerDI;
use App\DependencyInjection\Framework\MessageBusDI;
use App\DependencyInjection\Repository\Game\GameTicketRepositoryDI;
use App\DependencyInjection\Service\Data\Game\GameTicketServiceDI;
use App\DependencyInjection\Service\Data\Logs\LogsServiceDI;
use App\Entity\Loto\GameTicket;
use App\Enum\Game\TicketStatus;
use App\Enum\Payment\PaymentStatus;
use App\Enum\Payment\TransactionType;
use App\Event\Game\TicketEvent;
use App\Event\PaymentEvent;
use App\Message\Loto\LotoTicketFileCreateMessage;
use App\Message\Notification\TicketEmailMessage;
use App\Message\Payment\RefundTicketPaymentMessage;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Messenger\Stamp\DelayStamp;
class TicketPaymentEventSubscriber implements EventSubscriberInterface
{
use GameTicketRepositoryDI;
use MessageBusDI;
use LoggerDI;
use LogsServiceDI;
use EntityManagerDI;
use GameTicketServiceDI;
use MessageBusDI;
use EnvironmentDI;
use EventDispatcherDI;
public static function getSubscribedEvents(): array
{
return [
PaymentEvent::NAME => 'onPaymentUpdated',
];
}
public function onPaymentUpdated(PaymentEvent $event): void
{
$this->logger->info('Payment updated', [
'event' => $event,
]);
if (!$ticket = $this->gameTicketRepository->find($event->objectId)) {
return;
}
$this->logsService->addRequestLogToObject($ticket);
if ($event->transaction->getTicket() !== $ticket) {
$this->logger->error('Different transactions', [
'ticket' => $ticket->getId(),
'ticketTransaction' => $ticket->getPurchaseTransaction(),
'eventTransaction' => $event->transaction,
]);
$this->logsService->addLog(__METHOD__, __LINE__, 'Different transactions', [
'ticket' => $ticket->getId(),
'ticketTransaction' => $ticket->getPurchaseTransaction(),
'eventTransaction' => $event->transaction,
]);
return;
}
if (in_array($ticket->getPaymentStatus(), [PaymentStatus::Success, PaymentStatus::Refunded])) {
$this->logger->info('Ticket already paid', [
'ticket' => $ticket->getId(),
]);
$this->logsService->addLog(__METHOD__, __LINE__, 'Ticket already paid', [
'ticket' => $ticket->getId(),
]);
return;
}
if ($event->status === PaymentStatus::Success) {
$ticketPaymentStatus = match ($event->transaction->getType()) {
TransactionType::Purchase => PaymentStatus::Success,
TransactionType::Refund => PaymentStatus::Refunded
};
} else {
$ticketPaymentStatus = $event->status;
}
$ticket->setPaymentStatus($ticketPaymentStatus);
match ($ticketPaymentStatus) {
PaymentStatus::Success => $this->onTicketPaymentSuccess($ticket),
PaymentStatus::Canceled,
PaymentStatus::Failed => $this->onTicketPaymentFailed($ticket),
default => null,
};
$this->entityManager->flush();
$this->logsService->addLog(__METHOD__, __LINE__, 'Ticket payment status updated');
}
private function onTicketPaymentSuccess(GameTicket $ticket): void
{
$now = \DateTimeService::now();
$ticket->setPlayDate($now);
$salesEndDate = $ticket->getGamePeriod()->getSalesEndDate();
$exportTime = $ticket->getGamePeriod()->getExportTime();
$confirmationCutoff = (clone $salesEndDate)->setTime(
(int) $exportTime->format('H'),
(int) $exportTime->format('i'),
(int) $exportTime->format('s'),
);
// cancel the ticket if payment is confirmed after the (sales) period is closed
if (!$ticket->getGamePeriod()->isActive() || $now > $confirmationCutoff) {
$this->logger->warning('Ticket ID {id} is for an inactive game period ({game_period}). Marking as canceled.', [
'id' => $ticket->getId(),
'game_period' => $ticket->getGamePeriod()->getPeriodCode(),
]);
$ticket->setStatus(TicketStatus::Canceled);
$this->entityManager->flush();
$this->eventDispatcher->dispatch(new TicketEvent($ticket), TicketEvent::CANCELED);
} else {
$this->logger->info('Ticket ID {id} is in an active game period ({game_period}). Continuing processing.', [
'id' => $ticket->getId(),
'game_period' => $ticket->getGamePeriod()->getPeriodCode(),
]);
$ticket->getTicketDefinition()?->getInfo()?->updateInfo();
$this->entityManager->flush();
if (!$ticket->getFile()) {
$this->logger->info('Dispatching file generation message for ticket ID {id}', [
'id' => $ticket->getId(),
]);
$this->messageBus->dispatch(new LotoTicketFileCreateMessage($ticket));
}
$this->logger->info('Creating ticket purchase entry for ticket ID {id}', [
'id' => $ticket->getId(),
]);
$this->gameTicketService->createTicketPurchaseEntry($ticket);
}
}
private function onTicketPaymentFailed(GameTicket $ticket)
{
// there is no flow defined for failed payments
}
}