246 lines
8.8 KiB
PHP
246 lines
8.8 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Concerns;
|
|
|
|
use App\Enums\Order\OrderStatus;
|
|
use App\Helpers\OrderHelper;
|
|
use App\Jobs\Hosting\ProvisionHostingAccountJob;
|
|
use App\Jobs\VirtualMachine\Action\VMJobs;
|
|
use App\Jobs\VirtualMachine\Create\CreateVirtualMachineJob;
|
|
use App\Jobs\VirtualMachine\Provision\UpdateVmConfigJob;
|
|
use App\Http\Services\vCenter as ServiceVCenter;
|
|
use App\Models\Addon;
|
|
use App\Models\Invoice;
|
|
use App\Models\OrderItem;
|
|
use App\Models\Service;
|
|
use App\Models\ServiceAddon;
|
|
use Carbon\Carbon;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
trait ActivatesPaidInvoices
|
|
{
|
|
private function activateOrder(Invoice $invoice): void
|
|
{
|
|
$order = $invoice->order?->loadMissing(['orderItems.product', 'customer', 'invoice']);
|
|
if (! $order) {
|
|
return;
|
|
}
|
|
|
|
// payment_method/paid_at always mirror the invoice that was just paid, even if the
|
|
// order's status doesn't change (e.g. it's already ACTIVE from an earlier payment).
|
|
$order->update([
|
|
'status' => OrderStatus::ACTIVE->value,
|
|
'payment_method' => $invoice->payment_method,
|
|
'paid_at' => $invoice->paid_at,
|
|
]);
|
|
|
|
foreach ($order->orderItems as $item) {
|
|
$isHostingProvision = ! $item->os_id
|
|
&& $item->server_name
|
|
&& $item->product?->product_type === 'hosting';
|
|
|
|
$service = Service::firstOrCreate(
|
|
['order_item_id' => $item->id],
|
|
[
|
|
'customer_id' => $order->customer_id,
|
|
'product_id' => $item->product_id,
|
|
'invoice_id' => $invoice->id,
|
|
'server_name' => $item->server_name,
|
|
'status' => ($item->os_id || $isHostingProvision) ? 'pending' : 'active',
|
|
'auto_renew' => false,
|
|
'started_at' => now(),
|
|
'expires_at' => now()->addMonths($item->months ?: OrderHelper::cycleToMonths($item->billing_cycle)),
|
|
]
|
|
);
|
|
|
|
$this->attachServiceAddons($service, $item);
|
|
|
|
if ($service->wasRecentlyCreated && $item->product_id) {
|
|
if ($item->os_id) {
|
|
dispatch(new CreateVirtualMachineJob($item));
|
|
} elseif ($isHostingProvision) {
|
|
dispatch(new ProvisionHostingAccountJob($item));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private function activateRenewal(Invoice $invoice): void
|
|
{
|
|
$service = Service::query()
|
|
->with(['orderItem.product'])
|
|
->where('invoice_id', $invoice->id)
|
|
->first();
|
|
|
|
if (! $service) {
|
|
return;
|
|
}
|
|
|
|
if ($invoice->order) {
|
|
// Same rule as activateOrder(): payment_method/paid_at always mirror the invoice
|
|
// that was just paid, regardless of whether the order's status actually changes.
|
|
$invoice->order->update([
|
|
'status' => OrderStatus::ACTIVE->value,
|
|
'payment_method' => $invoice->payment_method,
|
|
'paid_at' => $invoice->paid_at,
|
|
]);
|
|
}
|
|
|
|
$months = (int) ($invoice->invoiceItems()->where('type', 'renewal')->value('months') ?: $service->orderItem?->months ?: 1);
|
|
$baseDate = $service->expires_at && $service->expires_at->isFuture()
|
|
? $service->expires_at
|
|
: now();
|
|
$expiresAt = Carbon::parse($baseDate)->addMonths($months);
|
|
|
|
$service->update([
|
|
'status' => 'active',
|
|
'expires_at' => $expiresAt,
|
|
'suspended_at' => null,
|
|
]);
|
|
|
|
if ($service->vm_id) {
|
|
dispatch(new VMJobs((int) $service->vm_id, 'Start', false))->delay(now()->addSeconds(5));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Merges the purchased addons into order_items.addons_meta (so RenewalPricing charges
|
|
* them on the next renewal) and applies the new absolute CPU/RAM/disk totals to the VM.
|
|
*/
|
|
private function activateUpgrade(Invoice $invoice): void
|
|
{
|
|
$service = Service::query()
|
|
->with(['orderItem.product', 'vmware'])
|
|
->find($invoice->service_id);
|
|
|
|
$orderItem = $service?->orderItem;
|
|
if (! $service || ! $orderItem) {
|
|
return;
|
|
}
|
|
|
|
$upgradeItems = $invoice->invoiceItems()->where('type', 'upgrade')->get();
|
|
if ($upgradeItems->isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
$meta = collect(is_array($orderItem->addons_meta) ? $orderItem->addons_meta : []);
|
|
$purchasedCodes = [];
|
|
|
|
foreach ($upgradeItems as $item) {
|
|
$addon = Addon::find($item->addon_id);
|
|
if (! $addon) {
|
|
continue;
|
|
}
|
|
|
|
$code = strtolower((string) $addon->code);
|
|
$purchasedCodes[] = $code;
|
|
|
|
$qty = (int) $item->quantity;
|
|
$index = $meta->search(fn ($entry) => (int) ($entry['addon_id'] ?? 0) === $addon->id);
|
|
$currentQty = $index === false ? 0 : (int) ($meta->get($index)['qty'] ?? 0);
|
|
$newQty = $currentQty + $qty;
|
|
|
|
$entry = [
|
|
'addon_id' => $addon->id,
|
|
'code' => $code,
|
|
'name' => $addon->name,
|
|
'qty' => $newQty,
|
|
'price' => (float) $addon->price,
|
|
'subtotal' => $newQty * (float) $addon->price,
|
|
];
|
|
|
|
if ($index === false) {
|
|
$meta->push($entry);
|
|
} else {
|
|
$meta->put($index, $entry);
|
|
}
|
|
}
|
|
|
|
$orderItem->update(['addons_meta' => $meta->values()->all()]);
|
|
|
|
$moduleVmware = $service->vmware;
|
|
if (! $moduleVmware) {
|
|
return;
|
|
}
|
|
|
|
$addonQtyByCode = $meta->keyBy(fn ($entry) => $entry['code'] ?? '');
|
|
$product = $orderItem->product;
|
|
|
|
if (array_intersect(['cpu', 'ram'], $purchasedCodes)) {
|
|
$totalCpu = (int) ($product?->cpu ?? 0) + (int) ($addonQtyByCode->get('cpu')['qty'] ?? 0);
|
|
$totalRam = (int) ($product?->ram ?? 0) + (int) ($addonQtyByCode->get('ram')['qty'] ?? 0);
|
|
|
|
UpdateVmConfigJob::dispatch(
|
|
$moduleVmware->id,
|
|
(string) $moduleVmware->vmware_id,
|
|
'',
|
|
$totalCpu,
|
|
$totalRam,
|
|
)->afterCommit();
|
|
}
|
|
|
|
if (in_array('disk', $purchasedCodes, true)) {
|
|
$totalDisk = (int) ($product?->disk ?? 0) + (int) ($addonQtyByCode->get('disk')['qty'] ?? 0);
|
|
$vcenter = $moduleVmware->vCenter ?? $moduleVmware->ipAddress?->host?->datacenter?->vCenter;
|
|
|
|
if ($vcenter) {
|
|
DB::afterCommit(function () use ($vcenter, $moduleVmware, $totalDisk, $invoice) {
|
|
try {
|
|
$serviceVCenter = new ServiceVCenter();
|
|
if (! $serviceVCenter->loginServer($vcenter)) {
|
|
throw new \RuntimeException('vCenter login failed');
|
|
}
|
|
$serviceVCenter->UpgradeDiskVirtualMachine($moduleVmware, $totalDisk);
|
|
} catch (\Throwable $e) {
|
|
Log::error('activateUpgrade: disk upgrade failed', [
|
|
'invoice_id' => $invoice->id,
|
|
'module_vmware_id' => $moduleVmware->id,
|
|
'total_disk' => $totalDisk,
|
|
'error' => $e->getMessage(),
|
|
]);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
private function attachServiceAddons(Service $service, OrderItem $item): void
|
|
{
|
|
if ($service->addons()->exists()) {
|
|
return;
|
|
}
|
|
|
|
$product = $item->product;
|
|
if ($product) {
|
|
foreach (['cpu' => $product->cpu, 'ram' => $product->ram, 'disk' => $product->disk, 'bandwidth' => $product->bandwidth] as $type => $value) {
|
|
if ($value !== null && $value !== '') {
|
|
ServiceAddon::create([
|
|
'service_id' => $service->id,
|
|
'addon_type' => $type,
|
|
'addon_value' => (string) $value,
|
|
'price' => 0,
|
|
]);
|
|
}
|
|
}
|
|
}
|
|
|
|
$addonsMeta = is_array($item->addons_meta)
|
|
? $item->addons_meta
|
|
: json_decode($item->addons_meta ?? '[]', true);
|
|
|
|
foreach ((array) $addonsMeta as $meta) {
|
|
$addon = Addon::find($meta['addon_id'] ?? null);
|
|
if (!$addon) {
|
|
continue;
|
|
}
|
|
ServiceAddon::create([
|
|
'service_id' => $service->id,
|
|
'addon_type' => strtolower((string) $addon->code),
|
|
'addon_value' => (string) ($meta['qty'] ?? 1),
|
|
'price' => (int) ($meta['price'] ?? $addon->price),
|
|
]);
|
|
}
|
|
}
|
|
}
|