feat: Implement order and invoice management, enhance addon features, and remove the 'Type' module.
This commit is contained in:
@@ -61,7 +61,38 @@ class ProcessOrderPaymentJob implements ShouldQueue
|
||||
return;
|
||||
}
|
||||
|
||||
// Tạo VM cho các sản phẩm VPS
|
||||
/**
|
||||
* OPTIMIZED PAYMENT FLOW
|
||||
*
|
||||
* Thay đổi: TRỪ TIỀN TRƯỚC → TẠO VM SAU
|
||||
*
|
||||
* Lý do:
|
||||
* 1. User đã có credit trong hệ thống (đã nạp qua VNPay)
|
||||
* 2. Nếu tạo VM fail → refund credit dễ dàng
|
||||
* 3. Tránh tình huống: VM tạo ok nhưng chưa trừ tiền
|
||||
* 4. Đảm bảo tính nhất quán: Đã trừ tiền = đã có order valid
|
||||
*/
|
||||
|
||||
DB::beginTransaction();
|
||||
|
||||
try {
|
||||
// STEP 1: Trừ tiền người dùng
|
||||
$user->decrement('credit', (int) $this->order->amount);
|
||||
|
||||
// STEP 2: Cập nhật trạng thái hóa đơn và đơn hàng
|
||||
$invoiceService->markAsPaid($invoice);
|
||||
$this->order->update(['status' => OrderStatus::ACTIVE->value]);
|
||||
|
||||
DB::commit();
|
||||
|
||||
Log::info("✓ Đã trừ credit và cập nhật order #{$this->order->order_num}");
|
||||
} catch (\Throwable $e) {
|
||||
DB::rollBack();
|
||||
Log::error("Lỗi khi trừ credit cho order #{$this->order->order_num}: " . $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
|
||||
// STEP 3: Tạo VM cho các sản phẩm VPS (sau khi đã trừ tiền)
|
||||
$vmCreationResults = [];
|
||||
$allVMsCreated = true;
|
||||
$hasVMCreationAttempt = false;
|
||||
@@ -97,119 +128,111 @@ class ProcessOrderPaymentJob implements ShouldQueue
|
||||
}
|
||||
}
|
||||
|
||||
// Kiểm tra xem có thực sự có VM nào được tạo không
|
||||
// STEP 4: Xử lý kết quả tạo VM
|
||||
if (!$hasVMCreationAttempt) {
|
||||
$errorMessage = "Không có sản phẩm VPS nào được xử lý trong đơn hàng #{$this->order->order_num}";
|
||||
Log::error($errorMessage);
|
||||
$telegram->SendErrorMessage($errorMessage, 'ProcessOrderPaymentJob - No VM Creation Attempt');
|
||||
// Không có VPS nào trong order → OK, chỉ là Hosting/Domain
|
||||
$telegram->SendOrderPaidNotification($invoice);
|
||||
$this->sendSuccessNotification([]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Kiểm tra kết quả tạo VM
|
||||
if (empty($vmCreationResults)) {
|
||||
$errorMessage = "Không có kết quả tạo VM nào được trả về cho đơn hàng #{$this->order->order_num}";
|
||||
Log::error($errorMessage);
|
||||
$telegram->SendErrorMessage($errorMessage, 'ProcessOrderPaymentJob - Empty VM Results');
|
||||
$this->handleVMCreationFailure($user, $invoice, $vmCreationResults, $telegram);
|
||||
return;
|
||||
}
|
||||
|
||||
// Kiểm tra xem có ít nhất một VM thành công không
|
||||
$successfulVMs = array_filter($vmCreationResults, fn($vm) => $vm['success']);
|
||||
$failedVMs = array_filter($vmCreationResults, fn($vm) => !$vm['success']);
|
||||
|
||||
// Nếu tất cả VM được tạo thành công, tiến hành thanh toán
|
||||
|
||||
// Nếu tất cả VM được tạo thành công
|
||||
if ($allVMsCreated && count($successfulVMs) > 0) {
|
||||
DB::beginTransaction();
|
||||
|
||||
try {
|
||||
// Trừ tiền người dùng
|
||||
$user->decrement('credit', (int) $this->order->amount);
|
||||
// Gửi thông báo thành công
|
||||
$telegram->SendOrderPaidNotification($invoice);
|
||||
$this->sendSuccessNotification($vmCreationResults);
|
||||
|
||||
// Cập nhật trạng thái hóa đơn và đơn hàng
|
||||
$invoiceService->markAsPaid($invoice);
|
||||
$this->order->update(['status' => OrderStatus::ACTIVE->value]);
|
||||
|
||||
// Gửi thông báo Telegram
|
||||
$telegram->SendOrderPaidNotification($invoice);
|
||||
|
||||
DB::commit();
|
||||
|
||||
// Gửi thông báo thành công cho khách hàng
|
||||
$this->sendSuccessNotification($vmCreationResults);
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
DB::rollBack();
|
||||
throw $e;
|
||||
}
|
||||
Log::info("✓ Hoàn tất order #{$this->order->order_num} - Tất cả VM đã được tạo");
|
||||
} else {
|
||||
// Có lỗi khi tạo VM hoặc không có VM nào thành công
|
||||
$errorDetails = [
|
||||
'order_id' => $this->order->id,
|
||||
'order_num' => $this->order->order_num,
|
||||
'vm_results' => $vmCreationResults,
|
||||
'total_vms' => count($vmCreationResults),
|
||||
'successful_vms' => count($successfulVMs),
|
||||
'failed_vms' => count($failedVMs),
|
||||
'all_vms_created' => $allVMsCreated
|
||||
];
|
||||
|
||||
// Kiểm tra trường hợp đặc biệt: có VPS nhưng không có VM nào thành công
|
||||
if (count($vmCreationResults) > 0 && count($successfulVMs) === 0) {
|
||||
Log::error("Có sản phẩm VPS nhưng không có VM nào được tạo thành công", $errorDetails);
|
||||
$telegram->SendErrorMessage(array_merge($errorDetails, [
|
||||
'error' => 'Tất cả VM đều thất bại - không có VM nào thành công'
|
||||
]), 'ProcessOrderPaymentJob - All VMs Failed');
|
||||
}
|
||||
|
||||
// Thử lại nếu chưa vượt quá số lần thử
|
||||
if ($this->retryCount < self::MAX_RETRIES) {
|
||||
// Kiểm tra nếu lỗi là timeout, tăng delay time
|
||||
$isTimeoutError = $this->isTimeoutError($failedVMs);
|
||||
$delayMinutes = $isTimeoutError ? 10 : 10; // Timeout thì chờ 10 phút, lỗi khác chờ 10 phút
|
||||
|
||||
Log::warning("Có lỗi khi tạo VM, thử lại lần " . ($this->retryCount + 1), array_merge($errorDetails, [
|
||||
'is_timeout_error' => $isTimeoutError,
|
||||
'delay_minutes' => $delayMinutes
|
||||
]));
|
||||
|
||||
// Thử lại với delay tùy theo loại lỗi
|
||||
self::dispatch($this->order, $this->retryCount + 1)
|
||||
->delay(now()->addMinutes($delayMinutes));
|
||||
} else {
|
||||
// Đã thử hết số lần, gửi thông báo lỗi
|
||||
Log::error("Đã thử tạo VM {$this->retryCount} lần nhưng vẫn thất bại", $errorDetails);
|
||||
|
||||
$telegram->SendErrorMessage(array_merge($errorDetails, [
|
||||
'error' => 'Không thể tạo VM sau nhiều lần thử',
|
||||
'retry_count' => $this->retryCount
|
||||
]), 'ProcessOrderPaymentJob - VM Creation Failed After Retries');
|
||||
|
||||
// Gửi thông báo lỗi cho khách hàng
|
||||
$this->sendFailureNotification($vmCreationResults);
|
||||
}
|
||||
// Có lỗi khi tạo VM
|
||||
$this->handleVMCreationFailure($user, $invoice, $vmCreationResults, $telegram);
|
||||
}
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
$isTimeoutException = strpos(strtolower($e->getMessage()), 'timeout') !== false ||
|
||||
strpos(strtolower($e->getMessage()), 'curl error 28') !== false;
|
||||
|
||||
Log::error("Lỗi trong ProcessOrderPaymentJob: " . $e->getMessage(), [
|
||||
Log::error("Lỗi nghiêm trọng trong ProcessOrderPaymentJob: " . $e->getMessage(), [
|
||||
'order_id' => $this->order->id,
|
||||
'order_num' => $this->order->order_num,
|
||||
'is_timeout' => $isTimeoutException,
|
||||
'retry_count' => $this->retryCount,
|
||||
'trace' => $e->getTraceAsString()
|
||||
]);
|
||||
$telegram->SendErrorMessage(
|
||||
"Lỗi nghiêm trọng khi xử lý order #{$this->order->order_num}: " . $e->getMessage(),
|
||||
'ProcessOrderPaymentJob - Critical Error'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$telegram->SendErrorMessage([
|
||||
'error' => $e->getMessage(),
|
||||
'order_id' => $this->order->id,
|
||||
'order_num' => $this->order->order_num,
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
'is_timeout' => $isTimeoutException,
|
||||
/**
|
||||
* Xử lý khi tạo VM thất bại
|
||||
*/
|
||||
private function handleVMCreationFailure($user, $invoice, $vmCreationResults, Telegram $telegram): void
|
||||
{
|
||||
$successfulVMs = array_filter($vmCreationResults, fn($vm) => $vm['success']);
|
||||
$failedVMs = array_filter($vmCreationResults, fn($vm) => !$vm['success']);
|
||||
|
||||
$errorDetails = [
|
||||
'order_id' => $this->order->id,
|
||||
'order_num' => $this->order->order_num,
|
||||
'vm_results' => $vmCreationResults,
|
||||
'total_vms' => count($vmCreationResults),
|
||||
'successful_vms' => count($successfulVMs),
|
||||
'failed_vms' => count($failedVMs),
|
||||
];
|
||||
|
||||
// Kiểm tra trường hợp đặc biệt: có VPS nhưng không có VM nào thành công
|
||||
if (count($vmCreationResults) > 0 && count($successfulVMs) === 0) {
|
||||
Log::error("Có sản phẩm VPS nhưng không có VM nào được tạo thành công", $errorDetails);
|
||||
$telegram->SendErrorMessage(array_merge($errorDetails, [
|
||||
'error' => 'Tất cả VM đều thất bại - không có VM nào thành công'
|
||||
]), 'ProcessOrderPaymentJob - All VMs Failed');
|
||||
|
||||
// REFUND CREDIT vì không tạo được VM nào
|
||||
try {
|
||||
DB::transaction(function () use ($user, $invoice) {
|
||||
$user->increment('credit', (int) $this->order->amount);
|
||||
$invoice->update(['status' => InvoiceStatus::UNPAID->value]);
|
||||
$this->order->update(['status' => OrderStatus::PENDING->value]);
|
||||
|
||||
Log::info("✓ Đã hoàn tiền cho order #{$this->order->order_num} do không tạo được VM");
|
||||
});
|
||||
} catch (\Throwable $e) {
|
||||
Log::error("Lỗi khi hoàn tiền: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Thử lại nếu chưa vượt quá số lần thử và có VM fail
|
||||
if ($this->retryCount < self::MAX_RETRIES && count($failedVMs) > 0) {
|
||||
// Kiểm tra nếu lỗi là timeout, tăng delay time
|
||||
$isTimeoutError = $this->isTimeoutError($failedVMs);
|
||||
$delayMinutes = $isTimeoutError ? 10 : 5; // Timeout chờ 10 phút, lỗi khác 5 phút
|
||||
|
||||
Log::warning("Có lỗi khi tạo VM, thử lại lần " . ($this->retryCount + 1), array_merge($errorDetails, [
|
||||
'is_timeout_error' => $isTimeoutError,
|
||||
'delay_minutes' => $delayMinutes
|
||||
]));
|
||||
|
||||
// Thử lại với delay tùy theo loại lỗi
|
||||
self::dispatch($this->order, $this->retryCount + 1)
|
||||
->delay(now()->addMinutes($delayMinutes));
|
||||
} else if (count($failedVMs) > 0) {
|
||||
// Đã thử hết số lần, gửi thông báo lỗi
|
||||
Log::error("Đã thử tạo VM {$this->retryCount} lần nhưng vẫn thất bại", $errorDetails);
|
||||
|
||||
$telegram->SendErrorMessage(array_merge($errorDetails, [
|
||||
'error' => 'Không thể tạo VM sau nhiều lần thử',
|
||||
'retry_count' => $this->retryCount
|
||||
], 'ProcessOrderPaymentJob - Exception');
|
||||
]), 'ProcessOrderPaymentJob - VM Creation Failed After Retries');
|
||||
|
||||
// Gửi thông báo lỗi cho khách hàng
|
||||
$this->sendFailureNotification($vmCreationResults);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user