Skip to content

REF-401-01: License Activation & Renewal

ADR: ADR-401 — Licensing: License & DownloadEscopo: Fluxo de ativação, WOO_SL adapter, renovação (auto + manual), lembretes escalonados, expiração


1. Fluxo de Ativação

1. Entitlement PLG provisionado (auto ou manual)
   └── License criada (chave gerada)

2. Cliente obtém chave
   ├── Portal: seção "Minhas Licenças"
   └── Email: notificação de provisioning inclui chave

3. Cliente ativa em domínio
   ├── POST /licenses/{id}/activate { domain: "moodle.cliente.com" }
   ├── Validação:
   │   ├── License existe e está ativa?
   │   ├── Domínio já ativado nesta license?
   │   ├── Max ativações não excedido?
   │   └── Domínio é válido (formato URL)?
   ├── Sucesso → Ativação registrada
   └── Falha → Erro específico (LICENSE_EXPIRED, MAX_ACTIVATIONS, DUPLICATE_DOMAIN)

4. Cliente desativa domínio
   └── DELETE /licenses/{id}/activate { domain: "moodle.cliente.com" }
       └── Ativação removida, slot liberado

2. Campos da License

CampoTipoDescrição
license_keystringChave única (gerada ou via WOO_SL)
entitlement_idFKEntitlement PLG pai
max_activationsintMáximo de domínios simultâneos
activationsjson[]Lista de domínios ativados com timestamps
statusenumactive / expired / revoked
expires_atdatetimeData de expiração (segue Entitlement)

3. WOO_SL Adapter Pattern

CenárioComportamento
WOO_SL ativoLicenseAdapter lê/escreve via WOO_SL API
WOO_SL ausenteGestão built-in: chave simples, ativação por domínio
Migração WOO_SL → built-inDados preservados, adapter troca transparentemente
LicenseRepositoryInterface (Domain/)
    ├── WooSLLicenseRepository (WordPress/) — usa WOO_SL
    └── BuiltInLicenseRepository (WordPress/) — gestão simplificada

Seleção automática via DI Container: class_exists('WC_Software_License') determina qual implementação.


4. Renovação

Auto-Renovação (WC Subscriptions ativo)

WC Subscription renova automaticamente
    → woocommerce_subscription_renewal_payment_complete
    → Entitlement PLG: expiration_date estendida
    → License: expires_at atualizada
    → Hook: middag_entitlement_status_changed (se expired → active)

Renovação Manual (sem WC Subscriptions)

30 dias antes da expiração:
    → Lembrete ao cliente (email + portal)
    → Link de renovação no portal

Cliente aceita renovação:
    → Quote de renovação gerado (ou compra direta)
    → Pagamento → Order → Entitlement estendido
    → License: expires_at atualizada

5. Lembretes Escalonados

Dias antesTipoConteúdo
30Informativo"Sua licença expira em 30 dias"
15Ação recomendada"Renove para manter acesso" + link
7Urgente"Expira em 7 dias — renove agora"
0Expiração"Sua licença expirou" + link

Configurável via NotificationPolicy (expiry_warning_days). Se auto-renovação ativa, tom muda para informativo.


6. Expiração → Lock de Acesso

License expira (Entitlement → expired)
    ├── Ativações existentes: mantidas (não removidas)
    ├── Novas ativações: bloqueadas (LICENSE_EXPIRED)
    ├── Validação de license (API): retorna expired
    ├── Downloads: bloqueados
    └── Portal: license visível mas marcada como expirada

Reativação: Renovação (pagamento) → Entitlement active → License active → ativações existentes voltam a funcionar.


7. Edge Validation (Cloudflare Workers)

O método LicenseService::checkLicenseValidity(licenseKey, domain) é o endpoint de validação consumido por Cloudflare Workers para autorizar downloads e verificar licenças na edge:

Request:  { license_key: "...", domain: "moodle.cliente.com" }
Response: { valid: true/false, reason: "...", product_slug: "...", expires_at: "..." }
ValidaçãoErro retornado
Chave não encontradaLICENSE_NOT_FOUND
Licença expiradaLICENSE_EXPIRED
Licença revogadaLICENSE_REVOKED
Domínio não ativadoDOMAIN_NOT_ACTIVATED
Produto não correspondePRODUCT_MISMATCH

Eventual consistency: Workers podem cachear resultado por até 5 minutos. Revogação/expiração tem delay aceitável.


8. DownloadsService — Access Checks

O DownloadsService implementa verificações de acesso como fallback para downloads diretos via WordPress (cenário legado ou quando CDN indisponível):

CheckComportamento se falhar
Licença ativa para o produto?HTTP 403, mensagem: licença expirada/inválida
Organização autorizada?HTTP 403, mensagem: sem permissão
Limite de downloads excedido?HTTP 429, mensagem: limite atingido
Produto existe e tem arquivo?HTTP 404, mensagem: produto não encontrado

Todos os downloads são logados para auditoria (user ID, timestamp, IP, produto, versão).


9. Tipos de Licença

TipoMax domíniosProdutos disponíveis
Licença individualConfigurável (ex: 1-5)1 plugin específico
Licença bundleConfigurável (ex: 5-10)Conjunto definido de plugins
Licença agencyIlimitadoTodos os plugins do catálogo

O tipo de licença é determinado pelo produto WC comprado. O mapeamento tipo → produtos disponíveis → max ativações é configurável no admin.


10. LicenseService API

MétodoDescrição
getLicensesByFilter(filters)Listagem com filtros (status, produto, domínio, org)
getLicenseDetails(licenseId)Detalhes completos da licença
activateLicense(licenseId, domain)Ativar licença em domínio (valida max ativações)
deactivateLicense(licenseId, domain)Desativar licença de domínio (libera slot)
checkLicenseValidity(licenseKey, domain)Validação para Cloudflare Workers (edge)
getDomainsByLicense(licenseId)Domínios associados com timestamps de ativação
renewLicense(licenseId)Renovação manual de licença

11. REST API v1 Endpoints

MétodoEndpointDescrição
GET/middag-account/v1/licensesListagem por organização (filtro: status, produto, domínio)
GET/middag-account/v1/licenses/{id}Detalhes da licença (chave, produto, domínios, datas)
POST/middag-account/v1/licenses/{id}/activateAtivar licença em domínio
POST/middag-account/v1/licenses/{id}/deactivateDesativar licença de domínio
GET/middag-account/v1/licenses/{id}/domainsListar domínios associados

Todos os endpoints requerem autenticação JWT e contexto de organização.