header('X-Cart-Session'); if (!$sessionId) { $sessionId = (string) Str::uuid(); } $cart = Cart::firstOrCreate( [ 'session_id' => $sessionId, 'sts' => 1, ] ); return [$cart, $sessionId]; } public function getCart(Request $request) { [$cart, $sessionId] = $this->getOrCreateCart($request); return response()->json([ 'cart' => $cart->load('items'), 'session_id' => $sessionId ]); } public function addItem(Request $request) { [$cart, $sessionId] = $this->getOrCreateCart($request); $product = Product::select('id', 'title', 'group_type')->where('id', $request->product_id)->first(); $item = CartItem::where('cart_id', $cart->id) ->where('product_id', $request->product_id) ->first(); if ($item) { $item->increment('quantity'); } else { CartItem::create([ 'cart_id' => $cart->id, 'product_id' => $product->id, 'title' => $product->title, 'group_type' => $product->group_type, 'quantity' => 1, ]); } return response()->json([ 'message' => 'محصول اضافه شد.', 'session_id' => $sessionId ]); } public function removeItem(Request $request) { $request->validate([ 'product_id' => 'required|integer', ]); $sessionId = $request->header('X-Cart-Session'); if (!$sessionId) { return response()->json([ 'message' => 'Cart session not found' ], 400); } $cart = Cart::where('session_id', $sessionId) ->where('sts', 'open') ->first(); if (!$cart) { return response()->json([ 'message' => 'Active cart not found' ], 404); } $item = CartItem::where('cart_id', $cart->id) ->where('product_id', $request->product_id) ->first(); if (!$item) { return response()->json([ 'message' => 'Item not found in cart' ], 404); } if ($item->quantity > 1) { $item->decrement('quantity'); } else { $item->delete(); } return response()->json([ 'message' => 'Item updated successfully' ]); } public function checkout(Request $request) { // return $request; app(ConfigService::class)->assertTradingOpen(); $data = $request->json()->all(); $user= auth()->user(); if ($user->is_legal == 0 || !$user) { throw new UnprocessableEntityHttpException('لطفا ابتدا فرآیند احراز هویت خود را تکمیل کنید'); } // $data->validate([ // 'session_id' => 'required' // ]); //dd($data['products']); if(isset($data['gateway']) && $data['gateway'] !== 'wallet'){ return response()->json(['massage' => "Under construction👷🏻"], 501); } if( isset($data['session_id']) && ($data['session_id'] !== '' || $data['session_id'] !== null)){ $cart = Cart::where('session_id', $data['session_id']) ->where('sts', 1) ->first()->load('items'); if(!$cart){ return response()->json(['message' => 'سبد خرید پیدا نشد!'], 404); } // اتصال سبد به کاربر لاگین شده $cart->update([ 'customer_id' => auth('customer')->id(), // 'sts' => 2, ]); } else{ $items = []; [$cart, $sessionId] = $this->getOrCreateCart($request); $cart->customer_id = auth('customer')->id(); $cart->save(); $productIds = collect($data) ->pluck('product_id') ->unique() ->values(); $products = Product::select('id', 'title', 'group_type', 'inventory', 'amount', 'percent_fee_buy', 'cash_fee_buy', 'price_profit') ->whereIn('id', $productIds) ->get() ->keyBy('id'); $numbers = collect($data) ->filter(fn ($item) => is_array($item) && isset($item['product_id'])) ->groupBy('product_id') ->map(fn ($group) => $group->sum(fn ($item) => (int) ($item['number'] ?? 1))); foreach ($products as $product) { if(($product->inventory - $numbers[$product->id]) < 0){ return response()->json(['message' => "موجودی محصول {$product->title} کافی نیست!"], 422); } $items[] = [ 'cart_id' => $cart->id, 'product_id' => $product->id, 'title' => $product->title, 'group_type' => $product->group_type, 'quantity' => $numbers[$product->id] ?? 1, 'created_at'=> now(), 'updated_at'=> now(), ]; } CartItem::insert($items); $cart = Cart::where('session_id', $cart->session_id) ->first() ->load('items'); } $wallet = Wallet::where('owner', auth('customer')->id()) ->where('owner_type', auth('customer')->user()->type) ->whereNull('fund_id') ->whereNull('product_id') ->first(); if (!$wallet) { return response()->json(['message' => 'کیف پول یافت نشد!'], 404); } if (app(ConfigService::class)->isStore()) { return $this->checkoutStore($data, $cart, $wallet); } $priceRow = Price::latest()->first(); $goldPrice = $priceRow->price; $data['price'] = $goldPrice; $data['price_id'] = $priceRow->id; $data['price_original'] = $goldPrice; $data['customer_id'] = auth('customer')->id(); $total_price = 0; $total_amount = 0; $product_fees = 0; $wallet_interface = new WalletInterface($data['customer_id'], auth('customer')->user()->type); // قیمت‌گیری لحظه‌ای $data['amount'] = 0; $data['service_charge_percentage'] = 0; $data['fee'] = 0; foreach ($cart->items as $item) { $product = Product::find($item->product_id); // کارمزد درصدی هر واحد از محصول $feeAmount = $product->percent_fee_buy * $product->amount; //وزن نهایی هر واحد از محصول با احتساب کارمزد $finalAmount = $product->amount + $feeAmount; //وزن کل محصول با احتساب تعداد و کارمزد $total_amount += $product->amount * $item->quantity; //محاسبه مجموع وزن سبد با احتساب کارمزد $data['amount'] += $finalAmount * $item->quantity; //محاسبه مجموع درصد کارمزد سبد با احتساب تعداد $data['service_charge_percentage'] += $product->percent_fee_buy * $item->quantity; //محاسبه مجموع کارمزد ثابت سبد با احتساب تعداد $unitCashFee = ProductPriceService::resolveCashFeeRial( $product, 1, max((float) $goldPrice, ProductPriceService::productBasePriceRial($product)) ); $data['fee'] += $unitCashFee * $item->quantity; // قیمت نهایی هر گرم $priceWithFee = $goldPrice + $data['fee']; //محاسبه مجموع کارمزد ثابت سبد با احتساب تعداد $product_fees += $unitCashFee; // قیمت آیتم $itemTotalPrice = $finalAmount * $priceWithFee; // قیمت کل این آیتم با احتساب تعداد $itemTotalPrice = $item->quantity * $itemTotalPrice; //مبلغ سبد خرید با احتساب این آیتم $total_price += $itemTotalPrice; // ذخیره شفاف برای گزارش و فاکتور $item->update([ 'price' => $itemTotalPrice, 'gold_price' => $goldPrice ]); } //قیمت هر گرم با احتساب کارمزد ثابت $total_price_with_fees = $goldPrice + $product_fees; //مجموع کارمز ثابت و متغیر در سبد خرید $data['total_fee'] = $data['fee'] + ($total_price_with_fees * ($data['service_charge_percentage'] * $total_amount)); //قیمت هر گرم بدون احتساب کارمزدها $data['total_price_original'] = $total_amount * $goldPrice; //کارمزد طلایی $data['service_charge'] = $data['service_charge_percentage'] * $total_amount; //کارمزد ریالی $data['service_charge_price'] = $data['service_charge'] * $data['price_original']; //قیمت کل برای مشتری $data['total_price'] = $total_price; //مقدار قابل پرداخت $data['payment_amount'] = $data['total_price']; if (!empty($data[0]['discount_code'])) { $discountCtrl = app(GiftCardController::class); $discountResult = $discountCtrl->applyDiscount((string) $data[0]['discount_code'], $data['customer_id']); if (!empty($discountResult['message'])) { throw new UnprocessableEntityHttpException($discountResult['message']); } $data['discount'] = true; if (!empty($discountResult['amount'])) { $data['payment_amount'] = $data['total_price'] = $data['total_price'] - ($data['total_price'] * (float) $discountResult['amount']); } } $data['cart_id'] = (int)$cart->id; $data['sts'] = 1; if ($wallet->cash < $data['total_price']){ throw new UnprocessableEntityHttpException('موجودی کیف پول شما کافی نیست.'); } $data['direction'] = 1; // خرید $data['pay_at'] = now(); $order = Order::create($data); $cart->order_id = $order->id; $cart->save(); $tr = new TransactionInterface($wallet_interface, $_inp = [ "cash" => $data['payment_amount'], 'expire_at' => now(), 'active' => true, 'order_id' => $order->id, 'start_at' => now(), 'title' => "سبد خرید" ], 12, false, false); foreach ($cart->items as $item) { $productId = (int) $item->product_id; $customerId = (int) auth('customer')->id(); $ownerType = (int) auth('customer')->user()->type; if (self::isMainWalletProduct($productId)) { app(\App\Services\WalletBalanceService::class)->consolidateOrphanedForOwner( $customerId, $ownerType, $productId ); } $wallet_interface_product = new WalletInterface( $customerId, $ownerType, null, self::effectiveProductWalletId($productId) ); $product = Product::query()->find($productId); if ( $wallet_interface_product->wallet && $wallet_interface_product->wallet->product_id !== null && $product && self::isCountBasedProduct(self::productUnitPayload($product)) ) { $wallet_interface_product->wallet->gold = intval($wallet_interface_product->wallet->gold); $wallet_interface_product->wallet->save(); } new TransactionInterface($wallet_interface_product, [ "gold" => $item->quantity, "product_id" => $productId, 'expire_at' => now(), 'active' => true, 'order_id' => $order->id, 'start_at' => now(), ], 1, true, false); } $order->sts = 2; // پرداخت شده // $order->direction = 3; // سبد خرید $order->payment_sts = 4; // وضعیت پرداخت شده $order->save(); foreach ($cart->items as $item) { Product::where('id', $item->product_id)->decrement('inventory', $item->quantity); // $product = Product::find($item->product_id); // $product->decrement('inventory', $item->quantity); } $this->syncCartOrderAccounting($order, $cart); return response()->json([ 'message' => 'true', ]); } /** * ثبت سبد در دفترکل داخلی + حسابداری بیرونی (مستقل از is_store؛ فقط accounting_provider). * ته‌حساب/زرین: per-line تا دستهٔ هر محصول درست برود. */ private function syncCartOrderAccounting(Order $order, Cart $cart): void { $config = app(ConfigService::class); try { app(AccountingLedgerService::class)->recordTrade($order); } catch (\Throwable $e) { report($e); } $lineIndex = 0; foreach ($cart->items as $item) { $product = Product::find($item->product_id); if (!$product) { continue; } $qty = max(1, (int) ($item->quantity ?? 1)); $unitWeight = (float) ($product->amount ?? 0); $lineWeight = $unitWeight > 0 ? $unitWeight * $qty : (float) $qty; $unitPrice = (float) ($item->unit_price ?? 0); if ($unitPrice <= 0) { $unitPrice = (float) ($product->price ?? 0) + (float) ($product->price_profit ?? 0); } $lineTotal = (float) ($item->total_price ?? ($unitPrice * $qty)); $payload = [ 'customer_id' => $order->customer_id, 'amount' => $lineWeight, 'number' => $qty, 'direction' => (int) ($order->direction ?? 1), 'price' => $unitPrice, 'total_price' => $lineTotal, 'pay_at' => $order->pay_at ?? $order->created_at, 'product_id' => (int) $product->id, 'product' => [ 'id' => $product->id, 'title' => $product->title, 'group_type' => $product->group_type, 'metal_id' => $product->metal_id, 'amount' => $product->amount, 'unit' => $product->unit, 'trade_by_quantity' => $product->trade_by_quantity, ], 'line' => $lineIndex + 1, 'order' => [ 'id' => (int) $order->id, 'product_id' => (int) $product->id, ], ]; $lineIndex++; if ($config->isTahesabAccounting()) { try { app(TahesabController::class)->storeTrade($payload); } catch (\Throwable $e) { report($e); } } if ($config->isZarinAccounting()) { try { app(\App\Http\Controllers\Admin\ZarinController::class)->storeTrade($payload); } catch (\Throwable $e) { report($e); } } } if ($config->isPargarAccounting()) { try { app(PargarSyncService::class)->recordTrade($order, true, [ 'payment_kind' => PargarSyncService::PAYMENT_CASH, 'product_id' => null, ]); } catch (\Throwable $e) { report($e); } } } /** * تسویه سبد فروشگاهی — قیمت نقدی هر محصول، بدون فرمول آبشده/گرم. */ private function checkoutStore(array $payload, Cart $cart, $wallet) { $customerId = (int) auth('customer')->id(); $priceRow = Price::latest()->first(); $goldPrice = $priceRow ? (float) $priceRow->price : 0; $wallet_interface = new WalletInterface($customerId, auth('customer')->user()->type); $total_price = 0; $total_amount = 0; $total_fee = 0; foreach ($cart->items as $item) { $product = Product::find($item->product_id); if (!$product) { throw new UnprocessableEntityHttpException('محصول یافت نشد.'); } if (!$product->open_buy) { throw new UnprocessableEntityHttpException("خرید {$product->title} در این لحظه مقدور نیست."); } app(ConfigService::class)->assertProductTradingOpen($product); app(ConfigService::class)->assertMainGoldZeroBeforeOtherProductBuy($wallet_interface->wallet, $product); $qty = (int) $item->quantity; // قیمت واحد از DB — نه قیمت ارسالی کلاینت (در فروشگاه قیمت نقدی هر عدد است، نه هر گرم) $unitPrice = (float) (($product->price ?? 0) + ($product->price_profit ?? 0)); if ($unitPrice <= 0 && (float) $product->amount > 0 && $goldPrice > 0) { $unitPrice = (float) $product->amount * $goldPrice; } if ($unitPrice <= 0) { throw new UnprocessableEntityHttpException("قیمت محصول {$product->title} مشخص نیست."); } $unitCashFee = ProductPriceService::resolveCashFeeRial($product, 1, $unitPrice); $cashFee = $unitCashFee * $qty; $lineTotal = ($unitPrice * $qty) + $cashFee; $total_price += $lineTotal; $total_amount += (float) $product->amount * $qty; $total_fee += $cashFee; $item->update([ 'price' => $lineTotal, 'gold_price' => $goldPrice, ]); } $orderData = [ 'customer_id' => $customerId, 'cart_id' => (int) $cart->id, 'price_id' => $priceRow?->id, // مظنهٔ مرجع (ریال/گرم) — نه total_price/amount که برای سبد فروشگاهی overflow می‌دهد 'price' => $goldPrice > 0 ? (int) round($goldPrice) : 0, 'price_original' => $goldPrice, 'amount' => $total_amount, 'service_charge_percentage' => 0, 'service_charge' => 0, 'service_charge_price' => 0, 'fee' => $total_fee, 'total_fee' => $total_fee, 'total_price_original' => $total_amount * $goldPrice, 'total_price' => $total_price, 'payment_amount' => $total_price, 'sts' => 1, 'direction' => 1, 'pay_at' => now(), ]; if (!empty($payload[0]['discount_code'])) { $discountCtrl = app(GiftCardController::class); $discountResult = $discountCtrl->applyDiscount((string) $payload[0]['discount_code'], $customerId); if (!empty($discountResult['message'])) { throw new UnprocessableEntityHttpException($discountResult['message']); } if (!empty($discountResult['amount'])) { $orderData['payment_amount'] = $orderData['total_price'] = $orderData['total_price'] - ($orderData['total_price'] * (float) $discountResult['amount']); } } if ($wallet->cash < $orderData['total_price']) { throw new UnprocessableEntityHttpException('موجودی کیف پول شما کافی نیست.'); } $order = Order::create($orderData); $cart->order_id = $order->id; $cart->save(); new TransactionInterface($wallet_interface, [ 'cash' => $orderData['payment_amount'], 'expire_at' => now(), 'active' => true, 'order_id' => $order->id, 'start_at' => now(), 'title' => 'سبد خرید', ], 12, false, false); foreach ($cart->items as $item) { $wallet_interface_product = new WalletInterface( $customerId, auth('customer')->user()->type, null, $item->product_id ); $wallet_interface_product->wallet->gold = (int) $wallet_interface_product->wallet->gold; $wallet_interface_product->wallet->save(); new TransactionInterface($wallet_interface_product, [ 'gold' => $item->quantity, 'product_id' => $item->product_id, 'expire_at' => now(), 'active' => true, 'order_id' => $order->id, 'start_at' => now(), ], 1, true, false); } $order->sts = 2; $order->payment_sts = 4; $order->save(); foreach ($cart->items as $item) { Product::where('id', $item->product_id)->decrement('inventory', $item->quantity); } $this->syncCartOrderAccounting($order, $cart); return response()->json([ 'message' => 'true', ]); } }