File: D:/HostingSpaces/ReturnIndustries/return-industries.nl/app/Komma/Shop/Checkout/CheckoutService.php
<?php
namespace App\Komma\Shop\Checkout;
use App\Komma\Addresses\AddressService;
use App\Komma\Addresses\Models\Address;
use App\Komma\Shop\Cart\ShoppingCartItemInterface;
use App\Komma\Shop\Orders\Kms\OrderServiceInterface;
use App\Komma\Shop\Orders\Models\Order;
use App\Komma\Shop\Orders\OrderNumberSequence;
use App\Komma\Shop\Orders\OrderStatus;
use App\Komma\Shop\Payment\PaymentServiceInterface;
use App\Komma\Shop\Products\Product\Product;
use App\Komma\Shop\Products\Product\ProductServiceInterface;
use App\Komma\Shop\Products\ProductComposite\ProductComposite;
use App\Komma\Shop\Products\ProductComposite\ProductCompositeServiceInterface;
use App\Komma\Shop\Products\ProductGroup\ProductGroup;
use App\Komma\Shop\Products\ProductGroup\ProductGroupServiceInterface;
use App\Komma\Users\Models\SiteUser;
use Illuminate\Http\RedirectResponse;
/**
* Class CheckoutService
*
* The checkout service sits between Order related, product related and payment related code.
* It directs those domains to work together.
*
* @package App\Komma\Shop\Checkout
*/
class CheckoutService implements CheckoutServiceInterface
{
/** @var OrderServiceInterface $orderService */
private $orderService;
/** @var ProductServiceInterface $productService */
private $productService;
/** @var ProductGroupServiceInterface $productGroupService */
private $productGroupService;
/** @var ProductCompositeServiceInterface $productCompositeService */
private $productCompositeService;
/** @var PaymentServiceInterface $paymentService */
private $paymentService;
/** @var AddressService $addressService */
private $addressService;
/** @var OrderNumberSequence */
private $orderNumberGenerator;
public function __construct()
{
$this->orderService = app(OrderServiceInterface::class);
$this->productService = app(ProductServiceInterface::class);
$this->productGroupService = app(ProductGroupServiceInterface::class);
$this->productCompositeService = app(ProductCompositeServiceInterface::class);
$this->paymentService = app(PaymentServiceInterface::class);
$this->addressService = app(AddressService::class);
$this->orderNumberGenerator = app(OrderNumberSequence::class);
}
/**
* Initializes a payment request for the user that owns the order.
*
* @param Order $order
* @return RedirectResponse
*/
public function startPaymentForOrder(Order $order): RedirectResponse
{
$pspAdapter = $this->paymentService->getAdapter();
$transaction = $pspAdapter->createTransaction($order);
return $pspAdapter->redirectForPayment($transaction);
}
/**
* @param ShoppingCartItemInterface[] $shoppingCartItems
* @param SiteUser $customer
* @param Address $addressForShipping
* @param Address $addressForInvoice
* @return Order|null
* @throws \Throwable
*/
public function createOrderFromShoppingCartItems(array $shoppingCartItems, SiteUser $customer, Address $addressForShipping, Address $addressForInvoice): ?Order {
foreach ($shoppingCartItems as $shoppingCartItem) {
if (!is_a($shoppingCartItem, ShoppingCartItemInterface::class)) {
throw new \RuntimeException('The given "ShoppingCartItem" wasn\'t an instance of: ' . ShoppingCartItemInterface::class);
}
}
$order = null;
\DB::transaction(function () use ($shoppingCartItems, $customer, $addressForShipping, $addressForInvoice, &$order) {
//Convert the addresses into shipping and invoice addresses
$attributes = $addressForShipping->getAttributes();
if (isset($attributes['id'])) {
unset($attributes['id']);
} //If you don't do this, saving will skip an id somehow. Even if you exclude id in the fillable or included it in the guarded properties of the model. laravel bug?
$attributes = $addressForInvoice->getAttributes();
if (isset($attributes['id'])) {
unset($attributes['id']);
} //If you don't do this, saving will skip an id somehow. Even if you exclude id in the fillable or included it in the guarded properties of the model. laravel bug?
//Create the order, link customer and addresses to it and save it. We need to save it because the ordered productables need a order id
/** @var Order $order */
$order = $this->orderService->newModel();
$order->order_number = (string) $this->orderNumberGenerator->next();
$order->customer()->associate($customer);
$order = $this->fillOrderWithInvoiceAddressFromAddress($order, $addressForInvoice);
$order = $this->fillOrderWithShippingAddressFromAddress($order, $addressForShipping);
$order->save();
//Loop over all shopping cart items and create ordered products for them. And link them to the order
foreach ($shoppingCartItems as $shoppingCartItem) {
$productable = $shoppingCartItem->getProductable();
$shoppingCartItem->getTotal(true);
$shoppingCartItem->getAmount();
$productableClassName = get_class($productable);
switch ($productableClassName) {
case Product::class:
/** @var Product $productable */
$this->productService->createOrderedProductFromProduct($productable, $order);
break;
case ProductGroup::class:
/** @var ProductGroup $productable */
$this->productGroupService->createOrderedProductGroupFromProductGroup($productable, $order);
break;
case ProductComposite::class;
/** @var ProductComposite $productable */
$this->productCompositeService->createOrderedProductCompositeProductComposite($productable, $order);
break;
default:
return null;
}
};
$this->orderService->calculateTotalOrderPrice($order);
$order->status = OrderStatus::NEW;
$order->save();
});
return $order;
}
/**
* @param Order $order
* @param Address $address
* @return Order
*/
private function fillOrderWithShippingAddressFromAddress(Order $order, Address $address): Order
{
foreach($address->getFillable() as $index => $attributeName) $order['shipping_'.$attributeName] = $address->$attributeName;
return $order;
}
/**
* @param Order $order
* @param Address $address
* @return Order
*/
private function fillOrderWithInvoiceAddressFromAddress(Order $order, Address $address)
{
foreach($address->getFillable() as $index => $attributeName) $order['invoice_'.$attributeName] = $address->$attributeName;
return $order;
}
/**
* Returns the last used shipping address for user or null if that not exists.
*
* @param SiteUser $user
* @return Address
*/
public function getLastUsedShippingAddressForUser(SiteUser $user): ? Address
{
$addressQuery = $user->addresses();
/** @var Order $latestOrder */
$latestOrder = $user->orders()->latest()->first();
if(!$latestOrder) return null;
$attributes = $latestOrder->getShippingAddressAttributes();
foreach ($attributes as $attribute => $value) $addressQuery->where(str_replace('shipping_', '', $attribute), '=', $value);
return $addressQuery->first();
}
/**
* Returns the last used invoice address for the user or null if that not exists
*
* @param SiteUser $user
* @return Address
* @see Address
*/
public function getLastUsedInvoiceAddressForUser(SiteUser $user): ? Address
{
$addressQuery = $user->addresses();
/** @var Order $latestOrder */
$latestOrder = $user->orders()->latest()->first();
if(!$latestOrder) return null;
$attributes = $latestOrder->getInvoiceAddressAttributes();
foreach ($attributes as $attribute => $value) $addressQuery->where(str_replace('invoice_', '', $attribute), '=', $value);
return $addressQuery->first();
}
}