<?php
namespace App\Controller\Webhook;
use App\Enum\PaymentStatusEnum;
use App\Enum\PaymentTypeEnum;
use App\Enum\RefundStatusEnum;
use App\Repository\RefundRepository;
use App\Service\StripeAPI;
use App\Traits\SentryNotifyTrait;
use App\Utils\InvoiceUtils;
use Doctrine\ORM\EntityManagerInterface;
use Evo\Infrastructure\MappingORM\Invoice;
use Evo\Infrastructure\MappingORM\Payment;
use Evo\Infrastructure\MappingORM\Refund;
use Evo\Infrastructure\Repository\InvoiceRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class StripeController extends AbstractController
{
use SentryNotifyTrait;
private EntityManagerInterface $em;
private StripeAPI $stripeAPI;
private InvoiceUtils $invoiceUtils;
public function __construct(
StripeAPI $stripeAPI,
EntityManagerInterface $em,
InvoiceUtils $invoiceUtils
) {
$this->em = $em;
$this->stripeAPI = $stripeAPI;
$this->invoiceUtils = $invoiceUtils;
}
/**
* @Route("/stripe", name="app_webhook_stripe", methods={"POST"})
*
* @return Response
*/
public function webhook(Request $request)
{
$request_body = file_get_contents('php://input');
$event = $this->stripeAPI->webhookCheck($request_body, $request);
$response = new Response();
if (null !== $event) {
$data = $event->data->toArray();
$object = $data['object'] ?? null;
switch ($event->type) {
case 'charge.succeeded':
$this->handleChargeSucceeded($object);
break;
case 'charge.dispute.closed':
case 'payment_intent.canceled':
case 'charge.failed':
$this->handleFailed($object);
break;
case 'charge.refunded':
$this->handleChargeRefunded($object);
break;
case 'payment_intent.succeeded':
$this->handlePaymentIntentSucceeded($object);
break;
}
$response->setStatusCode(Response::HTTP_OK);
return $response;
}
$response->setStatusCode(Response::HTTP_INTERNAL_SERVER_ERROR);
return $response;
}
private function handleChargeRefunded($object)
{
if ($object) {
$reference = $object['payment_intent'];
/** @var RefundRepository $refundRepository */
$refundRepository = $this->em->getRepository(Refund::class);
$refund = $refundRepository->findOneBy(['reference' => $reference]);
if (null !== $refund) {
switch ($object['status']) {
case 'succeeded':
$refund->setStatus(RefundStatusEnum::SUCCESS);
break;
case 'failed':
case 'cancelled':
$refund->setStatus(RefundStatusEnum::ERROR);
break;
case 'pending':
$refund->setStatus(RefundStatusEnum::SENT);
break;
}
$this->em->persist($refund);
$this->em->flush();
}
}
}
private function handleChargeSucceeded($object)
{
if ($object) {
$repository = $this->em->getRepository(Invoice::class);
$metadata = $object['metadata'];
$id = $object['id'];
$charge = $this->stripeAPI->getCharge($id);
$paymentId = $charge->payment_intent;
if (!empty($metadata) && isset($metadata['invoices']) && 'STRIPE' !== $metadata['type_payment']) {
echo 'metadata exists';
$invoices = explode(',', $metadata['invoices']);
foreach ($invoices as $invoiceID) {
/** @var Invoice $invoice */
$invoice = $repository->find($invoiceID);
if ($invoice) {
$response = $this->invoiceUtils->paid($invoice, PaymentTypeEnum::STRIPE, $paymentId);
echo 'response:';
if ($response) {
echo sprintf('update invoice #%s', $invoiceID);
}
}
}
} elseif ($charge && $charge->metadata) {
$invoices = explode(',', $charge->metadata->invoices);
if ('STRIPE - SEPA' === $charge->metadata->type_payment) {
foreach ($invoices as $invoiceID) {
$invoiceID = htmlspecialchars($invoiceID);
/** @var Invoice $invoice */
$invoice = $repository->find($invoiceID);
if ($invoice) {
$response = $this->invoiceUtils->paid($invoice, PaymentTypeEnum::STRIPE, $paymentId);
echo 'response:';
if ($response) {
echo sprintf('update invoice #%s', $invoiceID);
}
}
}
}
}
}
}
private function handleFailed($object)
{
if ($object) {
$repository = $this->em->getRepository(Invoice::class);
$metadata = $object['metadata'];
$id = $object['id'];
$charge = null;
$paymentId = null;
$dispute = $this->stripeAPI->getDispute($id);
if ($dispute) {
$charge = $this->stripeAPI->getCharge($dispute->charge);
$paymentId = $charge->payment_intent;
}
if (!empty($metadata) && isset($metadata['invoices'])) {
echo 'metadata exists';
$invoices = explode(',', $metadata['invoices']);
foreach ($invoices as $invoiceID) {
/** @var Invoice $invoice */
$invoice = $repository->find($invoiceID);
if ($invoice) {
$response = $this->invoiceUtils->unpaid($invoice, PaymentTypeEnum::STRIPE, $paymentId);
echo 'response:';
if ($response) {
echo sprintf('update invoice #%s', $invoiceID);
}
}
}
} elseif ($charge && $charge->metadata) {
$invoices = explode(',', $charge->metadata->invoices);
foreach ($invoices as $invoiceID) {
/** @var Invoice $invoice */
$invoice = $repository->find($invoiceID);
if ($invoice) {
$response = $this->invoiceUtils->unpaid($invoice, PaymentTypeEnum::STRIPE, $id);
echo 'response:';
if ($response) {
echo sprintf('update invoice #%s', $invoiceID);
}
}
}
}
}
}
private function handlePaymentIntentSucceeded($object)
{
if ($object) {
$metadata = $object['metadata'];
$id = $object['id'];
if (!empty($metadata) && isset($metadata['invoices'])) {
echo 'metadata exists';
/** @var InvoiceRepository $repository */
$repository = $this->em->getRepository(Invoice::class);
$invoices = explode(',', $metadata['invoices']);
if ('STRIPE' === $metadata['type_payment']) {
foreach ($invoices as $invoiceID) {
/** @var Invoice $invoice */
$invoice = $repository->find($invoiceID);
if ($invoice) {
$amount = $invoice->getRemainingToPay();
$payment = $this->invoiceUtils->getPayment($invoice, PaymentTypeEnum::STRIPE);
if (null === $payment) {
$payment = new Payment();
$payment->setType(PaymentTypeEnum::STRIPE);
$payment->setAmount($amount);
}
$payment->setReference($id);
$payment->setStatus(PaymentStatusEnum::SUCCESS);
$invoice->addPayment($payment);
try {
$this->em->persist($invoice);
$this->em->flush();
} catch (\Exception $e) {
$this->captureSentryException($e);
}
echo sprintf('update invoice #%s', $invoiceID);
}
}
}
}
}
}
}