HEX
Server: Microsoft-IIS/8.5
System: Windows NT YDAWBH120 6.3 build 9600 (Windows Server 2012 R2 Standard Edition) AMD64
User: tentjecom_web (0)
PHP: 7.4.14
Disabled: NONE
Upload Files
File: D:/HostingSpaces/SBogers10/shop.komma.nl/app/Checkout/CheckoutController.php
<?php
namespace App\Checkout;


use App\Addresses\AddressResource;
use App\Addresses\Models\Address;
use App\Base\Controller;
use App\Addresses\AddressService;
use App\Cart\HasCouponCodesInterface;
use App\Cart\Requests\AddCouponRequest;
use App\Cart\Requests\RemoveCouponRequest;
use App\Cart\Resources\ShoppingCartItemResource;
use App\Cart\ShoppingCartInterface;
use App\Cart\ShoppingCartItem;
use App\Cart\ShoppingCart;
use App\Checkout\Requests\UserDetailsFormRequest;
use App\Checkout\Resources\CheckoutInformationResource;
use App\Discounts\DiscountService;
use App\Discounts\DiscountTypes;
use App\Finance\RoundingService;
use App\Orders\Kms\OrderMailService;
use App\ShippingCosts\Resources\ShippingCostsResource;
use App\Users\Resources\SiteUserResource;
use App\Users\SiteUser;
use App\Users\SiteUserInterface;
use App\Vat\VatRateTotal;
use App\Vat\VatService;
use App\ShippingCosts\ShippingCostsService;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\View\View;
use Komma\KMS\Globalization\RegionInfo;
use Komma\KMS\Globalization\RegionInfoInterface;

/**
 * Handles the checkout process and the different views
 *
 * Class ShoppingCart
 * @package App\Cart
 */
class CheckoutController extends Controller
{
    private OrderMailService $orderMailService;
    private AddressService $addressService;
    private RegionInfoInterface $regionInfo;
    private VatService $vatService;

    /**
     * CheckoutController constructor.
     */
    public function __construct()
    {
        if(\App::runningInConsole()) return;

        $this->orderMailService = new OrderMailService();
        $this->addressService = app(AddressService::class);
        $this->vatService = new VatService();
        $this->regionInfo = app(RegionInfoInterface::class);

        parent::__construct();
    }

    /**
     * Start the checkout process.
     *
     * @return \Illuminate\Http\RedirectResponse
     */
    public function startCheckout()
    {
        if(auth()->guard('site')->check()) {
            //Authenticated, may proceed to confirmation page after loading some data
            $checkoutService = new CheckoutService();
            /** @var SiteUser $user */
            $user = auth()->guard('site')->user();

            $addressForInvoice = $user->invoiceAddress()->first();
            $addressForShipping = $user->shippingAddress()->first();

            //Give addresses and user to the checkout service, so they will be persisted between requests.
            $checkoutService->setInvoiceAddress($addressForInvoice);
            $checkoutService->setShippingAddress($addressForShipping);
            $checkoutService->setUser($user);

            return redirect()->to(localized_route('checkout.confirm'));
        } else {
            //Not authenticated, user must enter user details first, so we redirect him to a form in which he can do so.
            return redirect()->to(localized_route('checkout.user_details'));
        }
    }

