<?php
namespace Aviatur\ParkBundle\Controller;
use Doctrine\Persistence\ManagerRegistry;
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\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Aviatur\GeneralBundle\Controller\OrderController;
use Aviatur\AgencyBundle\Entity\Agency;
use Aviatur\GeneralBundle\Entity\FormUserInfo;
use Aviatur\CustomerBundle\Services\ValidateSanctionsRenewal;
use Aviatur\GeneralBundle\Services\AviaturChangeCoin;
use Aviatur\GeneralBundle\Services\AviaturEncoder;
use Aviatur\GeneralBundle\Services\AviaturQrcodeService;
use Aviatur\GeneralBundle\Services\AviaturErrorHandler;
use Aviatur\GeneralBundle\Services\AviaturLogSave;
use Aviatur\GeneralBundle\Services\AviaturMailer;
use Aviatur\GeneralBundle\Services\AviaturRestService;
use Aviatur\GeneralBundle\Services\AviaturWebService;
use Aviatur\GeneralBundle\Services\ExceptionLog;
use Aviatur\ParkBundle\Services\ListParkService;
use Aviatur\ParkBundle\Services\LegacyTicketParkService;
use Aviatur\ParkBundle\Services\ParkTemplateVoucherkService;
use Aviatur\ParkBundle\Services\CancelBookingParkService;
use Aviatur\ParkBundle\Services\SendVoucherEmailParkService;
use Aviatur\ParkBundle\Models\ParkSearchModel;
use Aviatur\ParkBundle\Models\ParkDetailModel;
use Aviatur\ParkBundle\Models\ParkBookingModel;
use Aviatur\PaymentBundle\Controller\P2PController;
use Aviatur\PaymentBundle\Services\CustomerMethodPaymentService;
use Aviatur\PaymentBundle\Services\TokenizerService;
use Aviatur\TwigBundle\Services\TwigFolder;
use Knp\Snappy\Pdf;
class ParkCheckoutController extends AbstractController
{
/**
* @var ManagerRegistry
*/
protected ManagerRegistry $managerRegistry;
/**
* @var AviaturRestService
*/
private $aviaturRestService;
/**
* @var SessionInterface
*/
protected $session;
/**
* @var Agency|object|null
*/
protected $agency;
protected static $exceptionLog;
private $em;
private $listParkService;
private $legacyTicketParkService;
private $parkTemplateVoucherkService;
private $cancelBookingParkService;
private $sendVoucherEmailParkService;
public function __construct(ManagerRegistry $managerRegistry, SessionInterface $session, AviaturRestService $aviaturRestService, ExceptionLog $exceptionLog, ListParkService $listParkService, ParkTemplateVoucherkService $parkTemplateVoucherkService, LegacyTicketParkService $legacyTicketParkService, CancelBookingParkService $cancelBookingParkService, SendVoucherEmailParkService $sendVoucherEmailParkService)
{
self::$exceptionLog = $exceptionLog;
$this->em = $managerRegistry->getManager();
$this->aviaturRestService = $aviaturRestService;
$this->session = $session;
$this->agency = $this->em->getRepository(Agency::class)->find($session->get('agencyId'));
$this->listParkService = $listParkService;
$this->legacyTicketParkService = $legacyTicketParkService;
$this->parkTemplateVoucherkService = $parkTemplateVoucherkService;
$this->cancelBookingParkService = $cancelBookingParkService;
$this->sendVoucherEmailParkService = $sendVoucherEmailParkService;
}
public function searchAction()
{
return $this->redirect(
$this->generateUrl(
'aviatur_search_parks',
[]
)
);
}
public function checkoutAction(Request $request, AviaturLogSave $logSave, ParameterBagInterface $parameterBag, TwigFolder $twigFolder, SessionInterface $session, AviaturErrorHandler $aviaturErrorHandler, AviaturChangeCoin $aviaturChangeCoin, AviaturWebService $webService, AviaturEncoder $aviaturEncoder
) {
if (empty($request->query->all()) && empty($request->request->all())) {
$titleError = "Página no accesible";
$textError = "No puedes acceder al detalle sin disponibilidad";
return $this->redirect($aviaturErrorHandler->errorRedirectNoEmail($this->generateUrl('aviatur_search_parks', []), $titleError, $textError));
}
// Obtener request enviado
$ticketsSelectedInfo = $request->request->all() ?? $request->query->all();
// Obtener TransactionId
$transactionId = (string) !empty($ticketsSelectedInfo['transactionId']) ? $ticketsSelectedInfo['transactionId'] : $ticketsSelectedInfo['transactionRetryId'];
// Validar si es reintento de pago
$isretry = !empty($ticketsSelectedInfo['transactionRetryId']);
if($isretry){
// Si ya no quedan intentos de compra, redirigir al home de parques
$retryCount = $session->get($transactionId . '[park][retry]');
if($retryCount <= 0){
$titleError = "Acceso no autorizado";
$textError = "Ha sido redirigido al home para que pueda seguir explorando opciones.";
return $this->redirect($aviaturErrorHandler->errorRedirectNoEmail($this->generateUrl("aviatur_search_parks", []), $titleError, $textError));
}
// Información de tickets seleccionados
$sessionKey = $transactionId . "[park][ticketsInfo]";
$ticketsSelectedInfo = $this->getAvailabilityFromSession($sessionKey, $aviaturErrorHandler);
$filteredTickets = $ticketsSelectedInfo['filteredTickets'];
$ticketsSelectedInfo['searchParams'] = json_encode($ticketsSelectedInfo["searchParams"]);
$ticketsSelectedInfo['transactionId'] = $transactionId;
// Información de formularios de usuario
$formUserInfo = $this->em->getRepository(\Aviatur\GeneralBundle\Entity\FormUserInfo::class)->find($session->get($transactionId . '[park][userInfo]'));
$decodedResponseInfoUser = json_decode($aviaturEncoder->AviaturDecode($formUserInfo->getInfo(), $formUserInfo->getPublicKey()), true);
$passengersInfoBD = $decodedResponseInfoUser["PI"];
$billingDataInfoBD = $decodedResponseInfoUser["BD"];
}
// Obtener la respuesta de disponibilidad desde la sesión
$sessionKey = $transactionId . '[park][ticketsAvailability]';
$parkAvailabilityInfo = $this->getAvailabilityFromSession($sessionKey, $aviaturErrorHandler);
$searchParams = $this->parseSearchParams($ticketsSelectedInfo['searchParams']);
if (empty($parkAvailabilityInfo) || !isset($parkAvailabilityInfo['productCatalogs'])) {
$url = $this->generateUrl('aviatur_park_avaliability', $searchParams['model']->getData());
return $this->redirect($aviaturErrorHandler->errorRedirectNoEmail($url, 'Página no accesible', 'No puedes acceder al detalle sin disponibilidad. Puede seguir explorando opciones.'));
}
// Validación de la existencia del parque y sede
$availabilityParameters = $this->validParkAndSede($searchParams['park'], $searchParams['sede'], $aviaturErrorHandler);
// Filtrar info dispo para ticket(s) seleccionado(s)
if(!$isretry){
$ticketsSelected = json_decode($ticketsSelectedInfo['ticketsSelected'], true);
$filteredTickets = array_values(array_filter($parkAvailabilityInfo['productCatalogs'], fn($ticket) => in_array($ticket['productId'], $ticketsSelected)));
}
// Arreglo RS del detalle
$arrayDetailParks = $this->getTicketDetails($filteredTickets, $searchParams, $availabilityParameters, $logSave, $transactionId, $aviaturErrorHandler);
// Precios totalizados de los tickets
$priceTotaltickets = $this->calculatePriceTotal($filteredTickets, $arrayDetailParks, $searchParams, $aviaturChangeCoin);
// Eliminar "Adult" o "Child" del nombre del ticket para agruparlos
$baseName = preg_replace('/(Adult|Child|Ad |Ch )/', '', $filteredTickets[0]['productName']);
$baseName = trim($baseName);
// Datos requeridos para los formularios del checkOut
$typeDocument = $this->em->getRepository(\Aviatur\CustomerBundle\Entity\DocumentType::class)->findAll();
$typeGender = $this->em->getRepository(\Aviatur\CustomerBundle\Entity\Gender::class)->findAll();
$repositoryDocumentType = $this->em->getRepository(\Aviatur\CustomerBundle\Entity\DocumentType::class);
$queryDocumentType = $repositoryDocumentType
->createQueryBuilder('p')
->where('p.paymentcode != :paymentcode')
->setParameter('paymentcode', '')
->getQuery();
$documentPaymentType = $queryDocumentType->getResult();
$passangerTypes[1] = [
'ADT' => (int) $searchParams['adults'],
'CHD' => (int) $searchParams['children'],
'INF' => 0,
];
/* Aplicando para vuelo, pero teniendo cuidado con los otros productos */
/* Necesitamos crear un arreglo que tenga todos los rangos de IIN asociados a su franquicia y a sus límites de número de tarjeta */
$iinRecordsArray = $webService->getIINRanges($this->em);
// Opciones de pago
$paymentMethodAgency = $this->em->getRepository(\Aviatur\GeneralBundle\Entity\PaymentMethodAgency::class)->findBy(['agency' => $this->agency, 'isactive' => 1]);
$paymentOptions = [];
foreach ($paymentMethodAgency as $payMethod) {
$paymentCode = $payMethod->getPaymentMethod()->getCode();
if (!in_array($paymentCode, $paymentOptions) && 'p2p' == $paymentCode || 'cybersource' == $paymentCode) {
$paymentOptions[] = $paymentCode;
}
}
// Guardar información de dispo y detalle de tickets, encodeada en session
$ticketsInfo = [
"availabilityParameters" => $availabilityParameters,
"filteredTickets" => $filteredTickets,
"searchParams" => $searchParams
];
$session->set($transactionId . '[park][ticketsInfo]', base64_encode(json_encode($ticketsInfo)));
// Extraer número de parques si existe "X-Park"
$parkNumber = "";
if (preg_match('/(\d+)-Park/', $baseName, $matches)) {
$parkNumber = $matches[1];
}
// Agregando politicas de los ticket's
$legacyTicketPark = $this->legacyTicketParkService->getLegacyTicketPark($availabilityParameters["parkSedesId"], (int) $filteredTickets[0]['salesProgramId'], $baseName, $filteredTickets[0]['numberOfDays'], $parkNumber);
// Validar si es Park to Park
$aditionalData = !empty($filteredTickets[0]["aditionalData"]) ? $filteredTickets[0]["aditionalData"] : $filteredTickets[0]["detailTicket"]["usosTicket"];
$isParkToPark = $this->sendVoucherEmailParkService->isParkToPark($aditionalData);
// Obtener lista de parques permitidos y conector de text park
$themeParkAccessNamesList = $filteredTickets[0]["themeParkAccessNames"];
$conectorText = $isParkToPark ? "<strong>AND</strong>" : "<strong>OR</strong>";
$nameParksVisitList = "";
if(!empty($themeParkAccessNamesList)){
$themeParkAccessNames = $this->sendVoucherEmailParkService->getParksVisitList($themeParkAccessNamesList);
$countParks = count($themeParkAccessNames);
foreach ($themeParkAccessNames as $i => $park) {
$nameParksVisitList .= $park;
if ($i < $countParks - 1) {
$nameParksVisitList .= " $conectorText ";
}
}
}
// Agregar políticas legacy y reemplazar valores
$legacyTicketPark = str_replace("{{productName}}", $baseName, $legacyTicketPark);
$legacyTicketPark = str_replace("{{nameParksVisitList}}", $nameParksVisitList, $legacyTicketPark);
// Renderizar la vista
$agencyFolder = $twigFolder->twigFlux();
return $this->render($twigFolder->twigExists(sprintf('@AviaturTwig/%s/Park/parkCheckout_index.html.twig', $agencyFolder)), [
'twig_readonly' => $isretry,
'isPark' => true,
"ticketsSelectedInfo" => $ticketsSelectedInfo,
"productName" => $baseName,
"eventDateTime" => $filteredTickets[0]['eventDateTime'],
"days" => $filteredTickets[0]['numberOfDays'],
"legacyTicketPark" => $legacyTicketPark,
"price" => $priceTotaltickets,
'payment_doc_type' => $documentPaymentType,
'doc_type' => $typeDocument,
'gender' => $typeGender,
'render' => $this->generateUrl('aviatur_park_avaliability', $searchParams['model']->getData()),
'services' => $passangerTypes,
'paymentOptions' => $paymentOptions,
'ccfranchises' => $iinRecordsArray["ccfranchises"],
'ccranges' => $iinRecordsArray["ccranges"],
'passengers' => $passengersInfoBD ?? [],
'billingData' => $billingDataInfoBD ?? []
]);
}
// Función que aplica el modelo de búsqueda
private function parseSearchParams(string $json): array
{
$params = json_decode($json, true);
$model = new ParkSearchModel();
$model->setNamePark($params['park']);
$model->setNameSedePark($params['sede']);
$model->setDatePark($params['datepark']);
$model->setPassengerPark($params['adults'] . "-" . $params['children']);
return array_merge($params, ['model' => $model]);
}
// Función que valida la existencia del parque y sede
private function validParkAndSede($namePark, $nameSede, AviaturErrorHandler $aviaturErrorHandler)
{
$availabilityParameters = $this->em->getRepository(\Aviatur\ParkBundle\Entity\ParkSedes::class)->findAvailabilityParameters($this->agency, $namePark, $nameSede);
// Si no se encuentra el parque o la sede, redirigir al Home de parques con el mensaje de error
if (empty($availabilityParameters)) {
$titleError = "La ubicación seleccionada no está disponible.";
$textError = "Ha sido redirigido al home para que pueda seguir explorando opciones.";
return $this->redirect($aviaturErrorHandler->errorRedirectNoEmail($this->generateUrl('aviatur_search_parks', []), $titleError, $textError));
}
return $availabilityParameters;
}
// Función que obtiene la disponibilidad de acuerdo al transactionId
private function getAvailabilityFromSession($sessionKey, AviaturErrorHandler $aviaturErrorHandler)
{
$encoded = $this->session->get($sessionKey);
if (!$encoded) {
return $this->redirect($aviaturErrorHandler->errorRedirectNoEmail($this->generateUrl('aviatur_search_parks', []), 'Página no accesible', 'No puedes acceder al detalle sin disponibilidad. Ha sido redirigido al home para que pueda seguir explorando opciones.'));
}
return json_decode(base64_decode($encoded), true);
}
// Función que realiza y mapea el servicio del detalle
private function getTicketDetails(array $filteredTickets, array $searchParams, array $availabilityParameters, AviaturLogSave $logSave, string $transactionId, AviaturErrorHandler $errorHandler)
{
$detailResults = [];
$formattedDetailRS = [];
$startDate = new \DateTime($searchParams['datepark']);
$endDate = clone $startDate;
$endDate->modify('+1 day');
foreach ($filteredTickets as $ticket) {
$passengerInfo = $this->getTicketPassengerInfo($ticket, $searchParams);
// Definir cantidad de tickets
$quantity = $passengerInfo['quantity'];
// Determinar el tipo de pasajero (Adulto, Niño, No importa la edad)
$typePassenger = $passengerInfo['typePassenger'];
// RQ detalle
$detailParkRQ = new ParkDetailModel(
$availabilityParameters['requestKind'],
$ticket['productId'],
$searchParams['datepark'],
$endDate->format('Y-m-d'),
$ticket['salesProgramId'],
$ticket['capacity'],
$ticket['numberOfDays'],
$availabilityParameters['environmentCode'],
$quantity,
$ticket['price']['amountAfterTax']
);
$logSave->logSave(json_encode($detailParkRQ->toArray()), 'ParkDetail', $availabilityParameters['providerIdentifier'] . '_' . $ticket['productId'] . '_RQ', $transactionId);
$response = $this->aviaturRestService->callRestWebService($availabilityParameters['wsUrl'], 'detalle', $detailParkRQ->toArray());
$logSave->logSave(json_encode($response), 'ParkDetail', $ticket['productId'] . '_RS', $transactionId);
$url = $this->generateUrl('aviatur_park_avaliability', $searchParams['model']->getData());
$redirectTitle = "Lo sentimos, no se encontró disponibilidad en este momento.";
$redirectText = "Ha sido redirigido a la búsqueda para que pueda seguir explorando opciones.";
if (empty($response) || !$response['succeeded'] || empty($response['productDetalle'])) {
return $this->redirect($errorHandler->errorRedirectNoEmail($url, $redirectTitle, $redirectText));
}
$filteredDetails = $this->filterEventDetails($response['productDetalle'], $searchParams['datepark'], $quantity);
if (empty($filteredDetails)) {
return $this->redirect($errorHandler->errorRedirectNoEmail($url, $redirectTitle, $redirectText));
}
$selectedDetail = count($filteredTickets) <= 1 ? $filteredDetails : $this->getCombinationsDetailParks($formattedDetailRS, $filteredDetails);
$detailResults[$ticket['productId']] = [
'productId' => $ticket['productId'],
'quantity' => $quantity,
'typePassenger' => $typePassenger,
'productDetalle' => reset($selectedDetail)
];
}
return $detailResults;
}
// Función que obtiene la cantidad y tipo de pasajeros
private function getTicketPassengerInfo(array $ticket, array $searchParams): array
{
$ageType = $ticket['ageValue'] ?? ($ticket['ageQualifies'][0]['ageType'] ?? '');
$quantity = 0;
$typePassenger = '';
switch ($ageType) {
case 'Adult':
case '10 Años en adelante':
$quantity = (int) !empty($searchParams) ? $searchParams['adults'] : 0;
$typePassenger = 'ADT';
break;
case 'Child':
case 'Entre 3 y 9 años':
$quantity = (int) !empty($searchParams) ? $searchParams['children'] : 0;
$typePassenger = 'CHD';
break;
case 'No importa la edad':
$quantity = (int) !empty($searchParams) ? ($searchParams['adults'] + (int)$searchParams['children']) : 0;
$typePassenger = 'ALL';
break;
default:
$quantity = 0;
$typePassenger = '';
}
return [
'quantity' => $quantity,
'typePassenger' => $typePassenger
];
}
// Función que filtra solo los tickets con eventDateTime de la fecha seleccionada y con capacidad disponible a la búsqueda
private function filterEventDetails(array $products, string $targetDate, int $minCapacity): array
{
return array_filter(
$products,
fn($product) =>
substr($product['eventDateTime'], 0, 10) === $targetDate &&
$product['capacityAvailable'] >= $minCapacity
);
}
// Función para filtrar las combinaciones de eventos
private function getCombinationsDetailParks($arrayDetailParks, $responseDetailParks)
{
$arrayDetailTickets = [];
if (!empty($arrayDetailParks)) {
// Obtener los valores comunes de eventId
$datesDetail1 = array_column($arrayDetailParks, 'eventId');
$datesDetail2 = array_column($responseDetailParks, 'eventId');
$commonDates = array_intersect($datesDetail1, $datesDetail2);
if (!empty($commonDates)) {
// Filtrar todos los elementos de responseDetailParks cuyo eventId esté en los comunes
$filteredDetail2 = array_filter($responseDetailParks, function ($item) use ($commonDates) {
return in_array($item['eventId'], $commonDates);
});
// Ordenar por totalPriceWithTax de menor a mayor
usort($filteredDetail2, function ($a, $b) {
return $a['totalPriceWithTax'] <=> $b['totalPriceWithTax'];
});
// Tomar el más barato, es decir, el primer elemento de filteredDetail2
$arrayDetailTickets = reset($filteredDetail2);
}
} else {
// Si no hay detalle base, se retorna el response del detalle
$arrayDetailTickets = $responseDetailParks;
}
return $arrayDetailTickets;
}
// Función integra la información de la respuesta del detalle en los tickets disponibles y realiza el cálculo de precios de acuerdo a los pasajeros
private function calculatePriceTotal(array &$filteredTickets, array $arrayDetailParks, array $searchParams, AviaturChangeCoin $changeCoin): array
{
// Obtener el porcentaje de markup del parque
$markupPark = $this->em->getRepository(\Aviatur\ParkBundle\Entity\ParkMarkup::class)->getMarkupPark($searchParams['park'], $searchParams['sede']);
$markup = empty($markupPark) ? 0 : $markupPark;
// Obtener TRM del día
$trm = $changeCoin->getExchangeRate('USD', 'COP', 'TRM')['OfficialExchangeRate'];
$currency = $trm == 1 ? 'USD' : 'COP';
// Inicialización de precios
$priceTotals = ['priceAdultTicket' => 0, 'priceChildTicket' => 0, 'currency' => $currency];
array_walk($filteredTickets, function (&$ticket) use ($arrayDetailParks, $markup, $trm, &$priceTotals, $searchParams) {
$detail = $arrayDetailParks[$ticket['productId']] ?? null;
if (!$detail) return;
$ticket['quantity'] = $detail['quantity'];
$ticket['eventId'] = $detail['productDetalle']['eventId'];
$ticket['eventDateTime'] = $detail['productDetalle']['eventDateTime'];
$ticket['capacityAvailable'] = $detail['productDetalle']['capacityAvailable'];
$ticket['totalPriceWithTax'] = $detail['productDetalle']['totalPriceWithTax'];
$ticket['usageDateRange'] = $detail['productDetalle']['usageDateRange'];
$price = $this->calculateTicketPrice($ticket['totalPriceWithTax'], $ticket['price']['decimalPlaces'], $markup, $trm);
switch ($detail['typePassenger']) {
case 'ADT':
$priceTotals['priceAdultTicket'] += $price * $ticket['quantity'];
break;
case 'CHD':
$priceTotals['priceChildTicket'] += $price * $ticket['quantity'];
break;
case 'ALL':
$priceTotals['priceAdultTicket'] += $price * (int) $searchParams['adults'];
$priceTotals['priceChildTicket'] += $price * (int) $searchParams['children'];
break;
}
});
return $priceTotals;
}
// Calcular el precio del ticket con markup en COP
private function calculateTicketPrice($priceTicket, $decimalPlacesTicket, $markupPark, $trm)
{
$priceTicket = $priceTicket * (1 / (100 - $markupPark)) * 100;
$priceTicket = round($priceTicket, (int) $decimalPlacesTicket); //Redondeando en USD
$priceTicket = $priceTicket * $trm;
return round($priceTicket, (int) $decimalPlacesTicket); //Redondeando en COP
}
// Proceso reserva, pago y facturación
public function processFullCheckoutAction(Request $request, \Swift_Mailer $mailer, TokenizerService $tokenizerService, CustomerMethodPaymentService $customerMethodPayment, TokenStorageInterface $tokenStorage, AviaturErrorHandler $aviaturErrorHandler, AviaturEncoder $aviaturEncoder, SessionInterface $session, ParameterBagInterface $parameterBag, OrderController $orderController, AviaturLogSave $aviaturLogSave, ManagerRegistry $registry, AviaturChangeCoin $aviaturChangeCoin)
{
if (empty($request->query->all()) && empty($request->request->all())) {
$titleError = "Acceso no autorizado";
$textError = "Ha sido redirigido al home para que pueda seguir explorando opciones.";
return $this->redirect($aviaturErrorHandler->errorRedirectNoEmail($this->generateUrl("aviatur_search_parks", []), $titleError, $textError));
}
$hostPark = $request->getHost();
$request = $request->request ?? $request->query;
$em = $this->em;
$session = $this->session;
$agency = $this->agency;
$checkoutInfo = $request->all();
// Obtener el transactionId
$transactionId = (string) $checkoutInfo["transactionIdCheckout"];
// Guardar intentos de compra
$aviaturPaymentRetryTimes = $parameterBag->get('aviatur_payment_retry_times');
$retryCount = $session->get($transactionId . '[park][retry]');
if($retryCount == null){
$session->set($transactionId . '[park][retry]', $aviaturPaymentRetryTimes);
}
// Validar si ya hay userInfo en la sesión para reintentos
if($session->get($transactionId . "[park][userInfo]") != null){
// Información de formularios de usuario
$formUserInfo = $this->em->getRepository(\Aviatur\GeneralBundle\Entity\FormUserInfo::class)->find($session->get($transactionId . '[park][userInfo]'));
$decodedResponseInfoUser = json_decode($aviaturEncoder->AviaturDecode($formUserInfo->getInfo(), $formUserInfo->getPublicKey()), true);
$decodedResponseInfoUser["PD"] = $checkoutInfo["PD"];
$checkoutInfo = $decodedResponseInfoUser;
}else{
$formUserInfo = new FormUserInfo();
}
// Token de la tarjeta, en caso que exista
$publicKey = $aviaturEncoder->aviaturRandomKey();
if (isset($checkoutInfo["PD"]["card_num"])) {
$postDataInfo = $checkoutInfo;
if (isset($postDataInfo["PD"]["cusPOptSelected"])) {
$customerLogin = $tokenStorage->getToken()->getUser();
$infoMethodPaymentByClient = $customerMethodPayment->getMethodsByCustomer($customerLogin, true);
$cardToken = $infoMethodPaymentByClient["info"][$postDataInfo["PD"]["cusPOptSelected"]]["token"];
$postDataInfo["PD"]["card_num"] = $cardToken;
} else {
$postDataInfo["PD"]["card_num"] = $tokenizerService->getToken($checkoutInfo["PD"]["card_num"]);
}
$checkoutInfo["PD"]["card_values"] = ["card_num_token" => $postDataInfo["PD"]["card_num"], "card_num" => $checkoutInfo["PD"]["card_num"]];
}
// Encodear y guardar la info de los formularios en la tabla form_user_info
$encodedInfo = $aviaturEncoder->AviaturEncode(json_encode($postDataInfo ?? $checkoutInfo), $publicKey);
$formUserInfo->setInfo($encodedInfo);
$formUserInfo->setPublicKey($publicKey);
$em->persist($formUserInfo);
$em->flush();
// Guardar en session el id de la tabla
$session->set($transactionId . "[park][userInfo]", $formUserInfo->getId());
// Obtener la información de los tickets desde la sesión
$sessionKey = $transactionId . "[park][ticketsInfo]";
$infoTickets = $this->getAvailabilityFromSession($sessionKey, $aviaturErrorHandler);
$filteredTickets = $infoTickets["filteredTickets"];
$searchParams = $infoTickets["searchParams"];
// Si no hay información de los tickets rediriguir a disponibilidad
if (empty($infoTickets) || !isset($filteredTickets)) {
$searchParams = $this->parseSearchParams(json_encode($searchParams));
$url = $this->generateUrl("aviatur_park_avaliability", $searchParams["model"]->getData());
return $this->json(["error" => true, "message" => $aviaturErrorHandler->errorRedirect($url, "Acceso no autorizado", "No encontramos información del detalle de tu búsqueda, por favor vuelve a intentarlo.")]);
}
// Información facturador
$billingData = $checkoutInfo["BD"];
$customer = $em->getRepository(\Aviatur\CustomerBundle\Entity\Customer::class)->find($billingData["id"]);
$ordersProduct = $em->getRepository(\Aviatur\GeneralBundle\Entity\OrderProduct::class)->getOrderProductsPending($customer);
if (null == $ordersProduct) {
$session->set($transactionId . '[park][detailDataPark]', json_encode($postDataInfo));
// Información pasajeros
$passangersData = $postDataInfo["PI"];
$passangerInfo = [];
for ($i = 1; $i <= $passangersData["person_count_1"]; ++$i) {
if ($i == 1) {
if (false !== strpos($passangersData["first_name_1_" . $i], '***')) {
$passangerInfo[$i]["first_name"] = $customer->getFirstname();
$passangerInfo[$i]["last_name"] = $customer->getLastname();
$passangerInfo[$i]["email"] = $customer->getEmail();
$passangerInfo[$i]["phone"] = $customer->getPhone();
$passangerInfo[$i]["passanger_type"] = $passangersData["passanger_type_1_" . $i];
continue;
} else {
$passangerInfo[$i]["email"] = $passangersData["email_1_" . $i];
$passangerInfo[$i]["phone"] = $passangersData["phone_1_" . $i] ?? $postDataInfo["CD"]["phone"];
}
}
$passangerInfo[$i]["first_name"] = mb_convert_case($passangersData["first_name_1_" . $i], MB_CASE_TITLE, "UTF-8");
$passangerInfo[$i]["last_name"] = mb_convert_case($passangersData["last_name_1_" . $i], MB_CASE_TITLE, "UTF-8");
$passangerInfo[$i]["passanger_type"] = $passangersData["passanger_type_1_" . $i];
}
// Reserva
$namePark = $searchParams["park"];
$productType = mb_strtolower($namePark);
if(true != $session->has($transactionId . '[' . $productType . '][order]')){
$bookingInfo = $this->bookingReservation($session, $mailer, $transactionId, $infoTickets, $passangerInfo, $aviaturLogSave);
$arrayBookingInfo = json_decode($bookingInfo);
}else{
// Obtener order y orderProduct
$orderInfo = json_decode($session->get($transactionId . '[' . $productType . '][order]'), true);
$productId = str_replace('PN', '', $orderInfo["products"]);
$orderProduct = $em->getRepository(\Aviatur\GeneralBundle\Entity\OrderProduct::class)->find($productId);
// obtener emailData y asignar valor de error
$arrayBookingInfo = json_decode($orderProduct->getEmail());
$arrayBookingInfo->error = !$arrayBookingInfo->bookingData->success;
}
if (!$arrayBookingInfo->error) {
$session->set($transactionId . '[park][prepayment_check]', true);
$bookingData = $arrayBookingInfo->bookingData;
$reservationId = (string) $bookingData->reservationIds->productResId;
// Guardar provedor en session para OrderController
$providerIdentifier = $infoTickets["availabilityParameters"]["providerIdentifier"];
$session->set($transactionId . '[' . $productType . '][provider]', $providerIdentifier);
$status = 'waiting';
$order = [];
if(true != $session->has($transactionId . '[' . $productType . '][order]')){
if (!isset($agency)) {
$titleError = "No se encontró la agencia con el dominio: " . $hostPark;
$textError = "Ha sido redirigido al home para que pueda seguir explorando opciones.";
return $this->redirect($aviaturErrorHandler->errorRedirectNoEmail($this->generateUrl("aviatur_search_parks", []), $titleError, $textError));
}
// Crear Order y order product, con add_productData
$productType = $em->getRepository(\Aviatur\MpaBundle\Entity\ProductType::class)->findByCode(strtoupper($namePark));
$order = $orderController->createAction($agency, $customer, $productType, $reservationId, $status);
$orderId = str_replace('ON', '', $order['order']);
$orderEntity = $em->getRepository(\Aviatur\GeneralBundle\Entity\Order::class)->find($orderId);
$formUserInfo = $em->getRepository(\Aviatur\GeneralBundle\Entity\FormUserInfo::class)->find($session->get($transactionId . '[park][userInfo]'));
$formUserInfo->setOrder($orderEntity);
$em->persist($formUserInfo);
$em->flush();
}
// Obtener orderProduct
$orderInfo = !empty($order) ? $order : json_decode($session->get($transactionId . '[' . $productType . '][order]'), true);
$productId = str_replace('PN', '', $orderInfo["products"]);
$orderProduct = $em->getRepository(\Aviatur\GeneralBundle\Entity\OrderProduct::class)->find($productId);
// Actualizar setEmissiondata
$orderProduct->setEmissiondata($reservationId);
$em->persist($orderProduct);
$em->flush();
// Eliminar "Adult" o "Child" del nombre del ticket para agruparlos
$baseName = preg_replace('/(Adult|Child|Ad |Ch )/', '', $filteredTickets[0]["productName"]);
$baseName = trim($baseName);
// Validación de la existencia del parque y sede
$availabilityParameters = $this->validParkAndSede($namePark, $searchParams['sede'], $aviaturErrorHandler);
// Arreglo RS del detalle
$searchParams = $this->parseSearchParams(json_encode($searchParams));
$arrayDetailParks = $this->getTicketDetails($filteredTickets, $searchParams, $availabilityParameters, $aviaturLogSave, $transactionId, $aviaturErrorHandler);
// Precios totalizados de los tickets
$priceTotaltickets = $this->calculatePriceTotal($filteredTickets, $arrayDetailParks, $searchParams, $aviaturChangeCoin);
// COLLECT INFORMATION TO PERSIST OrderProduct->Email
$this->generate_email($session, $registry, $parameterBag, $orderProduct, $infoTickets, $arrayBookingInfo, $aviaturErrorHandler, $baseName, $priceTotaltickets);
// Enviar correo de reserva exitosa con orden creada
$emailContent = 'Park reservation ' . $orderProduct->getEmissiondata() . ', the product id is: ' . $orderProduct->getId() . ', the customer id is: ' . $customer->getId() . '</b> Email customer new DB: <b>' . $customer->getEmail() . '</b>, the info is: ' . $orderProduct->getEmail();
$message = (new \Swift_Message())
->setContentType('text/html')
->setFrom('noreply@aviatur.com.co')
->setSubject('Park Reservation')
->setTo(['soptepagelectronic@aviatur.com', 'soportepagoelectronico@aviatur.com.co'])
->setBcc(['notificacionessitioweb@aviatur.com'])
->setBody($emailContent);
// Envio de correo
$mailer->send($message);
// Ir a pago
$orderInfo['url'] = $this->generateUrl('aviatur_park_payment_secure');
return $this->json($orderInfo);
} else {
// Datos de búsqueda
$searchParams = $this->parseSearchParams(json_encode($infoTickets['searchParams']));
// Mensaje codificado con estilos en HTML, debe ir centrado
$nameSedePark = (string) $searchParams["sede"];
$adults = (int) $searchParams["adults"];
$children = (int) $searchParams["children"];
$fechaFormateada = date("d/m/Y", strtotime($searchParams['datepark']));
$textoAdultos = $adults == 1 ? "1 adulto" : "$adults adultos";
$textoNinos = $children == 0 ? "" : ($children == 1 ? " y 1 niño" : " y $children niños");
$mensaje = "Hola! Estoy intentando hacer una reserva para " .
"$nameSedePark el $fechaFormateada, ($textoAdultos$textoNinos), " .
"pero algo falló al momento de procesar la reserva. Este es el ID de la transacción: " . $transactionId .
"\n ¿me podrías ayudar?";
$mensajeCodificado = rawurlencode($mensaje);
$linkWA = "https://api.whatsapp.com/send?phone=5713821616&text=$mensajeCodificado";
$urlRedirect = $this->generateUrl('aviatur_park_avaliability', $searchParams['model']->getData());
$return = json_encode(['error' => 'noReservation', 'product' => 'ticket', 'message' => $transactionId, 'link_wa' => $linkWA, 'url_redirect' => $urlRedirect]);
return new Response($return);
}
} else {
$booking = [];
$cus = [];
foreach ($ordersProduct as $orderProduct) {
$productResponse = $aviaturEncoder->AviaturDecode($orderProduct->getPayResponse(), $orderProduct->getPublicKey());
$paymentResponse = json_decode($productResponse);
array_push($booking, "ON" . $orderProduct->getOrder()->getId() . "-PN" . $orderProduct->getId());
if (isset($paymentResponse->x_approval_code)) {
array_push($cus, $paymentResponse->x_approval_code);
} elseif (isset($paymentResponse->createTransactionResult->trazabilityCode)) {
array_push($cus, $paymentResponse->createTransactionResult->trazabilityCode);
}
}
$return = [
"error" => "pending_payments",
"message" => "pending_payments",
"booking" => $booking,
"domain" => $domain ?? null,
"agencyId" => $this->agency ?? null,
"operatorId" => $operatorId ?? null,
"cus" => $cus,
];
return new JsonResponse($return);
}
}
// Consumo de reserva
private function bookingReservation(SessionInterface $session, \Swift_Mailer $mailer, $transactionId, $infoTickets, array $passangerInfo, AviaturLogSave $logSave)
{
// Combinando respuesta de dispo con detalle
$productCatalog = $this->combineDispoWithDetails($infoTickets["filteredTickets"], $passangerInfo);
$contactPerson = [
'firstName' => $passangerInfo[1]['first_name'],
'lastName' => $passangerInfo[1]['last_name'],
'phone' => $passangerInfo[1]['phone'],
'email' => $passangerInfo[1]['email'],
];
$bookingParkRQ = new ParkBookingModel(
$infoTickets['availabilityParameters']['requestKind'],
$contactPerson,
$productCatalog,
$infoTickets['availabilityParameters']['environmentCode']
);
$logSave->logSave(json_encode($bookingParkRQ->toArray()), "ParkBooking", $infoTickets["availabilityParameters"]["providerIdentifier"] . "_RQ", $transactionId);
$response = $this->aviaturRestService->callRestWebService($infoTickets['availabilityParameters']['wsUrl'], "book", $bookingParkRQ->toArray());
$logSave->logSave(json_encode($response), "ParkBooking", "RS", $transactionId);
$bookingSucceeded = true;
$reservation = $response["reservation"];
$bookingData = [];
if ($response["succeeded"] && !empty($reservation)) {
$bookingData = $reservation[0];
$bookingData["contactPerson"] = $contactPerson;
$bookingSucceeded = false;
// Actualizar info del ticket combinando dispo con detalle
$infoTickets["filteredTickets"] = $productCatalog;
$session->set($transactionId . '[park][ticketsInfo]', base64_encode(json_encode($infoTickets)));
};
// Enviar correo de reserva exitosa
if(!$bookingSucceeded){
// Información agencia
$agency = $this->agency;
$agencyData = [
"agency_name" => $agency->getName(),
"agency_nit" => $agency->getNit(),
"agency_phone" => $agency->getPhone(),
"agency_email" => $agency->getMailContact(),
];
$setTo = ['union@udr.com'];
$emailSubject = 'Park Reservation' ;
$emailContent = '
<b>Reserva exitosa</b><br/>
Info:<br/>
The agency is: '.json_encode($agencyData).'<br/></b>
The reservation ids is: '.json_encode($bookingData["reservationIds"]).'<br/></b>
The contact customer is: '.json_encode($contactPerson).'<br/></b>
The info is: '.json_encode($productCatalog);
$message = (new \Swift_Message())
->setContentType('text/html')
->setFrom('noreply@aviatur.com.co')
->setSubject($emailSubject)
->setTo($setTo)
->setBody($emailContent);
// Envio de correo
$mailer->send($message);
}
$result = [
"bookingData" => $bookingData,
"error" => $bookingSucceeded,
"message" => [
"title" => $bookingSucceeded ? "No se pudo completar la reserva." : "Operación exitosa",
"text" => $bookingSucceeded ? "Hubo un problema al procesar tu solicitud. Por favor, intenta nuevamente." : ""
]
];
return json_encode($result, JSON_UNESCAPED_SLASHES);
}
// Combinando dispo con detalle
private function combineDispoWithDetails($productCatalog, $passangerInfo)
{
foreach ($productCatalog as $keyTicket => $valueTicket) {
$passengerDetail = $this->getTicketPassengerInfo($valueTicket, []);
$typePassenger = $passengerDetail["typePassenger"];
if ($typePassenger === "ALL") {
// Si el tipo de pasajero es "ALL", incluir todos los pasajeros
$guestNames = $passangerInfo;
} else {
// Filtrar array de acuerdo al tipo de pasajero específico
$guestNames = array_filter($passangerInfo, fn($p) => $p["passanger_type"] === $typePassenger);
}
// Mapear guestNames y reindexar desde 0
$productCatalog[$keyTicket]["byTicketGuestNames"] = array_values(array_map(
fn($p) => ["first" => $p["first_name"], "last" => $p["last_name"]],
$guestNames
));
if (empty($valueTicket["usageDateRange"])) {
$productCatalog[$keyTicket]["usageDateRange"] = [
"startDate" => "string",
"endDate" => "string"
];
}
if (empty($valueTicket["detailTicket"])) {
$productCatalog[$keyTicket]["detailTicket"] = [
"usosTicket" => $valueTicket["aditionalData"]
];
// Al agregarlo en usosTicket se elimina el campo aditionalData
unset($productCatalog[$keyTicket]["aditionalData"]);
}
$productCatalog[$keyTicket]["quantity"] = (string) $productCatalog[$keyTicket]["quantity"];
$productCatalog[$keyTicket]["totalPriceWithTax"] = (string) $productCatalog[$keyTicket]["totalPriceWithTax"];
}
return $productCatalog;
}
// Funcion para guardar en email BD
public function generate_email(SessionInterface $session, ManagerRegistry $registry, ParameterBagInterface $parameterBag, $orderProduct, $infoTickets, $prepaymentInfo, aviaturErrorHandler $aviaturErrorHandler, $baseName, $priceTotaltickets)
{
$transactionIdSessionName = $parameterBag->get("transaction_id_session_name");
$transactionId = $session->get($transactionIdSessionName);
// Información agencia
$em = $registry->getManager();
$agency = $em->getRepository(\Aviatur\AgencyBundle\Entity\Agency::class)->find($session->get("agencyId"));
$agencyData = [
"agency_name" => $agency->getName(),
"agency_nit" => $agency->getNit(),
"agency_phone" => $agency->getPhone(),
"agency_email" => $agency->getMailContact(),
];
// Cantidad de intentos
$retryCount = (int) $session->get($transactionId . "[park][retry]");
// Información tickets
$filteredTickets = $infoTickets["filteredTickets"];
$searchParams = $infoTickets["searchParams"];
// Información formularios
$postDataJson = json_decode($session->get($transactionId . "[park][detailDataPark]"));
// Información pasajeros
$passangersData = json_decode(json_encode($postDataJson->PI), true);
$passangerInfo = [];
for ($i = 1; $i <= $passangersData["person_count_1"]; ++$i) {
$passangerInfo[$i]["first_name"] = mb_convert_case($passangersData["first_name_1_" . $i], MB_CASE_TITLE, "UTF-8");
$passangerInfo[$i]["last_name"] = mb_convert_case($passangersData["last_name_1_" . $i], MB_CASE_TITLE, "UTF-8");
$passangerInfo[$i]["passanger_type"] = $passangersData["passanger_type_1_" . $i];
}
// Politicas de cancelacion?
$cancelPolicies = (string) "";
// Información journey
$startDate = new \DateTime($searchParams["datepark"]);
$endDate = clone $startDate;
$endDate->modify("+1 day");
$journeySummary = [
"parkName" => $searchParams["park"],
"sedeParkName" => $searchParams["sede"],
"ticketName" => $baseName,
"ticketDays" => $filteredTickets[0]["numberOfDays"],
"startDate" => $searchParams["datepark"],
"endDate" => $endDate->format("Y-m-d"),
"ticketPricing" => [
"adults" => [
"quantity" => $searchParams["adults"],
"price" => $priceTotaltickets["priceAdultTicket"],
"currency" => $priceTotaltickets["currency"]
],
"children" => [
"quantity" => $searchParams["children"],
"price" => $priceTotaltickets["priceChildTicket"],
"currency" => $priceTotaltickets["currency"]
]
],
"cancelPolicies" => $cancelPolicies
];
// Información del facturador
$factInfo = $postDataJson->BD;
$facturationResume = [
"customer_names" => $factInfo->first_name . " " . $factInfo->last_name,
"customer_doc_num" => $factInfo->doc_num,
"customer_phone" => $factInfo->phone,
"customer_email" => $factInfo->email
];
// Información de medio de pago
$paymentData = $postDataJson->PD;
$description = 'Parques - ' . $searchParams["sede"] . ': ' . $baseName;
$paymentResume = [
'total_amount' => $priceTotaltickets["priceAdultTicket"] + $priceTotaltickets["priceChildTicket"],
'currency' => $priceTotaltickets["currency"],
'ip_address' => $this->get_client_ip(),
'bank_name' => '',
'franquice' => $paymentData->franquise,
'cuotas' => $paymentData->differed,
'card_num' => "************" . substr($paymentData->card_num, strlen($paymentData->card_num) - 4),
'reference' => '',
'auth' => '',
'transaction_date' => $orderProduct->getCreationDate()->format('Y-m-d H:i:s'),
'description' => $description,
'reason_code' => '',
'reason_description' => 'Reserva rechazada.',
'client_names' => $paymentData->first_name . ' ' . $paymentData->last_name
];
// Información de reserva si ya existe
$bookingData = [];
if (!empty($prepaymentInfo->bookingData)) {
$bookingData = [
"success" => $prepaymentInfo->bookingData->success,
"reservationIds" => $prepaymentInfo->bookingData->reservationIds,
"contactPerson" => $prepaymentInfo->bookingData->contactPerson,
"createdTicketResponses" => $prepaymentInfo->bookingData->createdTicketResponses
];
$filteredTickets = $this->combineDispoWithDetails($filteredTickets, $passangerInfo);
}
// agent
$responseOrder = null;
if ($session->has($transactionId . '[user]')) {
$responseOrder = \simplexml_load_string($session->get($transactionId . '[user]'));
}
$emailData = [
'agencyData' => $agencyData,
'retry_count' => $retryCount,
'facturationResume' => $facturationResume,
'journeySummary' => $journeySummary,
'paymentResume' => $paymentResume,
'bookingData' => $bookingData,
'transactionID' => $transactionId,
'tickets' => $filteredTickets,
'passangers' => $passangerInfo,
'agent' => $responseOrder
];
$orderProduct->setEmail(json_encode($emailData));
$em->persist($orderProduct);
$em->flush();
}
// Obtener ip del cliente
final private function get_client_ip()
{
$ipaddress = '';
if (getenv('HTTP_CLIENT_IP')) {
$ipaddress = getenv('HTTP_CLIENT_IP');
} elseif (getenv('HTTP_X_FORWARDED_FOR')) {
$ipaddress = getenv('HTTP_X_FORWARDED_FOR');
} elseif (getenv('HTTP_X_FORWARDED')) {
$ipaddress = getenv('HTTP_X_FORWARDED');
} elseif (getenv('HTTP_FORWARDED_FOR')) {
$ipaddress = getenv('HTTP_FORWARDED_FOR');
} elseif (getenv('HTTP_FORWARDED')) {
$ipaddress = getenv('HTTP_FORWARDED');
} elseif (getenv('REMOTE_ADDR')) {
$ipaddress = getenv('REMOTE_ADDR');
} else {
$ipaddress = 'UNKNOWN';
}
return (strpos($ipaddress, ',') !== false) ? trim(explode(',', $ipaddress)[0]) : (substr_count($ipaddress, ':') > 1 ? $ipaddress : explode(':', $ipaddress)[0]);
}
// Funcion de pago
public function paymentAction(\Swift_Mailer $mailer, P2PController $p2pPaymentController, AviaturErrorHandler $aviaturErrorHandler, SessionInterface $session, ParameterBagInterface $parameterBag, TokenizerService $tokenizerService, CustomerMethodPaymentService $customerMethodPayment, AviaturLogSave $aviaturLogSave, AviaturChangeCoin $aviaturChangeCoin)
{
$em = $this->em;
$session = $this->session;
// Iniciación de variables
$orderProduct = [];
$paymentResponse = null;
$array = [];
// Obtener el transactionId
$transactionIdSessionName = $parameterBag->get('transaction_id_session_name');
$transactionId = $session->get($transactionIdSessionName);
// Obtener la información de los tickets desde la sesión
$sessionKey = $transactionId . "[park][ticketsInfo]";
$infoTickets = $this->getAvailabilityFromSession($sessionKey, $aviaturErrorHandler);
$filteredTickets = $infoTickets["filteredTickets"];
$searchParams = $infoTickets["searchParams"];
$namePark = (string) $searchParams["park"];
$productType = mb_strtolower($namePark);
$nameParkSede = (string) $searchParams["sede"];
// Obtener información de los formularios del checkOut
$postData = json_decode($session->get($transactionId . '[park][detailDataPark]'));
$paymentData = $postData->PD;
// Eliminar "Adult" o "Child" del nombre del ticket para generalizarlo
$baseName = preg_replace('/(Adult|Child|Ad |Ch )/', '', $filteredTickets[0]["productName"]);
$baseName = trim($baseName);
// Validación de la existencia del parque y sede
$availabilityParameters = $this->validParkAndSede($searchParams['park'], $searchParams['sede'], $aviaturErrorHandler);
// Arreglo RS del detalle
$searchParams = $this->parseSearchParams(json_encode($searchParams));
$arrayDetailParks = $this->getTicketDetails($filteredTickets, $searchParams, $availabilityParameters, $aviaturLogSave, $transactionId, $aviaturErrorHandler);
// Precios totalizados de los tickets
$priceTotaltickets = $this->calculatePriceTotal($filteredTickets, $arrayDetailParks, $searchParams, $aviaturChangeCoin);
$totalAmountCOP = $priceTotaltickets["priceAdultTicket"] + $priceTotaltickets["priceChildTicket"];
// IdOrder y IdOrderProduct
$orderInfo = json_decode($session->get($transactionId . '[' . $productType . '][order]'));
$productId = str_replace('PN', '', $orderInfo->products);
$orderProduct = $em->getRepository(\Aviatur\GeneralBundle\Entity\OrderProduct::class)->find($productId);
// Actualizar reintentos de pago
$retryCount = (int) $session->get($transactionId . '[park][retry]');
$session->set($transactionId . '[park][retry]', $retryCount - 1);
$arrayEmail = json_decode($orderProduct->getEmail(), true);
if (isset($arrayEmail["retry_count"])) {
$arrayEmail["retry_count"] = $retryCount - 1;
$orderProduct->setEmail(json_encode($arrayEmail));
$em->persist($orderProduct);
$em->flush();
}
// Pago p2p
if ('p2p' == $paymentData->type) {
$customer = $em->getRepository(\Aviatur\CustomerBundle\Entity\Customer::class)->find($postData->BD->id);
if (false !== strpos($paymentData->address, '***')) {
$paymentData->address = $customer->getAddress();
$paymentData->phone = $customer->getPhone();
$paymentData->email = $customer->getEmail();
}
$description = 'Parques - ' . $nameParkSede . ': ' . $baseName;
$array = [
'x_currency_code' => (string)"COP",
'x_amount' => $totalAmountCOP,
'x_tax' => number_format(0, 2, '.', ''),
'x_amount_base' => number_format($totalAmountCOP, 2, '.', ''),
'x_invoice_num' => $orderInfo->order . '-' . $orderInfo->products,
'x_first_name' => $paymentData->first_name,
'x_last_name' => $paymentData->last_name,
'x_description' => $description,
'x_city' => $customer->getCity()->getIatacode(),
'x_country_id' => $customer->getCountry()->getIatacode(),
'x_cust_id' => $paymentData->doc_type . ' ' . $paymentData->doc_num,
'x_address' => $paymentData->address,
'x_phone' => $paymentData->phone,
'x_email' => $paymentData->email,
'x_card_num' => $paymentData->card_num,
'x_exp_date' => $paymentData->exp_month . $paymentData->exp_year,
'x_card_code' => $paymentData->card_code,
'x_differed' => $paymentData->differed,
'x_client_id' => $postData->BD->id,
'product_type' => $productType,
'productId' => str_replace('PN', '', $orderInfo->products),
'orderId' => str_replace('ON', '', $orderInfo->order),
'franchise' => $paymentData->franquise,
'worldpay_validate' => false,
];
if (isset($paymentData->card_values)) {
$array['card_values'] = (array) $paymentData->card_values;
}
// Consumo de pago
$paymentResponse = $p2pPaymentController->placetopayAction($parameterBag, $tokenizerService, $customerMethodPayment, $mailer, $aviaturLogSave, $array, $combination = false, $segment = null, false);
if (empty($paymentResponse)) {
// No hay respuesta por parte del servicio de pago, estado pendiente
$orderProduct->setStatus('pending');
$em->persist($orderProduct);
$em->flush();
$urlResume = $this->generateUrl('aviatur_park_payment_pending_secure');
return $this->redirect($urlResume);
}
return $this->redirect($this->generateUrl('aviatur_park_payment_p2p_secure', [], true));
} else {
// Cambiar estado de pago
$orderProduct->setStatus('reject');
$em->persist($orderProduct);
$em->flush();
// Cancelar Reserva
$this->cancelBookingParkService->cancelReservation($orderInfo->order . '-' . $orderInfo->products, true);
// Redireccionar a dispo
$url = $this->generateUrl('aviatur_park_avaliability', $searchParams['model']->getData());
$titleError = "El tipo de pago es inválido.";
$textError = "Ha sido redirigido a la búsqueda para que pueda seguir explorando opciones.";
return $this->redirect($aviaturErrorHandler->errorRedirectNoEmail($url, $titleError, $textError));
}
}
// Control de estados de pago
public function p2pCallbackAction(OrderController $orderController, ValidateSanctionsRenewal $validateSanctions, TokenStorageInterface $tokenStorage, CustomerMethodPaymentService $customerMethodPayment, AviaturMailer $aviaturMailer, AviaturEncoder $aviaturEncoder, AviaturErrorHandler $aviaturErrorHandler, ParameterBagInterface $parameterBag)
{
$transactionIdSessionName = $parameterBag->get('transaction_id_session_name');
$em = $this->em;
$session = $this->session;
// Obtener la información de los tickets desde la sesión
$transactionId = $session->get($transactionIdSessionName);
$sessionKey = $transactionId . "[park][ticketsInfo]";
$infoTickets = $this->getAvailabilityFromSession($sessionKey, $aviaturErrorHandler);
$searchParams = $infoTickets["searchParams"];
$namePark = (string) $searchParams["park"];
$productType = mb_strtolower($namePark);
$orderProductCode = $session->get($transactionId . '[' . $productType . '][order]');
$productId = str_replace('PN', '', json_decode($orderProductCode)->products);
$orderProduct = $em->getRepository(\Aviatur\GeneralBundle\Entity\OrderProduct::class)->find($productId);
// Traer información de pago
$decodedRequest = json_decode($aviaturEncoder->AviaturDecode($orderProduct->getPayrequest(), $orderProduct->getPublicKey()));
$decodedResponse = json_decode($aviaturEncoder->AviaturDecode($orderProduct->getPayresponse(), $orderProduct->getPublicKey()));
if (null != $decodedResponse) {
$twig = '';
$jsonSendEmail = $em->getRepository(\Aviatur\GeneralBundle\Entity\Parameter::class)->findOneByName('send_email');
if (!empty($jsonSendEmail->getDescription()) && isset(json_decode($jsonSendEmail->getDescription())->email)) {
$email = json_decode($jsonSendEmail->getDescription())->email->CallBack;
}
$reference = str_replace('{"order":"', '', $orderProductCode);
$reference = str_replace('","products":"', '-', $reference);
$reference = str_replace('"}', '', $reference);
$references = $reference;
$bookings = $orderProduct->getBooking();
// Cantidad de intentos de pago:
$aviaturPaymentRetryTimes = $parameterBag->get('aviatur_payment_retry_times');
$retryCount = (int) $session->get($transactionId . "[park][retry]");
switch ($decodedResponse->x_response_code) {
case 0: // error p2p
case 4: // error p2p
$twig = 'aviatur_park_payment_error_secure';
if (isset($email)) {
$from = $session->get('emailNoReply');
$error = $twig;
$subject = $orderProduct->getDescription() . ':Error en el proceso de pago de Aviatur';
$body = '</br>El proceso de pago a retornado un error </br>Referencia: ' . $references . '</br>Reserva:' . $bookings;
$aviaturMailer->sendEmailGeneral($from, $email, $subject, $body);
}
case 1: // aprobado p2p
$postData = json_decode($session->get($transactionId . '[park][detailDataPark]'));
if (isset($postData->PD->savePaymProc)) {
$customerLogin = $tokenStorage->getToken()->getUser();
$customerMethodPayment->setMethodsByCustomer($customerLogin, json_decode(json_encode($postData), true) && 'NOTVERIFIED' == $postData->PD->cusPOptSelectedStatus);
}
if (isset($postData->PD->cusPOptSelected) && isset($postData->PD->cusPOptSelectedStatus)) {
$postData->PD->cusPOptSelectedStatus = 'ACTIVE';
$customerLogin = $tokenStorage->getToken()->getUser();
$customerMethodPayment->setMethodsByCustomer($customerLogin, json_decode(json_encode($postData), true));
}
// Obtener la información de los tickets desde la sesión
$sessionKey = $transactionId . "[park][ticketsInfo]";
$infoTickets = $this->getAvailabilityFromSession($sessionKey, $aviaturErrorHandler);
$searchParams = $infoTickets["searchParams"];
$namePark = (string) $searchParams["park"];
$productType = mb_strtolower($namePark);
$decodedRequest->product_type = $productType;
$decodedResponse->product_type = $productType;
$encodedRequest = $aviaturEncoder->AviaturEncode(json_encode($decodedRequest), $orderProduct->getPublicKey());
$encodedResponse = $aviaturEncoder->AviaturEncode(json_encode($decodedResponse), $orderProduct->getPublicKey());
$orderProduct->setPayrequest($encodedRequest);
$orderProduct->setPayresponse($encodedResponse);
$twig = 'aviatur_park_payment_success_secure';
$orderController->updatePaymentAction($orderProduct, false, null);
break;
case 2: // rechazada p2p
$twig = '' != $twig ? $twig : 'aviatur_park_payment_rejected_secure';
$emissionData = 'No Reservation';
if (isset($email)) {
$from = $session->get('emailNoReply');
$error = $twig;
$subject = $orderProduct->getDescription() . ':Transacción rechazada';
$body = '</br>El pago fue rechazado </br>Referencia: ' . $references . '</br>Reserva:' . $bookings;
$aviaturMailer->sendEmailGeneral($from, $email, $subject, $body);
}
break;
case 3: // pendiente p2p
$twig = '' != $twig ? $twig : 'aviatur_park_payment_pending_secure';
$emissionData = 'No Reservation';
$orderProduct->setEmissiondata(json_encode($emissionData));
$orderProduct->setUpdatingdate(new \DateTime());
$em->persist($orderProduct);
$em->flush();
$retryCount = 1;
break;
case isset($decodedResponse->x_response_code_cyber) && (2 == $decodedResponse->x_response_code_cyber): //rechazado cybersource
//rechazado cybersource
$parameters = $em->getRepository(\Aviatur\GeneralBundle\Entity\Parameter::class)->findOneByName('aviatur_switch_rechazada_cyber');
if ($parameters && 1 == $parameters->getValue() && 1 == $decodedResponse->x_response_code) {
$postData = json_decode($session->get($transactionId . '[park][detailDataPark]'));
if (isset($postData->PD->savePaymProc)) {
$customerLogin = $tokenStorage->getToken()->getUser();
$customerMethodPayment->setMethodsByCustomer($customerLogin, json_decode(json_encode($postData), true));
}
if (isset($postData->PD->cusPOptSelected) && isset($postData->PD->cusPOptSelectedStatus) && 'NOTVERIFIED' == $postData->PD->cusPOptSelectedStatus) {
$postData->PD->cusPOptSelectedStatus = 'ACTIVE';
$customerLogin = $tokenStorage->getToken()->getUser();
$customerMethodPayment->setMethodsByCustomer($customerLogin, json_decode(json_encode($postData), true));
}
}
$twig = 'aviatur_park_payment_rejected_secure';
}
// Update Payment
$orderController->updatePaymentAction($orderProduct, false, null);
$orderUpdatePayment = str_replace(
['{web_book_id}', '{booking_id}'],
['PN' . $orderProduct->getId(), $orderProduct->getBooking()],
$orderProduct->getUpdatePaymentData()
);
$orderProduct->setUpdatePaymentData($orderUpdatePayment);
$em->persist($orderProduct);
$em->flush($orderProduct);
/* Pero solo si hay condicionados (no bloqueados) y que paguen con Tarjeta */
if ($session->has('Marked_users')) {
$validateSanctions->sendMarkedEmail($orderProductCode, $session, $this->agency, $orderProduct, $transactionId, $productType);
}
$urlResume = $this->generateUrl($twig);
return $this->redirect($urlResume);
} else {
// No hay respuesta por parte del servicio de pago, estado pendiente
$orderProduct->setStatus('pending');
$em->persist($orderProduct);
$em->flush();
$urlResume = $this->generateUrl('aviatur_park_payment_pending_secure');
return $this->redirect($urlResume);
}
}
public function paymentOutputAction(Request $request, \Swift_Mailer $mailer, aviaturErrorHandler $aviaturErrorHandler, AviaturEncoder $aviaturEncoder, TwigFolder $twigFolder, ParameterBagInterface $parameterBag, AviaturQrcodeService $Qrcode, Pdf $pdf)
{
$em = $this->em;
$transactionIdSessionName = $parameterBag->get('transaction_id_session_name');
$session = $this->session;
$transactionId = $session->get($transactionIdSessionName);
// Información tickets
$sessionKey = $transactionId . "[park][ticketsInfo]";
$infoTickets = $this->getAvailabilityFromSession($sessionKey, $aviaturErrorHandler);
// Información formularios
$postDataJson = json_decode($session->get($transactionId . "[park][detailDataPark]"));
$customer = $em->getRepository(\Aviatur\CustomerBundle\Entity\Customer::class)->find($postDataJson->BD->id);
// Obtener IdOrder
$searchParams = $infoTickets["searchParams"];
$namePark = (string) $searchParams["park"];
$productType = mb_strtolower($namePark);
$orderProductCode = $session->get($transactionId . '[' . $productType . '][order]');
// Obtener order_product
$productId = str_replace('PN', '', json_decode($orderProductCode)->products);
$orderProduct = $em->getRepository(\Aviatur\GeneralBundle\Entity\OrderProduct::class)->find($productId);
$emailData = json_decode($orderProduct->getEmail(), true);
// RQ Y RS de Pago
$opRequest = json_decode($aviaturEncoder->AviaturDecode($orderProduct->getPayrequest(), $orderProduct->getPublicKey()));
$opResponse = json_decode($aviaturEncoder->AviaturDecode($orderProduct->getPayResponse(), $orderProduct->getPublicKey()));
if ((null != $opRequest) && (null != $opResponse) && isset($opResponse->x_description)) {
$emailData["paymentResume"] = [
'transaction_state' => $opResponse->x_response_code,
'ta_transaction_state' => $opResponse->x_ta_response_code,
'transaction_state_cyber' => $opResponse->x_response_code_cyber ?? '1',
'id' => $orderProduct->getBooking(),
'id_context' => $opRequest->x_invoice_num,
'total_amount' => $opResponse->x_amount,
'currency' => $opResponse->x_bank_currency,
'amount' => 0 != $opRequest->x_amount_base ? $opRequest->x_amount_base : $opResponse->x_amount,
'iva' => $opRequest->x_tax,
'ip_address' => $opRequest->x_customer_ip,
'bank_name' => $opResponse->x_bank_name,
'franquice' => $opResponse->x_franchise,
'cuotas' => $opRequest->x_differed,
'card_num' => '************' . substr($opRequest->x_card_num, strlen($opRequest->x_card_num) - 4),
'reference' => $opResponse->x_transaction_id,
'auth' => $opResponse->x_approval_code,
'transaction_date' => $opResponse->x_transaction_date,
'description' => $opResponse->x_description,
'reason_code' => $opResponse->x_response_reason_code,
'reason_description' => $opResponse->x_response_reason_text,
'client_names' => $opResponse->x_first_name . ' ' . $opResponse->x_last_name,
'client_email' => $opResponse->x_email
];
} else {
$customer = null;
$emailData["paymentResume"]['id'] = $orderProduct->getBooking();
$emailData["paymentResume"]['transaction_state_cyber'] = '1';
$emailData["paymentResume"]['transaction_state'] = '2';
}
// Si existe reserva y pago
if (!empty($emailData["bookingData"]) && 1 == $emailData["paymentResume"]['transaction_state']) {
// Información de reserva (contactPerson y idReserva)
$bookingResume['firstName'] = $emailData["bookingData"]["contactPerson"]["firstName"];
$bookingResume['lastName'] = $emailData["bookingData"]["contactPerson"]["lastName"];
$bookingResume['ReservationID'] = (string) $emailData["bookingData"]["reservationIds"]["productResId"];
$bookingResume['ParkReservationID'] = "";
// Información del facturador
$emailData["facturationResume"] = [
'customer_names' => $customer->getFirstname() . ' ' . $customer->getLastname(),
'customer_doc_num' => $customer->getDocumentnumber(),
'customer_phone' => $customer->getPhone(),
'customer_email' => $customer->getEmail(),
];
// Pasajero 1 igual al facturador
if (false !== strpos($emailData["passangers"][1]["first_name"], '***')) {
// Actualizando información pasajeros
$emailData["passangers"][1] = [
"first_name" => $customer->getFirstname(),
"last_name" => $customer->getLastname(),
"passanger_type" => $emailData["passangers"][1]["passanger_type"]
];
// Actualizando información de reserva
$bookingResume['firstName'] = $customer->getFirstname();
$bookingResume['lastName'] = $customer->getLastname();
}
}
$emailData += [
'currency' => isset($emailData["paymentResume"]['currency']) ? $emailData["paymentResume"]['currency'] : '',
'bookingResume' => $bookingResume ?? [],
];
//validacion de parametros vacios usando en twig
$emailData["paymentResume"]["reason_description"] = isset($emailData["paymentResume"]["reason_description"]) ? $emailData["paymentResume"]["reason_description"] : "";
$emailData["paymentResume"]["auth"] = isset($emailData["paymentResume"]["auth"]) ? $emailData["paymentResume"]["auth"] : "";
$emailData["paymentResume"]["reason_code"] = isset($emailData["paymentResume"]["reason_code"]) ? $emailData["paymentResume"]["reason_code"] : "";
$emailData["paymentResume"]["reference"] = isset($emailData["paymentResume"]["reference"]) ? $emailData["paymentResume"]["reference"] : "";
$emailData["paymentResume"]["total_amount"] = isset($emailData["paymentResume"]["total_amount"]) ? $emailData["paymentResume"]["total_amount"] : "";
$emailData["paymentResume"]["currency"] = isset($emailData["paymentResume"]["currency"]) ? $emailData["paymentResume"]["currency"] : "";
$orderProduct->setEmail(json_encode($emailData));
$em->persist($orderProduct);
$em->flush();
//Envio de correo
$orderInfo = json_decode($orderProductCode);
if($orderProduct->getStatus() == 'approved'){
$ordersIdsString = $orderInfo->order . '-' . $orderInfo->products;
$sendEmailParkService = $this->sendVoucherEmailParkService->sendEmailParkService($ordersIdsString);
if($sendEmailParkService["error"]){
//Enviar correo en fallo de envio de voucher
$emailContent = '
<b>No se pudo realizar el envio del voucher.</b><br/>
Info:<br/>
The booking id is: ' . $orderProduct->getBooking() . '<br/>
The product id is: ' . $orderProduct->getId() . '<br/>
The reference id is: ' . $ordersIdsString . '<br/>
The customer id is: ' . $customer->getId() . '<br/></b>
Email customer DB: <b>' . $customer->getEmail() . '</b>,<br/>
The message is: ' . $sendEmailParkService["message"]["title"] . " - " . $sendEmailParkService["message"]["text"]
;
$message = (new \Swift_Message())
->setContentType('text/html')
->setFrom('noreply@aviatur.com.co')
->setSubject('Park reservation approved with payment - Voucher NOT attached')
->setTo(['soptepagelectronic@aviatur.com', 'soportepagoelectronico@aviatur.com.co'])
->setBcc(['notificacionessitioweb@aviatur.com'])
->setBody($emailContent)
;
$mailer->send($message);
}
} else {
//Enviar correo de reserva exitosa, pago no exitoso
$emailContent = '
<b>Se realizó la reserva del parque, pero no tiene pago aprobado.</b><br/>
Info:<br/>
The booking id is: ' . $orderProduct->getBooking() . '<br/>
The product id is: ' . $orderProduct->getId() . '<br/>
The customer id is: ' . $customer->getId() . '<br/></b>
Email customer DB: <b>' . $customer->getEmail() . '</b>,<br/>
The info is: ' . $orderProduct->getEmail()
;
$message = (new \Swift_Message())
->setContentType('text/html')
->setFrom('noreply@aviatur.com.co')
->setSubject('Park reservation aprroved with payment ' . $orderProduct->getStatus())
->setTo(['soptepagelectronic@aviatur.com', 'soportepagoelectronico@aviatur.com.co'])
->setBcc(['notificacionessitioweb@aviatur.com'])
->setBody($emailContent)
;
$mailer->send($message);
}
// En caso de que exista pago y sea aprobado, limpiar session con el transaction
if (isset($emailData["paymentResume"]['transaction_state']) && 1 == $emailData["paymentResume"]['transaction_state']) {
$session->remove($transactionId . '[park][retry]');
$session->remove($transactionId . '[park][prepayment_check]');
$session->remove($transactionId . '[park][ticketsAvailability]');
$session->remove($transactionId . '[park][userInfo]');
$session->remove($transactionId . '[park][detailDataPark]');
$session->remove($transactionId . '[' . $productType . '][provider]');
// $session->remove($transactionId . '[' . $productType . '][order]');
// Eliminar los archivos de códigos QR's generados
if (isset($sendEmailParkService["qrcodespath"]) && is_array($sendEmailParkService["qrcodespath"])) {
foreach ($sendEmailParkService["qrcodespath"] as $path) {
if (file_exists($path)) {
unlink($path);
}
}
}
}
$urlResume = $this->generateUrl('thank_you_page_park_secure');
return $this->redirect($urlResume);
}
// vista de gracias por tu compra
public function thankYouPageAction(TwigFolder $twigFolder, ParameterBagInterface $parameterBag, AviaturErrorHandler $aviaturErrorHandler)
{
$em = $this->em;
$session = $this->session;
$transactionIdSessionName = $parameterBag->get('transaction_id_session_name');
$transactionId = $session->get($transactionIdSessionName);
// Información tickets
$sessionKey = $transactionId . "[park][ticketsInfo]";
$infoTickets = $this->getAvailabilityFromSession($sessionKey, $aviaturErrorHandler);
// Obtener IdOrder
$searchParams = $infoTickets["searchParams"];
$namePark = (string) $searchParams["park"];
$productType = mb_strtolower($namePark);
$orderProductCode = $session->get($transactionId . '[' . $productType . '][order]');
// Obtener order_product
$productId = str_replace('PN', '', json_decode($orderProductCode)->products);
$orderProduct = $em->getRepository(\Aviatur\GeneralBundle\Entity\OrderProduct::class)->find($productId);
$emailData = json_decode($orderProduct->getEmail(), true);
$agencyFolder = $twigFolder->twigFlux();
$viewParams = $this->sendVoucherEmailParkService->formatTransactionData($productId);
$viewParams = json_decode($viewParams, true);
$orderProduct->setResume(json_encode($viewParams));
$em->persist($orderProduct);
$em->flush();
$view = "@AviaturTwig/{$agencyFolder}/Park/Default/paymentStatus.html.twig";
return $this->render($view, $viewParams);
}
}