Files
portal-api/app/Support/ServiceCancellation.php
baodev0andClaude Sonnet 5.5 b44a88bb52 feat(service): huỷ dịch vụ, SSL, đổi mật khẩu hosting, lịch sử thanh toán theo hình thức
- Lịch sử thanh toán hoá đơn trả thêm `method` (tự động gia hạn / số dư / admin / chuyển khoản / hoàn tiền), bỏ đuôi `(method)` khỏi ghi chú
- `DnsServer::panelUrl()` = https://<domain>/ dùng cho email cấp hosting và nút Plesk của khách
- Order item trả thêm `uuid`/`type` của dịch vụ để FE dẫn tới trang quản lý
- Yêu cầu huỷ dịch vụ, chứng chỉ SSL, lịch sử gia hạn, đổi mật khẩu hosting cho khách

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-28 16:40:24 +07:00

55 lines
1.6 KiB
PHP

<?php
namespace App\Support;
use App\Enums\Invoice\InvoiceStatus;
use App\Jobs\VirtualMachine\Action\VMJobs;
use App\Models\Invoice;
use App\Models\Service;
class ServiceCancellation
{
public const SCHEDULED = 'scheduled';
public const IMMEDIATE = 'immediate';
public static function approve(Service $service): string
{
self::voidOpenRenewalInvoices($service);
if (! $service->expires_at || $service->expires_at->isPast()) {
$service->update(['status' => 'cancelled', 'cancelled_at' => now(), 'auto_renew' => false]);
return self::IMMEDIATE;
}
$service->update(['cancel_at_expiry' => true, 'auto_renew' => false]);
return self::SCHEDULED;
}
public static function finalize(Service $service): void
{
$service->update(['status' => 'cancelled', 'cancelled_at' => now()]);
if ($service->vm_id) {
dispatch(new VMJobs((int) $service->vm_id, 'PowerOff', false));
}
}
private static function voidOpenRenewalInvoices(Service $service): void
{
$orderId = $service->orderItem?->order_id;
if (! $orderId) {
return;
}
Invoice::where('order_id', $orderId)
->where('invoice_type', 2)
->whereIn('status', [InvoiceStatus::UNPAID->value, InvoiceStatus::OVERDUE->value])
->where(fn ($query) => $query->whereNull('amount_paid')->orWhere('amount_paid', 0))
->get()
->each(fn (Invoice $invoice) => $invoice->update(['status' => InvoiceStatus::CANCELLED->value]));
}
}