    /**
     * Shows an address form in which the user can enter his details
     *
     * @param Request $request
     * @return \Illuminate\Http\RedirectResponse|View
     */
    public function showUserDetailsForm(Request $request) {
        $checkoutService = new CheckoutService();

        /** @var ShoppingCartInterface $shoppingCart */
        $shoppingCart = app(ShoppingCartInterface::class);

        $user = $checkoutService->getUser();

        $shippingAddress = $checkoutService->getShippingAddress();

        $neutralCulturesByIso3 = RegionInfo::getNeutralCultures()->map(function(RegionInfo $regionInfo) {
            return [$regionInfo->getThreeLetterISORegionName() => $regionInfo->getNativeName() !== '' ? $regionInfo->getNativeName() : $regionInfo->getDisplayName()];
        })->collapse()->sort();

        $neutralCulturesHotList = RegionInfo::getNeutralCultures()->filter(function(RegionInfo $regionInfo) {
           return in_array($regionInfo->getThreeLetterISORegionName(), config('shop.country_hotlist', []), true);
        })->map(function(RegionInfo $regionInfo) use(&$neutralCulturesByIso3) {
            if(isset($neutralCulturesByIso3[$regionInfo->getThreeLetterISORegionName()])) unset($neutralCulturesByIso3[$regionInfo->getThreeLetterISORegionName()]); //Remove the item from the regular cultures list
            return [$regionInfo->getThreeLetterISORegionName() => $regionInfo->getNativeName() !== '' ? $regionInfo->getNativeName() : $regionInfo->getDisplayName()];
        })->collapse()->sort();

        return $this->addressFormView()->with([
//            'addresses' => $addresses,
            'lastUsedShippingAddress' => $shippingAddress,
            'lastUsedInvoiceAddress' => $shippingAddress,
            'shippingAddress' => $shippingAddress,
            'items' => $shoppingCart->getItems(),
            'checkoutInformation' => $this->getCheckoutInformation(),
            'user' => $user,
            'regionInfo' => $this->regionInfo,
            'links' => $this->links,
            'culturesForSelectByIso3' => $neutralCulturesHotList->merge($neutralCulturesByIso3),
            'neutralCulturesByIso3' => $neutralCulturesByIso3,
            'neutralCulturesHotlistByIso3' => $neutralCulturesHotList,
            'cartRoute' => localized_route('shoppingcart'),
        ]);
    }

    /**
     * Saves the user details for the user in the session.
     * When it has done so, it returns a form for the user to confirm the checkout process
     *
     * @param UserDetailsFormRequest $request
     * @return \Illuminate\Http\RedirectResponse
     */
    public function saveUserDetailsForm(UserDetailsFormRequest $request) {
        $addressForInvoice = $this->addressService->getAddressForInvoiceFromInput();
        $addressForShipping = $this->addressService->getAddressForShippingFromInput();

        if($request->get('shipping-address-equals')) {
            $addressForShipping = (new Address($addressForInvoice->getAttributes()));
        }

        $checkoutService = new CheckoutService();
        $checkoutService->setInvoiceAddress($addressForInvoice);
        $checkoutService->setShippingAddress($addressForShipping);

        $checkoutService->setUser($checkoutService->getUserFromInput());

        return redirect()->to(localized_route('checkout.confirm'));
    }

    /**
     * Saves the shipping address for the user in the session
     */
    public function setShippingAddress() {
        $addressForShipping = $this->addressService->getAddressForShippingFromInput();

        /** @var CheckoutService $checkoutService */
        $checkoutService = new CheckoutService();
        $checkoutService->setShippingAddress($addressForShipping);
        return $checkoutService->getShippingAddress();
    }

    /**
     * Shows the form in which the user can see an overview of what he is about to order
     * along with his address details etc/
     *
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse|View
     */
    public function showConfirmCheckoutForm() {
        //Initalize some services. We cannot load them in the constructors since the session won't yet be available there.
        $checkoutService = new CheckoutService();
        /** @var ShippingCostsService $shippingCostsService */
        $shippingCostsService = app(ShippingCostsService::class);
        $shoppingCart = app(ShoppingCartInterface::class);
        $discountService = new DiscountService();

        //Get the user that is used in the checkout process. Remember It may be a guest user.
        $user = $checkoutService->getUser();

        $data = [];

        //Get the shipping address for the user
        $shippingAddress = $checkoutService->getShippingAddress();
        if(!$shippingAddress && $user) $shippingAddress = $checkoutService->getLastUsedShippingAddressForUser($user);

        //Re-calculate the shipping costs
        $shippingCosts = $shippingCostsService->get($shoppingCart, $shippingAddress);

        $this->vatService->calculateVatForModelWithVatScenarioEnum($shippingCosts);

        $discountService->applyDiscountsTo($shoppingCart);

        $subtotalInc = 0;
        $subtotalEx = 0;
        foreach($shoppingCart->getItems() as $item) {
            $discountService->applyDiscountsTo($item);
            $this->vatService->calculateVatForModelWithVatScenarioEnum($item);
            $subtotalInc += $item->getPriceInc();
            $subtotalEx += $item->getPriceEx();
        }

        //Get the vat rate totals collection from the shopping cart
        $vatRateTotals = $checkoutService->getVatRateTotalsFromShoppingCart($shoppingCart);

        //Add the vat amount of the shipping costs to the vat amount of the highest VatRateTotal.
        $highestVatRateTotal = $checkoutService->getVatRateTotalHavingTheHighestVatPercentage($vatRateTotals);
        if($highestVatRateTotal) {
            /** @var VatRateTotal $highestVatRateTotal */
            $highestVatRateTotal->setVatTotal(RoundingService::RoundVat($highestVatRateTotal->getVatTotal() + $shippingCosts->getVatAmount()));
        }

        $data['shippingAddress'] = $checkoutService->getShippingAddress();
        $data['invoiceAddress'] = $checkoutService->getInvoiceAddress();
        $data['user'] = $checkoutService->getUser();
        $data['checkoutRoute'] = localized_route('checkout.user_details');
        $data['subtotal'] = $subtotalInc;
        $data['total'] = $subtotalInc + $shippingCosts->getPriceInc();
        $data['shippingCosts'] = $shippingCosts;
        $data['vatRateTotals'] = $vatRateTotals;
        $data['shoppingCartItems'] = $shoppingCart->getItems();
        $data['regionInfo'] = app(RegionInfoInterface::class);
        $data['links'] = $this->links;

        return view('templates.confirmation', $data);
    }

