HEX
Server: LiteSpeed
System: Linux sv4.hami.host 5.14.0-611.54.3.el9_7.x86_64 #1 SMP PREEMPT_DYNAMIC Thu May 7 16:31:24 EDT 2026 x86_64
User: armgroup (1008)
PHP: 8.2.32
Disabled: show_source, system, shell_exec, passthru, exec, popen, proc_open, mail, socket_create, socket_create_listen, socket_create_pair, link, dl, openlog, syslog, stream_socket_server, curl_multi_init
Upload Files
File: //home/armgroup/libs/app/Http/Livewire/Frontend/Checkout/Index.php
<?php

namespace App\Http\Livewire\Frontend\Checkout;

use App\Enums\DiscountableType;
use App\Enums\OrderPaymentType;
use App\Events\OrderSaved;
use App\Http\Livewire\ConvertEmptyStringsToNull;
use App\Http\Livewire\General;
use App\Models\Discount;
use App\Models\Order;
use App\Models\Product;
use Gloudemans\Shoppingcart\Facades\Cart;
use Illuminate\Support\Facades\DB;
use Livewire\Component;
use Shetabit\Multipay\Invoice;
use Shetabit\Payment\Facade\Payment as shetabitPayment;

class Index extends Component
{
    use ConvertEmptyStringsToNull, General;

    public $cartTotal;

    public $total;

    public $description;

    public $shipping_price = null;

    public $shipping_type = null;

    public $shippings = [];

    public $delivery_date = null;

    public $delivery_time = null;

    public $address;

    public $show_delivery = false;

    public $discount_code = null;

    public $courier_price = 0;

    public $express_price = 0;

    public $max_sending_days = 0;

    public $discount_price = 0;

    public $discount_id = null;

    public $enable_dates = [];

    public $enable_times = [];

    public $action;

    public $method;

    public $inputs = [];

    public function applyDiscountCode()
    {
        if ($this->discount_price || $this->discount_id) {
            abort(403);
        }
        $this->validate([
            'discount_code' => 'required|exists:discounts,code',
        ]);

        $discount = Discount::where('code', $this->discount_code)->first();

        if (($result = $discount->isValid($this->cartTotal, auth()->user())) !== 'OK') {
            $this->addError('discount_code', $result);

            return;
        }

        $items = Cart::content();
        $include_products_total_price = $this->calculateDiscountableProductsPrice($discount, $items, Product::whereIn('id', $items->pluck('id')->toArray())->get());

        if ($include_products_total_price <= 0) {
            $this->addError('discount_code', 'هیچ محصولی که شامل این کد تخفیف باشد در سبد خرید شما وجود ندارد.');

            return;
        }

        if ($discount->exceedMinOrderPriceLimitation($include_products_total_price)) {
            $this->addError('discount_code', 'شما باید حداقل به میزان '.number_format($discount->minimum_order_price).' تومان از محصولات شامل کد تخفیف خرید کنید.');

            return;
        }

        $this->discount_id = $discount->id;
        $this->discount_price = $discount->calculateDiscountPrice($include_products_total_price);
        $this->total = $this->calculateTotal();
    }

    private function calculateDiscountableProductsPrice($discount, $items, $products)
    {
        $include_products = [];

        if (is_null($discount->discountable_items)) {
            $include_products = $products->pluck('id')->toArray();
        } else {
            switch ($discount->discountable_type) {
                case DiscountableType::PRODUCT:
                    $include_products = array_intersect($products->pluck('id')->toArray(), $discount->discountable_items);
                    break;
                case DiscountableType::BRAND:
                    $include_products = array_intersect($products->pluck('brand_id')->toArray(), $discount->discountable_items);
                    break;
                case DiscountableType::CATEGORY:
                    foreach ($products as $product) {
                        if (array_intersect($product->categories()->first()?->parents?->pluck('id')?->toArray(), $discount->discountable_items)) {
                            $include_products[] = $product->id;
                        }
                    }
                    break;
            }
        }

        $result = 0;
        foreach ($items as $item) {
            if (in_array($item->id, $include_products)) {
                $result += $item->price * $item->qty;
            }
        }

        return $result;
    }

