<?php
declare(strict_types=1);
/*
* This file is part of SolidInvoice project.
*
* (c) Pierre du Plessis <[email protected]>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace SolidInvoice\PaymentBundle\Listener;
use Brick\Math\Exception\MathException;
use Doctrine\Persistence\ManagerRegistry;
use Generator;
use SolidInvoice\ClientBundle\Entity\Credit;
use SolidInvoice\CoreBundle\Response\FlashResponse;
use SolidInvoice\InvoiceBundle\Entity\Invoice;
use SolidInvoice\InvoiceBundle\Model\Graph;
use SolidInvoice\PaymentBundle\Entity\Payment;
use SolidInvoice\PaymentBundle\Event\PaymentCompleteEvent;
use SolidInvoice\PaymentBundle\Event\PaymentEvents;
use SolidInvoice\PaymentBundle\Model\Status;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Workflow\StateMachine;
class PaymentCompleteListener implements EventSubscriberInterface
{
/**
* @return array<string, string>
*/
public static function getSubscribedEvents(): array
{
return [
PaymentEvents::PAYMENT_COMPLETE => 'onPaymentComplete',
];
}
public function __construct(
private readonly StateMachine $invoiceStateMachine,
private readonly ManagerRegistry $registry,
private readonly RouterInterface $router
) {
}
/**
* @throws MathException
*/
public function onPaymentComplete(PaymentCompleteEvent $event): void
{
$payment = $event->getPayment();
$status = (string) $payment->getStatus();
if ('credit' === $payment->getMethod()?->getGatewayName()) {
$creditRepository = $this->registry->getRepository(Credit::class);
$creditRepository->deductCredit(
$payment->getClient(),
$payment->getTotalAmount(),
);
}
if (($invoice = $event->getPayment()->getInvoice()) instanceof Invoice) {
$em = $this->registry->getManager();
if (Status::STATUS_CAPTURED === $status && $em->getRepository(Invoice::class)->isFullyPaid($invoice)) {
$this->invoiceStateMachine->apply($invoice, Graph::TRANSITION_PAY);
} else {
$paymentRepository = $this->registry->getRepository(Payment::class);
$invoiceTotal = $invoice->getTotal();
$totalPaid = $paymentRepository->getTotalPaidForInvoice($invoice);
$invoice->setBalance($invoiceTotal->minus($totalPaid));
$em = $this->registry->getManager();
$em->persist($invoice);
$em->flush();
}
$router = $this->router;
$event->setResponse(
new class($router->generate('_view_invoice_external', ['uuid' => $invoice->getUuid()]), $status) extends RedirectResponse implements FlashResponse {
public function __construct(
string $route,
private readonly string $paymentStatus
) {
parent::__construct($route);
}
public function getFlash(): Generator
{
yield from PaymentCompleteListener::addFlashMessage($this->paymentStatus);
}
}
);
}
}
public static function addFlashMessage(string $status): Generator
{
match ($status) {
Status::STATUS_CAPTURED => yield FlashResponse::FLASH_SUCCESS => 'payment.flash.status.success',
Status::STATUS_CANCELLED => yield FlashResponse::FLASH_DANGER => 'payment.flash.status.cancelled',
Status::STATUS_PENDING => yield FlashResponse::FLASH_WARNING => 'payment.flash.status.pending',
Status::STATUS_EXPIRED => yield FlashResponse::FLASH_DANGER => 'payment.flash.status.expired',
Status::STATUS_FAILED => yield FlashResponse::FLASH_DANGER => 'payment.flash.status.failed',
Status::STATUS_NEW => yield FlashResponse::FLASH_WARNING => 'payment.flash.status.new',
Status::STATUS_SUSPENDED => yield FlashResponse::FLASH_DANGER => 'payment.flash.status.suspended',
Status::STATUS_AUTHORIZED => yield FlashResponse::FLASH_INFO => 'payment.flash.status.authorized',
Status::STATUS_REFUNDED => yield FlashResponse::FLASH_WARNING => 'payment.flash.status.refunded',
default => yield FlashResponse::FLASH_DANGER => 'payment.flash.status.unknown',
};
}
}