<?php
namespace App\Subscriber;
use App\Entity\Order;
use App\Enum\Company\Config;
use App\Enum\Order\Status;
use App\Enum\PaymentEnum;
use App\Repository\OrderRepository;
use App\Repository\QuotationRepository;
use App\Service\Paygreen\Exception\PaygreenPaymentException;
use App\Service\PaygreenV3\API\PaygreenV3ServiceInterface;
use App\Service\PaygreenV3\Core\PaygreenV3Payment;
use App\Service\PaygreenV3\Event\PaygreenErrorPaymentEvent;
use App\Service\PaygreenV3\Event\PaygreenSuccessPaymentEvent;
use App\Service\PaygreenV3\Event\PaygreenWebhookEvent;
use Doctrine\ORM\EntityManagerInterface;
use Ramsey\Uuid\Uuid;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class PaygreenSubscriber implements EventSubscriberInterface
{
private PaygreenV3ServiceInterface $paygreenV3Service;
private EntityManagerInterface $entityManager;
private OrderRepository $orderRepository;
private QuotationRepository $quotationRepository;
private EventDispatcherInterface $eventDispatcher;
public function __construct(
EventDispatcherInterface $eventDispatcher,
EntityManagerInterface $entityManager,
PaygreenV3ServiceInterface $paygreenV3Service,
OrderRepository $orderRepository,
QuotationRepository $quotationRepository
) {
$this->eventDispatcher = $eventDispatcher;
$this->entityManager = $entityManager;
$this->paygreenV3Service = $paygreenV3Service;
$this->orderRepository = $orderRepository;
$this->quotationRepository = $quotationRepository;
}
public static function getSubscribedEvents(): array
{
return [
'paygreen.payment.webhook' => 'onWebhook',
'paygreen.payment.successed' => 'onSuccessPayment',
'paygreen.payment.error' => 'onErrorPayment'
];
}
/**
* @throws PaygreenPaymentException
*/
public function onWebhook(PaygreenWebhookEvent $event): void
{
/** @var Order $order */
$order = $this->orderRepository->find(Uuid::fromString(explode('_', $event->data()['reference'])[0]));
if ($order === null || $order->getStatus()->getValue() !== Status::PENDING) {
return;
}
$company = $order->getCompany();
if ($company === null) {
return;
}
$config = $company->getConfigs()->get(Config::PAYGREEN_V3);
if ($config === null) {
return;
}
$client = $this->paygreenV3Service->clientOf($company);
if (!$client->validate($event->data(), $event->sign(), $event->data()['status'], $event->url())) {
return;
}
$paygreenPayment = null;
$payment = null;
foreach ($order->getPayments() as $currentPayment) {
if (
$currentPayment->getType() === PaymentEnum::PAYGREEN
&& $currentPayment->getStatus()->getValue() === Status::PENDING
) {
$paygreenPayment = new PaygreenV3Payment($currentPayment->getInfo());
if ($paygreenPayment->id() === $event->data()['id']) {
$payment = $currentPayment;
break;
}
}
}
if ($payment === null || $paygreenPayment === null) {
return;
}
$paygreenPayment = $client->transaction()->update($paygreenPayment);
switch ($event->data()['status']) {
case 'payment_order.authorized':
case 'payment_order.successed':
$this->eventDispatcher->dispatch(
new PaygreenSuccessPaymentEvent($order, $paygreenPayment, $payment),
'paygreen.payment.successed'
);
break;
default:
$this->eventDispatcher->dispatch(
new PaygreenErrorPaymentEvent($order, $paygreenPayment, $payment),
'paygreen.payment.error'
);
}
}
public function onSuccessPayment(PaygreenSuccessPaymentEvent $event): void
{
try {
$this->entityManager->beginTransaction();
$event->orderPayment()
->setInfo($event->paygreenPayment()->toArray())
->setStatus(Status::SUCCESSED());
$event->order()->setStatus(Status::SUCCESSED());
$this->entityManager->persist($event->orderPayment());
$this->entityManager->persist($event->order());
$this->entityManager->flush();
$this->entityManager->commit();
} catch (\Throwable $throwable) {
$this->entityManager->rollback();
}
}
public function onErrorPayment(PaygreenErrorPaymentEvent $event): void
{
try {
$this->entityManager->beginTransaction();
$event->orderPayment()
->setInfo($event->paygreenPayment()->toArray())
->setStatus(Status::REFUSED());
$quotation = $this->quotationRepository->findOneBy(['order' => $event->order()]);
if ($quotation === null) {
$event->order()->setStatus(Status::ERROR());
}
$this->entityManager->persist($event->orderPayment());
$this->entityManager->persist($event->order());
$this->entityManager->flush();
$this->entityManager->commit();
} catch (\Throwable $throwable) {
$this->entityManager->rollback();
}
}
}