    /**
     * Triggered in response of a push on a checkout button
     *
     * @param ProceedToPaymentRequest $request
     * @return \Illuminate\Http\RedirectResponse
     */
    public function proceedToPayment(ProceedToPaymentRequest $request)
    {
        /** @var CheckoutService $checkoutService */
        $checkoutService = new CheckoutService();

        //Check if the checkout session does not has data.
        //In that case we redirect the user back to the cart. Because the order most likely was handed over to the payment service provider and an order was created.
        if(!$checkoutService->hasSession()) return redirect()->to(localized_route('shoppingcart'))->withErrors([__('checkout.payment_request_already_handed_off')]);


        /** @var SiteUserInterface $user */
        $user = $checkoutService->getUser();

        //If there is a previously used user and this user is a guest, update that with the data from the user from the session.
        if($prevUser = $checkoutService->previouslyUsedUser($user))
            $user = $checkoutService->updatePreviouslyUsedUserIfGuest($prevUser, $user);

        //Make sure the user is saved. At this point it can be a new guest user, or the existing one which is updated or not.
        $user->save();

        /** @var ShoppingCartInterface|HasCouponCodesInterface $shoppingCart */
        $shoppingCart = app(ShoppingCartInterface::class);
        /** @var CheckoutService $checkoutService */
        $checkoutService = new CheckoutService();

        //Create addresses for shipping and invoice.
        $addressForInvoice = $checkoutService->getInvoiceAddress();
        $addressForShipping = $checkoutService->getShippingAddress();

        //Make sure the names on the shipping and invoice addresses are the one of the user.
        $addressForInvoice->first_name = $addressForShipping->first_name = $user->first_name;
        $addressForInvoice->last_name_prefix = $addressForShipping->last_name_prefix = $user->last_name_prefix;
        $addressForInvoice->last_name = $addressForShipping->last_name = $user->last_name;

        //We only store the address for guests in the order themselves. Not as a separate address
        if(auth()->guard('site')->check()) {
//        if(!$user->is_guest) {
            $addressForInvoice = $this->addressService->saveAddressForUser($addressForInvoice, $user);
            if($addressForInvoice === $addressForShipping) $addressForShipping = $addressForInvoice;
            $addressForShipping = $this->addressService->saveAddressForUser($addressForShipping, $user);
        }

        /** @var ShoppingCartItem[] $items */
        $order = $checkoutService->createOrder($shoppingCart, $user, $addressForShipping, $addressForInvoice);

        //Add remarks to the order if any where given
        if($request->has('remarks')) {
            $order->remarks = $request->get('remarks');
            $order->save();
        }

        //Clear the checkout session data (shipping address and guest user if available)
        $checkoutService->clearCheckoutData();
        $shoppingCart->clear(); //Clear the shopping cart

        return $checkoutService->startPaymentForOrder($order);
    }

