<?php
/**
* Created by Elements.at New Media Solutions GmbH
*
*/
namespace App\Controller\BookingApi;
use App\Ecommerce\AvailabilitySystem\Event\AvailabilitySystem;
use App\Ecommerce\CartManager\CartPriceModificator\CashbackInsurance;
use App\Ecommerce\Checkout\Step\ConfirmStep;
use App\Ecommerce\Checkout\Step\PaymentStep;
use App\Ecommerce\Checkout\Step\ProductData;
use App\Model\BookingApi\Cart;
use App\Model\BookingApi\Exception\DataMissingException;
use App\Model\BookingApi\Order;
use App\Model\Shop\Checkout\ConfirmData;
use App\Model\Shop\Checkout\PaymentData;
use App\Model\Shop\Event\EventProduct;
use App\Model\Type\TenantType;
use App\Service\BookingApi\ApiService;
use App\Service\Shop\ShopService;
use App\Service\SiteConfigService;
use App\Traits\EnvironmentTrait;
use Carbon\Carbon;
use Elements\Bundle\SkidataTicketingSwebBundle\Service\OrderService;
use Elements\Bundle\TicketShopFrameworkBundle\Ecommerce\Checkout\Step\Address;
use Elements\Bundle\TicketShopFrameworkBundle\Model\Shop\Cart\TicketShopCartInterface;
use Elements\Bundle\TicketShopFrameworkBundle\Service\Swisspass\SwisspassService;
use Elements\Bundle\TicketShopFrameworkBundle\Service\VoucherCodeService;
use OpenApi\Annotations as OA;
use Pimcore\Bundle\EcommerceFrameworkBundle\CartManager\CartInterface;
use Pimcore\Bundle\EcommerceFrameworkBundle\Factory;
use Pimcore\Model\Asset;
use Pimcore\Model\DataObject\OnlineShopOrder;
use Pimcore\Model\DataObject\TicketProduct;
use Pimcore\Tool\Session;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
/**
* Class ApiOrderControllerApi
*
* @Route("/bookingAPI/order")
*
* @OA\Tag(name="Order")
*/
class ApiOrderControllerApi extends ApiAbstractController
{
use EnvironmentTrait;
/**
*
*
* @param Request $request
*
* @Route("/orderData", name="order_data", methods={"POST", "GET"})
*
* @OA\Post(
* path="/bookingAPI/order/orderData",
* description="get order data",
* tags={"Order"},
*
* @OA\RequestBody(
* required=true,
*
* @OA\JsonContent(
* required={"orderId"},
*
* @OA\Property( property="orderId", type="int", example=3467593),
* )
* ),
*
* @OA\Response(
* response=200,
* description="Successful operation",
*
* @OA\MediaType(
* mediaType="application/json",
*
* @OA\Schema(
* type = "object",
*
* @OA\Property(property="order", type="object", ref="#/components/schemas/Order")
* )
* )
* ),
*
* @OA\Response(
* response=500,
* description="Error",
*
* @OA\MediaType(
* mediaType="application/json",
*
* @OA\Schema(
* type = "object",
*
* @OA\Property(property="errors", type="array", @OA\Items(type="string")),
* )
* )
* ),
* )
**/
public function orderData(Request $request, VoucherCodeService $voucherCodeService, ShopService $shopService, SiteConfigService $siteConfigService, RouterInterface $router): JsonResponse
{
try {
$body = $request->getContent();
$requestData = json_decode($body, true);
if ($request->get('debug')) {
//$requestData['orderId'] = 3467593; //event; valid marketplace order
$requestData['orderId'] = 3768048;
}
if (!$requestData['orderId']) {
throw new DataMissingException('missing orderId in order/orderData request ');
}
$order = \App\Model\Shop\Order::getById($requestData['orderId']);
if (!$order) {
$this->bookingapiLogger->warning('Requested order with id ' . $requestData['orderId'] . ' not found.');
return $this->sendErrors(['order not found'], 404);
} elseif ($order->getTenant() !== 'marketplace') {
$this->bookingapiLogger->warning('Requested order with id ' . $requestData['orderId'] . ' is not a marketplace order.');
return $this->sendErrors(['order does not belong to tenant marketplace'], 404);
}
$bookingApiOrder = new Order();
$bookingApiOrder->setDataFromOnlineShopOrder($order, $voucherCodeService, $shopService, $siteConfigService, $router);
if ($request->get('debug')) {
p_r($bookingApiOrder);
die();
}
return new JsonResponse(
[
'order' => $bookingApiOrder->jsonSerialize(),
]
);
} catch (\Throwable $throwable) {
if ($request->get('debug')) {
throw $throwable;
}
$this->bookingapiLogger->error($throwable->getMessage() . 'Stack Trace: ' . $throwable->getTraceAsString());
return $this->sendErrors([$throwable->getMessage()], 500);
}
}
/**
*
*
* @param Request $request
*
* @Route("/submit", name="order_submit", methods={"POST", "GET"})
*
* @OA\Post(
* path="/bookingAPI/order/submit",
* description="Submit order (ticket, event)",
* tags={"Order"},
*
* @OA\RequestBody(
* required=true,
*
* @OA\JsonContent(
* required={"cart"},
*
* @OA\Property(
* property="cart",
* ref="#/components/schemas/Cart"
* ),
* @OA\Property(
* property="externalTransactionId",
* type="string"
* ),
*
* @OA\Examples(example="eventExample", value="{""externalTransactionId"":""asdf1234"",""cart"":
{
""userInfo"":{
""email"": ""john.doe@example.com"",
""address"": {
""salutation"": ""male"",
""firstName"": ""John"",
""lastName"": ""Doe"",
""phone"": ""1234567"",
""street"": ""Somewhere No.10"",
""streetAddition"": """",
""city"": ""Over The Rainbow"",
""zip"": ""1234"",
""country"": ""CH"",
""language"": ""de""
}
},
""eventItems"": [
{
""price"": 0,
""eventId"": 17563,
""dateTime"": 1713420000000,
""ageGroupId"": 3002233,
""amount"": 1
},
{
""price"": 0,
""eventId"": 17563,
""dateTime"": 1713420000000,
""ageGroupId"": 3002234,
""amount"": 1
}
],
""price"": 79.5,
""shipping"": 0,
""totalPrice"": 79.5
}}", summary="a valid example for an event order"),
@OA\Examples(example="ticketSandboxExampleAdultReload", value="{""cart"":
{
""userInfo"":{
""email"": ""john.doe@example.com"",
""address"": {
""salutation"": ""male"",
""firstName"": ""John"",
""lastName"": ""Doe"",
""phone"": ""1234567"",
""street"": ""Somewhere No.10"",
""streetAddition"": """",
""city"": ""Over The Rainbow"",
""zip"": ""1234"",
""country"": ""CH"",
""language"": ""de""
}
},
""ticketItems"": [
{
""price"": 30,
""ticketId"": 3002546,
""catalogId"": 3000188,
""consumerCategoryId"": 3000153,
""date"": ""2024-04-11"",
""deliveryType"": ""reload"",
""deliveryCountry"": ""CH"",
""firstName"": ""John"",
""lastName"": ""Doe"",
""birthday"": ""2000-01-01"",
""keycardNumber"": ""01-16147133534548799951-9""
}
],
""price"": 30,
""shipping"": 0,
""totalPrice"": 30
}}", summary="a valid example for a SANDBOX ticket order - adult reload"),
@OA\Examples(example="ticketSandboxExampleChildRequest", value="{""cart"":
{
""userInfo"":{
""email"": ""john.doe@example.com"",
""address"": {
""salutation"": ""male"",
""firstName"": ""John"",
""lastName"": ""Doe"",
""phone"": ""1234567"",
""street"": ""Somewhere No.10"",
""streetAddition"": """",
""city"": ""Over The Rainbow"",
""zip"": ""1234"",
""country"": ""CH"",
""language"": ""de""
}
},
""ticketItems"": [
{
""price"": 15,
""ticketId"": 3002546,
""catalogId"": 3000188,
""consumerCategoryId"": 3000159,
""date"": ""2024-04-11"",
""deliveryType"": ""request"",
""deliveryCountry"": ""CH"",
""firstName"": ""John"",
""lastName"": ""Doe"",
""birthday"": ""2005-01-01""
}
],
""price"": 15,
""shipping"": 3,
""totalPrice"": 18
}}", summary="a valid example for a SANDBOX ticket order - child request")
* )
* ),
* @OA\Response(
* response=200,
* description="Successful operation",
*
* @OA\MediaType(
* mediaType="application/json",
*
* @OA\Schema(
* type = "object",
*
* @OA\Property(property="success", type="boolean"),
* @OA\Property(property="order", type="object", ref="#/components/schemas/Order")
* )
* )
* ),
*
* @OA\Response(
* response=500,
* description="Error",
*
* @OA\MediaType(
* mediaType="application/json",
*
* @OA\Schema(
* type = "object",
*
* @OA\Property(property="success", type="boolean"),
* @OA\Property(property="errors", type="array", @OA\Items(type="string")),
* )
* )
* ),
* )
*
*/
public function submitAction(Request $request,
ApiService $apiService,
OrderService $orderService,
ShopService $shopService,
ValidatorInterface $validator,
VoucherCodeService $voucherCodeService,
SiteConfigService $siteConfigService,
RouterInterface $router,
SwisspassService $swisspassService): JsonResponse
{
try {
$body = $request->getContent();
$requestData = json_decode($body, true);
$this->setEnvironmentTenant();
$cart = $this->getCart();
$this->clearCartData($cart);
if ($request->get('debug')) {
$requestData = [
'cart' => [
'userInfo' => [
'email' => 'john.doe@example.com',
'address' => [
'salutation' => 'male',
'firstName' => 'John',
'lastName' => 'Doe',
'phone' => '1234567',
'street' => 'Somewhere No.10',
'streetAddition' => '',
'city' => 'Over The Rainbow',
'zip' => '1234',
'country' => 'CH',
'language' => 'de'],
],
'ticketItems' => [
[
/* 'price' => 30,
'ticketId' => 3002546,
'catalogId' => 3000188,
'consumerCategoryId' => 3000153,*/
'price' => 83,
'ticketId' => 518928,
'catalogId' => 1501805,
'consumerCategoryId' => 502338,
'date' => '2024-04-30',
'deliveryType' => 'reload',
'deliveryCountry' => 'CH',
'firstName' => 'John',
'lastName' => 'Doe',
'birthday' => '2000-01-01',
'keycardNumber' => '01-16147133534874566908-0',
],
],
'price' => 83,
'shipping' => 0,
//"cashbackInsuranceSelected" => true,
'totalPrice' => 83,
],
];
}
if (!isset($requestData['cart'])) {
$this->bookingapiLogger->error("received order submit without cart in payload:");
$this->bookingapiLogger->error("####### REQUEST BODY #######");
$this->bookingapiLogger->error($body);
$this->bookingapiLogger->error("############################");
throw new \Exception('endpoint requires a cart in request');
}
$externalTransactionId = null;
if(isset($requestData['externalTransactionId'])){
$externalTransactionId = $requestData['externalTransactionId'];
}
$bookingApiCart = new Cart();
$bookingApiCart->unmarshall($requestData['cart']);
foreach($bookingApiCart->getTicketItems() as $cartItemTicket){
$keycardCheck = $this->keyCardNumberShortToLongCheck($cartItemTicket);
if(!$keycardCheck){
return $this->sendErrors(['could not map keycard short number '.$cartItemTicket->getKeycardShortnumber()], 500);
}
}
//create session cart
$errors = $this->fillSessionCartFromRequest($cart, $bookingApiCart, $apiService, $orderService, $shopService, $validator, $swisspassService);
if (!empty($errors)) {
$this->bookingapiLogger->warning('Could not create session cart. Sending the following errors: ' . implode(',', $errors));
return $this->sendErrors($errors, 500);
}
//is cart checkoutable and personalisation complete
if (!$cart->isCheckoutable()) {
throw new \Exception('cart is not checkoutable');
}
//is cart price correct?
$cartTotal = (float)$cart->getPriceCalculator()->getGrandTotal()->getAmount()->asNumeric();
if ($bookingApiCart->getTotalPrice() != $cartTotal) {
throw new \Exception('booking api cart received price is wrong. Total should be ' . $cartTotal . ' but is ' . $bookingApiCart->getTotalPrice());
}
$checkoutManager = Factory::getInstance()->getCheckoutManager($cart);
/**
* @var \App\Model\Shop\Order $order
*/
$order = $checkoutManager->getOrder();
$order->setTenant(TenantType::MARKETPLACE);
$isSandboxOnly = true;
if (count($bookingApiCart->getTicketItems()) > 0) {
foreach ($bookingApiCart->getTicketItems() as $index => $bookingApiCartItemTicket) {
//TODO @msteyrer
if ($bookingApiCartItemTicket->getCatalogId() != 3000188/* && !$request->get('debug')*/) {
$isSandboxOnly = false;
//throw new \Exception('submitting orders with ticket items is currently only allowed for Skidata Sandbox Tickets from Catalog Id 3000188');
}
$errors = $apiService->validateTicketData($shopService, $orderService, $validator, $bookingApiCartItemTicket);
if (!empty($errors)) {
$errors[] = 'Error with ticket in index ' . $index;
return $this->sendErrors($errors, 500);
}
}
}
$addressStep = $checkoutManager->getCheckoutStep(Address::STEP_NAME);
$productDataStep = $checkoutManager->getCheckoutStep(ProductData::STEP_NAME);
$paymentStep = $checkoutManager->getCheckoutStep(PaymentStep::STEP_NAME);
$confirmOrderStep = $checkoutManager->getCheckoutStep(ConfirmStep::STEP_NAME);
$paymentData = new PaymentData();
$paymentData->setOrderLanguage($bookingApiCart->getUserInfo()->getAddress()->getLanguage());
$confirmOrderStepData = new ConfirmData();
$confirmOrderStepData->setGdprConsent(true);
$checkoutManager->commitStep($addressStep, json_decode($cart->getCheckoutData('address')));
$checkoutManager->commitStep($productDataStep, []);
$checkoutManager->commitStep($paymentStep, $paymentData);
$checkoutManager->commitStep($confirmOrderStep, $confirmOrderStepData);
$order->setOrderLanguage($bookingApiCart->getUserInfo()->getAddress()->getLanguage());
$order->setOrderDateFinished(Carbon::now());
//save image and set in order item
foreach ($bookingApiCart->getTicketItems() as $index => $bookingApiCartItemTicket) {
if ($bookingApiCartItemTicket->getImageData() && $bookingApiCartItemTicket->getImageFileFormat()) {
//add image to order item checkout data
try {
//find order item for cart item this is not ideal, but can not be done via Ids
$foundItem = null;
foreach ($order->getItems() as $orderItem) {
if ($orderItem->getCustomized()->getOrderItemCustomizedCheckoutData()) {
$checkoutData = $orderItem->getCustomized()->getOrderItemCustomizedCheckoutData();
if ($checkoutData->getFirstname() == $bookingApiCartItemTicket->getFirstName() && $checkoutData->getLastname() == $bookingApiCartItemTicket->getLastName() && $orderItem->getProduct()->getParent()->getId() == $bookingApiCartItemTicket->getTicketId() && !$checkoutData->getImage()) {
$foundItem = $orderItem;
break;
}
}
}
if (!$foundItem) {
throw new \Exception('could not process ticket image data - order item not found');
}
$checkoutData = $foundItem->getCustomized()->getOrderItemCustomizedCheckoutData();
//$parentFolderId = 692;
$parentFolder = $apiService->getTicketImageFolder($order);
$parentFolderId = $parentFolder->getId();
$asset = Asset\Image::create($parentFolderId, ['filename' => 'image-' . $foundItem->getId() . '.' . $bookingApiCartItemTicket->getImageFileFormat(), 'data' => base64_decode($bookingApiCartItemTicket->getImageData())]);
/** @phpstan-ignore-next-line */
$checkoutData->setImage($asset);
} catch (\Exception $e) {
$this->bookingapiLogger->error('Could not create image in ticket order: ' . $e->getMessage());
$errors[] = 'Error with ticket in index ' . $index . '. Could not process image.';
return $this->sendErrors($errors, 500);
}
}
}
if($externalTransactionId){
$order->setPaymentReference($externalTransactionId);
}
//skip transmit in dev (dev and stage are in dev environment)
//no skip transmit at the moment - dev and stage are using skidata sandbox
/*if(!$isSandboxOnly && $_SERVER["APP_ENV"] == "dev"){
//prevent order transmission to skidata unless it is sandbox order
$order->getOrCreateCheckoutDataBrick()->setSkipTransmit(true);
}*/
$order = $checkoutManager->commitOrder();
$bookingApiOrder = new Order();
/** @phpstan-ignore-next-line */
$bookingApiOrder->setDataFromOnlineShopOrder($order, $voucherCodeService, $shopService, $siteConfigService, $router);
$this->bookingapiLogger->info('Submitted order #' . $order->getId());
return new JsonResponse(
[
'success' => $bookingApiOrder->getOrderState() == 'committed',
'order' => $bookingApiOrder->jsonSerialize(),
]
);
} catch (\Throwable $throwable) {
$this->bookingapiLogger->error($throwable->getMessage() . 'Stack Trace: ' . $throwable->getTraceAsString());
return $this->sendErrors([$throwable->getMessage()], 500);
}
}
/**
* @Route("/totalPrice", name="order_price", methods={"POST", "GET"})
*
* @param Request $request
*
* @return JsonResponse
*
* @OA\Post(
* path="/bookingAPI/order/totalPrice",
* description="Get cart with total price for order before submitting the Order",
* tags={"Order"},
*
* @OA\RequestBody(
* required=true,
*
* @OA\JsonContent(
* required={"cart"},
*
* @OA\Property( property="cart", type="object", ref="#/components/schemas/Cart"),
*
* @OA\Examples(example="eventExample", value="{""cart"": {
""userInfo"":{
""email"": ""john.doe@example.com"",
""address"": {
""salutation"": ""male"",
""firstName"": ""John"",
""lastName"": ""Doe"",
""phone"": ""1234567"",
""street"": ""Somewhere No.10"",
""streetAddition"": """",
""city"": ""Over The Rainbow"",
""zip"": ""1234"",
""country"": ""CH"",
""language"": ""de""
}
},
""eventItems"": [
{
""price"": 0,
""eventId"": 17563,
""dateTime"": 1713420000000,
""ageGroupId"": 3002233,
""amount"": 1
},
{
""price"": 0,
""eventId"": 17563,
""dateTime"": 1713420000000,
""ageGroupId"": 3002234,
""amount"": 1
}
],
""price"": 0,
""shipping"": 0,
""totalPrice"": 0
}}", summary="event for one adult + one child"),
* @OA\Examples(example="ticketExampleRequest", value="{""cart"": {
""userInfo"":{
""email"": ""john.doe@example.com"",
""address"": {
""salutation"": ""male"",
""firstName"": ""John"",
""lastName"": ""Doe"",
""phone"": ""1234567"",
""street"": ""Somewhere No.10"",
""streetAddition"": """",
""city"": ""Over The Rainbow"",
""zip"": ""1234"",
""country"": ""CH"",
""language"": ""de""
}
},
""ticketItems"": [
{
""price"": 107,
""ticketId"": 518928,
""catalogId"": 1501805,
""consumerCategoryId"": 502338,
""date"": ""2024-04-30"",
""deliveryType"": ""request"",
""deliveryCountry"": ""CH"",
""firstName"": ""John"",
""lastName"": ""Doe"",
""birthday"": ""2000-01-01"",
""insuranceId"": 518668
}
],
""price"": 0,
""shipping"": 0,
""totalPrice"": 0
}}", summary="ticket for one adult - request"),
* @OA\Examples(example="ticketExampleReloadSandbox", value="{""cart"": {
""userInfo"":{
""email"": ""john.doe@example.com"",
""address"": {
""salutation"": ""male"",
""firstName"": ""John"",
""lastName"": ""Doe"",
""phone"": ""1234567"",
""street"": ""Somewhere No.10"",
""streetAddition"": """",
""city"": ""Over The Rainbow"",
""zip"": ""1234"",
""country"": ""CH"",
""language"": ""de""
}
},
""ticketItems"": [
{
""price"": 107,
""ticketId"": 518928,
""catalogId"": 1501805,
""consumerCategoryId"": 502338,
""date"": ""2024-04-30"",
""deliveryType"": ""reload"",
""deliveryCountry"": ""CH"",
""firstName"": ""John"",
""lastName"": ""Doe"",
""birthday"": ""2000-01-01"",
""keycardNumber"": ""01-16147133534548799951-9"",
""insuranceId"": 518668
}
],
""price"": 0,
""shipping"": 0,
""totalPrice"": 0
}}", summary="ticket for one adult - reload"),
@OA\Examples(example="ticketExampleReload", value="{""cart"": {
""userInfo"":{
""email"": ""john.doe@example.com"",
""address"": {
""salutation"": ""male"",
""firstName"": ""John"",
""lastName"": ""Doe"",
""phone"": ""1234567"",
""street"": ""Somewhere No.10"",
""streetAddition"": """",
""city"": ""Over The Rainbow"",
""zip"": ""1234"",
""country"": ""CH"",
""language"": ""de""
}
},
""ticketItems"": [
{
""price"": 30,
""ticketId"": 3002546,
""catalogId"": 3000188,
""consumerCategoryId"": 3000153,
""date"": ""2024-04-11"",
""deliveryType"": ""reload"",
""deliveryCountry"": ""CH"",
""firstName"": ""John"",
""lastName"": ""Doe"",
""birthday"": ""2000-01-01"",
""keycardNumber"": ""01-16147133534548799951-9""
}
],
""price"": 0,
""shipping"": 0,
""totalPrice"": 0
}}", summary="ticket for one adult - reload SANDBOX")
*
* )
* ),
* @OA\Response(
* response=200,
* description="Successful operation",
*
* @OA\MediaType(
* mediaType="application/json",
*
* @OA\Schema(
* type = "object",
*
* @OA\Property(property="success", type="boolean"),
* @OA\Property(property="cart", type="object", ref="#/components/schemas/Cart")
* )
* )
* ),
*
* @OA\Response(
* response=500,
* description="Error"
* )
* )
*/
public function getTotalPrice(Request $request,
ApiService $apiService,
OrderService $orderService,
ShopService $shopService,
ValidatorInterface $validator,
SwisspassService $swisspassService): JsonResponse
{
try {
$body = $request->getContent();
$requestData = json_decode($body, true);
$this->setEnvironmentTenant();
$cart = $this->getCart();
//p_r($cart);die();
$this->clearCartData($cart);
if ($request->get('debug')) {
$requestData = [
'cart' => [
'userInfo' => [
'email' => 'john.doe@example.com',
'address' => [
'salutation' => 'male',
'firstName' => 'John',
'lastName' => 'Doe',
'phone' => '1234567',
'street' => 'Somewhere No.10',
'streetAddition' => '',
'city' => 'Over The Rainbow',
'zip' => '1234',
'country' => 'CH',
'language' => 'de'],
],
"eventItems" => [
[
"price" => 0,
"eventId" => 36661,
"dateTime" => 1711407600000,
"ageGroupId" => 3002389,
"amount" => 1
]
],
/*
"ticketItems" => [
[
"price" => 107,
"ticketId" => 518928,
"catalogId" => 1501805,
"consumerCategoryId" => 502338,
"date" => "2024-04-30",
"deliveryType" => "reload",
"deliveryCountry" => "CH",
"firstName" => "John",
"lastName" => "Doe",
"birthday" => "2000-01-01",
// "keycardNumber"=> "01-16147133534548799951-9",
"keycardShortnumber"=> "U123123",
// "swisspassNumber"=> "",
// "swisspassZip"=> "",
//"insuranceId" => 518668
]
], */
/* 'ticketItems' => [
[
'price' => 30,
'ticketId' => 3002546,
'catalogId' => 3000188,
'consumerCategoryId' => 3000153,
'date' => '2024-04-11',
'deliveryType' => 'reload',
'deliveryCountry' => 'CH',
'firstName' => 'John',
'lastName' => 'Doe',
'birthday' => '2000-01-01',
'keycardNumber' => '01-16147133534548799951-9',
],
], */
'price' => 0,
'shipping' => 0,
//"cashbackInsuranceSelected" => true,
'totalPrice' => 0,
],
];
}
if (!isset($requestData['cart'])) {
$this->bookingapiLogger->error("received total price request without cart in payload:");
$this->bookingapiLogger->error("####### REQUEST BODY #######");
$this->bookingapiLogger->error($body);
$this->bookingapiLogger->error("############################");
throw new \Exception('endpoint requires a cart in request');
} else {
$this->bookingapiLogger->error("received total price request");
$this->bookingapiLogger->error("####### REQUEST BODY #######");
$this->bookingapiLogger->error($body);
$this->bookingapiLogger->error("############################");
}
$bookingApiCart = new Cart();
$bookingApiCart->unmarshall($requestData['cart']);
if ($bookingApiCart->getPrice() > 0 || $bookingApiCart->getTotalPrice() > 0 || $bookingApiCart->getShipping() > 0) {
$this->bookingapiLogger->warning('Invalid call to order/totalPrice endpoint: cart.price, cart.shipping and cart.totalPrice must be empty for totalPrice endpoint.');
return $this->sendErrors(['cart.price, cart.shipping and cart.totalPrice must be empty for totalPrice endpoint. prices will be calculated and returned in response'], 500);
}
foreach($bookingApiCart->getTicketItems() as $cartItemTicket){
$keycardCheck = $this->keyCardNumberShortToLongCheck($cartItemTicket);
if(!$keycardCheck){
return $this->sendErrors(['could not map keycard short number '.$cartItemTicket->getKeycardShortnumber()], 500);
}
}
$errors = $this->fillSessionCartFromRequest($cart, $bookingApiCart, $apiService, $orderService, $shopService, $validator, $swisspassService);
if (!empty($errors)) {
$this->bookingapiLogger->warning('Could not create session cart. Sending the following errors: ' . implode(',', $errors));
return $this->sendErrors($errors, 500);
}
//convert session cart to new booking api cart for return
$bookingApiCartReturn = new Cart();
$bookingApiCartReturn->setDataFromEcommerceCart($cart, $this->languages);
if ($request->get('debug') && \Pimcore::inDebugMode()) {
p_r($cart);
p_r($bookingApiCartReturn);
die();
}
return new JsonResponse(
[
'success' => true,
'cart' => $bookingApiCartReturn->jsonSerialize(),
]
);
} catch (\Throwable $throwable) {
if ($request->get('debug')) {
throw $throwable;
} else {
$this->bookingapiLogger->error($throwable->getMessage() . 'Stack Trace: ' . $throwable->getTraceAsString());
return $this->sendErrors([$throwable->getMessage()], 500);
}
}
}
/**
* @param TicketShopCartInterface $cart
* @param Cart $bookingApiCart
* @param ApiService $apiService
* @param OrderService $orderService
* @param ShopService $shopService
* @param ValidatorInterface $validator
*
* @return array<string>
*
* @throws \Exception
*/
protected function fillSessionCartFromRequest(TicketShopCartInterface $cart,
Cart $bookingApiCart,
ApiService $apiService,
OrderService $orderService,
ShopService $shopService,
ValidatorInterface $validator,
SwisspassService $swisspassService
): array
{
$errors = [];
$checkoutDataProduct = [];
//user info and delivery info
$userInfo = $bookingApiCart->getUserInfo();
$checkoutDataAddress = [
'customerSalutation' => $userInfo->getAddress()->getSalutation(),
'customerFirstname' => $userInfo->getAddress()->getFirstName(),
'customerLastname' => $userInfo->getAddress()->getLastName(),
'customerPhone' => $userInfo->getAddress()->getPhone(),
'customerStreet' => $userInfo->getAddress()->getStreet(),
'customerStreetAddon' => $userInfo->getAddress()->getStreetAddition(),
'customerZip' => $userInfo->getAddress()->getZip(),
'customerCity' => $userInfo->getAddress()->getCity(),
'customerCountry' => $userInfo->getAddress()->getCountry(),
'customerLanguage' => $userInfo->getAddress()->getLanguage(),
'customerEmail' => $userInfo->getEmail(),
'useDeliveryAddress' => false,
];
if ($bookingApiCart->getDeliveryInfo()) {
$deliveryInfo = $bookingApiCart->getDeliveryInfo();
$checkoutDataAddress['useDeliveryAddress'] = true;
$checkoutDataAddress['deliveryCountry'] = $deliveryInfo->getAddress()->getCountry();
$checkoutDataAddress['deliverySalutation'] = $deliveryInfo->getAddress()->getSalutation();
$checkoutDataAddress['deliveryFirstname'] = $deliveryInfo->getAddress()->getFirstName();
$checkoutDataAddress['deliveryLastname'] = $deliveryInfo->getAddress()->getLastName();
$checkoutDataAddress['deliveryPhone'] = $deliveryInfo->getAddress()->getPhone();
$checkoutDataAddress['deliveryStreet'] = $deliveryInfo->getAddress()->getStreet();
$checkoutDataAddress['deliveryStreetAddon'] = $deliveryInfo->getAddress()->getStreetAddition();
$checkoutDataAddress['deliveryZip'] = $deliveryInfo->getAddress()->getZip();
$checkoutDataAddress['deliveryCity'] = $deliveryInfo->getAddress()->getCity();
}
$cart->setCheckoutData('address', json_encode($checkoutDataAddress));
//event items
if ($bookingApiCart->eventItems) {
foreach ($bookingApiCart->getEventItems() as $index => $bookingApiCartItemEvent) {
$product = EventProduct::getById($bookingApiCartItemEvent->getEventId());
$selectedDate = new Carbon();
$selectedDate->setTimestamp($bookingApiCartItemEvent->getDateTime()/1000);
$quantity = $bookingApiCartItemEvent->getAmount();
$priceObj = \App\Model\Shop\Event\ShopDynamicPrice::getById($bookingApiCartItemEvent->getAgeGroupId());
/**@var $availabilitySystem AvailabilitySystem **/
$availabilitySystem = $product->getAvailabilitySystemImplementation();
/** @phpstan-ignore-next-line */
$availability = $availabilitySystem->getAvailableQuota($product, $selectedDate);
if($availability->getAvailableQuota() < $quantity){
$errors[] = 'Error with event in index '.$index.': available quota can not satisfy requested quantity';
return $errors;
}
$sessionCartItem = $shopService->addEventToCartPerPriceObject($product, $priceObj, $cart, $selectedDate, $quantity);
if(!$sessionCartItem){
$errors[] = 'Error with event in index ' . $index . ' could not add to cart event Id '.$product->getId().' for date '. $selectedDate->format('c');
return $errors;
}
}
}
//ticket items
if ($bookingApiCart->ticketItems) {
foreach ($bookingApiCart->getTicketItems() as $index => $bookingApiCartItemTicket) {
$errors = $apiService->validateTicketData($shopService, $orderService, $validator, $bookingApiCartItemTicket);
if (!empty($errors)) {
$errors[] = 'Error with ticket in index ' . $index;
return $errors;
} else {
//add ticket to session cart
$ticketProduct = \Elements\Bundle\TicketShopFrameworkBundle\Model\DataObject\TicketProduct::getById($bookingApiCartItemTicket->getTicketId());
$insurance = \Elements\Bundle\TicketShopFrameworkBundle\Model\DataObject\TicketProduct::getById($bookingApiCartItemTicket->getInsuranceId());
$consumerCategory = \Elements\Bundle\TicketShopFrameworkBundle\Model\DataObject\TicketConsumerCategory::getById($bookingApiCartItemTicket->getConsumerCategoryId());
$ticketCatalog = \App\Model\Shop\Ticket\ShopTicketCatalog::getById($bookingApiCartItemTicket->getCatalogId());
$ticketStartDate = Carbon::createFromFormat('Y-m-d', $bookingApiCartItemTicket->getDate());
$params = [
//isUpgradeOption setting is only relevant for ticket shop, not for API booking
'isUpgradeOption' => false,
'originalProduct' => $ticketProduct,
];
$addedItems = $shopService->addTicketItemsToCartPerConsumer(
$ticketProduct,
$consumerCategory,
1,
$insurance,
$ticketCatalog,
$cart,
$ticketStartDate,
$params
);
if (!isset($addedItems[0])) {
$errors[] = 'could not add ticket in index ' . $index . ' to cart';
return $errors;
}
$cartItemKey = $addedItems[0];
//acquisition type / delivery
$cartProduct = $cart->getItem($cartItemKey)->getProduct();
/**
* @var TicketProduct $cartProduct
*/
$cartProduct->setAcquisitionType($bookingApiCartItemTicket->getDeliveryType());
//personalisation and keycard
$checkoutDataProduct[$cartItemKey] = [
'firstname' => $bookingApiCartItemTicket->getFirstName(),
'lastname' => $bookingApiCartItemTicket->getLastName(),
'birthday' => $bookingApiCartItemTicket->getBirthday() . 'T00:00:00+0000',
'deliveryOption' => $bookingApiCartItemTicket->getDeliveryType(),
];
if ($bookingApiCartItemTicket->getKeycardNumber()) {
$checkoutDataProduct[$cartItemKey]['keycard'] = $bookingApiCartItemTicket->getKeycardNumber();
}
if ($bookingApiCartItemTicket->getSwisspassNumber()) {
$keycardNr = $swisspassService->getKeyCardNumberForSwissPass($bookingApiCartItemTicket->getSwisspassNumber(), $bookingApiCartItemTicket->getSwisspassZip());
if(!$keycardNr){
$errors = "could not get keycard number for SwissPass Id ".$bookingApiCartItemTicket->getSwisspassNumber();
}
$checkoutDataProduct[$cartItemKey]['isHTAConsumer'] = (bool)$cartProduct->getTicketConsumerCategory()->getIsSwisspassHTAConsumer();
$checkoutDataProduct[$cartItemKey]['keycard'] = $keycardNr;
$checkoutDataProduct[$cartItemKey]['swisspassId'] = $bookingApiCartItemTicket->getSwisspassNumber();
$checkoutDataProduct[$cartItemKey]['zip'] = $bookingApiCartItemTicket->getSwisspassZip();
}
$cart->setCheckoutData('productData', json_encode($checkoutDataProduct));
}
}
}
//cashback
if ($bookingApiCart->getCashbackInsuranceSelected()) {
foreach ($cart->getPriceCalculator()->getModificators() as $possibleMod) {
if ($possibleMod instanceof CashbackInsurance) {
if (!$possibleMod->isOptionAllowed($cart)) {
$errors[] = 'cashback insurance not allowed for cart';
} else {
/** @phpstan-ignore-next-line */
$cart->setCheckoutData('cashback', true);
}
break;
}
}
}
$cart->setCheckoutData('apiBooking', '');
return $errors;
}
}