Files
boursecu_server/app/Http/Controllers/Single/SingleOrderController.php
T

1765 lines
76 KiB
PHP
Raw Normal View History

2026-01-24 14:03:11 +00:00
<?php
namespace App\Http\Controllers\Single;
use App\DB\Te\TableEngine;
use App\Http\Controllers\Controller;
use App\Http\Controllers\Admin\TahesabController;
use App\Models\Customer;
use App\Models\Order;
use App\Models\Price;
use App\Models\Product;
use App\Models\User;
use App\Mail\YourMailableClass;
use Illuminate\Support\Facades\Mail;
use App\Models\Withdraw;
use Illuminate\Http\Request;
use Rooyesh\Wallet\interfaces\TransactionInterface;
use Rooyesh\Wallet\interfaces\WalletInterface;
use Rooyesh\Wallet\models\Transaction;
use Rooyesh\Wallet\models\Wallet;
use App\Models\TransactionsAdmin;
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
use Illuminate\Support\Facades\Http;
use App\Models\Setting;
2026-03-10 15:39:30 +03:30
use App\Models\Cnt;
2026-05-25 12:47:04 +03:30
use App\Services\MontzarService;
use App\Services\ConfigService;
2026-07-04 18:00:57 +03:30
use App\Services\ProductPriceService;
2026-06-28 10:46:09 +03:30
use App\Services\ProductDisplayPriceService;
use App\Services\FormulaPriceService;
2026-07-01 22:27:51 +03:30
use App\Services\ReferralGiftService;
2026-06-27 15:51:44 +03:30
use App\Services\WalletBroadcastService;
2026-01-24 14:03:11 +00:00
class SingleOrderController extends Controller
{
public function store(Request $request)
{
$data = $request->all();
2026-06-22 19:14:24 +03:30
$data['payment_product_id'] = $this->resolveGoldPaymentProductId($data);
if (preg_match('/^gold:\d+$/', (string) ($data['method'] ?? ''))) {
$data['method'] = 'gold';
}
2026-06-03 19:53:14 +03:30
$data['is_katod'] = ($data['method'] ?? null) === 'katod';
if ($data['is_katod']) {
2026-06-03 19:45:08 +03:30
$data['method'] = 'gold';
}
2026-05-25 12:47:04 +03:30
$config = app(ConfigService::class);
2026-06-28 15:16:39 +03:30
// فیلتر ساعت معامله per-product (سراسری یا override محصول) پایین‌تر در assertProductTradingOpen اعمال می‌شود.
2026-06-14 22:00:21 +03:30
2026-01-24 14:03:11 +00:00
// throw new UnprocessableEntityHttpException('به دلیل نوسانات سفارشات بسته میباشد.');
$transactionType = $data['direction'] == 1 ? "خرید" : "فروش";
2026-06-05 16:24:15 +03:30
$customer = Customer::find(auth('customer')->id() ?? ($data['customer_id'] ?? null));
$data['first_buy'] = auth('customer')->id() && $customer && $customer->first_buy_order !== 1;
2026-01-24 14:03:11 +00:00
if (auth('customer')->id()) {
2026-07-16 11:36:57 +03:30
if (!$customer || !$customer->trade_active) {
throw new UnprocessableEntityHttpException('امکان سفارش برای شما غیرفعال است.');
}
2026-07-18 22:22:01 +03:30
self::assertCustomerIdentityVerified($customer);
2026-07-16 11:36:57 +03:30
2026-01-24 14:03:11 +00:00
$data['customer_id'] = auth('customer')->id();
2026-06-28 10:46:09 +03:30
$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) {
2026-06-14 22:00:21 +03:30
if ($config->isOutsideTradingHours()) {
throw new UnprocessableEntityHttpException($config->tradingClosedMessage());
}
throw new UnprocessableEntityHttpException('عدم ارائه سرویس.');
}
2026-06-28 10:46:09 +03:30
$latestPrice = Price::latest()->first();
2026-01-24 14:03:11 +00:00
$data['all_price'] = $latestPrice;
2026-06-28 10:46:09 +03:30
$data['price'] = $basePrice;
$data['price_id'] = $latestPrice?->id;
2026-07-28 10:33:11 +03:30
$data['difference'] = $config->orderPriceDiffRial(isset($data['direction']) ? (int) $data['direction'] : null);
2026-07-01 16:33:15 +03:30
$data['service_charge_percentage'] = $config->orderServiceChargePercentage();
$fee_free = Setting::where('key', 'fee_free')->where('is_active', true)->value('value');
2026-03-10 15:39:30 +03:30
if ($fee_free && $this->feeFree($data['customer_id'], $fee_free)) {
$data['service_charge_percentage'] = 0;
// $data['difference'] = 0;
$data['fee_free'] = true;
}
2026-01-24 14:03:11 +00:00
$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(' سفارشات بسته میباشد.');
}
2026-01-24 14:03:11 +00:00
$data['sts'] = 2;
$data['type'] = 2;
$data['payment_sts'] = 4;
$data['price_original'] = $data['price'];
$data['payment_remind'] = 0;
$data['fee'] = $data['difference'];
2026-06-15 11:25:10 +03:30
$ownerType = (int) ($customer?->type ?? 2);
$wallet_interface = new WalletInterface($data['customer_id'], $ownerType);
$wallet = $wallet_interface->wallet;
2026-05-28 11:53:27 +03:30
if (!$wallet) {
throw new UnprocessableEntityHttpException('کیف پول یافت نشد.');
}
2026-01-24 14:03:11 +00:00
$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'])) {
2026-06-14 22:00:21 +03:30
$config->assertProductTradingOpen($data['product']);
2026-01-24 14:03:11 +00:00
// Check if the user wants to buy
if ($data['direction'] == 1) {
2026-07-20 21:29:30 +03:30
$config->assertMainGoldZeroBeforeOtherProductBuy($wallet, $data['product']);
2026-07-23 12:28:24 +03:30
// کارمزد فقط از fee_service_charge_percentage (بالا ست شده) — percent_fee_* مربوط به کارمزد نیست
if (!empty($data['fee_free'])) {
$data['service_charge_percentage'] = 0;
}
2026-07-13 19:03:30 +03:30
$data['price_original'] = $this->resolveProductUnitBasePrice($data['product']);
2026-07-18 17:37:41 +03:30
$this->applyCashPriceDiff($config, $data['product'], 1, $data, (float) $data['price_original']);
$data['total_fee'] = $data['number'] * $data['fee'];
$data['total_price_original'] = $this->resolveProductOrderPricingAmount($data['product'], 1.0)
* ($data['price_original'] ?? 0);
2026-01-24 14:03:11 +00:00
// Check if the method is cash
if ($data['method'] == "cash") {
2026-03-10 15:39:30 +03:30
$response['buy'] = $this->processBuyTransactionProduct($data, $wallet, $wallet_interface);
2026-01-24 14:03:11 +00:00
} // Check if the method is gold
else if ($data['method'] == "gold") {
$response['buy'] = $this->processBuyTransactionProductByGold($data, $wallet, $wallet_interface);
2026-06-03 19:41:00 +03:30
} else {
throw new UnprocessableEntityHttpException('روش پرداخت نامعتبر است.');
2026-01-24 14:03:11 +00:00
}
} // Check if the user wants to sell
else if ($data['direction'] == 2) {
2026-07-23 12:28:24 +03:30
if (!empty($data['fee_free'])) {
$data['service_charge_percentage'] = 0;
}
2026-07-13 19:03:30 +03:30
$data['price_original'] = $this->resolveProductUnitBasePrice($data['product']);
2026-07-18 17:37:41 +03:30
$this->applyCashPriceDiff($config, $data['product'], 2, $data, (float) $data['price_original']);
$data['total_fee'] = $data['number'] * $data['fee'];
$data['total_price_original'] = $this->resolveProductOrderPricingAmount($data['product'], 1.0)
* ($data['price_original'] ?? 0);
2026-01-24 14:03:11 +00:00
2026-07-09 18:17:09 +03:30
$this->assertMinimumSellCash($data);
2026-01-24 14:03:11 +00:00
// 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);
2026-06-03 19:41:00 +03:30
} else {
throw new UnprocessableEntityHttpException('روش پرداخت نامعتبر است.');
2026-01-24 14:03:11 +00:00
}
}
} else {
// Calculate total price and fee for non-product (gold) transaction
$data['total_price_original'] = $data['amount'] * ($data['price_original'] ?? 0);
2026-06-04 14:19:54 +03:30
$mainProductId = app(\App\Services\ConfigService::class)->mainProductId();
2026-06-04 13:48:37 +03:30
$productId = $data['abshode_id'] ?? $data['product_id'] ?? $mainProductId;
$data['product'] = $productId ? Product::find($productId) : null;
2026-05-28 11:53:27 +03:30
if (!$data['product']) {
throw new UnprocessableEntityHttpException('محصول آبشده یافت نشد.');
}
2026-06-14 22:00:21 +03:30
$config->assertProductTradingOpen($data['product']);
2026-06-04 13:48:37 +03:30
if ($data['direction'] == 1) {
2026-07-18 17:37:41 +03:30
$this->applyCashPriceDiff($config, $data['product'], 1, $data, (float) ($data['price_original'] ?? 0));
2026-07-23 12:28:24 +03:30
// کارمزد همان fee_service_charge_percentage — override با percent_fee_* ممنوع
if (!empty($data['fee_free'])) {
$data['service_charge_percentage'] = 0;
}
2026-06-04 13:48:37 +03:30
} elseif ($data['direction'] == 2) {
2026-07-18 17:37:41 +03:30
$this->applyCashPriceDiff($config, $data['product'], 2, $data, (float) ($data['price_original'] ?? 0));
2026-07-23 12:28:24 +03:30
if (!empty($data['fee_free'])) {
$data['service_charge_percentage'] = 0;
}
2026-06-04 13:48:37 +03:30
}
2026-07-18 16:38:46 +03:30
// fee اولیه از orderPriceDiffRial ممکن است با اختلاف واقعی محصول فرق کند
2026-06-04 13:48:37 +03:30
$data['total_fee'] = $data['amount'] * $data['difference'];
2026-05-28 11:53:27 +03:30
$data['product_id'] = $data['product']->id;
2026-01-24 14:03:11 +00:00
// 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('خرید این محصول در این لحظه مقدور نمیباشد');
}
2026-07-09 18:17:09 +03:30
$this->assertMinimumSellCash($data);
2026-01-24 14:03:11 +00:00
$response['sell'] = $this->processSellTransaction($data, $wallet, $wallet_interface);
}
}
2026-06-28 15:16:39 +03:30
// دفترکل داخلی همیشه ثبت شود — مستقل از اتصال به حسابداری بیرونی
if (isset($data['order'])) {
try {
app(\App\Services\AccountingLedgerService::class)->recordTrade($data['order']);
} catch (\Throwable $e) {
report($e);
}
}
2026-07-05 16:43:34 +03:30
if ($config->isTahesabAccounting()) {
try {
2026-06-25 22:20:40 +03:30
$data['tahesab'] = app(TahesabController::class)->storeTrade($data);
2026-03-10 15:39:30 +03:30
} catch (\Exception $e) {
2026-06-25 22:20:40 +03:30
report($e);
}
2026-02-08 08:02:11 +00:00
}
2026-07-05 16:43:34 +03:30
2026-07-23 20:24:58 +03:30
if ($config->isZarinAccounting()) {
try {
$data['zarin'] = app(\App\Http\Controllers\Admin\ZarinController::class)->storeTrade($data);
} catch (\Exception $e) {
report($e);
}
}
2026-07-05 16:43:34 +03:30
if ($config->isPargarAccounting()) {
try {
2026-07-20 15:52:42 +03:30
$paymentProductId = (int) ($data['payment_product_id'] ?? 0);
$paymentKind = app(\App\Services\PargarSyncService::class)
->inferPaymentKindFromContext([
'payment_product_id' => $paymentProductId,
]);
// خرید/فروش عادی از کیف ریالی (بدون payment_product_id) = نقدی
if ($paymentProductId <= 0) {
$paymentKind = \App\Services\PargarSyncService::PAYMENT_CASH;
}
$paymentGold = null;
if ($paymentKind === \App\Services\PargarSyncService::PAYMENT_MAIN_GOLD) {
$paymentGold = (float) ($data['total_amount'] ?? $data['amount'] ?? 0);
}
app(\App\Services\PargarSyncService::class)->recordTrade(
$data['order'] ?? null,
false,
[
'payment_kind' => $paymentKind,
'payment_product_id' => $paymentProductId > 0 ? $paymentProductId : null,
'product_id' => (int) ($data['product_id'] ?? 0) ?: null,
'number' => isset($data['number']) ? (float) $data['number'] : null,
'payment_gold' => $paymentGold,
]
);
2026-07-05 16:43:34 +03:30
} catch (\Throwable $e) {
report($e);
}
}
2026-01-24 14:03:11 +00:00
// $response['reward'] = $this->give_order_reward($data, $wallet, $wallet_interface);
2026-06-05 16:24:15 +03:30
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);
}
}
2026-07-08 11:09:11 +03:30
if ($customer && isset($data['order'])) {
try {
app(ReferralGiftService::class)->tryGiveOnFirstOrder($customer);
} catch (\Throwable $e) {
report($e);
}
}
2026-06-03 19:41:00 +03:30
if (isset($data['order'])) {
2026-06-09 10:21:30 +03:30
try {
$response['Notifications'] = $this->sendTransactionNotifications($data, $transactionType);
} catch (\Throwable $e) {
report($e);
}
2026-06-03 19:41:00 +03:30
}
2026-06-27 15:51:44 +03:30
if ($customer) {
app(WalletBroadcastService::class)->broadcastForCustomer(
(int) $customer->id,
$ownerType
);
}
2026-01-24 14:03:11 +00:00
return response()->json([$response, $data]);
}
private function processBuyTransaction(&$data, $wallet, $wallet_interface)
{
$response = [];
2026-07-01 20:59:24 +03:30
$config = app(ConfigService::class);
2026-07-02 20:59:26 +03:30
$targetCash = $this->resolveTargetCash($data);
2026-07-10 22:52:28 +03:30
// منطق واحد و مستقل از 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);
2026-01-24 14:03:11 +00:00
$data['price'] += $data['difference'];
2026-07-04 12:06:56 +03:30
2026-07-10 22:52:28 +03:30
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;
2026-07-04 12:06:56 +03:30
} else {
2026-07-10 22:52:28 +03:30
// وزن دقیق — کارمزد به مبلغ ریالی پرداختی اضافه می‌شود
2026-07-04 12:06:56 +03:30
$data['amount'] = $this->normalizeGoldAmount((float) ($data['amount'] ?? 0));
2026-07-10 22:52:28 +03:30
$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'];
2026-07-04 12:06:56 +03:30
}
2026-07-10 23:29:36 +03:30
// سمت کسر کارمزد برای گزارش تفکیک‌شده در داشبورد
$data['fee_asset'] = $rialLocked ? 'gold' : 'rial';
2026-07-10 00:19:30 +03:30
$this->enforceProductOrderLimits($data);
2026-06-06 22:06:53 +03:30
2026-06-05 16:24:15 +03:30
if (!empty($data['first_buy']) && ($minFirstBuy = Setting::where('is_active', 1)->where('key', 'min_first_buy')->value('value'))) {
2026-07-10 22:52:28 +03:30
if ($data['payment_amount'] < (int) $minFirstBuy) {
2026-06-05 16:24:15 +03:30
throw new UnprocessableEntityHttpException('حداقل سفارش اول ' . number_format((int) $minFirstBuy) . ' ریال میباشد.');
}
}
2026-01-24 14:03:11 +00:00
if ($wallet->cash < $data['payment_amount']) {
throw new UnprocessableEntityHttpException('موجودی شما کمتر از میزان سفارش است');
}
2026-07-02 21:24:26 +03:30
$this->assertProviderOrderAccepted($data);
2026-07-18 16:38:46 +03:30
$this->syncOrderProfitFields($data);
2026-01-24 14:03:11 +00:00
$data['order'] = $order = Order::create($data);
2026-06-09 13:34:43 +03:30
$abshode = $this->resolveAbshodeProductContext($data);
2026-06-22 21:42:48 +03:30
$productId = (int) ($data['product_id'] ?? $abshode['id'] ?? 0);
2026-07-09 17:47:27 +03:30
$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);
2026-06-09 13:28:02 +03:30
}
2026-07-02 21:24:26 +03:30
$this->finalizeProviderOrder($data);
2026-01-24 14:03:11 +00:00
$response['1'] = 2;
2026-07-02 21:24:26 +03:30
2026-01-24 14:03:11 +00:00
return $response;
}
private function processBuyTransactionProduct(&$data, $wallet, $wallet_interface)
{
2026-07-29 17:00:51 +03:30
// همان قیمت پایهٔ سفارش (price_original / قیمت اپ) — نه ستون کهنهٔ products.price
$data['price'] = $this->resolveProductOrderUnitPrice($data) + (float) ($data['difference'] ?? 0);
$data['amount'] = $this->resolveProductOrderPricingAmount($data['product'], (float) $data['number']);
2026-07-23 12:28:24 +03:30
// محصول تعدادی: کارمزد فقط از ریال (fee_service_charge_percentage)
$feeRate = isset($data['fee_free']) && $data['fee_free'] === true
? 0.0
: (float) ($data['service_charge_percentage'] ?? 0);
$data['service_charge'] = $this->normalizeGoldAmount($feeRate * $data['amount']);
$data['service_charge_price'] = $data['service_charge'] * $data['price_original'];
$data['total_amount'] = $data['amount'];
$data['total_price'] = $data['amount'] * $data['price'];
$data['payment_amount'] = $data['total_price'] + $data['service_charge_price'];
$data['fee_asset'] = 'rial';
2026-06-05 16:24:15 +03:30
2026-06-06 22:06:53 +03:30
$this->enforceProductOrderLimits($data, (float) $data['total_price']);
2026-06-05 16:24:15 +03:30
if (!empty($data['first_buy']) && ($minFirstBuy = Setting::where('is_active', 1)->where('key', 'min_first_buy')->value('value'))) {
2026-07-23 12:28:24 +03:30
if ($data['payment_amount'] < (int) $minFirstBuy) {
2026-06-05 16:24:15 +03:30
throw new UnprocessableEntityHttpException('حداقل سفارش اول ' . number_format((int) $minFirstBuy) . ' ریال میباشد.');
}
}
2026-01-24 14:03:11 +00:00
$data['wallet'] = $wallet;
if ($wallet->cash < $data['payment_amount']) {
throw new UnprocessableEntityHttpException('موجودی شما کمتر از میزان سفارش است');
}
if (!$data['product']['open_buy']) {
throw new UnprocessableEntityHttpException('خرید این محصول در این لحظه مقدور نمیباشد');
}
2026-07-18 16:38:46 +03:30
$this->syncOrderProfitFields($data);
2026-01-24 14:03:11 +00:00
$data['order'] = $order = Order::create($data);
$data['transaction'][] = new TransactionInterface($wallet_interface, $_inp = [
"number" => $data['number'],
2026-07-23 12:28:24 +03:30
"cash" => $data['payment_amount'],
2026-01-24 14:03:11 +00:00
"product_id" => $data["product_id"],
'expire_at' => now(),
'active' => true,
'order_id' => $order->id,
'title' => "برداشت وجه نقد"
2026-03-10 15:39:30 +03:30
], 12, false, false);
2026-06-10 17:31:38 +03:30
$wallet_interface_product = $this->productWalletInterface((int) $data["product_id"]);
$this->normalizeProductWalletGold($wallet_interface_product);
2026-01-24 14:03:11 +00:00
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(),
2026-06-22 21:42:48 +03:30
'title' => $this->meltedOrProductBuyTitle($data),
2026-01-24 14:03:11 +00:00
], 1, true, false);
}
private function processBuyTransactionProductByGold(&$data, $wallet, $wallet_interface)
{
$data['price'] += $data['difference'];
$data['amount'] = $this->resolveProductOrderPricingAmount($data['product'], (float) $data['number']);
2026-07-23 12:28:24 +03:30
// محصول تعدادی: کارمزد از ریال (fee_service_charge_percentage)
$feeRate = isset($data['fee_free']) && $data['fee_free'] === true
? 0.0
: (float) ($data['service_charge_percentage'] ?? 0);
$data['service_charge'] = $this->normalizeGoldAmount($feeRate * $data['amount']);
2026-01-24 14:03:11 +00:00
$data['service_charge_price'] = $data['service_charge'] * $data['price_original'];
2026-07-23 12:28:24 +03:30
$data['total_amount'] = $data['amount'];
$data['total_price'] = $data['amount'] * $data['price'];
$data['payment_amount'] = $data['total_price'];
$data['fee_asset'] = 'rial';
$data['total_fee'] = 0;
$this->enforceProductOrderLimits($data, (float) $data['total_price']);
2026-01-24 14:03:11 +00:00
$data['wallet'] = $wallet;
2026-06-22 19:14:24 +03:30
$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']) {
2026-06-03 19:53:14 +03:30
throw new UnprocessableEntityHttpException($this->insufficientGoldBalanceMessage($data));
2026-01-24 14:03:11 +00:00
}
2026-07-23 12:28:24 +03:30
if ($data['service_charge_price'] > 0 && $wallet->cash < $data['service_charge_price']) {
throw new UnprocessableEntityHttpException('موجودی ریالی شما برای کارمزد کافی نیست');
}
2026-01-24 14:03:11 +00:00
if (!$data['product']['open_buy']) {
throw new UnprocessableEntityHttpException('خرید این محصول در این لحظه مقدور نمیباشد');
}
2026-07-18 16:38:46 +03:30
$this->syncOrderProfitFields($data);
2026-01-24 14:03:11 +00:00
$data['order'] = $order = Order::create($data);
2026-06-22 19:14:24 +03:30
$paymentSellTitle = $paymentProduct
? self::productSellTitle($paymentProduct->title ?? '')
: self::mainMetalSellTitle();
$paymentTransaction = [
2026-01-24 14:03:11 +00:00
"number" => $data['number'],
"gold" => $data['total_amount'],
'expire_at' => now(),
'active' => true,
'order_id' => $order->id,
2026-06-22 19:14:24 +03:30
'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
);
}
2026-07-23 12:28:24 +03:30
if ($data['service_charge_price'] > 0) {
$data['transaction'][] = new TransactionInterface($wallet_interface, [
"cash" => $data['service_charge_price'],
"product_id" => $data["product_id"],
'expire_at' => now(),
'active' => true,
'order_id' => $order->id,
'title' => 'کارمزد معامله',
], 12, false, false);
}
2026-06-10 17:31:38 +03:30
$wallet_interface_product = $this->productWalletInterface((int) $data["product_id"]);
$this->normalizeProductWalletGold($wallet_interface_product);
2026-01-24 14:03:11 +00:00
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(),
2026-06-22 21:42:48 +03:30
'title' => $this->meltedOrProductBuyTitle($data),
2026-01-24 14:03:11 +00:00
], 1, true, false);
}
private function processSellTransaction(&$data, $wallet, $wallet_interface)
{
$response = [];
2026-07-01 20:59:24 +03:30
$config = app(ConfigService::class);
2026-07-04 12:06:56 +03:30
$targetCash = $this->resolveTargetCash($data);
2026-07-10 22:52:28 +03:30
// منطق واحد و مستقل از 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);
2026-01-24 14:03:11 +00:00
$data['price'] -= $data['difference'];
2026-07-04 12:06:56 +03:30
2026-07-10 22:52:28 +03:30
if ($rialLocked) {
// مبلغ ریالی دقیق دریافتی — کارمزد از طلای کیف (کسر اضافه‌تر) تأمین می‌شود
$unitPrice = (float) $data['price'];
$data['amount'] = $unitPrice > 0
? $this->normalizeGoldAmount($targetCash / $unitPrice)
2026-07-04 12:06:56 +03:30
: 0.0;
2026-07-10 22:52:28 +03:30
$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']);
2026-07-04 12:06:56 +03:30
} else {
2026-07-10 22:52:28 +03:30
// وزن دقیق فروش — کارمزد از مبلغ ریالی دریافتی کم می‌شود
2026-07-04 12:06:56 +03:30
$data['amount'] = $this->normalizeGoldAmount((float) ($data['amount'] ?? 0));
2026-07-10 22:52:28 +03:30
$data['service_charge'] = $this->normalizeGoldAmount($feeRate * $data['amount']);
$data['service_charge_price'] = $data['service_charge'] * $data['price_original'];
2026-07-01 20:59:24 +03:30
$data['total_price'] = $data['amount'] * $data['price'] - $data['service_charge_price'];
$data['payment_amount'] = $data['total_price'];
$goldDebit = $data['amount'];
}
2026-07-02 20:59:26 +03:30
2026-07-10 23:29:36 +03:30
// سمت کسر کارمزد برای گزارش تفکیک‌شده در داشبورد
$data['fee_asset'] = $rialLocked ? 'gold' : 'rial';
2026-07-29 19:57:32 +03:30
if (!$rialLocked && (float) $data['total_price'] < 0) {
throw new UnprocessableEntityHttpException(
'مبلغ دریافتی فروش پس از کارمزد منفی شد. درصد کارمزد سفارش را بررسی کنید (باید اعشاری باشد؛ مثلاً ۰٫۰۱ برای ۱٪).'
);
}
2026-07-10 22:52:28 +03:30
$grossTotal = $data['amount'] * $data['price'];
$this->enforceProductOrderLimits($data, (float) $grossTotal);
2026-07-02 20:59:26 +03:30
2026-07-09 17:47:27 +03:30
$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) {
2026-06-03 19:53:14 +03:30
throw new UnprocessableEntityHttpException($this->insufficientGoldBalanceMessage($data));
2026-01-24 14:03:11 +00:00
}
2026-07-02 21:24:26 +03:30
$this->assertProviderOrderAccepted($data);
2026-07-18 16:38:46 +03:30
$this->syncOrderProfitFields($data);
2026-01-24 14:03:11 +00:00
$data['order'] = $order = Order::create($data);
2026-07-09 17:47:27 +03:30
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);
2026-06-09 13:28:02 +03:30
}
2026-07-02 21:24:26 +03:30
$this->finalizeProviderOrder($data);
2026-01-24 14:03:11 +00:00
}
private function processSellTransactionProduct(&$data, $wallet, $wallet_interface)
{
2026-07-29 17:00:51 +03:30
// همان قیمت پایهٔ سفارش (price_original / قیمت اپ) — نه ستون کهنهٔ products.price
$data['price'] = max(0.0, $this->resolveProductOrderUnitPrice($data) - (float) ($data['difference'] ?? 0));
$data['amount'] = $this->resolveProductOrderPricingAmount($data['product'], (float) $data['number']);
2026-07-23 12:28:24 +03:30
// محصول تعدادی: کارمزد فقط از ریال
$feeRate = isset($data['fee_free']) && $data['fee_free'] === true
? 0.0
: (float) ($data['service_charge_percentage'] ?? 0);
$data['service_charge'] = $this->normalizeGoldAmount($feeRate * $data['amount']);
$data['service_charge_price'] = $data['service_charge'] * $data['price_original'];
$data['total_amount'] = $data['amount'];
$data['total_price'] = $data['amount'] * $data['price'] - $data['service_charge_price'];
2026-01-24 14:03:11 +00:00
$data['payment_amount'] = $data['total_price'];
2026-07-23 12:28:24 +03:30
$data['fee_asset'] = 'rial';
2026-07-29 19:57:32 +03:30
if ($data['total_price'] < 0) {
throw new UnprocessableEntityHttpException(
'مبلغ دریافتی فروش پس از کارمزد منفی شد. درصد کارمزد سفارش را بررسی کنید (باید اعشاری باشد؛ مثلاً ۰٫۰۱ برای ۱٪).'
);
}
2026-06-06 22:06:53 +03:30
$grossTotal = (float) ($data['amount'] * $data['price']);
$this->enforceProductOrderLimits($data, $grossTotal);
2026-06-10 17:31:38 +03:30
$wallet_interface_product = $this->productWalletInterface((int) $data["product_id"]);
2026-01-24 14:03:11 +00:00
if ($wallet_interface_product->wallet->gold < $data['number']) {
throw new UnprocessableEntityHttpException('موجودی شما کمتر از میزان سفارش است');
}
if (!$data['product']['open_sell']) {
throw new UnprocessableEntityHttpException('فروش این محصول در این لحظه مقدور نمیباشد');
}
2026-07-18 16:38:46 +03:30
$this->syncOrderProfitFields($data);
2026-01-24 14:03:11 +00:00
$data['order'] = $order = Order::create($data);
2026-06-10 17:31:38 +03:30
$this->normalizeProductWalletGold($wallet_interface_product);
2026-01-24 14:03:11 +00:00
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(),
2026-06-22 21:42:48 +03:30
'title' => $this->meltedOrProductSellTitle($data),
2026-01-24 14:03:11 +00:00
], 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' => "واریز وجه نقد"
2026-03-10 15:39:30 +03:30
], 12, false, true);
2026-01-24 14:03:11 +00:00
}
2026-03-10 15:39:30 +03:30
2026-01-24 14:03:11 +00:00
private function processSellTransactionProductByGold(&$data, $wallet, $wallet_interface)
{
$data['price'] -= $data['difference'];
$data['amount'] = $this->resolveProductOrderPricingAmount($data['product'], (float) $data['number']);
2026-07-23 12:28:24 +03:30
// محصول تعدادی: کارمزد فقط از ریال
$feeRate = isset($data['fee_free']) && $data['fee_free'] === true
? 0.0
: (float) ($data['service_charge_percentage'] ?? 0);
$data['service_charge'] = $this->normalizeGoldAmount($feeRate * $data['amount']);
$data['service_charge_price'] = $data['service_charge'] * $data['price_original'];
$data['total_amount'] = $data['amount'];
$data['total_price'] = $data['amount'] * $data['price'];
2026-01-24 14:03:11 +00:00
$data['payment_amount'] = $data['total_price'];
2026-07-23 12:28:24 +03:30
$data['fee_asset'] = 'rial';
$data['total_fee'] = 0;
2026-06-06 22:06:53 +03:30
$grossTotal = (float) ($data['amount'] * $data['price']);
$this->enforceProductOrderLimits($data, $grossTotal);
2026-06-10 17:31:38 +03:30
$wallet_interface_product = $this->productWalletInterface((int) $data["product_id"]);
2026-01-24 14:03:11 +00:00
if ($wallet_interface_product->wallet->gold < $data['number']) {
throw new UnprocessableEntityHttpException('موجودی شما کمتر از میزان سفارش است');
}
2026-07-23 12:28:24 +03:30
if ($data['service_charge_price'] > 0 && $wallet->cash < $data['service_charge_price']) {
throw new UnprocessableEntityHttpException('موجودی ریالی شما برای کارمزد کافی نیست');
}
2026-01-24 14:03:11 +00:00
if (!$data['product']['open_sell']) {
throw new UnprocessableEntityHttpException('فروش این محصول در این لحظه مقدور نمیباشد');
}
2026-07-18 16:38:46 +03:30
$this->syncOrderProfitFields($data);
2026-01-24 14:03:11 +00:00
$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,
2026-06-22 21:42:48 +03:30
'title' => $this->meltedOrProductSellTitle($data),
2026-03-10 15:39:30 +03:30
], 12, true, false);
2026-07-23 12:28:24 +03:30
if ($data['service_charge_price'] > 0) {
$data['transaction'][] = new TransactionInterface($wallet_interface, [
"cash" => $data['service_charge_price'],
"product_id" => $data["product_id"],
'expire_at' => now(),
'active' => true,
'order_id' => $order->id,
'title' => 'کارمزد معامله',
], 12, false, false);
}
2026-06-10 17:31:38 +03:30
$this->normalizeProductWalletGold($wallet_interface_product);
2026-01-24 14:03:11 +00:00
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(),
2026-06-22 21:42:48 +03:30
'title' => $this->meltedOrProductSellTitle($data),
2026-01-24 14:03:11 +00:00
], 1, false, false);
}
2026-06-10 17:31:38 +03:30
/**
* کیف مؤثر محصول برای تراکنش سفارش.
* محصولات متصل به کیف اصلی (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();
}
2026-03-10 15:39:30 +03:30
2026-01-24 14:03:11 +00:00
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');
2026-06-03 19:41:00 +03:30
$amount = $data['amount'] ?? (
isset($data['number'], $data['product']['amount'])
? $data['number'] * $data['product']['amount']
: 0
);
$totalPrice = $data['total_price'] ?? ($data['payment_amount'] ?? 0);
2026-06-03 21:37:05 +03:30
$subjectTitle = $this->transactionSubjectTitle($data);
2026-01-24 14:03:11 +00:00
2026-06-03 21:37:05 +03:30
$message = "کاربر {$customer->name} با شماره موبایل {$customer->mobile} اقدام به $transactionType {$subjectTitle} به مقدار " .
2026-06-03 19:41:00 +03:30
"{$amount} گرم به مبلغ " . number_format($totalPrice) .
2026-01-24 14:03:11 +00:00
" ریال کرد. با تشکر، $companyName";
2026-07-18 22:22:01 +03:30
if (self::isSmsNotifEnabled('sms_notif_order_admin')) {
User::where('send_sms', 1)->get()->each(function ($admin) use ($message) {
$this->send($admin->mobile, $message);
});
}
2026-06-14 21:09:43 +03:30
$wallet = Wallet::where('owner', $data['customer_id'])
->whereNull('fund_id')
->whereNull('product_id')
->first();
2026-06-03 21:37:05 +03:30
$balanceLabel = self::metalTypeLabel();
2026-06-14 21:09:43 +03:30
$goldBalance = $wallet ? ($wallet->gold ?? 0) : 0;
$cashBalance = $wallet ? ($wallet->cash ?? 0) : 0;
2026-06-03 21:37:05 +03:30
$customerMessage = "$companyName\n$transactionType {$subjectTitle}\nگرم: {$amount}\n" .
2026-06-03 19:41:00 +03:30
"مبلغ کل: " . number_format($totalPrice) . "\n\n" .
2026-06-14 21:09:43 +03:30
"موجودی {$balanceLabel}: {$goldBalance}\n" .
"مانده ریالی: " . number_format($cashBalance) . "\n" .
2026-07-21 13:25:18 +03:30
self::publicSiteUrl(request());
2026-01-24 14:03:11 +00:00
2026-07-18 22:22:01 +03:30
if (self::isSmsNotifEnabled('sms_notif_order_customer')) {
$this->send($customer->mobile, $customerMessage);
}
2026-07-27 22:09:08 +03:30
try {
app(\App\Services\CustomerAppNotifyService::class)->notifyCustomer(
(int) $customer->id,
$transactionType . ' ' . $subjectTitle,
"گرم: {$amount}\nمبلغ کل: " . number_format((float) $totalPrice) . " ریال",
'success'
);
} catch (\Throwable $e) {
report($e);
}
2026-01-24 14:03:11 +00:00
}
2026-07-02 21:24:26 +03:30
private function assertProviderOrderAccepted(array &$data): void
2026-05-24 22:29:43 +03:30
{
2026-07-02 21:24:26 +03:30
if (!app(ConfigService::class)->usesMontzarPricing()) {
2026-05-24 22:29:43 +03:30
return;
}
2026-07-02 21:24:26 +03:30
$this->assertNoRecentDuplicateLocalOrder($data);
$data['_montzar_parsed'] = app(MontzarService::class)->submitOrder($data);
}
private function finalizeProviderOrder(array &$data): void
{
if (!app(ConfigService::class)->usesMontzarPricing()) {
return;
2026-05-24 22:29:43 +03:30
}
2026-07-02 21:24:26 +03:30
$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(
'در حال حاضر سفارش مشابه ثبت شده است. لطفاً چند ثانیه دیگر تلاش کنید.'
);
}
2026-05-24 22:29:43 +03:30
}
2026-01-24 14:03:11 +00:00
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');
2026-06-15 11:25:10 +03:30
$customer = Customer::findOrFail($data['customer_id']);
$wallet_interface = new WalletInterface($data['customer_id'], (int) $customer->type);
$wallet = $wallet_interface->wallet;
2026-01-24 14:03:11 +00:00
$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('موجودی شما کمتر از میزان سفارش است');
}
2026-07-18 16:38:46 +03:30
$this->syncOrderProfitFields($data);
2026-01-24 14:03:11 +00:00
$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('موجودی شما کمتر از میزان سفارش است');
}
2026-07-18 16:38:46 +03:30
$this->syncOrderProfitFields($data);
2026-01-24 14:03:11 +00:00
$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);
});
2026-06-15 11:25:10 +03:30
$mainWallet = Wallet::where('owner', $data['customer_id'])
->where('owner_type', $customer->type)
->main()
->first();
2026-07-21 13:25:18 +03:30
$companyurl = self::publicSiteUrl(request());
2026-01-24 14:03:11 +00:00
$weight = $data['amount'];
$totalAmount = $data['total_price'];
2026-06-15 11:25:10 +03:30
$pricePerGram = $weight > 0 ? $totalAmount / $weight : 0;
$goldBalance = $mainWallet->gold ?? 0;
$remainingBalance = $mainWallet->cash ?? 0;
2026-01-24 14:03:11 +00:00
$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)
2026-03-10 15:39:30 +03:30
->where('type', 2)
->where('sts', 2)
->where('payment_sts', 4)
->count() < (int)$count;
}
2026-06-03 19:53:14 +03:30
2026-06-09 09:57:58 +03:30
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 $this->resolveProductOrderPricingAmount($product, (float) $data['number']);
2026-06-09 09:57:58 +03:30
}
}
return null;
}
2026-07-09 18:17:09 +03:30
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)) . ' ریال می‌باشد'
);
}
2026-07-29 17:00:51 +03:30
/**
* قیمت پایهٔ هر گرم/واحد برای سفارش محصول — اول price_original (زنده)، بعد DB.
*/
private function resolveProductOrderUnitPrice(array $data): float
{
$base = (float) ($data['price_original'] ?? 0);
if ($base > 0) {
return $base;
}
$product = $data['product'] ?? null;
if ($product instanceof Product) {
return (float) (($product->price ?? 0) + ($product->price_profit ?? 0));
}
if (is_array($product)) {
return (float) (($product['price'] ?? 0) + ($product['price_profit'] ?? 0));
}
return (float) ($data['price'] ?? 0);
}
2026-07-09 18:17:09 +03:30
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) {
$difference = (float) ($data['difference'] ?? 0);
$amount = $this->resolveProductOrderPricingAmount($product, (float) $data['number']);
2026-07-29 17:00:51 +03:30
$sellUnitPrice = max(0.0, $this->resolveProductOrderUnitPrice($data) - $difference);
2026-07-09 18:17:09 +03:30
return $amount * $sellUnitPrice;
}
}
if (isset($data['amount'])) {
$amount = (float) $data['amount'];
if ($amount > 0) {
$difference = (float) ($data['difference'] ?? 0);
2026-07-29 17:00:51 +03:30
$sellUnitPrice = max(0.0, $this->resolveProductOrderUnitPrice($data) - $difference);
2026-07-09 18:17:09 +03:30
return $amount * $sellUnitPrice;
}
}
return null;
}
2026-07-02 20:59:26 +03:30
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;
}
2026-07-04 12:06:56 +03:30
private function normalizeGoldAmount(float $amount): float
{
return round($amount, 10);
}
2026-06-22 19:14:24 +03:30
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;
}
2026-06-09 13:34:43 +03:30
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),
];
}
2026-06-09 13:28:02 +03:30
private function productTitleFromData(array $data): ?string
{
$product = $data['product'] ?? null;
2026-06-22 21:42:48 +03:30
$title = null;
2026-06-09 13:28:02 +03:30
2026-06-22 21:42:48 +03:30
if ($product) {
if (is_array($product)) {
$title = $product['title'] ?? null;
} else {
$title = $product->title ?? null;
}
2026-06-09 13:28:02 +03:30
}
$title = trim((string) $title);
2026-06-22 21:42:48 +03:30
if ($title === '' && !empty($data['product_id'])) {
$title = trim((string) Product::query()->whereKey((int) $data['product_id'])->value('title'));
}
2026-06-09 13:28:02 +03:30
return $title !== '' ? $title : null;
}
2026-06-03 21:37:05 +03:30
private function isProductOrder(array $data): bool
2026-06-03 19:53:14 +03:30
{
2026-06-09 13:28:02 +03:30
return !empty($data['product_id']) && $this->productTitleFromData($data) !== null;
2026-06-03 19:53:14 +03:30
}
2026-06-03 21:37:05 +03:30
private function transactionSubjectTitle(array $data): string
2026-06-03 19:53:14 +03:30
{
2026-06-09 13:28:02 +03:30
return $this->productTitleFromData($data) ?? self::meltedGoldLabel();
}
2026-06-03 21:37:05 +03:30
2026-06-09 13:28:02 +03:30
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();
2026-06-03 19:53:14 +03:30
}
private function insufficientGoldBalanceMessage(array $data): string
{
2026-06-03 20:24:07 +03:30
return 'موجودی ' . self::metalTypeLabel() . ' شما کمتر از میزان سفارش است';
2026-06-03 19:53:14 +03:30
}
2026-06-04 15:30:34 +03:30
/**
2026-07-18 17:37:41 +03:30
* اختلاف قیمت سفارش:
2026-07-28 10:33:11 +03:30
* - محصول با «قیمت اپ»: buy_fee / sell_fee + تب اختلاف محصول
* - بدون قیمت اپ: فقط تب اختلاف محصول (cash_fee)
2026-07-18 17:37:41 +03:30
*
* @return array{
* amount: float,
* source: string,
* key: string,
* label: string,
* uses_app_price: bool,
* base_price: float,
* lines: list<array<string, mixed>>
* }
2026-06-04 15:30:34 +03:30
*/
2026-07-18 17:37:41 +03:30
private function resolveCashPriceDiffMeta(
ConfigService $config,
Product $product,
int $direction,
?float $basePriceRial = null
): array {
$display = app(ProductDisplayPriceService::class);
$usesAppPrice = $display->usesAppPrice($product);
$isBuy = $direction === 1;
$side = $isBuy ? 'خرید' : 'فروش';
2026-06-04 15:30:34 +03:30
2026-07-18 17:37:41 +03:30
$base = (float) ($basePriceRial ?? 0);
if ($base <= 0) {
$base = (float) $this->resolveProductUnitBasePrice($product);
}
if ($base <= 0) {
$base = ProductPriceService::productBasePriceRial($product);
}
2026-06-04 15:30:34 +03:30
2026-07-18 17:37:41 +03:30
$lines = [];
$amount = 0.0;
$keys = [];
if ($usesAppPrice) {
if ($isBuy) {
2026-07-28 10:33:11 +03:30
$settingsAmt = $config->displayBuyPriceDiffRial();
$settingsKey = 'buy_fee';
$settingsTitle = 'اختلاف پایه اپ · اضافه قیمت خرید (buy_fee)';
2026-07-18 17:37:41 +03:30
} else {
2026-07-28 10:33:11 +03:30
$settingsAmt = $config->displaySellPriceDiffRial();
$settingsKey = 'sell_fee';
$settingsTitle = 'اختلاف پایه اپ · کسر قیمت فروش (sell_fee)';
2026-07-18 17:37:41 +03:30
}
if ($settingsAmt > 0) {
$amount += $settingsAmt;
$keys[] = $settingsKey;
$lines[] = [
'kind' => 'app_spread',
'key' => $settingsKey,
'label' => $settingsTitle,
'is_percent' => false,
'raw' => $settingsAmt,
'per_unit' => $settingsAmt,
'detail' => number_format($settingsAmt) . ' ریال / گرم',
];
2026-06-04 15:30:34 +03:30
}
}
2026-07-18 17:37:41 +03:30
$productAmt = ProductPriceService::resolveCashFeeRial($product, $direction, $base);
$productKey = $isBuy ? 'cash_fee_buy' : 'cash_fee_sell';
$isPercent = $isBuy
? (bool) ($product->cash_fee_buy_is_percent ?? false)
: (bool) ($product->cash_fee_sell_is_percent ?? false);
$raw = (float) ($isBuy ? ($product->cash_fee_buy ?? 0) : ($product->cash_fee_sell ?? 0));
if ($raw != 0.0 || $productAmt > 0) {
$amount += $productAmt;
$keys[] = $productKey;
$rawLabel = $isPercent
? (rtrim(rtrim(number_format($raw, 4, '.', ''), '0'), '.') . '%')
: (number_format($raw) . ' ریال');
$detail = $isPercent
? ($rawLabel . ' از ' . number_format($base) . ' ≈ ' . number_format($productAmt) . ' ریال / گرم')
: (number_format($productAmt) . ' ریال / گرم');
$lines[] = [
'kind' => 'product_spread',
'key' => $productKey,
'label' => 'تب اختلاف محصول · هزینه ' . $side,
'is_percent' => $isPercent,
'raw' => $raw,
'percent_of' => $isPercent ? $base : null,
'per_unit' => $productAmt,
'detail' => $detail,
];
}
$hasApp = collect($lines)->contains(fn ($l) => ($l['kind'] ?? '') === 'app_spread');
$hasProduct = collect($lines)->contains(fn ($l) => ($l['kind'] ?? '') === 'product_spread');
if ($hasApp && $hasProduct) {
$source = 'app+product';
2026-07-24 16:49:42 +03:30
$label = 'قیمت صفحه اصلی اپلیکیشن + تب اختلاف محصول = ' . number_format($amount) . ' ریال / گرم';
2026-07-18 17:37:41 +03:30
} elseif ($hasApp) {
$source = 'settings';
$label = ($lines[0]['label'] ?? 'اختلاف پایه اپ') . ' = ' . number_format($amount) . ' ریال';
} elseif ($hasProduct) {
$source = 'product';
$label = ($lines[0]['label'] ?? 'تب اختلاف محصول')
. (isset($lines[0]['detail']) ? ' (' . $lines[0]['detail'] . ')' : '');
} else {
$source = 'none';
$label = 'بدون اختلاف قیمت';
}
return [
'amount' => $amount,
'source' => $source,
'key' => implode('+', $keys) ?: 'none',
'label' => $label,
'uses_app_price' => $usesAppPrice,
'base_price' => $base,
'lines' => $lines,
];
}
private function resolveCashPriceDiff(
ConfigService $config,
Product $product,
int $direction,
?float $basePriceRial = null
): float {
return $this->resolveCashPriceDiffMeta($config, $product, $direction, $basePriceRial)['amount'];
}
/** اختلاف + منبع + اقلام فاکتور را روی $data می‌نشاند. */
private function applyCashPriceDiff(
ConfigService $config,
Product $product,
int $direction,
array &$data,
?float $basePriceRial = null
): void {
$meta = $this->resolveCashPriceDiffMeta($config, $product, $direction, $basePriceRial);
$data['difference'] = $meta['amount'];
$data['fee'] = $meta['amount'];
$data['fee_source'] = $meta['source'];
$data['fee_source_key'] = $meta['key'];
$data['fee_source_label'] = $meta['label'];
$data['_fee_spread_lines'] = $meta['lines'];
$data['_fee_uses_app_price'] = $meta['uses_app_price'];
$data['_fee_base_price'] = $meta['base_price'];
2026-06-04 15:30:34 +03:30
}
2026-06-05 16:24:15 +03:30
2026-07-13 19:03:30 +03:30
/**
* قیمت پایهٔ هر واحد محصول (بدون ضرب در وزن — وزن در total_price_original اعمال می‌شود).
2026-07-29 17:13:19 +03:30
* - قیمت اپ → مظنهٔ زندهٔ اپ
* - reading_method (مثل شمش zaarbox) → همان قیمت enrichشدهٔ لیست/UI، نه ستون کهنه/صفر DB
* - بقیه → قیمت ذخیره‌شدهٔ محصول
2026-07-13 19:03:30 +03:30
*/
private function resolveProductUnitBasePrice(Product $product): int
{
$display = app(ProductDisplayPriceService::class);
2026-07-29 17:13:19 +03:30
// قیمت اپ یا فید مرجع — همان منبع نمایش؛ DB خام برای reading_method اغلب ۰/کهنه است
if ($display->usesAppPrice($product) || $product->reading_method) {
2026-07-13 19:03:30 +03:30
return (int) round($display->resolveOrderBasePrice($product));
}
return (int) (($product->price ?? 0) + ($product->price_profit ?? 0));
}
2026-07-18 17:37:41 +03:30
/**
* product_id سفارش را برای ذخیره روی جدول orders تثبیت کن.
*/
private function ensureOrderProductId(array &$data): void
{
$productId = (int) ($data['product_id'] ?? $data['abshode_id'] ?? 0);
if ($productId <= 0) {
$productId = (int) (app(ConfigService::class)->mainProductId() ?? 0);
}
if ($productId > 0) {
$data['product_id'] = $productId;
}
}
2026-07-18 16:38:46 +03:30
/**
* سود هر گرم / سود کل را از قیمت نهایی و قیمت بدون سود همگام کن
2026-07-18 17:37:41 +03:30
* و فاکتور اقلام (مثل فاکتور رستوران) را کامل کن.
2026-07-18 16:38:46 +03:30
*/
private function syncOrderProfitFields(array &$data): void
{
2026-07-18 17:37:41 +03:30
$this->ensureOrderProductId($data);
2026-07-18 16:38:46 +03:30
$price = (float) ($data['price'] ?? 0);
$priceOriginal = (float) ($data['price_original'] ?? 0);
$amount = (float) ($data['amount'] ?? 0);
$data['fee'] = abs($price - $priceOriginal);
$data['total_price_original'] = $amount * $priceOriginal;
$data['total_fee'] = $amount * $data['fee'];
2026-07-18 17:37:41 +03:30
$spreadLines = is_array($data['_fee_spread_lines'] ?? null) ? $data['_fee_spread_lines'] : [];
$receiptLines = [];
$spreadTotal = 0.0;
foreach ($spreadLines as $line) {
$perUnit = (float) ($line['per_unit'] ?? 0);
2026-07-18 17:49:36 +03:30
$total = (int) round($perUnit * $amount);
2026-07-18 17:37:41 +03:30
$spreadTotal += $total;
2026-07-18 17:49:36 +03:30
$amountLabel = rtrim(rtrim(number_format($amount, 4, '.', ''), '0'), '.');
2026-07-18 17:37:41 +03:30
$receiptLines[] = array_merge($line, [
'amount_units' => $amount,
'total' => $total,
'line_text' => ($line['detail'] ?? number_format($perUnit) . ' ریال / گرم')
. ($amount > 0
2026-07-18 17:49:36 +03:30
? (' × ' . $amountLabel . ' گرم = ' . number_format($total) . ' ریال')
2026-07-18 17:37:41 +03:30
: ''),
]);
}
// اگر اقلام ذخیره نشده بود، حداقل یک خط از اختلاف واقعی بساز
if ($receiptLines === [] && $data['fee'] > 0) {
2026-07-18 17:49:36 +03:30
$feeTotal = (int) round((float) $data['total_fee']);
2026-07-18 17:37:41 +03:30
$receiptLines[] = [
'kind' => 'spread',
'key' => $data['fee_source_key'] ?? 'fee',
'label' => $data['fee_source_label'] ?? 'اختلاف قیمت',
'is_percent' => false,
'per_unit' => $data['fee'],
'amount_units' => $amount,
2026-07-18 17:49:36 +03:30
'total' => $feeTotal,
2026-07-18 17:37:41 +03:30
'detail' => number_format($data['fee']) . ' ریال / گرم',
'line_text' => number_format($data['fee']) . ' ریال / گرم × '
. rtrim(rtrim(number_format($amount, 4, '.', ''), '0'), '.')
2026-07-18 17:49:36 +03:30
. ' گرم = ' . number_format($feeTotal) . ' ریال',
2026-07-18 17:37:41 +03:30
];
2026-07-18 17:49:36 +03:30
$spreadTotal = $feeTotal;
2026-07-18 17:37:41 +03:30
}
$scGold = (float) ($data['service_charge'] ?? 0);
2026-07-18 17:49:36 +03:30
$scRial = (int) round((float) ($data['service_charge_price'] ?? 0));
2026-07-18 17:37:41 +03:30
$scPct = (float) ($data['service_charge_percentage'] ?? 0);
if ($scGold > 0 || $scRial > 0) {
2026-07-23 17:53:10 +03:30
$feeAsset = (string) ($data['fee_asset'] ?? 'rial');
$fromMetal = $feeAsset === 'gold';
$assetLabel = $fromMetal ? 'از فلز/مقدار' : 'از ریال';
2026-07-18 17:37:41 +03:30
$goldLabel = rtrim(rtrim(number_format($scGold, 4, '.', ''), '0'), '.') . ' گرم';
$detailParts = [];
if ($scPct > 0) {
$detailParts[] = rtrim(rtrim(number_format($scPct, 4, '.', ''), '0'), '.') . '%';
}
2026-07-23 17:53:10 +03:30
if ($fromMetal && $scGold > 0 && $priceOriginal > 0) {
2026-07-18 17:37:41 +03:30
$detailParts[] = $goldLabel . ' × ' . number_format($priceOriginal) . ' = ' . number_format($scRial) . ' ریال';
} elseif ($scRial > 0 && $priceOriginal > 0) {
$goldEquiv = $scRial / $priceOriginal;
$detailParts[] = number_format($scRial) . ' ریال ≈ '
. rtrim(rtrim(number_format($goldEquiv, 4, '.', ''), '0'), '.') . ' گرم';
} else {
$detailParts[] = number_format($scRial) . ' ریال';
}
2026-07-23 17:53:10 +03:30
$detailParts[] = $assetLabel;
2026-07-18 17:37:41 +03:30
$receiptLines[] = [
'kind' => 'service_charge',
'key' => 'service_charge',
2026-07-23 17:53:10 +03:30
'label' => 'کارمزد معامله · ' . $assetLabel,
2026-07-18 17:37:41 +03:30
'is_percent' => $scPct > 0,
'raw' => $scPct,
'gold' => $scGold,
'rial' => $scRial,
2026-07-23 17:53:10 +03:30
'fee_asset' => $feeAsset,
2026-07-18 17:37:41 +03:30
'per_unit' => $amount > 0 ? ($scRial / $amount) : 0,
'amount_units' => $amount,
'total' => $scRial,
'detail' => implode(' · ', $detailParts),
'line_text' => implode(' · ', $detailParts),
];
}
2026-07-18 17:49:36 +03:30
$baseTotal = (int) round($amount * $priceOriginal);
$customerTotal = (int) round((float) ($data['total_price'] ?? ($amount * $price)));
$spreadTotal = (int) round($spreadTotal);
2026-07-18 17:37:41 +03:30
$grandTotal = $spreadTotal + $scRial;
2026-07-18 17:49:36 +03:30
2026-07-18 17:37:41 +03:30
$data['fee_breakdown'] = [
'uses_app_price' => (bool) ($data['_fee_uses_app_price'] ?? false),
'amount_units' => $amount,
'price_original' => $priceOriginal,
'price_customer' => $price,
2026-07-18 17:49:36 +03:30
'base_total' => $baseTotal,
'customer_total' => $customerTotal,
2026-07-18 17:37:41 +03:30
'spread_per_unit' => (float) $data['fee'],
'spread_total' => $spreadTotal,
'service_charge_gold' => $scGold,
'service_charge_rial' => $scRial,
'grand_total' => $grandTotal,
'lines' => $receiptLines,
];
unset(
$data['_fee_spread_lines'],
$data['_fee_uses_app_price'],
$data['_fee_base_price']
);
2026-07-18 16:38:46 +03:30
}
2026-06-05 16:24:15 +03:30
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);
2026-07-26 14:15:26 +03:30
try {
app(\App\Services\PargarSyncService::class)->queueWalletDocument(
$customer,
'cash',
(float) $customer_bonus,
[
'withdraw_id' => $Withdraw->id,
'note' => 'پاداش اولین سفارش',
'source' => 'bonus_first_order',
]
);
} catch (\Throwable $e) {
report($e);
}
2026-06-05 16:24:15 +03:30
$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);
2026-07-26 14:15:26 +03:30
try {
app(\App\Services\PargarSyncService::class)->queueWalletDocument(
$referrer,
'cash',
(float) $referrer_bonus,
[
'withdraw_id' => $Withdraw->id,
'note' => 'پاداش اولین سفارش زیرمجموعه',
'source' => 'bonus_referrer',
]
);
} catch (\Throwable $e) {
report($e);
}
2026-06-05 16:24:15 +03:30
$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;
}
2026-06-06 22:06:53 +03:30
2026-07-10 00:19:30 +03:30
private function enforceProductOrderLimits(array &$data, ?float $totalPriceRial = null): void
2026-06-06 22:06:53 +03:30
{
$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) {
2026-07-10 00:19:30 +03:30
$product->validateOrderCashLimit($this->resolveOrderLimitCashRial($data, $totalPriceRial));
2026-06-06 22:06:53 +03:30
}
}
2026-07-10 00:19:30 +03:30
/**
* مبلغ مؤثر برای 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);
}
2026-01-24 14:03:11 +00:00
}