<?php
namespace App\Http\Controllers\Frontend;
use App\Http\Controllers\Controller;
use App\Models\Cart;
use App\Models\Order;
use App\Models\OrderItem;
use App\Models\Product;
use App\Models\Setting;
use App\Services\MetaConversionsService;
use Illuminate\Http\Request;
class CheckoutController extends Controller
{
private function getSessionId(): string
{
return session()->getId();
}
private function calculateShipping(iterable $cartItems, string $area): float
{
$globalInside = (float) Setting::get('shipping_inside_dhaka', 60);
$globalOutside = (float) Setting::get('shipping_outside_dhaka', 120);
$globalRate = $area === 'inside_dhaka' ? $globalInside : $globalOutside;
$maxCharge = 0;
foreach ($cartItems as $item) {
$product = $item->product;
$custom = $area === 'inside_dhaka'
? $product->shipping_inside_dhaka
: $product->shipping_outside_dhaka;
// null = use global; 0 = free; number = custom rate
$charge = $custom !== null ? (float) $custom : $globalRate;
if ($charge > $maxCharge) {
$maxCharge = $charge;
}
}
return $maxCharge;
}
public function index()
{
$cartItems = Cart::with(['product', 'variation'])
->where('session_id', $this->getSessionId())
->get();
if ($cartItems->isEmpty()) {
return redirect()->route('home')->with('error', 'Your cart is empty.');
}
$subtotal = $cartItems->sum(function ($item) {
$price = $item->product->current_price + ($item->variation?->price_adjustment ?? 0);
return $price * $item->quantity;
});
$insideCharge = $this->calculateShipping($cartItems, 'inside_dhaka');
$outsideCharge = $this->calculateShipping($cartItems, 'outside_dhaka');
// Server-side InitiateCheckout event
$capi = app(MetaConversionsService::class);
$pixelEventId = $capi->sendInitiateCheckout(
$subtotal,
$cartItems->sum('quantity')
);
return view('frontend.checkout', compact('cartItems', 'subtotal', 'insideCharge', 'outsideCharge', 'pixelEventId'));
}
public function buyNow(Request $request)
{
$request->validate([
'product_id' => 'required|exists:products,id',
'variation_id' => 'nullable|exists:product_variations,id',
'quantity' => 'required|integer|min:1',
]);
$sessionId = $this->getSessionId();
Cart::where('session_id', $sessionId)->delete();
Cart::create([
'session_id' => $sessionId,
'product_id' => $request->product_id,
'variation_id' => $request->variation_id,
'quantity' => $request->quantity,
]);
return redirect()->route('checkout');
}
public function store(Request $request)
{
$isMfs = in_array($request->payment_method, ['bkash', 'rocket', 'nagad']);
$request->validate([
'name' => 'required|string|max:255',
'address' => 'required|string|max:1000',
'mobile' => 'required|string|max:20',
'note' => 'nullable|string|max:1000',
'shipping_area' => 'required|in:inside_dhaka,outside_dhaka',
'payment_method' => 'required|in:cod,bkash,rocket,nagad',
'payment_phone' => $isMfs ? 'required|string|max:20' : 'nullable',
'payment_trx_id' => $isMfs ? 'required|string|max:100' : 'nullable',
], [
'payment_phone.required' => 'পেমেন্ট নম্বর দিন।',
'payment_trx_id.required' => 'Transaction ID দিন।',
]);
$sessionId = $this->getSessionId();
$cartItems = Cart::with(['product', 'variation'])
->where('session_id', $sessionId)
->get();
if ($cartItems->isEmpty()) {
return redirect()->route('home')->with('error', 'Your cart is empty.');
}
$subtotal = $cartItems->sum(function ($item) {
$price = $item->product->current_price + ($item->variation?->price_adjustment ?? 0);
return $price * $item->quantity;
});
$shippingArea = $request->shipping_area;
$shippingCharge = $this->calculateShipping($cartItems, $shippingArea);
$total = $subtotal + $shippingCharge;
$order = Order::create([
'name' => $request->name,
'address' => $request->address,
'mobile' => $request->mobile,
'note' => $request->note,
'payment_method' => $request->payment_method,
'payment_phone' => $request->payment_phone,
'payment_trx_id' => $request->payment_trx_id,
'shipping_area' => $shippingArea,
'shipping_charge' => $shippingCharge,
'subtotal' => $subtotal,
'total' => $total,
]);
foreach ($cartItems as $item) {
$price = $item->product->current_price + ($item->variation?->price_adjustment ?? 0);
OrderItem::create([
'order_id' => $order->id,
'product_id' => $item->product_id,
'variation_id' => $item->variation_id,
'product_name' => $item->product->name,
'variation_details' => $item->variation?->label,
'price' => $price,
'quantity' => $item->quantity,
'subtotal' => $price * $item->quantity,
]);
}
Cart::where('session_id', $sessionId)->delete();
// Server-side Purchase event (most important — fires after order confirmed)
$contentIds = $cartItems->pluck('product_id')->filter()->map(fn($id) => (string)$id)->values()->toArray();
$capi = app(MetaConversionsService::class);
$purchaseEventId = $capi->sendPurchase(
$total,
$request->mobile,
$contentIds,
$cartItems->sum('quantity'),
$order->order_number
);
// Flash pixel Purchase data — read by layout on thank-you page
session()->flash('pixel_purchase', json_encode([
'value' => round($total, 2),
'currency' => 'BDT',
'content_type' => 'product',
'content_ids' => $contentIds,
'num_items' => (int) $cartItems->sum('quantity'),
'order_id' => $order->order_number,
]));
session()->flash('pixel_event_id', $purchaseEventId);
session()->flash('purchase_event_id', $purchaseEventId);
return redirect()->route('order.success', $order->order_number);
}
public function success(string $orderNumber)
{
$order = Order::with('items')->where('order_number', $orderNumber)->firstOrFail();
$purchaseEventId = session('purchase_event_id', '');
return view('frontend.order-success', compact('order', 'purchaseEventId'));
}
public function track(Request $request)
{
$order = null;
if ($request->filled('order_number')) {
$order = Order::with(['items', 'trackingUpdates'])
->where('order_number', $request->order_number)
->where('mobile', $request->mobile)
->first();
}
return view('frontend.tracking', compact('order'));
}
}