    public function cancelDiscountCode()
    {
        $this->discount_price = 0;
        $this->discount_code = null;
        $this->discount_id = null;
        $this->total = $this->calculateTotal();
    }

    public function mount()
    {
        $this->max_sending_days = calculateMaxSendingDays();
        if ($this->checkFreeShipping()) {
            $this->shipping_price = 'رایگان';
        } else {
            if ($this->address->city->express_status) {
                $this->shippings[] = $this->shipping_type = Order::SHIPPING_EXPRESS;
                $this->shipping_price = $this->express_price = calculateShippingPrice($this->address->city, Order::SHIPPING_EXPRESS);
            }
            if ($this->address->city->courier_status) {
                $this->shippings[] = $this->shipping_type = Order::SHIPPING_COURIER;
                $this->shipping_price = $this->courier_price = calculateShippingPrice($this->address->city, Order::SHIPPING_COURIER);
            }
        }
        $this->total = $this->calculateTotal();

        if ($this->address->city->courier_status && $this->shipping_type !== Order::SHIPPING_EXPRESS) {
            $now = now();
            $disable_dates = DB::table('sending_days')
                ->where('day', '>', $now->clone()->format('Y-m-d'))
                ->pluck('day')
                ->toArray();

            $count = 0;
            $i = -1;
            while ($count != 7) {
                $date = $now->clone()->addDays($this->max_sending_days + $i++)->format('Y-m-d');
                if (! in_array($date, $disable_dates)) {
                    $count++;
                    array_push($this->enable_dates, $date);
                }
            }
            $this->enable_times = preg_split('/\r\n|\r|\n/', settings('shipping-enable-times'));
            $this->show_delivery = true;
        }
    }

    public function updatedShippingType($value)
    {
        if ($value == Order::SHIPPING_EXPRESS && $this->address->city->express_status) {
            $this->show_delivery = false;
            $this->shipping_price = $this->express_price;
        } elseif ($value == Order::SHIPPING_COURIER && $this->address->city->courier_status) {
            $this->show_delivery = true;
            $this->shipping_price = $this->courier_price;
        } else {
            $this->ajaxDoneMessage('روش پست انتخاب شده اشتباه می‌باشد', 'error');
        }
        $this->total = $this->calculateTotal();
        if ((! $this->checkFreeShipping()) && $this->shipping_price < 0) {
            $this->ajaxDoneMessage('خطایی در محاسبه هزینه ارسال بوجود آمده، لطفا صفحه را رفرش کنید و در صورت برطرف نشدن مشکل بعدا تلاش کنید', 'error');
        }
    }

    private function calculateTotal()
    {
        return $this->cartTotal + (is_numeric($this->shipping_price) ? $this->shipping_price : 0) - $this->discount_price;
    }

    protected function rules()
    {
        $rules['description'] = ['nullable', 'string', 'max:10000'];
        if ($this->show_delivery) {
            $rules['delivery_date'] = [
                'required',
                'date',
                'in:'.implode(',', $this->enable_dates),
            ];
            $rules['delivery_time'] = ['required', 'in:'.implode(',', $this->enable_times)];
        }

        return $rules;
    }

