first upload all files

This commit is contained in:
NW
2023-06-11 13:14:03 +01:00
parent f14dbc52b5
commit c08b36d1b6
1705 changed files with 106852 additions and 0 deletions

View File

@@ -0,0 +1,29 @@
<?php
namespace Modules\Checkout\Events;
use Modules\Order\Entities\Order;
use Illuminate\Queue\SerializesModels;
class OrderPlaced
{
use SerializesModels;
/**
* The instance of order.
*
* @var \Modules\Order\Entities\Order
*/
public $order;
/**
* Create a new event instance.
*
* @param \Modules\Order\Entities\Order $order
* @return void
*/
public function __construct(Order $order)
{
$this->order = $order;
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace Modules\Checkout\Http\Controllers;
use Exception;
use Modules\Order\Entities\Order;
use Modules\Payment\Facades\Gateway;
use Modules\Checkout\Events\OrderPlaced;
use Modules\Checkout\Services\OrderService;
class CheckoutCompleteController
{
/**
* Store a newly created resource in storage.
*
* @param int $orderId
* @param \Modules\Checkout\Services\OrderService $orderService
* @return \Illuminate\Http\Response
*/
public function store($orderId, OrderService $orderService)
{
$order = Order::findOrFail($orderId);
$gateway = Gateway::get(request('paymentMethod'));
try {
$response = $gateway->complete($order);
} catch (Exception $e) {
$orderService->delete($order);
return response()->json([
'message' => $e->getMessage(),
], 403);
}
$order->storeTransaction($response);
event(new OrderPlaced($order));
if (! request()->ajax()) {
return redirect()->route('checkout.complete.show');
}
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show()
{
$order = session('placed_order');
if (is_null($order)) {
return redirect()->route('home');
}
return view('public.checkout.complete.show', compact('order'));
}
}

View File

@@ -0,0 +1,97 @@
<?php
namespace Modules\Checkout\Http\Controllers;
use Exception;
use Modules\Support\Country;
use Modules\Cart\Facades\Cart;
use Modules\Page\Entities\Page;
use Illuminate\Routing\Controller;
use Modules\Payment\Facades\Gateway;
use Modules\User\Services\CustomerService;
use Modules\Checkout\Services\OrderService;
use Modules\Address\Entities\DefaultAddress;
use Modules\Cart\Http\Middleware\CheckCartStock;
use Modules\Order\Http\Requests\StoreOrderRequest;
use Modules\Cart\Http\Middleware\CheckCouponUsageLimit;
use Modules\Cart\Http\Middleware\RedirectIfCartIsEmpty;
class CheckoutController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware([
RedirectIfCartIsEmpty::class,
CheckCartStock::class,
CheckCouponUsageLimit::class,
]);
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
Gateway::all();
return view('public.checkout.create', [
'cart' => Cart::instance(),
'countries' => Country::supported(),
'gateways' => Gateway::all(),
'defaultAddress' => auth()->user()->defaultAddress ?? new DefaultAddress,
'addresses' => $this->getAddresses(),
'termsPageURL' => Page::urlForPage(setting('storefront_terms_page')),
]);
}
/**
* Get addresses for the logged in user.
*
* @return \Illuminate\Support\Collection
*/
private function getAddresses()
{
if (auth()->guest()) {
return collect();
}
return auth()->user()->addresses->keyBy('id');
}
/**
* Store a newly created resource in storage.
*
* @param \Modules\Order\Http\Requests\StoreOrderRequest $request
* @param \Modules\User\Services\CustomerService $customerService
* @param \Modules\Checkout\Services\OrderService $orderService
* @return \Illuminate\Http\Response
*/
public function store(StoreOrderRequest $request, CustomerService $customerService, OrderService $orderService)
{
if (auth()->guest() && $request->create_an_account) {
$customerService->register($request)->login();
}
$order = $orderService->create($request);
$gateway = Gateway::get($request->payment_method);
try {
$response = $gateway->purchase($order, $request);
} catch (Exception $e) {
$orderService->delete($order);
return response()->json([
'message' => $e->getMessage(),
], 403);
}
return response()->json($response);
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace Modules\Checkout\Http\Controllers;
use Illuminate\Http\Request;
use Modules\Cart\Facades\Cart;
use Modules\Order\Entities\Order;
class PaymentCanceledController
{
/**
* Store a newly created resource in storage.
*
* @param int $orderId
* @param string $paymentMethod
* @param \Modules\Checkout\Services\OrderService $orderService
* @return \Illuminate\Http\Response
*/
public function store(Request $request, $orderId)
{
Order::where('id', $orderId)->forceDelete();
Cart::restoreStock();
if ($request->ajax()) {
return response()->json(
[
'success' => true,
'message' => trans('payment::messages.payment_cancelled'),
],
200,
);
}
return redirect()
->route('checkout.create')
->with('error', trans('payment::messages.payment_cancelled'));
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace Modules\Checkout\Listeners;
class AddPlacedOrderToSession
{
/**
* Handle the event.
*
* @param \Modules\Checkout\Events\OrderPlaced $event
* @return void
*/
public function handle($event)
{
session()->flash('placed_order', $event->order);
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace Modules\Checkout\Listeners;
use Swift_TransportException;
use Modules\Checkout\Mail\Invoice;
use Modules\Checkout\Mail\NewOrder;
use Illuminate\Support\Facades\Mail;
use Modules\Checkout\Events\OrderPlaced;
class SendNewOrderEmails
{
/**
* Handle the event.
*
* @param \Modules\Checkout\Events\OrderPlaced $event
* @return void
*/
public function handle(OrderPlaced $event)
{
try {
if (setting('admin_order_email')) {
Mail::to(setting('store_email'))
->send(new NewOrder($event->order));
}
if (setting('invoice_email')) {
Mail::to($event->order->customer_email)
->send(new Invoice($event->order));
}
} catch (Swift_TransportException $e) {
//
}
}
}

View File

@@ -0,0 +1,64 @@
<?php
namespace Modules\Checkout\Listeners;
use Modules\Sms\Sms;
use Modules\Order\Entities\Order;
use Modules\Checkout\Events\OrderPlaced;
use Modules\Sms\Exceptions\SmsException;
class SendNewOrderSms
{
/**
* Handle the event.
*
* @param \Modules\Checkout\Events\OrderPlaced $event
* @return void
*/
public function handle(OrderPlaced $event)
{
try {
$this->sendAdminSms($event->order);
$this->sendCustomerSms($event->order);
} catch (SmsException $e) {
//
}
}
private function sendAdminSms(Order $order)
{
if (! setting('new_order_admin_sms')) {
return;
}
Sms::send(
setting('store_phone'),
$this->adminMessage($order)
);
}
private function adminMessage(Order $order)
{
return trans('sms::messages.new_order', ['order_id' => $order->id]);
}
private function sendCustomerSms(Order $order)
{
if (! setting('new_order_sms')) {
return;
}
Sms::send(
$order->customer_phone,
$this->customerMessage($order)
);
}
private function customerMessage(Order $order)
{
return trans('sms::messages.order_has_been_placed', [
'first_name' => $order->customer_first_name,
'order_id' => $order->id,
]);
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace Modules\Checkout\Listeners;
use Modules\Order\Entities\Order;
class UpdateOrderStatus
{
/**
* Handle the event.
*
* @param \Modules\Checkout\Events\OrderPlaced $event
* @return void
*/
public function handle($event)
{
$event->order->update(['status' => Order::PENDING]);
}
}

View File

@@ -0,0 +1,54 @@
<?php
namespace Modules\Checkout\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Modules\Media\Entities\File;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;
class Invoice extends Mailable implements ShouldQueue
{
use Queueable, SerializesModels;
/**
* The instance of the order.
*
* @var \Modules\Order\Entities\Order
*/
public $order;
/**
* Create a new message instance.
*
* @param \Modules\Order\Entities\Order $order
* @return void
*/
public function __construct($order)
{
$this->order = $order;
}
/**
* Build the message.
*
* @return $this
*/
public function build()
{
app()->setLocale($this->order->locale);
$this->order->load('products');
return $this->subject(trans('storefront::invoice.subject', ['id' => $this->order->id]))
->view("emails.{$this->getViewName()}", [
'logo' => File::findOrNew(setting('storefront_mail_logo'))->path,
]);
}
private function getViewName()
{
return 'invoice' . (is_rtl() ? '_rtl' : '');
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace Modules\Checkout\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Modules\Media\Entities\File;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;
class NewOrder extends Mailable implements ShouldQueue
{
use Queueable, SerializesModels;
public $order;
/**
* Create a new message instance.
*
* @param \Modules\Order\Entities\Order $order
* @return void
*/
public function __construct($order)
{
$this->order = $order;
}
/**
* Build the message.
*
* @return $this
*/
public function build()
{
return $this->subject(trans('checkout::mail.new_order_subject'))
->view("emails.{$this->getViewName()}", [
'logo' => File::findOrNew(setting('storefront_mail_logo'))->path,
]);
}
private function getViewName()
{
return 'new_order' . (is_rtl() ? '_rtl' : '');
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace Modules\Checkout\Providers;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
class EventServiceProvider extends ServiceProvider
{
/**
* The event listener mappings for the application.
*
* @var array
*/
protected $listen = [
\Modules\Checkout\Events\OrderPlaced::class => [
\Modules\Checkout\Listeners\UpdateOrderStatus::class,
\Modules\Checkout\Listeners\SendNewOrderEmails::class,
\Modules\Checkout\Listeners\SendNewOrderSms::class,
\Modules\Checkout\Listeners\AddPlacedOrderToSession::class,
],
];
}

View File

@@ -0,0 +1,31 @@
<?php
return [
'billing' => [
'first_name' => 'First Name',
'last_name' => 'Last Name',
'address_1' => 'Address Line 1',
'address_2' => 'Address Line 2 (optional)',
'city' => 'City',
'zip' => 'Postcode / ZIP',
'country' => 'Country',
'state' => 'State / Province',
],
'shipping' => [
'first_name' => 'First Name',
'last_name' => 'Last Name',
'address_1' => 'Address Line 1',
'address_2' => 'Address Line 2 (optional)',
'city' => 'City',
'zip' => 'Postcode / ZIP',
'country' => 'Country',
'state' => 'State / Province',
],
'street_address' => 'Street Address',
'customer_email' => 'Email',
'customer_phone' => 'Phone',
'create_an_account' => 'Create an account?',
'password' => 'Password',
'ship_to_a_different_address' => 'Ship to a different address',
'order_note' => 'Order Note',
];

View File

@@ -0,0 +1,8 @@
<?php
return [
'new_order_subject' => 'A new order has been placed',
'new_order_text' => 'A new order has been placed. The order id is #:order_id.',
'view_order' => 'View Order',
'if_you\re_having_trouble' => 'If youre having trouble clicking the "View Order" button, copy and paste the URL below into your web browser:',
];

View File

@@ -0,0 +1,12 @@
<?php
use Illuminate\Support\Facades\Route;
Route::get('checkout', 'CheckoutController@create')->name('checkout.create');
Route::post('checkout', 'CheckoutController@store')->name('checkout.store');
Route::get('checkout/{orderId}/complete', 'CheckoutCompleteController@store')->name('checkout.complete.store');
Route::get('checkout/complete', 'CheckoutCompleteController@show')->name('checkout.complete.show');
Route::get('checkout/{orderId}/payment-canceled', 'PaymentCanceledController@store')->name('checkout.payment_canceled.store');

View File

@@ -0,0 +1,195 @@
<?php
namespace Modules\Checkout\Services;
use Modules\Cart\CartTax;
use Modules\Cart\CartItem;
use Modules\Cart\Facades\Cart;
use Modules\Order\Entities\Order;
use Modules\Address\Entities\Address;
use Modules\FlashSale\Entities\FlashSale;
use Modules\Currency\Entities\CurrencyRate;
use Modules\Address\Entities\DefaultAddress;
use Modules\Shipping\Facades\ShippingMethod;
class OrderService
{
public function create($request)
{
$this->mergeShippingAddress($request);
$this->saveAddress($request);
$this->addShippingMethodToCart($request);
return tap($this->store($request), function ($order) {
$this->storeOrderProducts($order);
$this->storeOrderDownloads($order);
$this->storeFlashSaleProductOrders($order);
$this->incrementCouponUsage($order);
$this->attachTaxes($order);
$this->reduceStock();
});
}
private function mergeShippingAddress($request)
{
$request->merge([
'shipping' => $request->ship_to_a_different_address ? $request->shipping : $request->billing,
]);
}
private function saveAddress($request)
{
if (auth()->guest()) {
return;
}
if ($request->newBillingAddress) {
$address = auth()
->user()
->addresses()
->create($this->extractAddress($request->billing));
$this->makeDefaultAddress($address);
}
if ($request->ship_to_a_different_address && $request->newShippingAddress) {
auth()
->user()
->addresses()
->create($this->extractAddress($request->shipping));
}
}
private function extractAddress($data)
{
return [
'first_name' => $data['first_name'],
'last_name' => $data['last_name'],
'address_1' => $data['address_1'],
'address_2' => $data['address_2'] ?? null,
'city' => $data['city'],
'state' => $data['state'],
'zip' => $data['zip'],
'country' => $data['country'],
];
}
private function makeDefaultAddress(Address $address)
{
if (
auth()
->user()
->addresses()
->count() > 1
) {
return;
}
DefaultAddress::create([
'address_id' => $address->id,
'customer_id' => auth()->id(),
]);
}
private function addShippingMethodToCart($request)
{
if (!Cart::allItemsAreVirtual() && !Cart::hasShippingMethod()) {
Cart::addShippingMethod(ShippingMethod::get($request->shipping_method));
}
}
private function store($request)
{
return Order::create([
'customer_id' => auth()->id(),
'customer_email' => $request->customer_email,
'customer_phone' => $request->customer_phone,
'customer_first_name' => $request->billing['first_name'],
'customer_last_name' => $request->billing['last_name'],
'billing_first_name' => $request->billing['first_name'],
'billing_last_name' => $request->billing['last_name'],
'billing_address_1' => $request->billing['address_1'],
'billing_address_2' => $request->billing['address_2'] ?? null,
'billing_city' => $request->billing['city'],
'billing_state' => $request->billing['state'],
'billing_zip' => $request->billing['zip'],
'billing_country' => $request->billing['country'],
'shipping_first_name' => $request->shipping['first_name'],
'shipping_last_name' => $request->shipping['last_name'],
'shipping_address_1' => $request->shipping['address_1'],
'shipping_address_2' => $request->shipping['address_2'] ?? null,
'shipping_city' => $request->shipping['city'],
'shipping_state' => $request->shipping['state'],
'shipping_zip' => $request->shipping['zip'],
'shipping_country' => $request->shipping['country'],
'sub_total' => Cart::subTotal()->amount(),
'shipping_method' => Cart::shippingMethod()->name(),
'shipping_cost' => Cart::shippingCost()->amount(),
'coupon_id' => Cart::coupon()->id(),
'discount' => Cart::discount()->amount(),
'total' => Cart::total()->amount(),
'payment_method' => $request->payment_method,
'currency' => currency(),
'currency_rate' => CurrencyRate::for(currency()),
'locale' => locale(),
'status' => Order::PENDING_PAYMENT,
'note' => $request->order_note,
]);
}
private function storeOrderProducts(Order $order)
{
Cart::items()->each(function (CartItem $cartItem) use ($order) {
$order->storeProducts($cartItem);
});
}
private function storeOrderDownloads(Order $order)
{
Cart::items()->each(function (CartItem $cartItem) use ($order) {
$order->storeDownloads($cartItem);
});
}
private function storeFlashSaleProductOrders(Order $order)
{
Cart::items()->each(function (CartItem $cartItem) use ($order) {
if (!FlashSale::contains($cartItem->product)) {
return;
}
FlashSale::pivot($cartItem->product)
->orders()
->attach([
$cartItem->product->id => [
'order_id' => $order->id,
'qty' => $cartItem->qty,
],
]);
});
}
private function incrementCouponUsage()
{
Cart::coupon()->usedOnce();
}
private function attachTaxes(Order $order)
{
Cart::taxes()->each(function (CartTax $cartTax) use ($order) {
$order->attachTax($cartTax);
});
}
public function reduceStock()
{
Cart::reduceStock();
}
public function delete(Order $order)
{
$order->delete();
Cart::restoreStock();
}
}

View File

@@ -0,0 +1,27 @@
{
"name": "fleetcart/checkout",
"description": "The FleetCart Checkout Module.",
"authors": [
{
"name": "Envay Soft",
"email": "envaysoft@gmail.com"
}
],
"require": {
"php": "^8.0.2"
},
"autoload": {
"psr-4": {
"Modules\\Checkout\\": ""
}
},
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"config": {
"sort-packages": true
},
"minimum-stability": "dev"
}

View File

@@ -0,0 +1,9 @@
{
"name": "Checkout",
"alias": "checkout",
"description": "The FleetCart Checkout Module.",
"priority": 100,
"providers": [
"Modules\\Checkout\\Providers\\EventServiceProvider"
]
}