all(); $data['payment_product_id'] = $this->resolveGoldPaymentProductId($data); if (preg_match('/^gold:\d+$/', (string) ($data['method'] ?? ''))) { $data['method'] = 'gold'; } $data['is_katod'] = ($data['method'] ?? null) === 'katod'; if ($data['is_katod']) { $data['method'] = 'gold'; } $config = app(ConfigService::class); // فیلتر ساعت معامله per-product (سراسری یا override محصول) پایین‌تر در assertProductTradingOpen اعمال می‌شود. // throw new UnprocessableEntityHttpException('به دلیل نوسانات سفارشات بسته میباشد.'); $transactionType = $data['direction'] == 1 ? "خرید" : "فروش"; $customer = Customer::find(auth('customer')->id() ?? ($data['customer_id'] ?? null)); $data['first_buy'] = auth('customer')->id() && $customer && $customer->first_buy_order !== 1; if (auth('customer')->id()) { if (!$customer || !$customer->trade_active) { throw new UnprocessableEntityHttpException('امکان سفارش برای شما غیرفعال است.'); } $data['customer_id'] = auth('customer')->id(); $orderProductId = (int) ($data['abshode_id'] ?? $data['product_id'] ?? $config->mainProductId() ?? 0); $orderProduct = $orderProductId > 0 ? Product::find($orderProductId) : null; $basePrice = app(ProductDisplayPriceService::class)->resolveOrderBasePrice($orderProduct); if ($basePrice <= 0) { if ($config->isOutsideTradingHours()) { throw new UnprocessableEntityHttpException($config->tradingClosedMessage()); } throw new UnprocessableEntityHttpException('عدم ارائه سرویس.'); } $latestPrice = Price::latest()->first(); $data['all_price'] = $latestPrice; $data['price'] = $basePrice; $data['price_id'] = $latestPrice?->id; $data['difference'] = $config->orderPriceDiffRial(); $data['service_charge_percentage'] = $config->orderServiceChargePercentage(); $fee_free = Setting::where('key', 'fee_free')->where('is_active', true)->value('value'); if ($fee_free && $this->feeFree($data['customer_id'], $fee_free)) { $data['service_charge_percentage'] = 0; // $data['difference'] = 0; $data['fee_free'] = true; } $data['pay_at'] = (new \DateTime())->format('Y-m-d H:i:s'); } else if (auth('user')->id()) {//admin $data['service_charge_percentage'] = 0; } if($data['price'] === 0){ throw new UnprocessableEntityHttpException(' سفارشات بسته میباشد.'); } $data['sts'] = 2; $data['type'] = 2; $data['payment_sts'] = 4; $data['price_original'] = $data['price']; $data['payment_remind'] = 0; $data['fee'] = $data['difference']; $ownerType = (int) ($customer?->type ?? 2); $wallet_interface = new WalletInterface($data['customer_id'], $ownerType); $wallet = $wallet_interface->wallet; if (!$wallet) { throw new UnprocessableEntityHttpException('کیف پول یافت نشد.'); } $response = []; // Check if the user wants to transact with a product or non-product (gold) if (isset($data['product_id']) and $data['product'] = Product::find($data['product_id'])) { $config->assertProductTradingOpen($data['product']); // Check if the user wants to buy if ($data['direction'] == 1) { $data['service_charge_percentage'] = isset($data['fee_free']) && $data['fee_free'] === true ? 0 : ($data['product']['percent_fee_buy']) * $data['number']; $data['difference'] = $data['fee'] = $this->resolveCashPriceDiff($config, $data['product'], 1); $data['total_fee'] = $data['number'] * $data['fee']; $data['price_original'] = $this->resolveProductUnitBasePrice($data['product']); $data['total_price_original'] = $data['product']['amount'] * ($data['price_original'] ?? 0); // Check if the method is cash if ($data['method'] == "cash") { $response['buy'] = $this->processBuyTransactionProduct($data, $wallet, $wallet_interface); } // Check if the method is gold else if ($data['method'] == "gold") { $response['buy'] = $this->processBuyTransactionProductByGold($data, $wallet, $wallet_interface); } else { throw new UnprocessableEntityHttpException('روش پرداخت نامعتبر است.'); } } // Check if the user wants to sell else if ($data['direction'] == 2) { $data['service_charge_percentage'] = isset($data['fee_free']) && $data['fee_free'] === true ? 0 : ($data['product']['percent_fee_sell']) * $data['number']; $data['difference'] = $data['fee'] = $this->resolveCashPriceDiff($config, $data['product'], 2); $data['total_fee'] = $data['number'] * $data['fee']; $data['price_original'] = $this->resolveProductUnitBasePrice($data['product']); $data['total_price_original'] = $data['product']['amount'] * ($data['price_original'] ?? 0); $this->assertMinimumSellCash($data); // Check if the method is cash if ($data['method'] == "cash") { $response['sell'] = $this->processSellTransactionProduct($data, $wallet, $wallet_interface); } // Check if the method is gold else if ($data['method'] == "gold") { $response['sell'] = $this->processSellTransactionProductByGold($data, $wallet, $wallet_interface); } else { throw new UnprocessableEntityHttpException('روش پرداخت نامعتبر است.'); } } } else { // Calculate total price and fee for non-product (gold) transaction $data['total_price_original'] = $data['amount'] * ($data['price_original'] ?? 0); $mainProductId = app(\App\Services\ConfigService::class)->mainProductId(); $productId = $data['abshode_id'] ?? $data['product_id'] ?? $mainProductId; $data['product'] = $productId ? Product::find($productId) : null; if (!$data['product']) { throw new UnprocessableEntityHttpException('محصول آبشده یافت نشد.'); } $config->assertProductTradingOpen($data['product']); if ($data['direction'] == 1) { $data['difference'] = $this->resolveCashPriceDiff($config, $data['product'], 1); $data['service_charge_percentage'] = $data['product']['percent_fee_buy']; } elseif ($data['direction'] == 2) { $data['difference'] = $this->resolveCashPriceDiff($config, $data['product'], 2); $data['service_charge_percentage'] = $data['product']['percent_fee_sell']; } $data['total_fee'] = $data['amount'] * $data['difference']; $data['product_id'] = $data['product']->id; // Check if the user wants to buy if ($data['direction'] == 1) { if (!$data['product']['open_buy']) { throw new UnprocessableEntityHttpException('خرید این محصول در این لحظه مقدور نمیباشد'); } $response['buy'] = $this->processBuyTransaction($data, $wallet, $wallet_interface); } // Check if the user wants to sell else if ($data['direction'] == 2) { if (!$data['product']['open_sell']) { throw new UnprocessableEntityHttpException('خرید این محصول در این لحظه مقدور نمیباشد'); } $this->assertMinimumSellCash($data); $response['sell'] = $this->processSellTransaction($data, $wallet, $wallet_interface); } } // دفترکل داخلی همیشه ثبت شود — مستقل از اتصال به حسابداری بیرونی if (isset($data['order'])) { try { app(\App\Services\AccountingLedgerService::class)->recordTrade($data['order']); } catch (\Throwable $e) { report($e); } } if ($config->isTahesabAccounting()) { try { $data['tahesab'] = app(TahesabController::class)->storeTrade($data); } catch (\Exception $e) { report($e); } } if ($config->isPargarAccounting()) { try { app(\App\Services\PargarSyncService::class)->recordTrade($data['order'] ?? null); } catch (\Throwable $e) { report($e); } } // $response['reward'] = $this->give_order_reward($data, $wallet, $wallet_interface); if ((int) $data['direction'] === 1 && $customer) { $referrer = Customer::where('referral_id', $customer->referral_to_id)->first(); if (!empty($referrer) && $referrer->first_buy_order == 1 && $customer->first_buy_order !== 1) { $customer->first_buy_order = true; $customer->save(); $response['bonus_gift'] = $this->bonusGift($customer, $referrer); } } if ($customer && isset($data['order'])) { try { app(ReferralGiftService::class)->tryGiveOnFirstOrder($customer); } catch (\Throwable $e) { report($e); } } if (isset($data['order'])) { try { $response['Notifications'] = $this->sendTransactionNotifications($data, $transactionType); } catch (\Throwable $e) { report($e); } } if ($customer) { app(WalletBroadcastService::class)->broadcastForCustomer( (int) $customer->id, $ownerType ); } return response()->json([$response, $data]); } private function processBuyTransaction(&$data, $wallet, $wallet_interface) { $response = []; $config = app(ConfigService::class); $targetCash = $this->resolveTargetCash($data); // منطق واحد و مستقل از fee_deduct_from_gold: // • کاربر مبلغ ریالی زده (rialLocked) → دقیقاً همان مبلغ پرداخت می‌شود و کارمزد از طلا کم می‌شود. // • کاربر وزن زده (gramLocked) → دقیقاً همان وزن دریافت می‌شود و کارمزد به ریال قابل پرداخت اضافه می‌شود. $rialLocked = $targetCash !== null; $feeRate = isset($data['fee_free']) && $data['fee_free'] === true ? 0.0 : (float) ($data['service_charge_percentage'] ?? 0); $data['price'] += $data['difference']; if ($rialLocked) { // مبلغ ریالی دقیق — کارمزد از طلای دریافتی کسر می‌شود $unitPrice = (float) $data['price']; $grossGold = $unitPrice > 0 ? $targetCash / $unitPrice : 0.0; $data['service_charge'] = $this->normalizeGoldAmount($feeRate * $grossGold); $data['amount'] = $this->normalizeGoldAmount($grossGold - $data['service_charge']); $data['service_charge_price'] = $data['service_charge'] * $data['price_original']; $data['total_price'] = $data['amount'] * $data['price']; $data['payment_amount'] = $targetCash; } else { // وزن دقیق — کارمزد به مبلغ ریالی پرداختی اضافه می‌شود $data['amount'] = $this->normalizeGoldAmount((float) ($data['amount'] ?? 0)); $data['service_charge'] = $this->normalizeGoldAmount($feeRate * $data['amount']); $data['service_charge_price'] = $data['service_charge'] * $data['price_original']; $data['total_price'] = $data['amount'] * $data['price']; $data['payment_amount'] = $data['total_price'] + $data['service_charge_price']; } // سمت کسر کارمزد برای گزارش تفکیک‌شده در داشبورد $data['fee_asset'] = $rialLocked ? 'gold' : 'rial'; $this->enforceProductOrderLimits($data); if (!empty($data['first_buy']) && ($minFirstBuy = Setting::where('is_active', 1)->where('key', 'min_first_buy')->value('value'))) { if ($data['payment_amount'] < (int) $minFirstBuy) { throw new UnprocessableEntityHttpException('حداقل سفارش اول ' . number_format((int) $minFirstBuy) . ' ریال میباشد.'); } } if ($wallet->cash < $data['payment_amount']) { throw new UnprocessableEntityHttpException('موجودی شما کمتر از میزان سفارش است'); } $this->assertProviderOrderAccepted($data); $data['order'] = $order = Order::create($data); $abshode = $this->resolveAbshodeProductContext($data); $productId = (int) ($data['product_id'] ?? $abshode['id'] ?? 0); $usesProductWallet = $productId > 0 && !$config->isMainWalletProduct($productId); if ($usesProductWallet) { new TransactionInterface($wallet_interface, [ "cash" => $data['payment_amount'], "product_id" => $productId, 'expire_at' => now(), 'active' => true, 'order_id' => $order->id, 'start_at' => now(), 'title' => 'برداشت وجه نقد', ], 12, false, false); $wallet_interface_product = $this->productWalletInterface($productId); $this->normalizeProductWalletGold($wallet_interface_product); new TransactionInterface($wallet_interface_product, [ "gold" => $data['amount'], "product_id" => $productId, 'expire_at' => now(), 'active' => true, 'order_id' => $order->id, 'start_at' => now(), 'title' => $this->meltedOrProductBuyTitle($data), ], 1, true, false); } else { $buyTransaction = [ "cash" => $data['payment_amount'], "gold" => $data['amount'], 'expire_at' => now(), 'active' => true, 'order_id' => $order->id, 'start_at' => now(), 'title' => $this->meltedOrProductBuyTitle($data), ]; if ($productId > 0) { $buyTransaction['product_id'] = $productId; $buyTransaction['note'] = 'product_id:' . $productId; } new TransactionInterface($wallet_interface, $buyTransaction, 1, true, false); } $this->finalizeProviderOrder($data); $response['1'] = 2; return $response; } private function processBuyTransactionProduct(&$data, $wallet, $wallet_interface) { $data['price'] = $data['product']['price'] + $data['product']['price_profit']; $data['price'] += $data['difference']; $data['amount'] = $data['number'] * $data['product']['amount']; $data['total_amount'] = $data['amount'] + ($data['product']['percent_fee_buy'] * $data['amount']); $data['total_price'] = $data['total_amount'] * $data['price']; $data['payment_amount'] = $data['total_price']; $this->enforceProductOrderLimits($data, (float) $data['total_price']); if (!empty($data['first_buy']) && ($minFirstBuy = Setting::where('is_active', 1)->where('key', 'min_first_buy')->value('value'))) { if ($data['total_price'] < (int) $minFirstBuy) { throw new UnprocessableEntityHttpException('حداقل سفارش اول ' . number_format((int) $minFirstBuy) . ' ریال میباشد.'); } } $data['service_charge'] = isset($data['fee_free']) && $data['fee_free'] === true ? 0 : ($data['product']['percent_fee_buy'] * $data['amount']); $data['service_charge_price'] = $data['service_charge'] * $data['price_original']; $data['wallet'] = $wallet; if ($wallet->cash < $data['payment_amount']) { throw new UnprocessableEntityHttpException('موجودی شما کمتر از میزان سفارش است'); } if (!$data['product']['open_buy']) { throw new UnprocessableEntityHttpException('خرید این محصول در این لحظه مقدور نمیباشد'); } $data['order'] = $order = Order::create($data); $data['transaction'][] = new TransactionInterface($wallet_interface, $_inp = [ "number" => $data['number'], "cash" => $data['total_price'], "product_id" => $data["product_id"], 'expire_at' => now(), 'active' => true, 'order_id' => $order->id, 'title' => "برداشت وجه نقد" ], 12, false, false); $wallet_interface_product = $this->productWalletInterface((int) $data["product_id"]); $this->normalizeProductWalletGold($wallet_interface_product); new TransactionInterface($wallet_interface_product, [ "gold" => $data['number'], "product_id" => $data["product_id"], 'expire_at' => now(), 'active' => true, 'order_id' => $order->id, 'start_at' => now(), 'title' => $this->meltedOrProductBuyTitle($data), ], 1, true, false); } private function processBuyTransactionProductByGold(&$data, $wallet, $wallet_interface) { $data['price'] += $data['difference']; $data['amount'] = $data['number'] * $data['product']['amount']; $data['total_amount'] = $data['amount'] + ($data['product']['percent_fee_buy'] * $data['amount']); $data['total_price'] = $data['total_amount'] * $data['price']; $data['payment_amount'] = $data['total_price']; $this->enforceProductOrderLimits($data, (float) $data['total_price']); $data['service_charge'] = isset($data['fee_free']) && $data['fee_free'] === true ? 0 : ($data['product']['percent_fee_buy'] * $data['amount']); $data['total_fee'] = 0; $data['service_charge_price'] = $data['service_charge'] * $data['price_original']; $data['wallet'] = $wallet; $paymentProductId = (int) ($data['payment_product_id'] ?? 0); $paymentWalletInterface = $wallet_interface; $paymentProduct = null; $paymentWallet = $wallet; if ($paymentProductId > 0) { $paymentProduct = Product::find($paymentProductId); if ( !$paymentProduct || (int) ($paymentProduct->metal_id ?? 0) !== (int) ($data['product']['metal_id'] ?? 0) ) { throw new UnprocessableEntityHttpException('روش پرداخت نامعتبر است.'); } $paymentWalletInterface = $this->productWalletInterface($paymentProductId); $paymentWallet = $paymentWalletInterface->wallet; if (!$paymentWallet) { throw new UnprocessableEntityHttpException('کیف پول یافت نشد.'); } } if ((float) $paymentWallet->gold < $data['total_amount']) { throw new UnprocessableEntityHttpException($this->insufficientGoldBalanceMessage($data)); } if (!$data['product']['open_buy']) { throw new UnprocessableEntityHttpException('خرید این محصول در این لحظه مقدور نمیباشد'); } $data['order'] = $order = Order::create($data); $paymentSellTitle = $paymentProduct ? self::productSellTitle($paymentProduct->title ?? '') : self::mainMetalSellTitle(); $paymentTransaction = [ "number" => $data['number'], "gold" => $data['total_amount'], 'expire_at' => now(), 'active' => true, 'order_id' => $order->id, 'title' => $paymentSellTitle, ]; if ($paymentProductId > 0) { $paymentTransaction['product_id'] = $paymentProductId; $this->normalizeProductWalletGold($paymentWalletInterface); $data['transaction'][] = new TransactionInterface( $paymentWalletInterface, $paymentTransaction, 1, false, false ); } else { $data['transaction'][] = new TransactionInterface( $paymentWalletInterface, $paymentTransaction, 12, false, true ); } $wallet_interface_product = $this->productWalletInterface((int) $data["product_id"]); $this->normalizeProductWalletGold($wallet_interface_product); new TransactionInterface($wallet_interface_product, [ "gold" => $data['number'], "product_id" => $data["product_id"], 'expire_at' => now(), 'active' => true, 'order_id' => $order->id, 'start_at' => now(), 'title' => $this->meltedOrProductBuyTitle($data), ], 1, true, false); } private function processSellTransaction(&$data, $wallet, $wallet_interface) { $response = []; $config = app(ConfigService::class); $targetCash = $this->resolveTargetCash($data); // منطق واحد و مستقل از fee_deduct_from_gold: // • کاربر مبلغ ریالی زده (rialLocked) → دقیقاً همان مبلغ واریز می‌شود و کارمزد از طلای کیف کسر می‌شود. // • کاربر وزن زده (gramLocked) → دقیقاً همان وزن فروخته می‌شود و کارمزد از مبلغ ریالی دریافتی کم می‌شود. $rialLocked = $targetCash !== null; $feeRate = isset($data['fee_free']) && $data['fee_free'] === true ? 0.0 : (float) ($data['service_charge_percentage'] ?? 0); $data['price'] -= $data['difference']; if ($rialLocked) { // مبلغ ریالی دقیق دریافتی — کارمزد از طلای کیف (کسر اضافه‌تر) تأمین می‌شود $unitPrice = (float) $data['price']; $data['amount'] = $unitPrice > 0 ? $this->normalizeGoldAmount($targetCash / $unitPrice) : 0.0; $data['service_charge'] = $this->normalizeGoldAmount($feeRate * $data['amount']); $data['service_charge_price'] = $data['service_charge'] * $data['price_original']; $data['total_price'] = $targetCash; $data['payment_amount'] = $targetCash; $goldDebit = $this->normalizeGoldAmount($data['amount'] + $data['service_charge']); } else { // وزن دقیق فروش — کارمزد از مبلغ ریالی دریافتی کم می‌شود $data['amount'] = $this->normalizeGoldAmount((float) ($data['amount'] ?? 0)); $data['service_charge'] = $this->normalizeGoldAmount($feeRate * $data['amount']); $data['service_charge_price'] = $data['service_charge'] * $data['price_original']; $data['total_price'] = $data['amount'] * $data['price'] - $data['service_charge_price']; $data['payment_amount'] = $data['total_price']; $goldDebit = $data['amount']; } // سمت کسر کارمزد برای گزارش تفکیک‌شده در داشبورد $data['fee_asset'] = $rialLocked ? 'gold' : 'rial'; $grossTotal = $data['amount'] * $data['price']; $this->enforceProductOrderLimits($data, (float) $grossTotal); $abshode = $this->resolveAbshodeProductContext($data); $productId = (int) ($data['product_id'] ?? $abshode['id'] ?? 0); $usesProductWallet = $productId > 0 && !$config->isMainWalletProduct($productId); $goldWallet = $wallet; $wallet_interface_product = null; if ($usesProductWallet) { $wallet_interface_product = $this->productWalletInterface($productId); $goldWallet = $wallet_interface_product->wallet; if (!$goldWallet) { throw new UnprocessableEntityHttpException('کیف پول محصول یافت نشد.'); } } if ($goldWallet->gold < $goldDebit) { throw new UnprocessableEntityHttpException($this->insufficientGoldBalanceMessage($data)); } $this->assertProviderOrderAccepted($data); $data['order'] = $order = Order::create($data); if ($usesProductWallet) { $this->normalizeProductWalletGold($wallet_interface_product); new TransactionInterface($wallet_interface_product, [ "gold" => $goldDebit, "product_id" => $productId, 'expire_at' => now(), 'active' => true, 'order_id' => $order->id, 'start_at' => now(), 'title' => $this->meltedOrProductSellTitle($data), ], 1, false, false); new TransactionInterface($wallet_interface, [ "cash" => $data['total_price'], "product_id" => $productId, 'expire_at' => now(), 'active' => true, 'order_id' => $order->id, 'start_at' => now(), 'title' => 'واریز وجه نقد', ], 12, false, true); } else { $sellTransaction = [ "gold" => $goldDebit, "cash" => $data['total_price'], 'expire_at' => now(), 'active' => true, 'order_id' => $order->id, 'title' => $this->meltedOrProductSellTitle($data), ]; if ($productId > 0) { $sellTransaction['product_id'] = $productId; $sellTransaction['note'] = 'product_id:' . $productId; } new TransactionInterface($wallet_interface, $sellTransaction, 2, false, true); } $this->finalizeProviderOrder($data); } private function processSellTransactionProduct(&$data, $wallet, $wallet_interface) { $data['price'] = $data['product']['price'] + $data['product']['price_profit']; $data['price'] -= $data['difference']; $data['amount'] = $data['number'] * $data['product']['amount']; $data['total_amount'] = $data['amount'] - ($data['product']['percent_fee_sell'] * $data['amount']); $data['total_price'] = $data['total_amount'] * $data['price']; $data['payment_amount'] = $data['total_price']; $grossTotal = (float) ($data['amount'] * $data['price']); $this->enforceProductOrderLimits($data, $grossTotal); $data['service_charge'] = isset($data['fee_free']) && $data['fee_free'] === true ? 0 : ($data['product']['percent_fee_sell'] * $data['amount']); $data['service_charge_price'] = $data['service_charge'] * $data['price_original']; $wallet_interface_product = $this->productWalletInterface((int) $data["product_id"]); if ($wallet_interface_product->wallet->gold < $data['number']) { throw new UnprocessableEntityHttpException('موجودی شما کمتر از میزان سفارش است'); } if (!$data['product']['open_sell']) { throw new UnprocessableEntityHttpException('فروش این محصول در این لحظه مقدور نمیباشد'); } $data['order'] = $order = Order::create($data); $this->normalizeProductWalletGold($wallet_interface_product); new TransactionInterface($wallet_interface_product, [ "gold" => $data['number'], "product_id" => $data["product_id"], 'expire_at' => now(), 'active' => true, 'order_id' => $order->id, 'start_at' => now(), 'title' => $this->meltedOrProductSellTitle($data), ], 1, false, false); $data['transaction'][] = new TransactionInterface($wallet_interface, $_inp = [ "number" => $data['number'], "cash" => $data['total_price'], "product_id" => $data["product_id"], 'expire_at' => now(), 'active' => true, 'order_id' => $order->id, 'title' => "واریز وجه نقد" ], 12, false, true); } private function processSellTransactionProductByGold(&$data, $wallet, $wallet_interface) { $data['price'] -= $data['difference']; $data['amount'] = $data['number'] * $data['product']['amount']; $data['total_amount'] = $data['amount'] - ($data['product']['percent_fee_sell'] * $data['amount']); $data['total_price'] = $data['total_amount'] * $data['price']; $data['payment_amount'] = $data['total_price']; $grossTotal = (float) ($data['amount'] * $data['price']); $this->enforceProductOrderLimits($data, $grossTotal); $data['service_charge'] = isset($data['fee_free']) && $data['fee_free'] === true ? 0 : ($data['product']['percent_fee_sell'] * $data['amount']); $data['total_fee'] = 0; $data['service_charge_price'] = $data['service_charge'] * $data['price_original']; $wallet_interface_product = $this->productWalletInterface((int) $data["product_id"]); if ($wallet_interface_product->wallet->gold < $data['number']) { throw new UnprocessableEntityHttpException('موجودی شما کمتر از میزان سفارش است'); } if (!$data['product']['open_sell']) { throw new UnprocessableEntityHttpException('فروش این محصول در این لحظه مقدور نمیباشد'); } $data['order'] = $order = Order::create($data); $data['transaction'][] = new TransactionInterface($wallet_interface, $_inp = [ "number" => $data['number'], "gold" => $data['total_amount'], "product_id" => $data["product_id"], 'expire_at' => now(), 'active' => true, 'order_id' => $order->id, 'title' => $this->meltedOrProductSellTitle($data), ], 12, true, false); $this->normalizeProductWalletGold($wallet_interface_product); new TransactionInterface($wallet_interface_product, [ "gold" => $data['number'], "product_id" => $data["product_id"], 'expire_at' => now(), 'active' => true, 'order_id' => $order->id, 'start_at' => now(), 'title' => $this->meltedOrProductSellTitle($data), ], 1, false, false); } /** * کیف مؤثر محصول برای تراکنش سفارش. * محصولات متصل به کیف اصلی (main_wallet_products) مستقیماً روی کیف اصلی می‌نشینند. */ private function productWalletInterface(int $productId): WalletInterface { return new WalletInterface( auth('customer')->id(), auth('customer')->user()->type, null, self::effectiveProductWalletId($productId) ); } /** رفتار قدیمیِ رُند کردن موجودی فقط برای کیف خود محصول؛ کیف اصلی هرگز رُند نمی‌شود */ private function normalizeProductWalletGold(WalletInterface $walletInterface): void { if ($walletInterface->wallet->product_id === null) { return; } $walletInterface->wallet->gold = intval($walletInterface->wallet->gold); $walletInterface->wallet->save(); } private function give_order_reward(&$data, $wallet, $wallet_interface) { $data['aCustomers'] = []; $i = 0; $data['aCustomers'][$i] = Customer::findOrFail($data['customer_id']); while ($data['aCustomers'][$i + 1] = Customer::where('referral_id', $data['aCustomers'][$i]->referral_to_id)->first()) { if ($data['aCustomers'][$i + 1]->profit_amount_by_lvl) { $levels = json_decode(str_replace(['{', '}'], ['[', ']'], $data['aCustomers'][$i + 1]->profit_amount_by_lvl), true); foreach ($levels as $level) { if ($level[0] == $i + 1) { // [$level[1],$data['aCustomers'][$i+1]]; if ($this->Config('has_page_earn_money') == true) { $gift_for_invite = ($data['order']['service_charge'] * $level[1]) / 100; $profit_receiver = $data['aCustomers'][$i + 1]->id; $profit_sender = $data['aCustomers'][0]->id; $Withdraw = Withdraw::create([ 'user_id' => $profit_sender, 'customer_id' => $profit_receiver, "cash" => 0, "gold" => $gift_for_invite, 'sts' => 9, ]); $wallet_interface = new WalletInterface($profit_receiver, 2); new TransactionInterface($wallet_interface, $_inp = [ "cash" => 0, "gold" => $gift_for_invite, 'expire_at' => now(), 'active' => true, 'withdraw_id' => $Withdraw->id, 'start_at' => now(), ], 9, true, false); $transaction = Transaction::where('customer_id', $profit_receiver)->latest()->first(); TransactionsAdmin::create([ 'customer_id' => $profit_sender, 'customer_to_id' => $profit_receiver, 'user_id' => $profit_sender, 'order_id' => $data['order']['id'], 'wallet' => null, // یا مقدار مناسب دیگری 'transaction_id' => $transaction->id, 'cash' => 0, 'note' => ' سود بابت سفارش', 'type' => 1, // یا مقدار مناسب دیگری 'gold' => $gift_for_invite, 'balance_cash' => 0, // یا مقدار مناسب دیگری 'balance_gold' => $gift_for_invite, // مقدار صحیح برای `balance_gold` 'withdraw_id' => $Withdraw->id, ]); } } } } if ($i == 100) break; $i++; // برای جلوگیری از حلقه بی‌نهایت } return $data; } private function sendTransactionNotifications(array $data, string $transactionType) { $customer = Customer::findOrFail($data['customer_id']); $companyName = $this->Config('name_fa'); $amount = $data['amount'] ?? ( isset($data['number'], $data['product']['amount']) ? $data['number'] * $data['product']['amount'] : 0 ); $totalPrice = $data['total_price'] ?? ($data['payment_amount'] ?? 0); $subjectTitle = $this->transactionSubjectTitle($data); $message = "کاربر {$customer->name} با شماره موبایل {$customer->mobile} اقدام به $transactionType {$subjectTitle} به مقدار " . "{$amount} گرم به مبلغ " . number_format($totalPrice) . " ریال کرد. با تشکر، $companyName"; User::where('send_sms', 1)->get()->each(function ($admin) use ($message, $data, $transactionType, $companyName) { $this->send($admin->mobile, $message); }); $wallet = Wallet::where('owner', $data['customer_id']) ->whereNull('fund_id') ->whereNull('product_id') ->first(); $balanceLabel = self::metalTypeLabel(); $goldBalance = $wallet ? ($wallet->gold ?? 0) : 0; $cashBalance = $wallet ? ($wallet->cash ?? 0) : 0; $customerMessage = "$companyName\n$transactionType {$subjectTitle}\nگرم: {$amount}\n" . "مبلغ کل: " . number_format($totalPrice) . "\n\n" . "موجودی {$balanceLabel}: {$goldBalance}\n" . "مانده ریالی: " . number_format($cashBalance) . "\n" . $this->Config('app_url'); // ارسال SMS به مشتری $this->send($customer->mobile, $customerMessage); } private function assertProviderOrderAccepted(array &$data): void { if (!app(ConfigService::class)->usesMontzarPricing()) { return; } $this->assertNoRecentDuplicateLocalOrder($data); $data['_montzar_parsed'] = app(MontzarService::class)->submitOrder($data); } private function finalizeProviderOrder(array &$data): void { if (!app(ConfigService::class)->usesMontzarPricing()) { return; } $parsed = $data['_montzar_parsed'] ?? null; if (!is_array($parsed)) { return; } app(MontzarService::class)->applyParsedOrder($data, $parsed); unset($data['_montzar_parsed']); } private function assertNoRecentDuplicateLocalOrder(array $data): void { $customerId = (int) ($data['customer_id'] ?? 0); if ($customerId <= 0) { return; } $direction = (int) ($data['direction'] ?? 0); $amount = round((float) ($data['amount'] ?? 0), 6); $windowSeconds = 20; $duplicate = Order::where('customer_id', $customerId) ->where('direction', $direction) ->where('created_at', '>=', now()->subSeconds($windowSeconds)) ->whereRaw('ROUND(amount, 6) = ?', [$amount]) ->exists(); if ($duplicate) { throw new UnprocessableEntityHttpException( 'در حال حاضر سفارش مشابه ثبت شده است. لطفاً چند ثانیه دیگر تلاش کنید.' ); } } public function storeBackUp(Request $request) { $data = $request->all(); if (auth('customer')->id()) {// متد در پنل کاربر $data['customer_id'] = auth('customer')->id(); $data['price'] = Price::orderBy('id', 'desc')->first()->price; } elseif (auth('user')->user()) {// متد در پنل ادمین $data['customer_id'] = $request->customer_id; $data['price'] = $request->price; } $data['sts'] = 2; $data['type'] = 2; $data['payment_sts'] = 4; $data['price_original'] = $data['price']; $add_sell_price = config('add_sell_price.single'); $customer = Customer::findOrFail($data['customer_id']); $wallet_interface = new WalletInterface($data['customer_id'], (int) $customer->type); $wallet = $wallet_interface->wallet; $data['total_price_original'] = $data['amount'] * $data['price_original']; $data['payment_remind'] = 0; $data['fee'] = $add_sell_price; $data['total_fee'] = $data['amount'] * $data['fee']; if ($data['direction'] == 1) { $data['price'] += $add_sell_price; $data['total_price'] = $data['amount'] * $data['price']; $data['payment_amount'] = $data['total_price']; if ($wallet->cash < $data['total_price']) { throw new UnprocessableEntityHttpException('موجودی شما کمتر از میزان سفارش است'); } $order = Order::create($data);//buy new TransactionInterface($wallet_interface, $_inp = [ "cash" => $data['total_price'], "gold" => $data['amount'], 'expire_at' => now(), 'active' => true, 'order_id' => $order->id, 'start_at' => now(), ], 1, true, false); } else {//sell $data['price'] -= $add_sell_price; $data['total_price'] = $data['amount'] * $data['price']; $data['payment_amount'] = $data['total_price']; if ($wallet->gold < $data['amount']) { throw new UnprocessableEntityHttpException('موجودی شما کمتر از میزان سفارش است'); } $order = Order::create($data); new TransactionInterface($wallet_interface, $_inp = [ "gold" => $data['amount'], "cash" => $data['total_price'], 'expire_at' => now(), 'active' => true, 'order_id' => $order->id ], 2, false, true); } //----------------------------------------------------------------------------sms // Determine transaction type $transactionType = ($data['direction'] == 1) ? "خرید" : "فروش"; // User information $customer = Customer::where("id", $data['customer_id'])->firstOrFail(); $userMobile = $customer->mobile; // User's mobile number $userName = $customer->name; // User's name $chargedAmount = $data['total_price']; // Total price in Rial $chargedAmountGold = $data['amount']; // Amount of gold // Company name $companyName = $this->Config('name_fa'); // Company name from configuration // Constructing the SMS message $message = "کاربر $userName با شماره موبایل $userMobile اقدام به $transactionType طلا به مقدار " . $chargedAmountGold . " گرم به مبلغ " . number_format($chargedAmount) . " ریال کرد. با تشکر، $companyName"; // Getting admin mobile numbers and sending the SMS User::where('active', 1)->get()->each(function ($admin) use ($message) { $this->Send($admin->mobile, $message); }); $mainWallet = Wallet::where('owner', $data['customer_id']) ->where('owner_type', $customer->type) ->main() ->first(); $companyurl = $this->Config('app_url'); $weight = $data['amount']; $totalAmount = $data['total_price']; $pricePerGram = $weight > 0 ? $totalAmount / $weight : 0; $goldBalance = $mainWallet->gold ?? 0; $remainingBalance = $mainWallet->cash ?? 0; $message = "$companyName\n"; $message .= "خرید آبشده\n"; $message .= "گرم: $weight\n"; $message .= "قیمت گرم: " . number_format($pricePerGram) . "\n"; $message .= "مبلغ کل: " . number_format($totalAmount) . "\n\n"; $message .= "موجودی طلایی: $goldBalance\n"; $message .= "مانده ریالی: " . number_format($remainingBalance) . "\n"; $message .= "$companyurl" . "\n"; $this->Send($customer->mobile, $message); return response()->json($order); } public function table(Request $request) { $records = Order::where('customer_id', auth('customer')->id()) ->where('type', 2); TableEngine::create($records, $request->all()) ->initSearch([]) ->select([ '*', Order::$SELECT_STS_STR ])->export($records); return response()->json($records); } private function feeFree($customer_id, $count) { return Order::where('customer_id', $customer_id) ->where('type', 2) ->where('sts', 2) ->where('payment_sts', 4) ->count() < (int)$count; } private function resolveOrderAmount(array $data): ?float { if (isset($data['amount'])) { return (float) $data['amount']; } if (!empty($data['product_id']) && isset($data['number'])) { $product = $data['product'] ?? Product::find($data['product_id']); if ($product) { return (float) $data['number'] * (float) $product->amount; } } return null; } private function assertMinimumSellCash(array $data): void { $minSell = Setting::where('key', 'minimum_sell')->where('is_active', 1)->first(); if (!$minSell) { return; } $minRial = (float) $minSell->value; if ($minRial <= 0) { return; } $sellCash = $this->resolveSellOrderCashRial($data); if ($sellCash === null || $sellCash >= $minRial) { return; } throw new UnprocessableEntityHttpException( 'حداقل مبلغ فروش ' . number_format((int) round($minRial)) . ' ریال می‌باشد' ); } private function resolveSellOrderCashRial(array $data): ?float { $targetCash = $this->resolveTargetCash($data); if ($targetCash !== null) { return (float) $targetCash; } if (!empty($data['product_id']) && isset($data['number'])) { $product = $data['product'] ?? Product::find($data['product_id']); if ($product) { $unitPrice = (float) ($product->price + $product->price_profit); $difference = (float) ($data['difference'] ?? 0); $amount = (float) $data['number'] * (float) $product->amount; $sellUnitPrice = max(0.0, $unitPrice - $difference); return $amount * $sellUnitPrice; } } if (isset($data['amount'])) { $amount = (float) $data['amount']; if ($amount > 0) { $price = (float) ($data['price'] ?? 0); $difference = (float) ($data['difference'] ?? 0); $sellUnitPrice = max(0.0, $price - $difference); return $amount * $sellUnitPrice; } } return null; } private function resolveTargetCash(array $data): ?int { if (!isset($data['target_cash'])) { return null; } $target = (int) round((float) $data['target_cash']); return $target > 0 ? $target : null; } private function normalizeGoldAmount(float $amount): float { return round($amount, 10); } private function resolveGoldPaymentProductId(array $data): ?int { if (!empty($data['payment_product_id'])) { return (int) $data['payment_product_id']; } $method = (string) ($data['method'] ?? ''); if (preg_match('/^gold:(\d+)$/', $method, $matches)) { return (int) $matches[1]; } return null; } private function resolveAbshodeProductContext(array $data): array { $productId = (int) ($data['product_id'] ?? $data['abshode_id'] ?? 0); $product = null; if (!empty($data['product'])) { $candidate = $data['product']; if ($candidate instanceof Product) { $product = $candidate; } elseif (is_array($candidate) && !empty($candidate['id'])) { $product = Product::find($candidate['id']); } } if (!$product && $productId > 0) { $product = Product::find($productId); } if (!$product) { return ['id' => 0, 'title' => '']; } return [ 'id' => (int) $product->id, 'title' => trim((string) $product->title), ]; } private function productTitleFromData(array $data): ?string { $product = $data['product'] ?? null; $title = null; if ($product) { if (is_array($product)) { $title = $product['title'] ?? null; } else { $title = $product->title ?? null; } } $title = trim((string) $title); if ($title === '' && !empty($data['product_id'])) { $title = trim((string) Product::query()->whereKey((int) $data['product_id'])->value('title')); } return $title !== '' ? $title : null; } private function isProductOrder(array $data): bool { return !empty($data['product_id']) && $this->productTitleFromData($data) !== null; } private function transactionSubjectTitle(array $data): string { return $this->productTitleFromData($data) ?? self::meltedGoldLabel(); } private function meltedOrProductBuyTitle(array $data): string { $productTitle = $this->productTitleFromData($data); return $productTitle !== null ? self::productBuyTitle($productTitle) : self::meltedGoldBuyTitle(); } private function meltedOrProductSellTitle(array $data): string { $productTitle = $this->productTitleFromData($data); return $productTitle !== null ? self::productSellTitle($productTitle) : self::meltedGoldSellTitle(); } private function insufficientGoldBalanceMessage(array $data): string { return 'موجودی ' . self::metalTypeLabel() . ' شما کمتر از میزان سفارش است'; } /** * گلد علی / zaarbox / main_product: اختلاف سفارش = buy_fee/sell_fee نمایش (مثل /api/price/last). * montzar و بقیه: همان cash_fee_buy/sell محصول (رفتار قبلی ابشده‌خانگی). */ private function resolveCashPriceDiff(ConfigService $config, Product $product, int $direction): float { $provider = $config->priceProvider(); $alignWithDisplay = in_array($provider, ['zaarbox', 'main_product'], true); if ($alignWithDisplay) { $display = $direction === 1 ? $config->displayBuyPriceDiffRial() : $config->displaySellPriceDiffRial(); if ($display > 0) { return $display; } } return ProductPriceService::resolveCashFeeRial( $product, $direction, ProductPriceService::productBasePriceRial($product) ); } /** * قیمت پایهٔ هر واحد محصول (بدون ضرب در وزن — وزن در total_price_original اعمال می‌شود). * محصول با پرچم «قیمت اپ» → قیمت زندهٔ هر گرم اپ (با تعدیل درصدی)؛ * بقیه → همان قیمت ذخیره‌شدهٔ محصول. */ private function resolveProductUnitBasePrice(Product $product): int { $display = app(ProductDisplayPriceService::class); if ($display->usesAppPrice($product)) { return (int) round($display->resolveOrderBasePrice($product)); } return (int) (($product->price ?? 0) + ($product->price_profit ?? 0)); } private function bonusGift($customer, $referrer) { $bonus = Setting::where('key', 'referral_bonus_amount')->where('is_active', 1)->first(); if ($bonus === null) { return false; } $bonus = json_decode($bonus->value, true); $customer_bonus = $bonus[0]['customer'] ?? 0; $referrer_bonus = $bonus[0]['referrer'] ?? 0; if ($customer_bonus > 0) { $wallet_interface = new WalletInterface($customer->id, $customer->type); $Withdraw = Withdraw::create([ 'customer_id' => $customer->id, 'cash' => $customer_bonus, 'note' => 'پاداش اولین سفارش', 'sts' => 4, ]); $transaction = new TransactionInterface($wallet_interface, $_inp = [ 'gold' => 0, 'cash' => $customer_bonus, 'expire_at' => now(), 'note' => 'پاداش اولین سفارش', 'withdraw_id' => $Withdraw->id, 'active' => true, ], 3, true, true, 0); $message = 'کیف پول شما به اندازه ' . number_format($customer_bonus) . ' ریال در تاریخ ' . verta()->format('Y/n/j H:i:s') . " شارژ شد بابت\n" . 'پاداش اولین سفارش' . ' ' . $this->Config('name_fa'); $this->Send($customer->mobile, $message); TransactionsAdmin::create([ 'customer_id' => $customer->id, 'customer_to_id' => $customer->referral->id ?? null, 'user_id' => $customer->id, 'wallet' => null, 'transaction_id' => $transaction->id ?? null, 'cash' => $customer_bonus, 'note' => 'پاداش خرید اول', 'type' => 1, 'gold' => 0, 'balance_cash' => 0, 'withdraw_id' => $Withdraw->id, ]); } if ($referrer_bonus > 0) { $wallet_interface = new WalletInterface($referrer->id, $referrer->type); $Withdraw = Withdraw::create([ 'customer_id' => $referrer->id, 'cash' => $referrer_bonus, 'note' => 'پاداش اولین سفارش زیرمجموعه', 'sts' => 4, ]); $referrerTransaction = new TransactionInterface($wallet_interface, $_inp = [ 'gold' => 0, 'cash' => $referrer_bonus, 'expire_at' => now(), 'note' => 'پاداش اولین سفارش زیرمجموعه', 'withdraw_id' => $Withdraw->id, 'active' => true, ], 3, true, true, 0); $message = 'کیف پول شما به اندازه ' . number_format($referrer_bonus) . ' ریال در تاریخ ' . verta()->format('Y/n/j H:i:s') . " شارژ شد بابت\n" . 'بابت پاداش اولین سفارش زیرمجموعه' . ' ' . $this->Config('name_fa'); $this->Send($referrer->mobile, $message); TransactionsAdmin::create([ 'customer_id' => $customer->id, 'customer_to_id' => $referrer->id, 'user_id' => $customer->id, 'wallet' => null, 'transaction_id' => $referrerTransaction->id ?? null, 'cash' => $referrer_bonus, 'note' => 'پاداش خرید اول زیرمجموعه', 'type' => 1, 'gold' => 0, 'balance_cash' => 0, 'withdraw_id' => $Withdraw->id, ]); } return false; } private function enforceProductOrderLimits(array &$data, ?float $totalPriceRial = null): void { $product = $data['product'] ?? null; if (!$product instanceof Product) { $productId = (int) ($data['abshode_id'] ?? $data['product_id'] ?? 0); if ($productId) { $product = Product::find($productId); if ($product) { $data['product'] = $product; } } } elseif (is_array($product)) { $product = Product::find($product['id'] ?? $data['product_id'] ?? 0); if ($product) { $data['product'] = $product; } } if ($product instanceof Product) { $product->validateOrderCashLimit($this->resolveOrderLimitCashRial($data, $totalPriceRial)); } } /** * مبلغ مؤثر برای min/max محصول — با target_cash همان مبلغی که کاربر وارد کرده * (نه total_price موقت قبل از رُند گرم که ممکن است چند ریال کمتر شود). */ private function resolveOrderLimitCashRial(array $data, ?float $fallbackTotal = null): float { $targetCash = $this->resolveTargetCash($data); if ($targetCash !== null) { return (float) $targetCash; } if ($fallbackTotal !== null) { return $fallbackTotal; } return (float) ($data['payment_amount'] ?? $data['total_price'] ?? 0); } }