feat: implement upgrade validation to prevent downgrades and enhance service upgrade process with improved error handling

This commit is contained in:
2025-12-04 06:21:10 -05:00
parent 3e3f5187d1
commit 97b15e07c5
3 changed files with 356 additions and 45 deletions

View File

@@ -118,6 +118,15 @@ class Upgrade extends Component
->first();
if (!$plan) {
$fail(__('Invalid upgrade.'));
return;
}
// Prevent downgrades - only allow upgrades where new price >= current price
$currentPrice = $this->service->plan->price()->price;
$newPrice = $plan->price()->price;
if ($newPrice < $currentPrice) {
$fail(__('Downgrades are not allowed. You may only upgrade to a higher-priced plan.'));
}
},
],
@@ -146,6 +155,12 @@ class Upgrade extends Component
$this->validate();
$upgradePlan = $this->upgradeProduct->availablePlans()->where('billing_period', $this->service->plan->billing_period)->where('billing_unit', $this->service->plan->billing_unit)->first();
// Additional check to prevent downgrades
if ($upgradePlan && $upgradePlan->price()->price < $this->service->plan->price()->price) {
$this->notify('Downgrades are not allowed. You may only upgrade to a higher-priced plan.', 'error');
return;
}
// The old config options must be in upgradableConfigOptions
$serviceConfigOptions = $this->service->configs->pluck('config_value_id', 'config_option_id')->toArray();
@@ -177,7 +192,9 @@ class Upgrade extends Component
}
$price = $upgrade->calculatePrice();
if ($price->price <= 0) {
// Only process free upgrades immediately (price == 0)
// Negative prices (downgrades) are blocked by validation
if ($price->price == 0) {
$upgrade->status = ServiceUpgrade::STATUS_COMPLETED;
$upgrade->save();
@@ -203,26 +220,7 @@ class Upgrade extends Component
UpgradeJob::dispatch($this->service);
}
// Check if user has credits in this currency
/** @var \App\Models\User */
$user = Auth::user();
$credit = $user->credits()->where('currency_code', $price->currency->code)->first();
if ($credit) {
// Increment the credits, `abs()` ensures the amount to add is positive
$credit->increment('amount', abs($price->price));
} else {
$user->credits()->create([
'currency_code' => $price->currency->code,
'amount' => $price->price,
]);
}
if ($price->price < 0) {
$this->notify('The upgrade has been completed. We\'ve added the remaining amount to your account balance.', 'success');
} else {
$this->notify('The upgrade has been completed.', 'success');
}
$this->notify('The upgrade has been completed.', 'success');
return $this->redirect(route('services.show', $this->service), true);
}

View File

@@ -191,8 +191,14 @@ class Service extends Model
// Only get the upgrades that have the exact same billing cycle as the service
if ($plan->count() > 0) {
$product->plan = $plan->first();
return $product;
// Only allow upgrades (not downgrades) - check if new price is greater than or equal to current price
$currentPrice = $this->plan->price()->price;
$newPrice = $product->plan->price()->price;
if ($newPrice >= $currentPrice) {
return $product;
}
}
return null;