# 🔄 Sistema de Proration (Cálculo Proporcional)

## 📋 Overview

Sistema de cálculo proporcional para **upgrades** e **downgrades** de planos, considerando:
- ✅ Dias restantes até o vencimento
- ✅ Valor proporcional baseado no **mês** (não 30 dias fixos)
- ✅ Créditos para downgrades
- ✅ Aplicação automática de créditos em renovações

---

## 🎯 Como Funciona

### 1. **Upgrade (Plano Mais Caro)**

**Exemplo:**
- Plano atual: R$ 50/mês
- Novo plano: R$ 100/mês
- Dias restantes: 15 dias
- Mês: Janeiro (31 dias)

**Cálculo:**
```
Crédito não utilizado = (50 / 31) × 15 = R$ 24,19
Valor novo plano (proporcional) = (100 / 31) × 15 = R$ 48,39
Valor a pagar = R$ 48,39 - R$ 24,19 = R$ 24,20
```

**Resultado:**
- ✅ Usuário paga **R$ 24,20**
- ✅ Plano muda imediatamente após pagamento
- ✅ Data de vencimento **mantém-se** (15 dias)

### 2. **Downgrade (Plano Mais Barato)**

**Exemplo:**
- Plano atual: R$ 100/mês
- Novo plano: R$ 50/mês
- Dias restantes: 15 dias
- Mês: Janeiro (31 dias)

**Cálculo:**
```
Crédito não utilizado = (100 / 31) × 15 = R$ 48,39
Valor novo plano (proporcional) = (50 / 31) × 15 = R$ 24,19
Crédito gerado = R$ 48,39 - R$ 24,19 = R$ 24,20
```

**Resultado:**
- ✅ Plano muda **imediatamente** (sem pagamento)
- ✅ Crédito de **R$ 24,20** adicionado à conta
- ✅ Data de vencimento **mantém-se** (15 dias)
- ✅ Crédito será usado na próxima renovação

### 3. **Renovação com Crédito**

**Exemplo:**
- Plano: R$ 50/mês
- Crédito disponível: R$ 24,20

**Cálculo:**
```
Valor base = R$ 50,00
Crédito aplicado = R$ 24,20
Valor final = R$ 50,00 - R$ 24,20 = R$ 25,80
```

**Resultado:**
- ✅ Usuário paga **R$ 25,80** na renovação
- ✅ Crédito consumido automaticamente

---

## 🔧 Implementação Técnica

### Backend

#### 1. **ProrationService** (`app/Services/ProrationService.php`)

Métodos principais:

```php
// Calcular proration
ProrationService::calculateProration($currentSubscription, $newPlan);

// Aplicar proration
ProrationService::applyProration($subscription, $newPlan, $prorationData);

// Obter saldo de crédito
ProrationService::getCreditBalance($subscription);

// Consumir crédito
ProrationService::consumeCredit($subscription, $amount);

// Calcular valor de renovação
ProrationService::calculateRenewalAmount($subscription);
```

#### 2. **SubscriptionController**

**Novo endpoint: Calcular proration**
```bash
POST /api/v1/subscriptions/calculate-proration
Body: {
  "plan_id": 2
}
```

**Resposta:**
```json
{
  "type": "upgrade",
  "amount": 24.20,
  "credit": 0,
  "description": "Upgrade com cálculo proporcional (15 dias restantes)",
  "calculation": {
    "current_plan_price": 50.00,
    "new_plan_price": 100.00,
    "days_remaining": 15,
    "days_in_period": 31,
    "unused_credit": 24.19,
    "new_plan_prorated": 48.39,
    "difference": 24.20
  },
  "current_plan": {
    "id": 1,
    "name": "Basic",
    "price": 50.00
  },
  "new_plan": {
    "id": 2,
    "name": "Pro",
    "price": 100.00
  },
  "current_credit_balance": 0
}
```

**Checkout com proration:**
```bash
POST /api/v1/subscriptions
Body: {
  "plan_id": 2,
  "gateway": "openpix"
}
```

- Se for **upgrade**: Retorna pagamento com valor calculado
- Se for **downgrade**: Aplica imediatamente e retorna crédito gerado

#### 3. **Subscription Model**

Créditos armazenados em `meta`:
```json
{
  "credit_balance": 24.20,
  "credit_history": [
    {
      "amount": 24.20,
      "reason": "downgrade",
      "date": "2026-01-12T22:00:00",
      "description": "Downgrade com crédito proporcional (15 dias restantes)"
    },
    {
      "amount": -10.00,
      "reason": "consumed",
      "date": "2026-02-01T00:00:00",
      "description": "Crédito utilizado em renovação"
    }
  ],
  "proration": {
    "type": "downgrade",
    "date": "2026-01-12T22:00:00",
    "old_plan_id": 2,
    "new_plan_id": 1,
    "calculation": { ... }
  }
}
```

