<?php
declare(strict_types=1);
namespace App\Subscriber\Order;
use App\Event\Cinema\CinemaContextChangedEvent;
use App\Event\Order\OrderClosedEvent;
use App\Event\Order\OrderCompletedEvent;
use App\Event\Order\OrderPaymentTransactionCancelledEvent;
use App\Event\Order\OrderPaymentTransactionExceptionEvent;
use App\Event\Order\OrderPaymentTransactionHandledEvent;
use App\Event\Order\OrderPaymentTransactionReceivedEvent;
use App\Manager\OrderManager;
use App\Model\Order\Item\ScreeningItem;
use App\Model\Order\Order;
use App\Service\Order\OrderPaymentLogBuilderService;
use App\Subscriber\Order\Model\OrderPaymentTransactionExceptionFlowModel;
use App\Subscriber\Order\Model\OrderPaymentTransactionFlowModel;
use Doctrine\Common\Collections\ArrayCollection;
use Psr\Log\LoggerInterface;
use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\UuidInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class OrderSubscriber implements EventSubscriberInterface
{
private OrderManager $orderManager;
private UuidInterface $currentCinemaId;
private LoggerInterface $paymentLogger;
private ArrayCollection $transactionFlowModelCollection;
private OrderPaymentLogBuilderService $paymentLogBuilderService;
public function __construct(
OrderManager $orderManager,
LoggerInterface $paymentLogger,
OrderPaymentLogBuilderService $orderPaymentLogBuilderService
) {
$this->orderManager = $orderManager;
$this->paymentLogger = $paymentLogger;
$this->paymentLogBuilderService = $orderPaymentLogBuilderService;
$this->transactionFlowModelCollection = new ArrayCollection();
}
public static function getSubscribedEvents(): array
{
return [
OrderCompletedEvent::class => 'onOrderCompleted',
CinemaContextChangedEvent::class => 'onCinemaContextChanged',
OrderPaymentTransactionReceivedEvent::class => 'onOrderPaymentTransactionReceived',
OrderPaymentTransactionExceptionEvent::class => 'onOrderPaymentTransactionException',
OrderClosedEvent::class => 'onOrderClosed',
OrderPaymentTransactionCancelledEvent::class => 'onOrderTransactionCancelled',
OrderPaymentTransactionHandledEvent::class => 'onOrderTransactionHandled',
];
}
public function onCinemaContextChanged(CinemaContextChangedEvent $event): void
{
$this->currentCinemaId = $event->getCinemaId();
}
public function onOrderCompleted(OrderCompletedEvent $event): void
{
$order = $this->orderManager->get($event->getOrder()->getId());
if ($order !== null) {
$this->createOrderNotification($order, $event->getSalesDocumentId());
}
}
private function createOrderNotification(Order $order, Uuid $salesDocumentId): void
{
if ($order->getStatus() === Order::CLOSED) {
$this->orderManager->createOrderAlias($order, $this->currentCinemaId, $salesDocumentId);
}
}
public function onOrderPaymentTransactionReceived(OrderPaymentTransactionReceivedEvent $event): void
{
$this->getTransactionFlowModel($event->getOrder())
->setOrderId($event->getOrder()->getId())
->setTransactionId($event->getPaymentProviderResponse()->getTransactionId())
->setCinemaId($event->getPaymentProviderResponse()->getCinemaId())
->setValueToPay($event->getOrder()->getPriceToPay())
->setPaymentChannel($event->getPaymentChannel())
->setTransactionStatus($event->getPaymentProviderResponse()->getStatus())
->setBookingIds(array_unique(array_map(static function (ScreeningItem $screeningItem) {
return (string)$screeningItem->getId();
}, $event->getOrder()->getScreeningItems()?->toArray() ?? []
)
))
;
}
public function onOrderPaymentTransactionException(OrderPaymentTransactionExceptionEvent $event): void
{
$exceptionFlowModel = new OrderPaymentTransactionExceptionFlowModel(
$event->getException(),
$event->getAdditionalMessage()
);
$this->getTransactionFlowModel($event->getOrder())
->getExceptionCollection()
->add($exceptionFlowModel);
}
public function onOrderClosed(OrderClosedEvent $event): void
{
$this->getTransactionFlowModel($event->getOrder())
->setIsOrderClosed(true);
}
public function onOrderTransactionCancelled(OrderPaymentTransactionCancelledEvent $event): void
{
$this->getTransactionFlowModel($event->getOrder())
->setIsPaymentTransactionCancelled(true)
->setPaymentTransactionCancelDetails($event->getReason());
}
public function onOrderTransactionHandled(OrderPaymentTransactionHandledEvent $event): void
{
$transactionFlowModel = $this->getTransactionFlowModel($event->getOrder());
$log = $this->paymentLogBuilderService->build($transactionFlowModel);
if ($transactionFlowModel->getExceptionCollection()->isEmpty()) {
$this->paymentLogger->info($log);
} else {
$this->paymentLogger->error($log);
}
}
private function getTransactionFlowModel(Order $order): OrderPaymentTransactionFlowModel
{
$transactionFlowModel = $this->transactionFlowModelCollection->filter(
static function (OrderPaymentTransactionFlowModel $flowModel) use ($order) {
return (string)$flowModel->getOrderId() === (string)$order->getId();
}
)->first();
if ($transactionFlowModel === false) {
$transactionFlowModel = new OrderPaymentTransactionFlowModel();
$this->transactionFlowModelCollection->add($transactionFlowModel);
return $transactionFlowModel;
}
assert($transactionFlowModel instanceof OrderPaymentTransactionFlowModel);
return $transactionFlowModel;
}
}