    public function save($payment_type)
    {
        if ($this->cartTotal != (int) Cart::subtotal(0, '', '')) {
            $this->doneMessage('سبد خرید تغییر کرده کرده است لطفا مجددا اقدام کنید.', 'error');

            return redirect()->route('checkout.index');
        }
        if (! checkSaleStatus()) {
            $this->doneMessage('در حال حاضر فروش محصولات غیرفعال می‌باشد، لطفا بعدا امتحان کنید.', 'warning');

            return redirect()->route('index');
        }
        if ($this->total != $this->calculateTotal()) {
            $this->doneMessage('مشکلی در محاسبه هزینه سفارش پیش آمده، مجددا امتحان کنید.', 'error');

            return redirect()->route('checkout.index');
        }
        if ((! in_array($payment_type, [OrderPaymentType::OFFLINE, OrderPaymentType::WALLET])) && $this->total < 500) {
            $this->ajaxDoneMessage('امکان پرداخت آنلاین برای مبالغ کمتر از ۵۰۰ تومان نمی‌باشد', 'error');

            return;
        }

        $items = Cart::content();
        $products = Product::whereIn('id', $items->pluck('id')->toArray())->get();
        if (! session()->has('address_id') || count($items) == 0) {
            return redirect()->route('addresses.index');
        }

        $address = auth()->user()->addresses()->where('address_id', session('address_id'))->with(['city', 'city.province'])->first();
        if (! $address) {
            session()->forget('address_id');

            return redirect()->route('addresses.index');
        }
        if ($this->checkPaymentType($payment_type)) {
            $this->ajaxDoneMessage('لطفا روش پرداخت درست را انتخاب کنید!', 'error');

            return;
        }
        if (! $this->checkFreeShipping()) {
            // وقتی هزینه پست خطا برگردونه مثلا اگه api خطا بده و یا هر مورد دیگه یا روش تحویل رو انتخاب نکرده باشه
            if ($this->shipping_price < 0 || is_null($this->shipping_type)) {
                $this->ajaxDoneMessage('خطایی در محاسبه هزینه ارسال بوجود آمده، لطفا صفحه را رفرش کنید و در صورت برطرف نشدن مشکل بعدا تلاش کنید', 'error');

                return;
            }
            // وقتی توی صفحه صروتحساب هست و قیمت ارسال رو مدیر تغییر داده یا از یه تب دیگه یه محصول جدید اضافه کرده و یا وقتی کاربر از یه تب دیگه استان و شهرش رو تغییر میده
            if ($this->shipping_price != calculateShippingPrice($address->city, $this->shipping_type)) {
                $this->doneMessage('هزینه ارسال تغییر کرده است، لطفا مجددا تلاش کنید.', 'error');

                return redirect()->route('checkout.index');
            }
        }
        // اینجا میتونم درستی تاریخ و زمان تحویل رو دوباره چک کنم ولی فک نکنم لزومی داشته باشه.
        $this->validate();
        foreach ($items as $item) {
            $canBuyStatus = $products->find($item->id)->canBuy($item->qty, $item->options, $item->price);
            if ($canBuyStatus !== 'OK') {
                $this->doneMessage($canBuyStatus, 'error');

                return redirect()->route('cart.index');
            }
        }

        if (! $this->canUseDiscountCode($items, $products)) {
            $this->doneMessage('مشکلی در ثبت کد تخفیف بوجود آمده است، لطفا کد خود را مجددا ثبت کنید.', 'error');

            return redirect()->route('checkout.index');
        }

        DB::transaction(function () use ($payment_type, $items, $address, $products) {
            $discount = 0;
            $order_product_data = [];
            $locked_stock_data = [];
            foreach ($items as $item) {
                $product = $products->find($item->id);
                $product_discount = $item->name != 'none' ? ($item->name - $item->price) : 0;
                $order_product_data[] = [
                    'product_id' => $product->id,
                    'option_id' => $item->options['option_id'] ?? null,
                    'value_id' => $item->options['value_id'] ?? null,
                    'quantity' => $item->qty,
                    'price' => $item->price,
                    'discount' => $product_discount,
                    'name' => $product->name.(isset($item->options['name']) ? (' ('.$item->options['name'].')') : ''),
                ];

                $locked_stock_data[] = [
                    'product_id' => $product->id,
                    'option_id' => count($item->options) ? $item->options['option_id'] : null,
                    'value_id' => count($item->options) ? $item->options['value_id'] : null,
                    'quantity' => $item->qty,
                ];

                $discount += ($product_discount * $item->qty);
            }
            $order = Order::create([
                'user_id' => auth()->id(),
                'city_id' => $this->address->city_id,
                'discount_id' => $this->discount_id,
                'tracking_code' => auth()->id().''.time(),
                'description' => $this->description,
                'price' => $this->total,
                'discount' => $discount,
                'discount_code_price' => $this->discount_price,
                'shipping_price' => is_numeric($this->shipping_price) ? $this->shipping_price : 0,
                'shipping_type' => $this->shipping_type,
                'delivery_date' => $this->show_delivery ? $this->delivery_date : null,
                'delivery_time' => $this->show_delivery ? $this->delivery_time : null,
                'delivery_code' => $this->show_delivery ? Order::createDeliveryCode() : null,
                'payment_type' => $payment_type,
            ]);

            $order->products()->attach($order_product_data);
            $order->addresses()->attach($address);
            $order->lockedStocks()->createMany($locked_stock_data);
            if (in_array($payment_type, [OrderPaymentType::OFFLINE, OrderPaymentType::WALLET])) {
                OrderSaved::dispatch($order);
                if ($payment_type == OrderPaymentType::WALLET) {
                    $order->wallets()->create([
                        'user_id' => auth()->id(),
                        'amount' => $order->price * -1,
                        'description' => "کسر بابت پرداخت سفارش {$order->tracking_code}",
                    ]);
                }
                $this->doneMessage('سفارش شما با موفقیت ثبت شد.');

                return redirect()->route('account.orders.show', $order);
            } else {
                $payment = $order->payments()->create([
                    'user_id' => auth()->id(),
                    'amount' => $order->price,
                    'ip' => request()->ip(),
                ]);

                try {
                    $data = json_decode(shetabitPayment::callbackUrl(route('verify.order', $payment->id))->purchase(
                        (new Invoice)->amount($order->price),
                        function ($driver, $transactionId) use ($payment) {
                            $payment->update([
                                'transaction_id' => $transactionId,
                                'driver' => config('payment.default'), // اگر چند درگاه داشته باشد میتوانیم با via مشخص کنیم و مقدار را اینجا هم بزاریم
                            ]);
                        }
                    )->pay()->toJson());

                    $this->action = $data->action;
                    $this->method = $data->method;
                    $this->inputs = (array) $data->inputs;

                    $this->dispatchBrowserEvent('submit-payment-form');
                } catch (\Exception $exception) {
                    $payment->update([
                        'error_message' => $exception->getMessage(),
                    ]);

                    $order->delete();

                    $this->doneMessage($exception->getMessage(), 'error');

                    return redirect()->route('cart.index');
                }
            }
        });
    }

