<?php
namespace App\Controller;
use App\Entity\Booking;
use App\Form\BookingFinishType;
use App\Form\PaymentTypeFormType;
use App\Interfaces\BookingsInterface;
use App\Interfaces\HotelMongoInterface;
use App\Interfaces\IHotels;
use App\Interfaces\OrderInterface;
use App\Interfaces\SecurityInterface;
use App\Security\AppCustomAuthenticator;
use App\Vendor\Helper;
use App\Vendor\Symfony\MAbstractController;
use App\Vendor\Symfony\ValidationInterface;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
use Symfony\Bundle\SecurityBundle\Security\FirewallConfig;
use Symfony\Bundle\SecurityBundle\SecurityBundle;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Intl\Timezones;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Http\Authentication\UserAuthenticatorInterface;
use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface;
use Symfony\Contracts\Service\ServiceProviderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use Twig\Environment;
class BookingController extends MAbstractController
{
private ValidationInterface $validation;
public function __construct(ParameterBagInterface $bag, KernelInterface $kernel, EntityManagerInterface $em, ValidationInterface $validation)
{
parent::__construct($bag, $kernel, $em);
$this->validation = $validation;
}
#[Route("/booking/{_locale}", name: "booking", methods: ["GET","POST"])]
public function booking(Request $request, IHotels $hotelsService, OrderInterface $orderService, LoggerInterface $zstLogger, BookingsInterface $bookingsService){
$session = $request->getSession();
$user = $this->getUser();
if($session->has("all_params")){
$params = $session->get("all_params");
} else {
$params = $bookingsService->hotelStartData();
}
if($session->has("guests_data")){
$params["guests"] = json_decode($session->get("guests_data"),true);
$params["bookingStatus"] = OrderInterface::STATUS_STARTED;
if($session->has("bookingResult")){
$orderBookingResponse = json_decode($session->get("bookingResult"), true);
} else {
$orderService->setUserId($user->getId());
$orderBookingResponse = $orderService->orderBooking($params["b_hash"], $params["p_id"], $params["_locale"], $params);
$orderService->addRate($orderBookingResponse["bookingResult"]["id"], $params);
$orderService->setGuests($orderBookingResponse["bookingResult"]["orderId"],$params);
}
if($orderBookingResponse["status"] == 'ok'){
return $this->redirectToRoute('booking_form');
}
if($orderBookingResponse["status"] == 'error'){
$params["error"] = $orderBookingResponse["error"];
}
}
$params = $this->asideParams($request, $orderService);
if(empty($this->getUser())){
$this->session->set('back_url', $this->generateUrl($request->get("_route"), $params));
}
return $this->render('booking/booking.html.twig',$params);
}
#[Route("/booking/form/{_locale}", name: "booking_form", methods: ["GET","POST"])]
public function bookingForm(Request $request,OrderInterface $orderService,IHotels $hotelsService){
$session = $request->getSession();
$orderBookingResponse = json_decode($session->get("bookingResult"), true);
if(!empty($orderBookingResponse["paymentTypes"][0]["needCreditCardData"])){
return $this->redirectToRoute('credit_card_tokenization');
} else {
return $this->redirectToRoute('booking_form_finish');
}
}
#[Route("/credit/card/tokenization/{_locale}", name: "credit_card_tokenization", methods: ["GET","POST"])]
public function creditCardTokenizationAjax(Request $request,OrderInterface $orderService,IHotels $hotelsService, TranslatorInterface $translator,LoggerInterface $bookingLogger){
$session = $request->getSession();
$params = $this->asideParams($request, $orderService);
$params = array_merge($params, $request->request->all());
$orderBookingResponse = json_decode($session->get("bookingResult"), true);
$orderService->generateBookingData($params);
$bookingResponse = $orderService->getBookingAssocByOrderId($orderBookingResponse["orderId"]);
$params["guests"] = json_decode($session->get("guests_data"), true);
$params["matchHash"] = $bookingResponse["matchHash"];
$params["mainGuest"] = $params["guests"][0]["adults"][0];
$params["bookingResult"] = [
"object_id" => $bookingResponse["object_id"],
"payUuid" => $bookingResponse["payUuid"],
"initUuid" => $bookingResponse["initUuid"],
"paymentTypes" => $bookingResponse["paymentTypes"],
"partnerId" => $bookingResponse["partnerOrderId"],
];
if(isset($params["checkStatus"]) && !empty($params["checkStatus"]["status"])){
if($params["checkStatus"]["status"] == 'ok'){
$payment_type_form = [];
parse_str($params["params"], $payment_type_form);
$errorMessage = $this->validation->validatePaymentTypeForm($payment_type_form["payment_type_form"]);
if(!empty($errorMessage)){
$chekInDate = new \DateTime($params["search"]["checkin"]);
$params["search"]["checkin"] = $chekInDate->format("Y-m-d");
$chekOutDate = new \DateTime($params["search"]["checkout"]);
$params["search"]["checkout"] = $chekOutDate->format("Y-m-d");
unset($params["params"]);
$newParams = [];
$newParams["search"] = $params["search"];
$hotelDetailUrl = $this->generateUrl('app_detail_hotel_detail', ['id'=>$params["search"]["hotelId"], $newParams]);
return new JsonResponse(["status" => false, "error" => $errorMessage, 'detailUrl' => $hotelDetailUrl]);
}
$bookingLogger->info("CREDITCARDTOKENIZATION request params", $payment_type_form);
$res = $orderService->creditCardDataTokenization($payment_type_form);
$bookingLogger->info("CREDITCARDTOKENIZATION response data", $res);
if($res["status"] == "ok"){
return new JsonResponse(['status' => true]);
}
if($res["status"] == "error"){
$chekInDate = new \DateTime($params["search"]["checkin"]);
$params["search"]["checkin"] = $chekInDate->format("Y-m-d");
$chekOutDate = new \DateTime($params["search"]["checkout"]);
$params["search"]["checkout"] = $chekOutDate->format("Y-m-d");
unset($params["params"]);
$newParams = [];
$newParams["search"] = $params["search"];
$hotelDetailUrl = $this->generateUrl('app_detail_hotel_detail', ['id'=>$params["search"]["hotelId"], $newParams]);
return new JsonResponse(["status" => false, "error" => $translator->trans($res["error"]), 'detailUrl' => $hotelDetailUrl]);
}
}
if($params["checkStatus"]["status"] == 'no'){
$newParams = [];
$newParams["search"] = $params["search"];
$hotelDetailUrl = $this->generateUrl('app_detail_hotel_detail', ['id'=>$params["search"]["hotelId"], $newParams]);
return new JsonResponse(["status" => false, "error" => $translator->trans($res["error"]), 'detailUrl' => $hotelDetailUrl]);
}
}
$paymentForm = $this->createForm(PaymentTypeFormType::class);
$paymentForm->handleRequest($request);
$params["paymentForm"] = $paymentForm->createView();
$params["status"] = "card";
if(empty($this->getUser())){
$this->session->set('back_url', $this->generateUrl($request->get("_route"), $params));
}
return $this->render('booking/booking.html.twig',$params);
}
#[Route("/booking/form/finish/{_locale}", name: "booking_form_finish", methods: ["GET","POST"])]
public function bookingFormFinish(Request $request,OrderInterface $orderService,IHotels $hotelsService, BookingsInterface $bookingsService, LoggerInterface $zstLogger, LoggerInterface $bookingLogger, TranslatorInterface $translator){
$session = $request->getSession();
$sessionParams = $session->get("all_params");
$params = $this->getParams();
$params = array_merge($params, $sessionParams);
$params["guests"] = json_decode($session->get("guests_data"), true);
$bookingResponse = json_decode($session->get('bookingResult'),true);
// if(!empty($params["guests"])){
// $params["guests"] = $orderService->convertBookingGuests($params["guests"]);
// }
$params["orderId"] = $bookingResponse["orderId"];
$params["matchHash"] = $bookingResponse["matchHash"];
$session->set("all_params", $params);
$params = $this->asideParams($request, $orderService);
$bookingFinishForm = $this->createForm(BookingFinishType::class);
$bookingFinishForm->handleRequest($request);
if(isset($params["checkStatus"]) && !empty($params["checkStatus"]["status"])){
if($params["checkStatus"]["status"] == 'ok'){
$bookingFinishParams = $orderService->generateBookingFinishData($params["orderId"], $params["search"]["language"], $params);
$bookingLogger->info("BOOKINGFORMFINISH request params", $bookingFinishParams);
$res = $orderService->orderBookingFinish($bookingFinishParams);
$bookingLogger->info("BOOKINGFORMFINISH response data", $res);
if($res["status"] == 'ok' || $res["status"] == "error" && $res["error"] == 'unknown' || $res["status"] == "error" && $res["error"] == 'timeout'){
$startDate = date('Y-m-d H:i:s');
$k = true;
while ($k){
$endDate = date('Y-m-d H:i:s');
$diff = strtotime($endDate) - strtotime($startDate);
$l = abs(round($diff));
$result = $this->getFinishStatus($bookingResponse["partnerOrderId"], $bookingsService,$orderService);
$bookingLogger->info("ORDER BOOKING FINISH STATUS response data", $result);
if($result["status"] == 'error'){
$k = false;
$newParams = [];
$newParams["search"] = $params["search"];
$hotelDetailUrl = $this->generateUrl('app_detail_hotel_detail', ['id'=>$params["search"]["hotelId"],$newParams]);
return new JsonResponse(["status" => 'error', "error" => $translator->trans($res["error"]), 'detailUrl' => $hotelDetailUrl]);
}
if($result["status"] == 'ok'){
$voucherUrl = "https://api.worldota.net/api/b2b/v3/hotel/order/document/voucher/download/";
$urlStr = '{"partner_order_id":"'.$bookingResponse["partnerOrderId"].'","language":"'.$params["search"]["language"].'"}';
$url = $voucherUrl.'?data='.urlencode($urlStr);
$pdfData = $orderService->worldotaAuth($url,$params["orderId"]);
$bookingsService->setVoucher((int)$params["orderId"], $pdfData);
$bookingsService->setBookingStatus($params["orderId"], OrderInterface::STATUS_COMPLETED);
$bookingsUrl = $this->generateUrl('app_profile_my_bookings');
return new JsonResponse(['status' => 'ok', 'bookingsUrl' => $bookingsUrl]);
}
if($result["status"] == 'processing'){
$k = true;
}
if($result["status"] == '3ds'){
$bookingLogger->info("3DS STATUS REQUEST", $result);
$dsRes = $this->postData3ds($result["data"]);
$bookingLogger->info("3DS STATUS RESPONSE", $dsRes);
}
if(intval($l) >= 600){
$k = false;
}
usleep(5000000);
}
} else {
$newParams = [];
$newParams["search"] = $params["search"];
$hotelDetailUrl = $this->generateUrl('app_detail_hotel_detail', ['id'=>$params["search"]["hotelId"],$newParams]);
return new JsonResponse(["status" => 'error', "error" => $translator->trans($res["error"]), 'detailUrl' => $hotelDetailUrl]);
}
}
if($params["checkStatus"]["status"] == 'no'){
$newParams = [];
$newParams["search"] = $params["search"];
$hotelDetailUrl = $this->generateUrl('app_detail_hotel_detail', ['id'=>$params["search"]["hotelId"],$newParams]);
return new JsonResponse(["status" => false, "error" => $translator->trans('rate_not_found'), 'detailUrl' => $hotelDetailUrl]);
}
}
$bookingFinishForm = $this->createForm(BookingFinishType::class);
$bookingFinishForm->handleRequest($request);
$params["bookingFinishForm"] = $bookingFinishForm->createView();
$params["status"] = "finish";
$rate = json_decode($session->get('rate'),true);
$params["payment_types"] = $rate["payment_options"]["payment_types"];
if(empty($this->getUser())){
$this->session->set('back_url', $this->generateUrl($request->get("_route"), $params));
}
return $this->render('booking/booking.html.twig',$params);
}
#[Route("/booking/form/finish/status", name: "booking_form_finish_status")]
public function bookingFinishStatus(Request $request, OrderInterface $orderService, LoggerInterface $zstLogger, IHotels $hotelService){
$params = $this->getParams();
return new JsonResponse($params);
}
#[Route("/booking/form/finish/status/ajax", name: "booking_form_finish_status_ajax", methods: ["POST"])]
public function bookingFormFinishStatus(Request $request, OrderInterface $orderService, BookingsInterface $bookingsService, Environment $twig, LoggerInterface $zstLogger){
$partnerId = $request->get("partnerId");
/** @var Booking $booking */
$booking = $bookingsService->getBookingByPartnerId($partnerId);
$orderId = $booking->getOrderId();
$res = $orderService->checkBookingFinishStatus($partnerId);
if($res["status"] == 'ok'){
$bookingsService->setBookingStatus($orderId, OrderInterface::STATUS_COMPLETED);
$zstLogger->log(LogLevel::INFO, "BookingFormFinishStatus:".json_encode($res));
$bookingsUrl = $this->generateUrl('app_profile_my_bookings');
return new JsonResponse(['status' => 'ok', 'bookingsUrl' => $bookingsUrl]);
} else {
$bookingsService->setBookingStatus($orderId, OrderInterface::STATUS_ERROR);
$zstLogger->log(LogLevel::ERROR, "BookingFormFinishStatus:".json_encode($res));
return new JsonResponse(['status' => 'no']);
}
}
#[Route("/booking/form/add/gusts", name: "booking_form_add_guests_ajax", methods: ["POST","GET"])]
public function bookingFormAddGuests(Request $request, OrderInterface $orderService, Environment $twig,SecurityInterface $security,UserPasswordHasherInterface $userPasswordHasher, UserAuthenticatorInterface $userAuthenticator, AppCustomAuthenticator $authenticator){
$session = $request->getSession();
$response["success"] = false;
$params = $this->getParams();
$mainGuest = $params["room"][0]["adults"][0];
$email = $mainGuest['email'];
$loggedInUser = $this->getUser();
$user = $security->getUserByEmail($email);
if(!empty($loggedInUser)){
if ($loggedInUser->getEmail() != $email){
if(empty($user)){
$request->request->set('first_name', $mainGuest["first_name"]);
$request->request->set('last_name', $mainGuest["last_name"]);
$request->request->set('mobile', $mainGuest["mobile"]);
$request->request->set('email', $mainGuest["email"]);
$security->register($userPasswordHasher,$userAuthenticator,$authenticator, false);
}
}
} else {
if(empty($user)){
$request->request->set('first_name', $mainGuest["first_name"]);
$request->request->set('last_name', $mainGuest["last_name"]);
$request->request->set('mobile', $mainGuest["mobile"]);
$request->request->set('email', $mainGuest["email"]);
$security->register($userPasswordHasher,$userAuthenticator,$authenticator, true);
} else {
$security->login($email,$userAuthenticator,$authenticator);
}
}
try {
$session->set("guests_data", json_encode($params["room"]));
$response["success"] = true;
$guests = $params["room"];
$html = $twig->render('pages/booking_guests.html.twig',[
"guests" => $guests
]);
$response["html"] = $html;
} catch (\Exception $e){
$response["error"] = $e->getMessage();
}
return new JsonResponse($response);
}
#[Route("/booking/download/voucher", name: "booking_download_voucher")]
public function downloadVoucher(Request $request){
$voucherPath = $request->get('voucherPath');
$voucher = $request->get('voucher');
$response = new BinaryFileResponse($voucherPath);
$response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT,$voucher);
return $response;
}
#[Route("/booking/rate/check", name: "booking_rate_check", methods: ["POST","GET"])]
public function checkHotel(Request $request){
$session = $request->getSession();
$sessionParams = $session->get("all_params");
$res["status"] = false;
$params = $this->getParams();
$params = array_merge($params, $sessionParams);
// foreach ($params['search']["guests"] as &$guest){
// $guest["adults"] = (int)$guest["adults"];
// if(!empty($guest["children"])){
// foreach ($guest["children"] as &$child){
// $child = (int)$child;
// }
// }
// }
$guests = [];
foreach ($params['search']["guests"] as $room){
$item["adults"] = (int)$room["adults"];
if(empty($room["children"])){
$item["children"] = [];
} else{
foreach ($room["children"] as $child){
$item["children"][] = (int)$child;
}
}
$guests[] = $item;
}
try{
$checkin = $params['search']["checkin"];
$checkout = $params['search']["checkout"];
$matchHash = $params["matchHash"];
$hotelSearch = [
'checkin' => $checkin,
'checkout' => $checkout,
'language' => $params['search']["language"],
'guests' => $guests,
'currency' => $params['search']["currency"],
'id' => trim($params['search']["hotelId"])
];
$jsonParams = json_encode($hotelSearch);
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => 'https://api.worldota.net/api/b2b/v3/search/hp/',
CURLOPT_USERPWD => $this->bag->get('zstd_user').":".$this->bag->get('zstd_pass'),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => $jsonParams,
CURLOPT_HTTPHEADER => array(
'Content-Type: application/json'
),
));
$response = curl_exec($curl);
curl_close($curl);
$hotelResponseData = json_decode($response, true);
if($hotelResponseData["status"] == 'ok'){
$rates = $hotelResponseData['data']['hotels'][0]['rates'];
foreach ($rates as $rate){
if($rate['match_hash'] == $matchHash){
$res['data']["status"] = 'ok';
break;
} else {
$res['data']["status"] = 'no';
}
}
} else {
$res['data'] = $hotelResponseData;
}
$res["status"] = true;
} catch (\Exception $e){
$res["error"] = $e->getMessage();
}
return new JsonResponse($res);
}
#[Route("/booking-cancel", name: "booking_cancel")]
public function cancelBooking(Request $request, OrderInterface $orderService, BookingsInterface $bookingsService){
$partnerId = $request->get('partnerId');
$orderId = $request->get('orderId');
$status = 2;
$res = $orderService->cancelBooking($partnerId);
if(!empty($res["error"])){
$status = 1;
} else {
$bookingsService->setBookingStatus($orderId, OrderInterface::STATUS_CANCELED);
}
return $this->redirectToRoute('app_profile_my_bookings', ['status' => $status, 'result' => $res]);
}
private function asideParams(Request $request,$orderService){
$session = $request->getSession();
$params = $session->get("all_params");
$rate = json_decode($session->get('rate'),true);
$params["taxes"] = $orderService->getTaxesData();
$params["cancelMessages"] = $orderService->generateCancelationPenalties();
$params["payment_types"] = $rate["payment_options"]["payment_types"];
return $params;
}
private function getAuthenticator(?string $authenticatorName, string $firewallName): AuthenticatorInterface
{
if (!isset($this->authenticators[$firewallName])) {
throw new \LogicException(sprintf('No authenticators found for firewall "%s".', $firewallName));
}
/** @var ServiceProviderInterface $firewallAuthenticatorLocator */
$firewallAuthenticatorLocator = $this->authenticators[$firewallName];
if (!$authenticatorName) {
$authenticatorIds = array_keys($firewallAuthenticatorLocator->getProvidedServices());
if (!$authenticatorIds) {
throw new \LogicException(sprintf('No authenticator was found for the firewall "%s".', $firewallName));
}
if (1 < \count($authenticatorIds)) {
throw new \LogicException(sprintf('Too many authenticators were found for the current firewall "%s". You must provide an instance of "%s" to login programmatically. The available authenticators for the firewall "%s" are "%s".', $firewallName, AuthenticatorInterface::class, $firewallName, implode('" ,"', $authenticatorIds)));
}
return $firewallAuthenticatorLocator->get($authenticatorIds[0]);
}
if ($firewallAuthenticatorLocator->has($authenticatorName)) {
return $firewallAuthenticatorLocator->get($authenticatorName);
}
$authenticatorId = 'security.authenticator.'.$authenticatorName.'.'.$firewallName;
if (!$firewallAuthenticatorLocator->has($authenticatorId)) {
throw new \LogicException(sprintf('Unable to find an authenticator named "%s" for the firewall "%s". Available authenticators: "%s".', $authenticatorName, $firewallName, implode('", "', array_keys($firewallAuthenticatorLocator->getProvidedServices()))));
}
return $firewallAuthenticatorLocator->get($authenticatorId);
}
private function getFinishStatus($partnerId, $bookingsService,$orderService){
return $orderService->checkBookingFinishStatus($partnerId);
}
private function postData3ds($params3ds){
$data = json_decode($params3ds, true);
$getData = $data["data"]["data_3ds"];
$action_url = $getData["action_url"];
$postData = [
"md" => $getData["data"]["MD"],
"PaReq" => $getData["data"]["PaReq"],
"TermUrl" => $getData["data"]["TermUrl"]
];
$jsonData = json_encode($postData);
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => $action_url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS =>$jsonData,
CURLOPT_HTTPHEADER => array(
'Content-Type: application/json'
),
));
$response = curl_exec($curl);
curl_close($curl);
return json_decode($response, true);
}
}