    /**
     * Returns info about checkout totals. Used for ajax / api requests
     *
     * @return CheckoutInformationResource
     */
    public function getCheckoutInformation(): CheckoutInformationResource
    {
        //Initialize some services which cannot be initialized in the construction phase. Because session isn't available in there.
        /** @var CheckoutService $checkoutService */
        $checkoutService = new CheckoutService();
        /** @var ShoppingCartInterface|HasCouponCodesInterface $shoppingCart */
        $shoppingCart = app(ShoppingCartInterface::class);
        /** @var ShippingCostsService $shippingCostsService */
        $shippingCostsService = app(ShippingCostsService::class);
        $discountService = new DiscountService();

        //Get the user if available already.
        $user = $checkoutService->getUser();

        //Get the users addresses
        $invoiceAddress = $checkoutService->getInvoiceAddress();
        $shippingAddress = $checkoutService->getShippingAddress();

        //Re-calculate the shipping costs
        $shippingCosts = $shippingCostsService->get($shoppingCart, $shippingAddress);

        $this->vatService->calculateVatForModelWithVatScenarioEnum($shippingCosts);

        $discountService->applyDiscountsTo($shoppingCart);

        //Re-calculate the total inc and ex vat price of the shopping cart
        $subtotalInc = 0;
        $subtotalEx = 0;
        foreach($shoppingCart->getItems() as $item) {
            $discountService->applyDiscountsTo($item);
            $this->vatService->calculateVatForModelWithVatScenarioEnum($item);
            $subtotalInc += $item->getPriceInc();
            $subtotalEx += $item->getPriceEx();
        }

        $cartPriceModifications = $shoppingCart->getDiscountPriceMutations();
        $subtotalWithDiscountsEx = $subtotalEx;
        $subtotalWithDiscountsInc = $subtotalInc;
        foreach($cartPriceModifications as $cartPriceModification) {
            $subtotalWithDiscountsEx += $cartPriceModification->getPriceEx();
            $subtotalWithDiscountsInc += $cartPriceModification->getPriceInc();
        }

        //Get the vat rate totals collection
        $vatRateTotals = $checkoutService->getVatRateTotalsFromShoppingCart($shoppingCart);

        //Add the vat amount of the shipping costs to the vat amount of the highest VatRateTotal.
        $highestVatRateTotal = $checkoutService->getVatRateTotalHavingTheHighestVatPercentage($vatRateTotals);
        if($highestVatRateTotal) {
            /** @var VatRateTotal $highestVatRateTotal */
            $highestVatRateTotal->setVatTotal(RoundingService::RoundVat($highestVatRateTotal->getVatTotal() + $shippingCosts->getVatAmount()));
        }

        //Determine the continue shopping route
        $continueShoppingRoute = '';
        if(property_exists($this->links, 'categories')) $continueShoppingRoute = url($this->links->categories->route) ?? url();

        //Prepare a resource, that will be used as the (json) response.
        $checkoutInformationResource = new CheckoutInformationResource();
        $checkoutInformationResource->couponCodes = $shoppingCart->getCouponCodes();
        $checkoutInformationResource->subtotalInc = $subtotalInc;
        $checkoutInformationResource->subtotalEx = $subtotalEx;
        $checkoutInformationResource->subtotalWithDiscountsInc = $subtotalWithDiscountsInc;
        $checkoutInformationResource->subtotalWithDiscountsEx = $subtotalWithDiscountsEx;
        $checkoutInformationResource->vatRateTotals = $vatRateTotals; //Warning: Vat Rate totals represent the vat rate totals of producs
        $checkoutInformationResource->shippingCosts = new ShippingCostsResource($shippingCosts);
        $checkoutInformationResource->shippingCostsVatAmount = RoundingService::Round($shippingCosts->getVatAmount());
        $checkoutInformationResource->shoppingCartItems = ShoppingCartItemResource::collection($shoppingCart->getItems());
        $checkoutInformationResource->totalItemsCount = $shoppingCart->getItemsCount();
        $checkoutInformationResource->cartRoute = localized_route('shoppingcart');
        $checkoutInformationResource->startCheckoutRoute = route('checkout.start');
        $checkoutInformationResource->editDetailsRoute = auth()->guard('site')->check() ? localized_route('my_account') : localized_route('checkout.user_details');
        $checkoutInformationResource->startPaymentRoute = route('checkout.start_payment');
        $checkoutInformationResource->continueShoppingRoute = $continueShoppingRoute;
        $checkoutInformationResource->shippingAddress = new AddressResource($shippingAddress);
        $checkoutInformationResource->invoiceAddress = new AddressResource($invoiceAddress);
        $checkoutInformationResource->discountPriceMutations = $shoppingCart->getDiscountPriceMutations();
        $checkoutInformationResource->user = $user;

        return $checkoutInformationResource;
    }

