License API para autores de módulos
Como um módulo pago se licencia: a chave do manifesto, a chamada de ativação, o heartbeat e a política de degradação.
Um módulo pago é licenciado pelo mesmo servidor com o qual o core conversa, usando o mesmo transporte assinado. Você declara isso no manifesto; o kernel faz o resto.
A chave do manifesto#
"licensing": { "product_slug": "your-module" }
Presente significa que o módulo é licenciado: o painel mostra uma tela
License para ele, e o módulo pode perguntar se updates são permitidos.
Ausente significa que o módulo é gratuito e nada é verificado. Os módulos de
fábrica que acompanham o core (payments, gamification) não carregam chave
licensing nenhuma.
O product_slug é como o servidor de licenças conhece o produto, e é o que
cai no claim prd do token.
Ativação#
O membro digita o serial em Modules > License. O kernel chama o servidor:
POST /v1/activate
{ "serial": "...", "domain": "example.com", "host": "...", "app_version": "1.2.0" }
Um sucesso traz o token assinado e o entitlement:
{
"ok": true,
"token": "<JWT RS256>",
"license": { "serial": "...", "product": "your-module", "updates_until": 1789000000, "domain": "example.com" },
"features": []
}
Uma recusa volta com ok: false e um motivo: invalid_serial,
domain_in_use, revoked ou rate_limited. Ela chega como HTTP 200; um 4xx
significa que o servidor está indisponível, o que é uma situação totalmente
diferente.
Uma licença de módulo é ativada no mesmo domínio de produção que a licença do core.
O transporte assinado#
Toda chamada a /v1/* carrega quatro cabeçalhos:
X-Bondry-Key: o identificador da chave compartilhada
X-Bondry-Timestamp: tempo unix, válido por 300 segundos
X-Bondry-Nonce: uuid v4, uso único
X-Bondry-Signature: hex(hmac_sha256(secret, METHOD\nPATH\nTIMESTAMP\nNONCE\nsha256(body)))
Você nunca constrói isso sozinho: o client de licença do kernel assina a chamada. Está documentado aqui para você reconhecer isso num log.
O token#
RS256, verificado localmente contra a chave pública que acompanha o Bondry.
Claims: iss, sub (o serial), prd (o slug do seu produto), dom (o
domínio ativado), upd (o fim da janela de updates), feat, iat, exp
(30 dias) e jti.
O token é armazenado cifrado e nunca sai da instalação.
O heartbeat#
Diariamente, a partir do scheduler:
POST /v1/heartbeat
{ "token": "...", "domain": "example.com", "app_version": "1.2.0" }
A resposta renova o token quando ele está perto de expirar e devolve o
updates_until atual mais flags de revogado e expirado, com uma mensagem
humana opcional mostrada no painel.
Se o servidor não puder ser alcançado, nada se degrada por catorze dias. Seu módulo não deve tratar um erro de rede como uma recusa.
A política de degradação#
Isso é uma regra de produto, não uma sugestão.
| Estado | O que o seu módulo pode fazer |
|---|---|
| Ativo | Tudo |
| Janela de updates expirada | Tudo que o comprador já tem. Updates indisponíveis. |
| Revogado | Tudo que o comprador já tem. Um aviso persistente no painel. Updates indisponíveis. |
| Offline além do período de tolerância | O mesmo que expirado, reversível ao reconectar |
Nunca bloqueie conteúdo ou funcionalidade básica. Uma licença controla updates e extras marcados, não o direito de continuar usando o que foi comprado. Um módulo que tranca um comprador fora dos próprios dados não será aceito, e é o jeito mais rápido de ganhar uma contestação de cobrança.
Perguntando sobre a licença#
Pergunte ao kernel, não chame o servidor você mesmo:
if (\App\Kernel\License\ModuleLicenses::allowsUpdates('your-module')) {
// Ofereça o update.
}
Testando sem servidor#
Durante o desenvolvimento o client de licença pode rodar sem nenhum servidor configurado. Ele registra a intenção e permanece desbloqueado, então você nunca fica bloqueado por acesso de rede enquanto escreve um módulo.