    public function canUseDiscountCode($items, $products): bool
    {
        if ($this->discount_price && $this->discount_id) {
            $discount = Discount::where('code', $this->discount_code)->first();

            if (! $discount || ($this->discount_id != $discount->id)) {
                return false;
            }

            if ($discount->isValid($this->cartTotal, auth()->user()) !== 'OK') {
                return false;
            }

            $include_products_total_price = $this->calculateDiscountableProductsPrice($discount, $items, $products);

            if ($this->discount_price != $discount->calculateDiscountPrice($include_products_total_price)) {
                return false;
            }

            if ($discount->exceedMinOrderPriceLimitation($include_products_total_price)) {
                return false;
            }
        }

        return true;
    }

    public function render()
    {
        return view('livewire.frontend.checkout.index');
    }

    private function checkPaymentType($payment_type): bool
    {
        return (settings('offline-status') != 1 && $payment_type == OrderPaymentType::OFFLINE) ||
            (settings('online-status') != 1 && $payment_type == OrderPaymentType::ONLINE) ||
            (settings('wallet-status') != 1 && $payment_type == OrderPaymentType::WALLET) ||
            ($payment_type == OrderPaymentType::WALLET && auth()->user()->credit < $this->total) ||
            (! in_array($payment_type, OrderPaymentType::asArray()));
    }

    private function checkFreeShipping(): bool
    {
        return settings('shipping-free-price') && ($this->cartTotal > settings('shipping-free-price'));
    }
}