webmoney/backend/app/Http/Middleware/CheckPlanLimits.php
marcoitaloesp-ai 3a336eb692
feat: Admin user management + SaaS limits tested v1.51.0
- Add UserManagementController@store for creating users
- Add POST /api/admin/users endpoint
- Support user types: Free, Pro, Admin
- Auto-create 100-year subscription for Pro/Admin users
- Add user creation modal to Users.jsx
- Complete SaaS limit testing:
  - Free user limits: 1 account, 10 categories, 3 budgets, 100 tx
  - Middleware blocks correctly at limits
  - Error messages are user-friendly
  - Usage stats API working correctly
- Update SAAS_STATUS.md with test results
- Bump version to 1.51.0
2025-12-17 15:22:01 +00:00

107 lines
3.3 KiB
PHP

<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class CheckPlanLimits
{
/**
* Resource type to limit mapping
*/
protected array $resourceLimits = [
'accounts' => 'accounts',
'categories' => 'categories',
'budgets' => 'budgets',
'transactions' => 'transactions',
'goals' => 'goals',
];
/**
* Handle an incoming request.
*/
public function handle(Request $request, Closure $next, string $resource): Response
{
// Only check on store (create) requests
if (!in_array($request->method(), ['POST'])) {
return $next($request);
}
$user = $request->user();
if (!$user) {
return $next($request);
}
$plan = $user->currentPlan();
if (!$plan) {
return $next($request);
}
$limits = $plan->limits ?? [];
$limitKey = $this->resourceLimits[$resource] ?? null;
if (!$limitKey || !isset($limits[$limitKey])) {
return $next($request);
}
$limit = $limits[$limitKey];
// null means unlimited
if ($limit === null) {
return $next($request);
}
$currentCount = $this->getCurrentCount($user, $resource);
if ($currentCount >= $limit) {
return response()->json([
'success' => false,
'message' => $this->getLimitMessage($resource, $limit),
'error' => 'plan_limit_exceeded',
'data' => [
'resource' => $resource,
'current' => $currentCount,
'limit' => $limit,
'plan' => $plan->name,
'upgrade_url' => '/pricing',
],
], 403);
}
return $next($request);
}
/**
* Get current count for a resource
*/
protected function getCurrentCount($user, string $resource): int
{
return match ($resource) {
'accounts' => $user->accounts()->count(),
'categories' => $user->categories()->count(),
'budgets' => $user->budgets()->count(),
'transactions' => $user->transactions()->count(),
'goals' => $user->goals()->count(),
default => 0,
};
}
/**
* Get user-friendly limit message
*/
protected function getLimitMessage(string $resource, int $limit): string
{
$messages = [
'accounts' => "Has alcanzado el límite de {$limit} cuenta(s) de tu plan. Actualiza a Pro para cuentas ilimitadas.",
'categories' => "Has alcanzado el límite de {$limit} categorías de tu plan. Actualiza a Pro para categorías ilimitadas.",
'budgets' => "Has alcanzado el límite de {$limit} presupuesto(s) de tu plan. Actualiza a Pro para presupuestos ilimitados.",
'transactions' => "Has alcanzado el límite de {$limit} transacciones de tu plan. Actualiza a Pro para transacciones ilimitadas.",
'goals' => "Has alcanzado el límite de {$limit} meta(s) de tu plan. Actualiza a Pro para metas ilimitadas.",
];
return $messages[$resource] ?? "Has alcanzado el límite de tu plan para este recurso.";
}
}