---

## 🧪 Testes

### 1. Teste de Upgrade

```bash
# 1. Criar assinatura com plano básico
POST /api/v1/subscriptions
{
  "plan_id": 1,
  "gateway": "openpix"
}

# 2. Pagar e ativar

# 3. Calcular proration para upgrade
POST /api/v1/subscriptions/calculate-proration
{
  "plan_id": 2
}

# 4. Fazer upgrade
POST /api/v1/subscriptions
{
  "plan_id": 2,
  "gateway": "openpix"
}

# 5. Pagar valor proporcional
```

### 2. Teste de Downgrade

```bash
# 1. Ter assinatura com plano premium

# 2. Calcular proration para downgrade
POST /api/v1/subscriptions/calculate-proration
{
  "plan_id": 1
}

# 3. Fazer downgrade (imediato, sem pagamento)
POST /api/v1/subscriptions
{
  "plan_id": 1,
  "gateway": "openpix"
}

# 4. Verificar crédito gerado
GET /api/v1/subscriptions/current
```

### 3. Teste de Renovação com Crédito

```bash
# Simular renovação
php artisan tinker

>>> $subscription = Subscription::find(1);
>>> $renewal = \App\Services\ProrationService::calculateRenewalAmount($subscription);
>>> print_r($renewal);
```

---

## 📱 Frontend

### 1. Mostrar Cálculo no Checkout

```typescript
// Buscar proration antes de abrir checkout
const getProration = async (planId: number) => {
  const response = await api.post('/subscriptions/calculate-proration', {
    plan_id: planId
  });
  return response.data;
};

// Exemplo de uso
const proration = await getProration(2);

if (proration.type === 'upgrade') {
  showMessage(`Você pagará R$ ${proration.amount.toFixed(2)} (valor proporcional)`);
} else if (proration.type === 'downgrade') {
  showMessage(`Você receberá R$ ${proration.credit.toFixed(2)} de crédito`);
}
```

### 2. Exibir Crédito Disponível

```typescript
// Na página de assinatura
const { data: subscription } = await api.get('/subscriptions/current');
const creditBalance = subscription.meta?.credit_balance || 0;

if (creditBalance > 0) {
  showCreditBadge(`Crédito: R$ ${creditBalance.toFixed(2)}`);
}
```

---

## 🔐 Segurança

1. **Validações:**
   - ✅ Usuário só pode mudar seu próprio plano
   - ✅ Plano de destino deve existir e estar ativo
   - ✅ Cálculos feitos no backend (não confia no frontend)

2. **Auditoria:**
   - ✅ Todos os cálculos são logados
   - ✅ Histórico de créditos armazenado
   - ✅ Metadata da proration salva

3. **Consistência:**
   - ✅ Transações de banco de dados
   - ✅ Rollback em caso de erro
   - ✅ Logs detalhados

---

## 📊 Casos Especiais

### 1. Assinatura Expirada

Se a assinatura já expirou:
- ✅ Cobra valor **integral** do novo plano
- ✅ Não há crédito a considerar

### 2. Mesmo Plano

Se tentar "mudar" para o mesmo plano:
- ✅ Retorna valor integral
- ✅ Não há proration

### 3. Primeira Assinatura

Se não houver assinatura ativa:
- ✅ Cobra valor integral
- ✅ `type: 'full'`

### 4. Downgrade com Valor Zero

Se downgrade resultar em `amount = 0`:
- ✅ Aplica mudança **imediatamente**
- ✅ Não cria pagamento
- ✅ Retorna sucesso com crédito gerado

---

## 🚀 Deploy

```bash
cd /var/www/gestorstream

# Atualizar código
git pull origin main

# Limpar cache
cd backend
php artisan config:clear
php artisan cache:clear
php artisan route:clear

# Verificar rotas
php artisan route:list | grep proration
```

---

## 📝 Checklist de Implementação

- [x] ✅ `ProrationService` criado
- [x] ✅ Endpoint `/calculate-proration` adicionado
- [x] ✅ `store()` modificado para usar proration
- [x] ✅ Rota adicionada
- [ ] ⏳ Frontend atualizado para mostrar cálculo
- [ ] ⏳ Testes manuais
- [ ] ⏳ Deploy no servidor

---

**Data**: 2026-01-12  
**Autor**: Sistema de Proration  
**Status**: Backend implementado, frontend pendente
