- Removido README.md padrão do Laravel (backend) - Removidos scripts de deploy (não mais necessários) - Atualizado copilot-instructions.md para novo fluxo - Adicionada documentação de auditoria do servidor - Sincronizado código de produção com repositório Novo workflow: - Trabalhamos diretamente em /root/webmoney (symlink para /var/www/webmoney) - Mudanças PHP são instantâneas - Mudanças React requerem 'npm run build' - Commit após validação funcional
163 lines
3.8 KiB
PHP
Executable File
163 lines
3.8 KiB
PHP
Executable File
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
|
|
|
class Plan extends Model
|
|
{
|
|
use HasFactory;
|
|
|
|
protected $fillable = [
|
|
'name',
|
|
'slug',
|
|
'description',
|
|
'price',
|
|
'currency',
|
|
'billing_period',
|
|
'trial_days',
|
|
'features',
|
|
'limits',
|
|
'is_active',
|
|
'is_featured',
|
|
'is_free',
|
|
'sort_order',
|
|
'paypal_plan_id',
|
|
];
|
|
|
|
protected $casts = [
|
|
'price' => 'decimal:2',
|
|
'features' => 'array',
|
|
'limits' => 'array',
|
|
'is_active' => 'boolean',
|
|
'is_featured' => 'boolean',
|
|
'is_free' => 'boolean',
|
|
'trial_days' => 'integer',
|
|
'sort_order' => 'integer',
|
|
];
|
|
|
|
// ==================== RELATIONSHIPS ====================
|
|
|
|
public function subscriptions(): HasMany
|
|
{
|
|
return $this->hasMany(Subscription::class);
|
|
}
|
|
|
|
// ==================== SCOPES ====================
|
|
|
|
public function scopeActive($query)
|
|
{
|
|
return $query->where('is_active', true);
|
|
}
|
|
|
|
public function scopeOrdered($query)
|
|
{
|
|
return $query->orderBy('sort_order')->orderBy('price');
|
|
}
|
|
|
|
public function scopeFree($query)
|
|
{
|
|
return $query->where('billing_period', 'free');
|
|
}
|
|
|
|
public function scopePaid($query)
|
|
{
|
|
return $query->where('billing_period', '!=', 'free');
|
|
}
|
|
|
|
// ==================== ACCESSORS ====================
|
|
|
|
public function getIsFreeAttribute(): bool
|
|
{
|
|
return $this->billing_period === 'free' || $this->price == 0;
|
|
}
|
|
|
|
public function getHasTrialAttribute(): bool
|
|
{
|
|
return $this->trial_days > 0;
|
|
}
|
|
|
|
public function getFormattedPriceAttribute(): string
|
|
{
|
|
if ($this->is_free) {
|
|
return 'Gratis';
|
|
}
|
|
|
|
$symbols = [
|
|
'EUR' => '€',
|
|
'USD' => '$',
|
|
'BRL' => 'R$',
|
|
];
|
|
|
|
$symbol = $symbols[$this->currency] ?? $this->currency;
|
|
$period = match ($this->billing_period) {
|
|
'monthly' => '/mes',
|
|
'annual' => '/año',
|
|
'lifetime' => ' (único)',
|
|
default => '',
|
|
};
|
|
|
|
return $symbol . number_format($this->price, 2, ',', '.') . $period;
|
|
}
|
|
|
|
public function getMonthlyPriceAttribute(): float
|
|
{
|
|
if ($this->billing_period === 'annual') {
|
|
return round($this->price / 12, 2);
|
|
}
|
|
return $this->price;
|
|
}
|
|
|
|
public function getSavingsPercentAttribute(): ?int
|
|
{
|
|
if ($this->billing_period !== 'annual') {
|
|
return null;
|
|
}
|
|
|
|
// Find monthly equivalent
|
|
$monthlyPlan = static::where('billing_period', 'monthly')
|
|
->where('is_active', true)
|
|
->where('price', '>', 0)
|
|
->first();
|
|
|
|
if (!$monthlyPlan) {
|
|
return null;
|
|
}
|
|
|
|
$annualEquivalent = $monthlyPlan->price * 12;
|
|
$savings = (($annualEquivalent - $this->price) / $annualEquivalent) * 100;
|
|
|
|
return (int) round($savings);
|
|
}
|
|
|
|
// ==================== METHODS ====================
|
|
|
|
public function hasFeature(string $feature): bool
|
|
{
|
|
return in_array($feature, $this->features ?? []);
|
|
}
|
|
|
|
public function getLimit(string $key, $default = null)
|
|
{
|
|
return $this->limits[$key] ?? $default;
|
|
}
|
|
|
|
/**
|
|
* Get the default free plan
|
|
*/
|
|
public static function getFreePlan(): ?self
|
|
{
|
|
return static::where('slug', 'free')->first();
|
|
}
|
|
|
|
/**
|
|
* Get plans for pricing page
|
|
*/
|
|
public static function getForPricing(): \Illuminate\Database\Eloquent\Collection
|
|
{
|
|
return static::active()->ordered()->get();
|
|
}
|
|
}
|