user(); $filters = [ 'total_price' => $request->query('total_price'), 'pending' => $request->query('pending') ]; $orders = $this->orderRepo->getOrders($customer->id, $filters); return response()->json([ 'success' => true, 'data' => $orders ]); } catch (\Exception $e) { return response()->json([ 'success' => false, 'message' => $e->getMessage() ], 400); } } public function getClientOrders(Request $request): JsonResponse { try { $customer = $request->user(); $orders = OrderModel::query() ->with(['invoice']) ->where('customer_id', $customer->id) ->orderByDesc('created_at') ->get(); $data = $orders->map(function ($order) { return [ 'id' => $order->id, 'order_num' => $order->order_num, 'status' => $order->status, 'status_text' => OrderStatus::from((int) $order->status)->label(), 'amount' => (float) $order->amount, 'first_payment' => $order->first_payment, 'next_payment' => $order->next_payment, 'created_at' => $order->created_at?->toISOString(), 'invoice' => [ 'id' => $order->invoice?->id, 'invoice_num' => $order->invoice?->invoice_num, 'status' => $order->invoice?->status, 'total' => $order->invoice?->total, ], ]; }); return response()->json([ 'success' => true, 'data' => $data, ]); } catch (\Exception $e) { return response()->json([ 'success' => false, 'message' => $e->getMessage(), ], 400); } } public function getClientOrderDetail(Request $request, int $id): JsonResponse { try { $customer = $request->user(); $order = OrderModel::query() ->with(['invoice', 'orderItems.product', 'orderItems.addons.addonItems', 'customer']) ->where('customer_id', $customer->id) ->where('id', $id) ->first(); if (!$order) { return response()->json([ 'success' => false, 'message' => 'Đơn hàng không tồn tại', ], 404); } return response()->json([ 'success' => true, 'data' => [ 'id' => $order->id, 'order_num' => $order->order_num, 'status' => $order->status, 'status_text' => OrderStatus::from((int) $order->status)->label(), 'amount' => (float) $order->amount, 'created_at' => $this->toIsoDate($order->created_at), 'customer' => [ 'id' => $order->customer?->id, 'name' => $order->customer?->fullname, 'email' => $order->customer?->email, 'address' => $order->customer?->address, ], 'invoice' => [ 'id' => $order->invoice?->id, 'invoice_num' => $order->invoice?->invoice_num, 'status' => $order->invoice?->status, 'status_text' => $order->invoice?->status ? InvoiceStatus::from((int) $order->invoice->status)->label() : null, 'sub_total' => $order->invoice?->sub_total, 'tax' => $order->invoice?->tax, 'total' => $order->invoice?->total, 'due_date' => $this->toIsoDate($order->invoice?->due_date), 'paid_at' => $this->toIsoDate($order->invoice?->date_paid), ], 'items' => $order->orderItems->map(function ($item) { return [ 'id' => $item->id, 'product_name' => $item->product?->name, 'billing_cycle' => $item->billing_cycle, 'server_name' => $item->server_name, 'price' => (float) $item->price, 'addons' => $item->addons->map(function ($addon) { return [ 'id' => $addon->id, 'name' => $addon->addonItems?->name, 'qty' => $addon->qty, 'price' => (float) $addon->price, ]; }), ]; }), ], ]); } catch (\Exception $e) { return response()->json([ 'success' => false, 'message' => $e->getMessage(), ], 400); } } public function getClientInvoices(Request $request): JsonResponse { try { $customer = $request->user(); $orders = OrderModel::query() ->with(['invoice', 'orderItems.product']) ->where('customer_id', $customer->id) ->whereNotNull('invoice_id') ->orderByDesc('created_at') ->get(); $data = $orders->map(function ($order) { $firstItem = $order->orderItems->first(); $invoice = $order->invoice; return [ 'id' => $invoice?->id, 'invoice_num' => $invoice?->invoice_num, 'service_name' => $firstItem?->product?->name ?? ('Đơn hàng ' . $order->order_num), 'amount' => (float) ($invoice?->total ?? $order->amount ?? 0), 'status' => $invoice?->status, 'status_text' => $invoice?->status ? InvoiceStatus::from((int) $invoice->status)->label() : null, 'created_at' => $this->toIsoDate($invoice?->created_at ?? $order->created_at), 'order_id' => $order->id, 'order_num' => $order->order_num, ]; })->filter(fn($row) => !empty($row['id']))->values(); return response()->json([ 'success' => true, 'data' => $data, ]); } catch (\Exception $e) { return response()->json([ 'success' => false, 'message' => $e->getMessage(), ], 400); } } public function getClientInvoiceDetail(Request $request, int $id): JsonResponse { try { $customer = $request->user(); $order = OrderModel::query() ->with(['invoice', 'orderItems.product', 'customer', 'vmWare.ipAddress', 'vmWare.os']) ->where('customer_id', $customer->id) ->where('invoice_id', $id) ->first(); if (!$order || !$order->invoice) { return response()->json([ 'success' => false, 'message' => 'Hóa đơn không tồn tại', ], 404); } $invoice = $order->invoice; $firstItem = $order->orderItems->first(); $vmware = $order->vmWare->first(); return response()->json([ 'success' => true, 'data' => [ 'id' => $invoice->id, 'invoice_num' => $invoice->invoice_num, 'status' => $invoice->status, 'status_text' => InvoiceStatus::from((int) $invoice->status)->label(), 'sub_total' => (float) ($invoice->sub_total ?? 0), 'tax' => (float) ($invoice->tax ?? 0), 'total' => (float) ($invoice->total ?? 0), 'created_at' => $this->toIsoDate($invoice->created_at), 'due_date' => $this->toIsoDate($invoice->due_date), 'paid_at' => $this->toIsoDate($invoice->date_paid), 'payment_method' => $order->payment_method ?? null, 'order' => [ 'id' => $order->id, 'order_num' => $order->order_num, 'amount' => (float) $order->amount, ], 'service' => [ 'name' => $firstItem?->product?->name, 'billing_cycle' => $firstItem?->billing_cycle, 'server_name' => $firstItem?->server_name, ], 'customer' => [ 'id' => $order->customer?->id, 'name' => $order->customer?->fullname, 'email' => $order->customer?->email, 'address' => $order->customer?->address, ], 'items' => $order->orderItems->map(function ($item) { return [ 'id' => $item->id, 'product_name' => $item->product?->name, 'billing_cycle' => $item->billing_cycle, 'server_name' => $item->server_name, 'price' => (float) $item->price, ]; }), 'vmware' => $vmware ? [ 'id' => $vmware->id, 'name' => $vmware->name, 'hostname' => $vmware->hostname, 'ip_address' => $vmware->ipAddress?->ip, 'os_name' => $vmware->os?->name, 'status' => $vmware->status, 'next_due_date' => $this->toIsoDate($vmware->next_due_date), ] : null, ], ]); } catch (\Exception $e) { return response()->json([ 'success' => false, 'message' => $e->getMessage(), ], 400); } } public function getClientServices(Request $request): JsonResponse { try { $customer = $request->user(); $invoices = \App\Models\Invoice::query() ->with(['order.orderItems.product', 'order.vmWare.ipAddress', 'order.vmWare.os']) ->where('customer_id', $customer->id) ->where('status', \App\Enums\Invoice\InvoiceStatus::PAID->value) ->orderByDesc('created_at') ->get(); $data = $invoices->map(function ($invoice) { $order = $invoice->order; $firstItem = $order?->orderItems->first(); $vmware = $order?->vmWare->first(); return [ 'id' => $invoice->id, 'invoice_num' => $invoice->order_num, 'service_name' => $firstItem?->product?->name ?? ('Đơn hàng ' . $order?->order_num), 'server_name' => $firstItem?->server_name, 'billing_cycle'=> $firstItem?->billing_cycle, 'amount' => (float) ($invoice->total ?? 0), 'vm_id' => $invoice->vm_id, 'ip_address' => $vmware?->ipAddress?->ip, 'os_name' => $vmware?->os?->name, 'vm_status' => $vmware?->status, 'paid_at' => $this->toIsoDate($invoice->date_paid), 'next_due_date'=> $vmware ? $this->toIsoDate($vmware->next_due_date) : null, 'created_at' => $this->toIsoDate($invoice->created_at), 'order_id' => $order?->id, 'order_num' => $order?->order_num, ]; })->values(); return response()->json([ 'success' => true, 'data' => $data, ]); } catch (\Exception $e) { return response()->json([ 'success' => false, 'message' => $e->getMessage(), ], 400); } } private function toIsoDate($value): ?string { if (empty($value)) { return null; } try { if ($value instanceof \DateTimeInterface) { return Carbon::instance($value)->toISOString(); } if (is_numeric($value)) { return Carbon::createFromTimestamp((int) $value)->toISOString(); } return Carbon::parse($value)->toISOString(); } catch (\Throwable) { return null; } } /** * @OA\Post( * path="/orders", * operationId="storeOrder", * tags={"Orders"}, * summary="Create a new order", * description="Create a new order for a product", * security={{"bearerAuth":{}}}, * @OA\RequestBody( * required=true, * @OA\JsonContent( * required={"product_id","billing_cycle"}, * @OA\Property(property="product_id", type="string", example="550e8400-e29b-41d4-a716-446655440000", description="Product UUID"), * @OA\Property(property="billing_cycle", type="string", example="monthly", enum={"monthly", "quarterly", "semi_annually", "annually", "biennially"}), * @OA\Property(property="server_name", type="string", example="My VPS Server"), * @OA\Property(property="os_id", type="integer", example=1, description="Operating System ID"), * @OA\Property(property="addons", type="array", @OA\Items( * @OA\Property(property="addon_id", type="integer", example=1), * @OA\Property(property="qty", type="integer", example=1) * )) * ) * ), * @OA\Response( * response=201, * description="Order created successfully", * @OA\JsonContent( * @OA\Property(property="success", type="boolean", example=true), * @OA\Property(property="data", type="object"), * @OA\Property(property="message", type="string", example="Tạo đơn hàng thành công") * ) * ), * @OA\Response( * response=401, * description="Unauthenticated" * ), * @OA\Response( * response=422, * description="Validation error" * ) * ) */ public function storeOrder(StoreOrderRequest $request): JsonResponse { try { $customer = $request->user(); $data = $request->validated(); // Convert UUID to ID for internal processing $product = \App\Models\Product::where('uuid', $data['product_id'])->first(); if (!$product) { throw new \Exception('Sản phẩm không tìm thấy hoặc đã bị xóa.'); } $data['product_id'] = $product->id; $customer = $request->user(); if (!$customer) { return response()->json([ 'success' => false, 'message' => 'Người dùng chưa đăng nhập hoặc phiên đăng nhập hết hạn.' ], 401); } $result = $this->orderService->create($data, $customer); // Load relationships for response $order = $result['order']->load(['orderItems.addons.addonItems', 'orderItems.product', 'invoice', 'customer']); // Format response $responseData = [ 'order' => [ 'id' => $order->id, 'order_num' => $order->order_num, 'status' => $order->status, 'status_text' => \App\Enums\Order\OrderStatus::from($order->status)->label(), 'amount' => $order->amount, 'first_payment' => $order->first_payment, 'next_payment' => $order->next_payment, 'created_at' => $order->created_at->toISOString(), ], 'invoice' => [ 'id' => $order->invoice?->id, 'invoice_num' => $order->invoice?->invoice_num, 'sub_total' => $order->invoice?->sub_total, 'tax' => $order->invoice?->tax, 'total' => $order->invoice?->total, 'status' => $order->invoice?->status, 'due_date' => $order->invoice?->due_date, ], 'items' => $order->orderItems->map(function ($item) { return [ 'id' => $item->id, 'product' => [ 'id' => $item->product?->id, 'uuid' => $item->product?->uuid, 'name' => $item->product?->name, 'product_type' => $item->product?->product_type, 'specs' => [ 'cpu' => $item->product?->cpu, 'ram' => $item->product?->ram, 'disk' => $item->product?->disk, 'bandwidth' => $item->product?->bandwidth, ], ], 'billing_cycle' => $item->billing_cycle, 'server_name' => $item->server_name, 'os_id' => $item->os_id, 'price' => $item->price, 'addons' => $item->addons->map(function ($addon) { return [ 'id' => $addon->id, 'addon_id' => $addon->addon_id, 'addon_name' => $addon->addonItems?->name, 'qty' => $addon->qty, 'price' => $addon->price, ]; }), ]; }), 'customer' => [ 'id' => $order->customer?->id, 'email' => $order->customer?->email, 'name' => trim($order->customer?->firstname . ' ' . $order->customer?->lastname), ], ]; return response()->json([ 'success' => true, 'data' => $responseData, 'message' => 'Tạo đơn hàng thành công', 'notice' => 'Dịch vụ VPS sẽ được khởi tạo trong vòng 5-10 phút sau khi thanh toán. Bạn sẽ nhận được email thông báo khi hoàn tất.' ], 201); } catch (\Exception $e) { return response()->json([ 'success' => false, 'message' => $e->getMessage() ], 400); } } /** * @OA\Post( * path="/orders/{id}/pay", * operationId="payOrder", * tags={"Orders"}, * summary="Pay for an order", * description="Process payment for a specific order", * security={{"bearerAuth":{}}}, * @OA\Parameter( * name="id", * in="path", * description="Order ID", * required=true, * @OA\Schema(type="integer") * ), * @OA\RequestBody( * required=true, * @OA\JsonContent( * required={"payment_method"}, * @OA\Property(property="payment_method", type="string", example="credit", enum={"credit", "vnpay", "momo", "bank_transfer"}) * ) * ), * @OA\Response( * response=200, * description="Payment successful", * @OA\JsonContent( * @OA\Property(property="success", type="boolean", example=true), * @OA\Property(property="message", type="string", example="Thanh toán thành công") * ) * ), * @OA\Response( * response=401, * description="Unauthenticated" * ), * @OA\Response( * response=403, * description="Forbidden - Not your order" * ), * @OA\Response( * response=404, * description="Order not found" * ) * ) */ public function payOrder(int $id, PayOrderRequest $request): JsonResponse { try { $order = $this->orderRepo->find($id); if (!$order) { return response()->json(['message' => 'Đơn hàng không tồn tại'], 404); } if ($order->customer_id !== $request->user()->id) { return response()->json(['message' => 'Không có quyền truy cập'], 403); } $this->orderService->pay($order, $request->payment_method); return response()->json([ 'success' => true, 'message' => 'Thanh toán thành công' ]); } catch (\Exception $e) { return response()->json([ 'success' => false, 'message' => $e->getMessage() ], 400); } } }