<?php namespace App\Controller; use App\Events\MLCEvents; use App\Events\FluxEvent; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Form; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\StreamedResponse; use Symfony\Component\HttpFoundation\Session\SessionInterface; use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Security\Core\Security; use Symfony\Component\Translation\TranslatorInterface; use Payum\Core\Payum; use Payum\Core\Request\GetHumanStatus; use Payum\Core\Request\Notify; use App\Entity\Flux; use App\Entity\Payment; use App\Entity\Siege; use App\Entity\User; use App\Entity\Adherent; use App\Entity\Prestataire; use App\Entity\Geoloc; use App\Entity\Groupe; use App\Entity\Usergroup; use App\Entity\AchatMonnaieAdherent; use App\Entity\AchatMonnaiePrestataire; use App\Entity\CotisationAdherent; use App\Entity\CotisationPrestataire; use App\Entity\GlobalParameter; use Sonata\Exporter\Handler; use Sonata\Exporter\Source\DoctrineORMQuerySourceIterator; use Sonata\Exporter\Writer\CsvWriter; use Sonata\Exporter\Writer\JsonWriter; use Sonata\Exporter\Writer\XmlWriter; use Sonata\Exporter\Writer\XlsWriter; use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; use FOS\UserBundle\Model\UserManagerInterface; use App\Security\LoginAuthenticator; use Symfony\Component\Security\Guard\GuardAuthenticatorHandler; /** * * Types de transfert : (Les transferts dans la structure sont les flux de billets détenus par les opérateurs.) * * - SIEGE => GROUPES LOCAUX (Transfert du siège au groupe) * - GROUPE => SIEGE (Transfert du groupe au siège) * - GROUPES LOCAUX => COMPTOIRS (Transfert du groupe au comptoir) * - COMPTOIRS => GROUPES LOCAUX (Transfert du comptoir au groupe) * - COMPTOIRS => ADHERENTS (Diffusion de monnaie papier auprès des adhérents) * - COMPTOIRS => PRESTATAIRES (Diffusion de monnaie papier auprès des prestataires) * - PRESTATAIRES => COMPTOIRS (Reconversion) * * Types de transaction : * * - PRESTATAIRES => ADHERENTS (Virement vers un adherent) * - PRESTATAIRES => PRESTATAIRES (Virement entre prestataires) * - ADHERENTS => PRESTATAIRES (Paiement numérique) * - SIEGE => ADHERENTS (Achat de monnaie numérique par CB d'un adhérent) * - SIEGE => PRESTATAIRES (Achat de monnaie numérique par CB d'un prestataire) * */ class FluxController extends AbstractController { protected $em; protected $translator; protected $eventDispatcher; protected $session; protected $payum; protected $authenticator; protected $guardHandler; public function __construct(Security $security, EntityManagerInterface $em, TranslatorInterface $translator, EventDispatcherInterface $eventDispatcher, SessionInterface $session, LoginAuthenticator $authenticator, GuardAuthenticatorHandler $guardHandler, Payum $payum) { $this->security = $security; $this->em = $em; $this->translator = $translator; $this->eventDispatcher = $eventDispatcher; $this->session = $session; $this->payum = $payum; $this->authenticator = $authenticator; $this->guardHandler = $guardHandler; } protected function manageFluxForm(Request $request, Form $form, $compte, $success, $title) { if ($this->security->getUser() == null) { throw new \Exception("[FLUX] Opération impossible ! Utilisateur déconnecté !"); } $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { $data = $form->getData(); $this->em->persist($data); $this->em->flush(); $this->addFlash( 'success', $success ); $this->eventDispatcher->dispatch( MLCEvents::FLUX, new FluxEvent($data) ); $referer = $request->headers->get('referer'); if ($referer && !$request->isXmlHttpRequest()) { return $this->redirect($referer); } elseif (!$request->isXmlHttpRequest()) { return new Response('', Response::HTTP_BAD_REQUEST); } } return $this->render('flux/transaction.html.twig', [ 'form' => $form->createView(), 'title' => $title ]); } /** * Export all transferts / transactions for a user role * * @param Request $request Request * @param String $format Format of export ('json', 'xml', 'csv', 'xls') * @Route("/flux/export/{format}/", name="exportUserFlux", defaults={"format": "csv"}) */ public function exportFluxAction(Request $request, $format = 'csv') { //Prepare query depending on user role $query = null; $user = $this->security->getUser(); if ($this->session->get('_prestagere') != null && $this->security->getUser()->isGranted('ROLE_PRESTATAIRE')) { $query = $this->em->getRepository(Flux::class)->getQueryByPrestataire($this->session->get('_prestagere')); } elseif ($user->getAdherent() != null && $this->security->getUser()->isGranted('ROLE_ADHERENT')) { $query = $this->em->getRepository(Flux::class)->getQueryByAdherent($user->getAdherent()); } elseif ($this->session->get('_comptoirgere') != null && $this->security->getUser()->isGranted('ROLE_COMPTOIR')) { $query = $this->em->getRepository(Flux::class)->getQueryByComptoir($this->session->get('_comptoirgere')); } elseif ($this->session->get('_groupegere') != null && $this->security->getUser()->isGranted('ROLE_GESTION_GROUPE')) { $query = $this->em->getRepository(Flux::class)->getQueryByGroupe($this->session->get('_groupegere')); } if ($query != null) { // Prepare the data source $fields = ['expediteur', 'destinataire', 'type', 'parenttype', 'montant', 'moyen', 'operateur']; $source = new DoctrineORMQuerySourceIterator($query, $fields); $filename = sprintf( 'export_flux_%s.%s', date('Y_m_d_H_i_s', strtotime('now')), $format ); return $this->getResponse( $format, $filename, $source ); } else { $this->addFlash( 'error', $this->translator->trans('Export impossible.') ); return $this->redirectToRoute('index'); } } private function getResponse($format, $filename, $source) { switch ($format) { case 'xls': $writer = new XlsWriter('php://output'); $contentType = 'application/vnd.ms-excel'; break; case 'xml': $writer = new XmlWriter('php://output'); $contentType = 'text/xml'; break; case 'json': $writer = new JsonWriter('php://output'); $contentType = 'application/json'; break; case 'csv': $writer = new CsvWriter('php://output', ',', '"', '\\', true, true); $contentType = 'text/csv'; break; default: throw new \RuntimeException('Invalid format'); } $callback = static function () use ($source, $writer) { $handler = Handler::create($source, $writer); $handler->export(); }; return new StreamedResponse($callback, 200, [ 'Content-Type' => $contentType, 'Content-Disposition' => sprintf('attachment; filename="%s"', $filename), ]); } /** * Crée une instance de Payment et redirige vers la page de paiement */ public function preparePaymentAction(Form $form, $type) { // Enregistre les données du Flux en json, pour l'enregistrer une fois le paiement validé $serializer = $this->container->get('serializer'); $toSerialize = Payment::TYPE_ADHESION == $type ? $form->get('cotisation')->getData() : $form->getData(); $data = $serializer->normalize($toSerialize, null, [AbstractNormalizer::ATTRIBUTES => ['reference', 'moyen', 'montant', 'role', 'expediteur' => ['id'], 'destinataire' => ['id'], 'operateur' => ['id']]]); $jsondata = $serializer->serialize($data, 'json'); // Prepare CB Payment if ($this->em->getRepository(GlobalParameter::class)->val(GlobalParameter::USE_PAYZEN) === 'true') { $gatewayName = 'payzen'; } else { $this->addFlash( 'error', $this->translator->trans('Une erreur est survenue due à la configuration du paiement dans l\'application. Il est pour l\'instant impossible de payer par CB, merci de contacter votre monnaie locale.') ); return $this->redirectToRoute('index'); } $storage = $this->payum->getStorage('App\Entity\Payment'); $payment = $storage->create(); $payment->setNumber(uniqid()); $payment->setCurrencyCode('978'); $payment->setDescription($type); $payment->setFluxData($jsondata); if ($type == Payment::TYPE_ADHESION) { $payment->setTotalAmount($form->get('cotisation')->get('montant')->getData()*100); // 1.23 EUR $payment->setClientId('Nouvel adhérent'); $payment->setClientEmail($form->get('user')->get('email')->getData()); } else { $payment->setTotalAmount($form->get('montant')->getData()*100); // 1.23 EUR $payment->setClientId($this->getUser()->getId()); $payment->setClientEmail($this->getUser()->getEmail()); } $storage->update($payment); $captureToken = $this->payum->getTokenFactory()->createCaptureToken( $gatewayName, $payment, 'payment_done' // the route to redirect after capture ); // Symfony creates URLs with http and not https -> replace $targetUrl = preg_replace('/^http:/', 'https:', $captureToken->getTargetUrl()); $afterUrl = preg_replace('/^http:/', 'https:', $captureToken->getAfterUrl()); $captureToken->setTargetUrl($targetUrl); $captureToken->setAfterUrl($afterUrl); $this->em->persist($captureToken); $this->em->flush(); return $this->redirect($captureToken->getTargetUrl()); } /** * Fonction de traitement du paiement, à appeler : * - automatiquement lorsqu'un événement se produit sur le site de Paiement * - au retour sur le site par l'utilisateur * * @Route("/payment/done/", name="payment_done") */ public function doneAction(Request $request) { try { $token = $this->payum->getHttpRequestVerifier()->verify($request); } catch (\Exception $e) { // Token expiré : retour sur site après paiement // Get last payment $payment = $this->em->getRepository(Payment::class)->getUserLastPayment($this->getUser()->getId()); if (!is_null($payment)) { if ($payment->getStatus() == GetHumanStatus::STATUS_CAPTURED || $payment->getStatus() == GetHumanStatus::STATUS_AUTHORIZED) { $type = $payment->getDescription(); if (Payment::TYPE_ACHAT_MONNAIE_ADHERENT == $type || Payment::TYPE_ACHAT_MONNAIE_PRESTA == $type) { $this->addFlash( 'success', $this->translator->trans('Achat de monnaie locale bien effectué !') ); } else if (Payment::TYPE_COTISATION_ADHERENT == $type || Payment::TYPE_COTISATION_PRESTA == $type) { $this->addFlash( 'success', $this->translator->trans('Cotisation bien reçue. Merci !') ); } else if (Payment::TYPE_ADHESION == $type) { $this->addFlash( 'success', $this->translator->trans('Votre adhésion a bien été prise en compte, bienvenue !') ); } // Update payment status $payment->setStatus($payment->getStatus().';done'); $this->em->persist($payment); $this->em->flush(); } else if ($payment->getStatus() == GetHumanStatus::STATUS_CANCELED || $payment->getStatus() == GetHumanStatus::STATUS_EXPIRED || $payment->getStatus() == GetHumanStatus::STATUS_FAILED) { $this->addFlash( 'error', $this->translator->trans('La transaction a été annulée.') ); $payment->setStatus($payment->getStatus().';done'); $this->em->persist($payment); $this->em->flush(); } } return $this->redirectToRoute('index'); } $gateway = $this->payum->getGateway($token->getGatewayName()); // Execute Notify action $gateway->execute(new Notify($token)); // Execute 'done' action according to payment status $gateway->execute($status = new GetHumanStatus($token)); // Get payment & update status $payment = $status->getFirstModel(); $payment->setStatus($status->getValue()); $this->em->persist($payment); $this->em->flush(); // If payment succesful, persist serialized 'Flux' stored in payment if ($status->getValue() == GetHumanStatus::STATUS_CAPTURED || $status->getValue() == GetHumanStatus::STATUS_AUTHORIZED) { $serializer = $this->container->get('serializer'); $flux_array = json_decode($payment->getFluxData(), true); $type = $payment->getDescription(); if (Payment::TYPE_ACHAT_MONNAIE_ADHERENT == $type) { $flux = $serializer->deserialize( $payment->getFluxData(), AchatMonnaieAdherent::class, 'json', ['disable_type_enforcement' => true] ); $exp = $this->em->getRepository(Siege::class)->find($flux_array['expediteur']); $flux->setExpediteur($exp); $dest = $this->em->getRepository(Adherent::class)->find($flux_array['destinataire']); $flux->setDestinataire($dest); $op = $this->em->getRepository(User::class)->find($flux_array['operateur']); $flux->setOperateur($op); } else if (Payment::TYPE_ACHAT_MONNAIE_PRESTA == $type) { $flux = $serializer->deserialize( $payment->getFluxData(), AchatMonnaiePrestataire::class, 'json', ['disable_type_enforcement' => true] ); $exp = $this->em->getRepository(Siege::class)->find($flux_array['expediteur']); $flux->setExpediteur($exp); $dest = $this->em->getRepository(Prestataire::class)->find($flux_array['destinataire']); $flux->setDestinataire($dest); $op = $this->em->getRepository(User::class)->find($flux_array['operateur']); $flux->setOperateur($op); } else if (Payment::TYPE_COTISATION_ADHERENT == $type) { $flux = $serializer->deserialize( $payment->getFluxData(), CotisationAdherent::class, 'json', ['disable_type_enforcement' => true] ); $exp = $this->em->getRepository(Adherent::class)->find($flux_array['expediteur']); $flux->setExpediteur($exp); $dest = $this->em->getRepository(Prestataire::class)->find($flux_array['destinataire']); $flux->setDestinataire($dest); $op = $this->em->getRepository(User::class)->find($flux_array['operateur']); $flux->setOperateur($op); $flux->setRecu(true); } else if (Payment::TYPE_COTISATION_PRESTA == $type) { $flux = $serializer->deserialize( $payment->getFluxData(), CotisationPrestataire::class, 'json', ['disable_type_enforcement' => true] ); $exp = $this->em->getRepository(Prestataire::class)->find($flux_array['expediteur']); $flux->setExpediteur($exp); $dest = $this->em->getRepository(Prestataire::class)->find($flux_array['destinataire']); $flux->setDestinataire($dest); $op = $this->em->getRepository(User::class)->find($flux_array['operateur']); $flux->setOperateur($op); $flux->setRecu(true); } else if (Payment::TYPE_ADHESION == $type) { $new_adherent_data = json_decode($this->session->get('new_adherent')); $adherent = new Adherent(); $user = $this->userManager->createUser(); $usergroup = $this->em->getRepository(Usergroup::class)->findOneByName('Adherent'); $group = $this->em->getRepository(Groupe::class)->findOneBy(array('id' => $new_adherent_data->groupe->id)); $user->setEmail($new_adherent_data->user->email); $user->setUsername($new_adherent_data->user->username); $user->setFirstname($new_adherent_data->user->firstname); $user->setLastname($new_adherent_data->user->lastname); $user->setPlainPassword($new_adherent_data->user->plainPassword); $user->setEnabled(true); $user->addPossiblegroup($usergroup); $user->addGroup($usergroup); $user->addRole('ROLE_ADHERENT'); $user->setAdherent($adherent); $adherent->setEcompte('0'); $adherent->setUser($user); $adherent->setGroupe($group); if ($adherent->getGeoloc() == null) { $geoloc = new Geoloc(); $geoloc->setAdresse($new_adherent_data->geoloc->adresse); $geoloc->setCpostal($new_adherent_data->geoloc->cpostal); $geoloc->setVille($new_adherent_data->geoloc->ville); $adherent->setGeoloc($geoloc); } $this->em->persist($adherent); $this->em->flush(); // Remove new user data from session $this->session->remove('new_adherent'); // Create first cotisation $flux = $serializer->deserialize( $payment->getFluxData(), CotisationAdherent::class, 'json', ['disable_type_enforcement' => true] ); $flux->setOperateur($user); $flux->setExpediteur($adherent); $flux->setDestinataire($this->em->getRepository(Prestataire::class)->findOneBy(array('mlc' => true))); $flux->setRole('Adherent'); $flux->setRecu(true); // Update payment with new user id $payment->setClientId($user->getId()); $this->em->persist($payment); } else { return new Response('', Response::HTTP_BAD_REQUEST); } $this->em->persist($flux); $this->em->flush(); $this->eventDispatcher->dispatch( MLCEvents::FLUX, new FluxEvent($flux) ); // Add flash message here too in case Gataway doesn't notify if (Payment::TYPE_ACHAT_MONNAIE_ADHERENT == $type || Payment::TYPE_ACHAT_MONNAIE_PRESTA == $type) { $this->addFlash( 'success', $this->translator->trans('Achat de monnaie locale bien effectué !') ); } else if (Payment::TYPE_COTISATION_ADHERENT == $type || Payment::TYPE_COTISATION_PRESTA == $type) { $this->addFlash( 'success', $this->translator->trans('Cotisation bien reçue. Merci !') ); } else if (Payment::TYPE_ADHESION == $type) { $this->addFlash( 'success', $this->translator->trans('Votre adhésion a bien été prise en compte, bienvenue !') ); } // Invalidate token $this->payum->getHttpRequestVerifier()->invalidate($token); } else if ($status->getValue() == GetHumanStatus::STATUS_CANCELED || $status->getValue() == GetHumanStatus::STATUS_EXPIRED || $status->getValue() == GetHumanStatus::STATUS_FAILED) { $this->addFlash( 'error', $this->translator->trans('La transaction a été annulée.') ); $this->payum->getHttpRequestVerifier()->invalidate($token); } if (Payment::TYPE_ADHESION == $type) { // Auto login after adhesion return $this->guardHandler ->authenticateUserAndHandleSuccess( $user, $request, $this->authenticator, 'main' ); } else { return $this->redirectToRoute('index'); } } }