    public function makeUsersAddressTheCheckoutAddresses() {
        $checkoutService = new CheckoutService();
        /** @var SiteUser $user */
        $user = auth()->guard('site')->user();

        $checkoutService->setInvoiceAddress($user->invoiceAddress()->first());
        $checkoutService->setShippingAddress($user->shippingAddress()->first());
    }

    /**
     * @param Request $request
     * @param Address $address
     */
    public function markAddressAsInvoiceAddress(Request $request, Address $address) {
        $this->authorizeForUser(\Auth::guard('site')->user(),'mark_as_invoice_address_for_checkout', $address);
        $checkoutService = new CheckoutService();
        $checkoutService->setInvoiceAddress($address);
    }

    /**
     * @param Request $request
     * @param Address $address
     */
    public function markAddressAsShippingAddress(Request $request, Address $address) {
        $this->authorizeForUser(\Auth::guard('site')->user(), 'mark_as_shipping_address_for_checkout', $address);
        $checkoutService = new CheckoutService();
        $checkoutService->setShippingAddress($address);
    }

    public function makeUserTheCheckoutUser() {
        $checkoutService = new CheckoutService();
        $user = auth()->guard('site')->user();
        $checkoutService->setUser($user);

        return new SiteUserResource($checkoutService->getUser());
    }

    /**
     * Adds a coupon code via an ajax request
     *
     * @param AddCouponRequest $request
     *
     * @return CheckoutInformationResource|\Illuminate\Http\JsonResponse
     */
    public function addCouponCode(AddCouponRequest $request)
    {
        /** @var ShoppingCartInterface|HasCouponCodesInterface $shoppingCart */
        $shoppingCart = app(ShoppingCartInterface::class);
        $couponCodesBeforeAddedNewOne = $shoppingCart->getDiscountsByType(DiscountTypes::Coupon);
        $shoppingCart->addCouponCode($request->get('coupon'));
        $couponCodesAfterAddedNewOne = $shoppingCart->getDiscountsByType(DiscountTypes::Coupon);

        if(count($couponCodesAfterAddedNewOne) == count($couponCodesBeforeAddedNewOne)) { //If the amount of valid coupon codes is the same as before we tried to add one, the coupon code is invalid. Else if would be added.
            $shoppingCart->removeCouponCode($request->get('coupon'));
            return response()->json([
               'message' => 'Coupon code invalid',
               'errors' => [
                   'coupon_code' => [__('shop/discounts.coupon_fail')]
               ]
            ], 400);
        }

        $request->flash();
        return $this->getCheckoutInformation();
    }

    /**
     * Removes an coupon code via an ajax request
     *
     * @param RemoveCouponRequest $request
     * @return CheckoutInformationResource
     */
    public function removeCouponCode(RemoveCouponRequest $request)
    {
        /** @var ShoppingCartInterface|HasCouponCodesInterface $shoppingCart */
        $shoppingCart = app(ShoppingCartInterface::class);
        $shoppingCart->removeCouponCode($request->get('coupon'));

        $request->flash();
        return $this->getCheckoutInformation();
    }

    /**
     * @return View
     */
    public function index()
    {
        return $this->addressFormView();
    }

    /**
     * @return View
     */
    protected function addressFormView(): View
    {
        return view('templates.checkout')->with('links', $this->links);
    }

    public function checkoutUser(Request $request) {
        if(!$request->ajax()) return response('Only ajax requests please', Response::HTTP_NOT_IMPLEMENTED);
        $checkoutService = new CheckoutService();

        $siteUser = $checkoutService->getUser();
        if($siteUser) return new SiteUserResource($siteUser);
        return response(null, Response::HTTP_NO_CONTENT);
    }


}