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