/** * Renew VPS Service */ public function renew(Request $request) { try { $user = customer(); if (!$user) { return redirect()->route('user.login')->with('error', __('Vui lòng đăng nhập.')); } // 1. Validate Input $validator = FacadesValidator::make($request->all(), [ 'product_id' => 'required|string', 'plan' => 'required', 'months' => 'nullable|integer|min:2|max:60' ]); if ($validator->fails()) { return redirect()->back()->withErrors($validator)->withInput()->with('error', __('Dữ liệu không hợp lệ.')); } $vmware_id = $this->sanitizeInput($request->product_id); $plan = $request->input('plan'); $months = $plan === 'other' ? (int)$request->input('months') : (int)$plan; if ($months < 2) { return redirect()->back()->with('error', __('Thời hạn gia hạn tối thiểu là 2 tháng.')); } // 2. Find Service & Verify Ownership $vm = ModuleVmware::where('vmware_id', $vmware_id) ->where('customer_id', $user->id) ->first(); if (!$vm) { return redirect()->back()->with('error', __('Không tìm thấy dịch vụ hoặc bạn không có quyền truy cập.')); } // 3. Calculate Price based on Original Order/InvoiceItem // Strategy: Find the InvoiceItem from the Order associated with this VM. // If multiple items, try to match type VPS (4). $order = Order::find($vm->order_id); if (!$order) { // Fallback if order missing (rare) // Try finding by invoice_id if stored on VM? ModuleVmware has next_invoice but not current_invoice. return redirect()->back()->with('error', __('Không tìm thấy đơn hàng gốc của dịch vụ này.')); } // Find InvoiceItem // Order has relationship to Invoice (order->invoice) // Or Order has orderItems (which are InvoiceItems) $invoiceItem = null; if ($order->invoice) { // Try finding item in the invoice related to this order // Note: The modal uses $invoice->invoiceItem->first(). We should try to be specific. // We don't have a direct link from ModuleVmware to InvoiceItem. // But ModuleVmware links to Order, which links to items. // Let's take the first item of type 4 (VPS) from the order's invoice. $invoiceItem = $order->invoice->invoiceItem->where('type_id', 4)->first(); if (!$invoiceItem) { $invoiceItem = $order->invoice->invoiceItem->first(); } } if (!$invoiceItem) { // Extreme fallback or failure return redirect()->back()->with('error', __('Không tìm thấy thông tin giá của dịch vụ.')); } // Calculate Unit Price (Monthly) // 'amount' in InvoiceItem is total price for 'months' duration. $billing_months = $invoiceItem->months > 0 ? $invoiceItem->months : 1; if ($invoiceItem->amount <= 0) { // If amount is 0 (free?), unit price is 0. $base_unit_price = 0; } else { $base_unit_price = $invoiceItem->amount / $billing_months; } // Calculate Addons Unit Price $addons_unit_price = 0; if ($invoiceItem->addons && $invoiceItem->addons->count() > 0) { foreach ($invoiceItem->addons as $addonPivot) { // Try to find price. Assuming Addon model has current price. // Ideally we should use the price stored in pivot if available, but pivot structure is unknown. // We'll use the Addon catalog price as fallback or primary if pivot doesn't store it. // $addonPivot->addonItems returns the Addon model. if ($addonPivot->addonItems) { $addons_unit_price += $addonPivot->addonItems->price; } } } $total_unit_price = $base_unit_price + $addons_unit_price; $total_cost = $total_unit_price * $months; // 4. Check Balance if ($user->credit < $total_cost) { return redirect()->back()->with('error', __('Số dư tài khoản không đủ để gia hạn (Cần: :amount). Vui lòng nạp thêm tiền.', ['amount' => number_format($total_cost) . ' VNĐ'])); } // 5. Process Renewal DB::beginTransaction(); try { // Deduct Credit $user->decrement('credit', $total_cost); // Create Renewal Invoice (Paid) - Optional but good for history $newInvoice = Invoice::create([ 'customer_id' => $user->id, 'status' => 1, // Paid 'date' => now(), 'due_date' => now(), 'total' => $total_cost, 'payment_method' => 'credit_balance', // or similar 'description' => "Gia hạn dịch vụ VPS #{$vm->name} ({$months} tháng)" ]); // Update VM Expiry // If current expiry is in the past, renew from NOW? Or from expiry? // Usually from expiry if it's recent grace period. If long expired, maybe from now. // Let's simply add to next_due_date. $current_due = $vm->next_due_date ? \Carbon\Carbon::parse($vm->next_due_date) : now(); if ($current_due->isPast()) { // If expired, maybe we should start from today? // Or just extend the gap. User usually pays for the gap. // Let's extend from current_due to maintain billing cycle. } $new_due_date = $current_due->copy()->addMonths($months); $vm->update([ 'next_due_date' => $new_due_date, 'status' => 1, // Ensure active if it was suspended ]); // Log or Notify (Optional) DB::commit(); // Clear cache Cache::forget("vm_info_{$vm->vmware_id}"); return redirect()->back()->with('success', __('Gia hạn dịch vụ thành công! Hạn sử dụng mới: :date', ['date' => $new_due_date->format('d/m/Y')])); } catch (\Exception $e) { DB::rollBack(); throw $e; } } catch (\Exception $e) { Log::error("Error renewing VM: " . $e->getMessage()); return redirect()->back()->with('error', __('Có lỗi xảy ra khi gia hạn dịch vụ. Vui lòng thử lại sau.')); } }