<?php
namespace App\Controller\Webhook;
use App\Enum\ActionTypeEnum;
use App\Enum\DocumentStatusEnum;
use App\Enum\DocumentTypeOrganizationEnum;
use App\Service\DocumentUtils;
use App\Service\GocardlessAPI;
use App\Service\SegmentAPI;
use App\Service\SubscriptionUtils;
use App\Traits\SentryNotifyTrait;
use Doctrine\ORM\EntityManagerInterface;
use Evo\Infrastructure\MappingORM\Document;
use Evo\Infrastructure\MappingORM\Organization;
use Evo\Infrastructure\MappingORM\Subscription;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Contracts\Translation\TranslatorInterface;
class GocardlessController extends AbstractController
{
use SentryNotifyTrait;
private GocardlessAPI $gocardlessAPI;
private EntityManagerInterface $em;
private ParameterBagInterface $params;
private TranslatorInterface $translator;
private SegmentAPI $segment;
private DocumentUtils $documentUtils;
public function __construct(
GocardlessAPI $gocardlessAPI,
EntityManagerInterface $em,
SegmentAPI $segment,
TranslatorInterface $translator,
DocumentUtils $documentUtils,
ParameterBagInterface $params
) {
$this->gocardlessAPI = $gocardlessAPI;
$this->em = $em;
$this->segment = $segment;
$this->documentUtils = $documentUtils;
$this->translator = $translator;
$this->params = $params;
}
/**
* @Route("/gocardless", name="app_webhook_gocardless", methods={"POST"})
*/
public function index(Request $request)
{
$request_body = file_get_contents('php://input');
$headers = $this->gocardlessAPI->getallheaders($request);
$signature_header = $headers['Webhook-Signature'];
$webhook_endpoint_secret = $this->params->get('webhook_secret');
$events = $this->gocardlessAPI->webhookParse($request_body, $signature_header, $webhook_endpoint_secret);
if (500 === $events) {
return new Response('', 498);
}
foreach ($events as $event) {
if ('mandates' === $event->resource_type) {
$this->mandatesEvent($event);
}
}
return new Response('', Response::HTTP_OK);
}
/**
* @Route("/customers", name="app_webhook_customers", methods={"POST"})
*/
public function customers(): JsonResponse
{
$payload = file_get_contents('php://input');
if (!$payload) {
return $this->json(['message' => 'No payload'], Response::HTTP_BAD_REQUEST);
}
$data = json_decode($payload, true, 512, JSON_THROW_ON_ERROR);
if (!isset($data['registerID']) || !isset($data['sessionID'])) {
return $this->json(['message' => 'Not found parameter registerID'], Response::HTTP_NOT_FOUND);
}
$registerID = $data['registerID'];
$sessionID = $data['sessionID'];
$organizationID = $data['organization'];
$action = $data['action'];
$redirectFlows = $this->gocardlessAPI->getRedirectFlows($registerID, $sessionID);
if (!$redirectFlows) {
return $this->json(['message' => 'RedirectFlows not found'], Response::HTTP_NOT_FOUND);
}
$mandatID = $redirectFlows->links->mandate;
$mandate = $this->gocardlessAPI->getMandate($mandatID);
$customerID = $redirectFlows->links->customer;
$customerBankAccountsID = $redirectFlows->links->customer_bank_account;
if (!$mandate || !$mandate->reference) {
return $this->json(['message' => 'Mandate not found'], Response::HTTP_NOT_FOUND);
}
$customerBankAccounts = $this->gocardlessAPI->getCustomerBankAccounts($customerBankAccountsID);
if (!$customerBankAccounts) {
return $this->json(['message' => 'CustomerBankAccounts not found'], Response::HTTP_NOT_FOUND);
}
$iban = $customerBankAccounts->account_number_ending;
if (!$iban) {
return $this->json(['message' => 'Iban not found'], Response::HTTP_NOT_FOUND);
}
/**
* Update customers.
*/
$params = [
'metadata' => [
'id_client' => (string) $organizationID,
],
];
$this->gocardlessAPI->updateCustomer($customerID, $params);
$repository = $this->em->getRepository(Organization::class);
/** @var Organization $organization */
$organization = $repository->find($organizationID);
if (!$organization) {
return $this->json(['message' => 'organization not found'], Response::HTTP_NOT_FOUND);
}
$trans = DocumentTypeOrganizationEnum::getReadableValue(DocumentTypeOrganizationEnum::RIB);
if (ActionTypeEnum::NEW === $action) {
$document = new Document();
$document
->setType(DocumentTypeOrganizationEnum::RIB)
->setStatus(DocumentStatusEnum::APPROVED)
->setOrganization($organization)
->setName($this->translator->trans($trans));
$this->em->persist($document);
} elseif (ActionTypeEnum::EDIT === $action) {
/** @var Subscription $subscription */
$subscription = SubscriptionUtils::getDomSubscription($organization);
/*
* Génération du RIB
*/
if ($subscription) {
$document = new Document();
$document
->setType(DocumentTypeOrganizationEnum::RIB)
->setStatus(DocumentStatusEnum::APPROVED)
->setOrganization($organization)
->setName($this->translator->trans($trans));
$this->em->persist($document);
}
}
$this->em->flush();
// Update organization
$this->em->clear();
$organization = $this->em->getRepository(Organization::class)->find($organizationID);
/**
* Annulation mandate gocardless.
*/
$oldMandateID = $organization->getMandatID();
if ($oldMandateID && $oldMandateID !== $mandatID) {
$this->gocardlessAPI->canceledMandate($oldMandateID);
$organization->setReferenceGocardless(null);
$organization->setMandatID(null);
}
$organization->setReferenceGocardless($mandate->reference);
$bankName = $customerBankAccounts->bank_name;
$titulaire = $customerBankAccounts->account_holder_name;
$organization->setIban($iban);
$organization->setBankName($bankName);
$organization->setTitulaire($titulaire);
$organization->setMandatID($mandatID);
$organization->setIsAuthorizedDebit(true);
$this->em->flush();
$params = SubscriptionUtils::getSubscriptionDataToSegment($organization);
$documentRIB = new Document();
$documentRIB
->setType(DocumentTypeOrganizationEnum::RIB)
->setStatus(DocumentStatusEnum::APPROVED)
->setOrganization($organization);
$this->segment->trackApprovedDocument($documentRIB, $params);
return $this->json(['message' => 'Gocardless updated', 'result' => 200], Response::HTTP_OK);
}
private function mandatesEvent($event): ?bool
{
if ('cancelled' === $event->action) {
$mandateID = $event->links->mandate;
$mandate = $this->gocardlessAPI->getMandate($mandateID);
$customer = $this->gocardlessAPI->getCustomer($mandate->links->customer);
$customerBankAccounts = $this->gocardlessAPI->getCustomerBankAccounts($mandate->links->customer_bank_account);
$organizationID = $customer->metadata->id_client;
$repositoryOrganization = $this->em->getRepository(Organization::class);
$organization = $repositoryOrganization->find($organizationID);
if (!$organization instanceof Organization) {
return null;
}
if ($organization->getMandatID() === $mandateID) {
$organization->setReferenceGocardless(null);
$organization->setMandatID(null);
$this->em->persist($organization);
$this->em->flush();
$params = [];
/** @var Subscription $subscription */
foreach ($organization->getSubscriptions() as $subscription) {
$params[] = [
'price' => $subscription->getTotalPriceWithTax(),
'date' => null !== $organization->getDomiciliationStartDate() ? $organization->getDomiciliationStartDate()->format('Y-m-d') : null,
'frequence' => $subscription->getType(),
'reference' => ($mandate) ? $mandate->reference : '',
'iban_last2' => (null !== $customerBankAccounts) ? $customerBankAccounts->account_number_ending : '',
];
}
$document = new Document();
$document
->setType(DocumentTypeOrganizationEnum::RIB)
->setStatus(DocumentStatusEnum::APPROVED)
->setOrganization($organization);
$this->segment->trackRejectedDocument($document, $params);
}
}
return true;
}
}