Baixar MarkdownDocumento original no GitHub
Guia de integração de um sistema de licenciamento personalizado
O código atual oferece dois provedores de licenciamento: Licentivo e custom_http. O comerciante seleciona e ativa um deles em Configurações do sistema → Sistema de licenciamento. Todos os produtos e ambientes sandbox/live usam o mesmo provedor; as conexões personalizadas e os mapeamentos de planos continuam separados por produto e ambiente. Este recurso não inclui um sistema próprio de licenciamento; o comerciante continua usando seu serviço e SDK existentes.
A troca de contas Licentivo, os SDKs para nove linguagens e a gestão de dispositivos mantêm o fluxo existente. Os produtos personalizados sincronizam o estado de compra por este protocolo; o cliente consulta a entrega da licença na central da conta, sem passar pelo fluxo do SDK Licentivo.
Início rápido
- Implemente as quatro interfaces HTTP abaixo no seu serviço, com chaves de assinatura HMAC e identificadores independentes para cada conexão.
- Em “Sistema de licenciamento”, selecione “Serviço de licenciamento personalizado”, adicione uma conexão e informe endereço, ambiente, referência da conexão, ID da chave e chave em Base64 padrão. Verifique, salve e clique em “Ativar serviço de licenciamento personalizado”.
- Crie rascunhos de produto e plano comercial. Launch gerencia o tipo e o preço da compra; seu sistema gerencia as regras de licenciamento.
- Selecione produto, ambiente e conexão, preencha as referências externas de produto e plano e salve. O provedor é definido para todo o site; não é possível ativar Licentivo simultaneamente para outros produtos.
- Publique uma versão de teste na página do plano, conclua os testes de pagamento, entrega, renovação, upgrade, reembolso e recuperação de falhas e depois configure a produção separadamente.
Endereço, ambiente, referência da conexão e política de acesso à rede não podem ser alterados após a criação; crie outra conexão quando necessário. A chave de assinatura pode ser rotacionada; deixe-a vazia para conservar a atual. Havendo pedidos de outro sistema de licenciamento, conclua a migração antes de trocar o provedor global. Após a troca, os planos anteriores ficam apenas para visualização e precisam ser republicados; credenciais e mapeamentos salvos são preservados. Produtos com pedidos existentes não podem trocar diretamente de conexão, e mapeamentos em venda não podem ser substituídos diretamente; novos preços ou mapeamentos exigem uma nova versão do plano.
Ao configurar pela API, primeiro chame PUT /api/v1/admin/licensing/provider com {"provider":"custom_http"} ou {"provider":"licentivo"} e depois salve o mapeamento do produto. Provedores inativos não podem salvar vínculos de licenciamento de produtos nem mapeamentos de políticas de planos Licentivo.
A verificação da conexão confirma protocolo, autenticação, ambiente e tipos de compra suportados; não cria nem confirma planos externos pelo comerciante. Antes de receber pagamentos reais, conclua a validação sandbox usando planos externos reais.
3. Fluxo de integração do comerciante
- Implemente as quatro interfaces deste documento à frente do sistema de licenciamento existente, preservando sua lógica de licenças.
- Crie uma conexão de “Serviço de licenciamento personalizado” no Launch e configure endereço HTTPS, referência da conexão, ambiente e chave de assinatura.
- Launch chama a interface de capacidades para confirmar a versão do protocolo, o suporte a compras permanentes ou temporárias e a forma de entrega.
- Selecione essa conexão para o produto e mapeie a versão do plano comercial para
product_refeplan_refexternos. Os códigos externos podem ser strings próprias do comerciante; não precisam ser UUIDs Licentivo. - Conclua a validação sandbox de pagamento, renovação, upgrade, reembolso e recuperação de falhas; depois publique a configuração live separadamente.
- Após verificar o pagamento, Launch salva o pedido, a cobertura comprada e as tarefas pendentes na mesma transação de banco de dados.
- O servidor envia um instantâneo completo da compra ao serviço personalizado; o serviço persiste o recebimento antes de confirmá-lo.
- O serviço de licenciamento aplica o estado desejado. Launch consulta o recibo do evento original; somente
appliedconfirma a conclusão dessa sincronização. - Launch consulta a entrega para clientes autenticados que possuem a compra e mostra o código de licença, o acesso de ativação ou as instruções em texto na central da conta. O aplicativo conecta-se ao sistema de licenciamento do comerciante.
- Renovação, upgrade, reembolso, reversão, suspensão e expiração geram novas versões do instantâneo completo. O serviço de licenciamento processa o estado desejado mais recente.
As conexões sandbox/live do mesmo produto são vinculadas separadamente. Depois de uma compra, trocar o provedor exige migração independente; pedidos e tarefas antigos não podem acompanhar a seleção administrativa e ser enviados silenciosamente ao novo serviço.
4. APIs exigidas do serviço de licenciamento personalizado
As interfaces abaixo são recebidas pelo serviço de licenciamento do comerciante e chamadas pelo servidor Launch. base_url pode incluir um prefixo de caminho fixo; os caminhos da tabela são acrescentados após esse prefixo.
| Métodos e caminhos | Finalidade |
|---|---|
GET /launch/v1/capabilities |
Verificação da conexão e declaração de protocolo e capacidades |
POST /launch/v1/events |
Receber o estado completo desejado da compra; repetir usando o evento original |
GET /launch/v1/events/{event_id} |
Consultar se o evento foi realmente aplicado e recuperar respostas perdidas |
GET /launch/v1/entitlements/{entitlement_ref}/delivery |
Ler uma entrega de licença existente sem emitir uma segunda licença |
A primeira versão consulta ativamente os recibos e não adiciona callbacks externos para alterar pedidos. Serviços personalizados não podem confirmar pagamentos, alterar preços nem reescrever os períodos comprados.
4.1 Convenções gerais e autenticação
- JSON usa UTF-8; horários usam UTC RFC 3339;
versionusa inteiros positivos. - A resposta de sucesso é
{"data": {...}, "request_id": "..."}; a de erro é{"error": {"code": "...", "message": "..."}, "request_id": "..."}. - Cada ambiente e conexão usa uma chave separada. A chave deve conter pelo menos 32 bytes aleatórios e ser referenciada pelo armazenamento criptografado de chaves já existente no Launch; nunca deve entrar na configuração do frontend, nos exemplos ou nos pacotes do aplicativo.
- Cada requisição inclui
X-Launch-Key-Id,X-Launch-Timestamp,X-Launch-NonceeX-Launch-Signature. - O timestamp usa segundos Unix decimais. O nonce é uma codificação base64url sem padding de pelo menos 128 bits aleatórios, com um novo valor a cada tentativa de rede.
A entrada da assinatura contém os sete itens a seguir, codificados como bytes UTF-8 e unidos por uma única quebra de linha, sem quebra ao final:
v1
<key_id>
<timestamp>
<nonce>
<UPPERCASE HTTP method>
<encoded request path and raw query, including the base_url path prefix>
<lowercase hexadecimal SHA-256 digest of the exact request body>
X-Launch-Signature é formado pelo prefixo v1= seguido do resultado de HMAC-SHA256 codificado em base64url sem padding. O corpo das requisições GET é vazio. O destinatário verifica a assinatura e a janela de tempo antes de desserializar, usa comparação em tempo constante, aceita por padrão desvio de ±300 segundos e conserva os nonces por pelo menos 600 segundos para impedir replay. Não reutilize nonces entre tentativas; os IDs de evento e corpos das requisições de negócio devem permanecer iguais.
Assinaturas não substituem HTTPS. As requisições não levam senhas de clientes, chaves de pagamento nem chaves administrativas Licentivo; por padrão, enviam apenas referências anônimas de conta, sem e-mail. Os clientes HTTP do servidor precisam de timeout, limite de resposta e bloqueio de encaminhamento de credenciais em redirecionamentos. Valide os endereços personalizados; o acesso à intranet deve ser configurado explicitamente pelo administrador.
4.2 Verificação de capacidades
GET /launch/v1/capabilities retorna HTTP 200, por exemplo:
{
"data": {
"protocol": "launch-external-licensing-v1",
"connection_ref": "merchant-01-sandbox",
"environment": "sandbox",
"purchase_types": ["perpetual", "fixed_term"],
"delivery_types": ["license_key", "activation_url", "instructions"],
"devices": false,
"identity_exchange": false
},
"request_id": "check-001"
}
protocol, connection_ref, environment, purchase_types e delivery_types são obrigatórios. Os dois últimos devem ser arrays não vazios; tipos de compra não suportados não podem ser vendidos. Na primeira versão, devices e identity_exchange devem ser false; só declare suporte após concluir o contrato estendido e sua implementação.
4.3 Instantâneo da compra
Exemplo de requisição POST /launch/v1/events:
{
"schema": "launch-external-licensing-v1",
"event_id": "7bf84b91-2749-4b01-a6a7-bd527c488ee8",
"connection_ref": "merchant-01-sandbox",
"environment": "sandbox",
"type": "entitlement.snapshot",
"change_reason": "purchase",
"account_ref": "customer_opaque_001",
"entitlement_ref": "purchase_opaque_001",
"launch_product_id": "c6dd463e-50f7-4b76-8239-0a8ec6fc304b",
"launch_plan_version_id": "5459a073-9362-4b46-949d-9b0c5d84421a",
"product_ref": "desktop-editor",
"plan_ref": "professional-yearly-v1",
"version": 1,
"desired_state": "active",
"valid_from": "2026-10-25T00:00:00Z",
"valid_until": "2027-10-25T00:00:00Z"
}
Todos os campos do exemplo acima são obrigatórios. valid_until com valor null indica compra permanente; qualquer outro valor deve ser posterior a valid_from.
| Campo | Restrições e significado |
|---|---|
event_id |
UUID gerado pelo Launch; permanece igual ao repetir o mesmo evento |
connection_ref / environment |
Devem corresponder à conexão e ao ambiente da chave de assinatura |
account_ref |
Referência anônima estável da conta; o destinatário identifica o cliente pela combinação de conexão, ambiente e referência |
entitlement_ref |
Referência estável de uma relação de compra; após revogação integral, uma recompra usa nova referência |
launch_product_id / launch_plan_version_id |
UUIDs do produto Launch e da versão imutável do plano |
product_ref / plan_ref |
Referência externa configurada pelo integrador; 1–128 caracteres ASCII, aceitando letras, números, _, - e .; planos externos não são criados automaticamente |
version |
Versão desejada que aumenta monotonicamente na mesma conexão, ambiente e referência de compra |
desired_state |
active, suspended, expired, revoked |
change_reason |
purchase, renewal, upgrade, refund, reversal, suspension, resumption, expiry, reconciliation |
account_ref e entitlement_ref usam 1–128 letras, números, sublinhados ou hífens. schema e type são fixados nos valores do exemplo. O destinatário aplica desired_state e o período absoluto completo, sem acrescentar dias nem revogar toda a autorização por conta própria.
Nos reembolsos, em especial, devolver um pedido de renovação ou upgrade pode preservar uma compra-base válida. Nesse caso, o instantâneo pode continuar active, com prazo menor ou outro plano. O estado de encerramento só é enviado após recalcular o histórico e confirmar que nenhuma compra válida mantém a cobertura.
4.4 Recebimento, confirmação e recuperação
O destinatário só deve retornar HTTP 202 depois de persistir o ID do evento, o resumo do corpo original e a tarefa a executar. Repetições já sincronizadas podem retornar HTTP 200. Ambos retornam a estrutura de recibo a seguir.
{
"data": {
"event_id": "7bf84b91-2749-4b01-a6a7-bd527c488ee8",
"connection_ref": "merchant-01-sandbox",
"environment": "sandbox",
"entitlement_ref": "purchase_opaque_001",
"version": 1,
"payload_sha256": "<64-character lowercase hexadecimal SHA-256 digest of the exact event body>",
"status": "pending",
"error_code": null
},
"request_id": "receipt-001"
}
Substitua o marcador do resumo pelo resumo real. Todos os campos do recibo acima são obrigatórios; error_code é null quando não há erro. A consulta GET /launch/v1/events/{event_id} retorna a mesma estrutura com HTTP 200, e o estado pode ser:
status |
Significado | Tratamento pelo Launch |
|---|---|---|
pending |
Persistido, mas a alteração real de autorização ainda não terminou | Consultar mais tarde; não marcar como sincronizado |
applied |
O serviço de licenciamento confirma que o estado desejado foi realmente aplicado | Confirmar a sincronização após conferir todos os campos relacionados e o resumo |
rejected |
Não pode ser aplicado, por exemplo porque o plano externo não existe | Interromper tentativas automáticas e mostrar um erro claro |
manual_review |
Resultado incerto ou conflito de estado; exige verificação manual | Conservar tarefa e evento originais, aguardando tratamento auditável |
O estado da entrega é independente do recibo de sincronização; applied não pode autorizar diretamente a execução no aplicativo cliente.
Requisitos de confiabilidade:
- Entrega pelo menos uma vez; o destinatário deve garantir idempotência de negócio. Não se pode prometer “uma única chamada” na camada de rede.
- Mesmo ID de evento e mesmo corpo original retornam o resultado existente; mesmo ID com corpo diferente retorna HTTP 409
IDEMPOTENCY_CONFLICT. - Versões antigas não podem substituir versões novas. Eventos ou estados diferentes para a mesma versão de compra não devem ser aceitos silenciosamente; retorne HTTP 409
STATE_VERSION_CONFLICTpara conferência. - Se a resposta POST se perder ou o processo reiniciar, Launch consulta primeiro o evento original por GET; só reenvia o ID e corpo originais após um
EVENT_NOT_FOUNDexplícito. - Se a entrega real remota for incerta, o integrador também deve conferir sua própria referência idempotente persistida; reenviar não pode simplesmente emitir outra licença.
- HTTP 429 respeita
Retry-After; timeouts e erros 5xx usam espera progressiva. Ao ultrapassar o limite, exija verificação manual. Recusas de autenticação, configuração ou negócio não podem ser tratadas como sincronização bem-sucedida.
Códigos de erro sugeridos: SIGNATURE_INVALID (401), SIGNATURE_EXPIRED (401), NONCE_REPLAYED (409), SCOPE_MISMATCH (403), EVENT_NOT_FOUND (404), IDEMPOTENCY_CONFLICT (409), STATE_VERSION_CONFLICT (409), PLAN_UNKNOWN (422), PURCHASE_TYPE_UNSUPPORTED (422), RATE_LIMITED (429) e SERVICE_UNAVAILABLE (503). As mensagens não devem conter credenciais.
4.5 Entrega da licença
GET /launch/v1/entitlements/{entitlement_ref}/delivery apenas lê uma entrega existente na conexão e ambiente atuais; uma consulta não deve emitir outra licença. Exemplo HTTP 200:
{
"data": {
"connection_ref": "merchant-01-sandbox",
"environment": "sandbox",
"entitlement_ref": "purchase_opaque_001",
"version": 1,
"status": "ready",
"delivery": {
"type": "license_key",
"value": "EXAMPLE-ONLY-NOT-A-REAL-LICENSE",
"expires_at": null
}
},
"request_id": "delivery-001"
}
status é pending, ready ou unavailable; sem disponibilidade, delivery é null. Quando disponível, type deve ser um tipo declarado: license_key, activation_url ou instructions; value aceita até 4096 bytes UTF-8 e expires_at é um horário UTC ou null. Todos os campos relacionados devem corresponder à versão de compra atual no Launch; uma entrega de versão antiga não pode ser tratada como resultado atual.
Códigos de licença são dados sensíveis. Respostas de entrega usam Cache-Control: no-store, não registram o corpo em logs e devem ser criptografadas se persistidas. URLs de ativação aceitam apenas origens HTTPS permitidas pela conexão; instruções são exibidas como texto simples, sem executar HTML ou scripts. Contas revogadas, integralmente reembolsadas ou sem compra válida não podem continuar obtendo a entrega pelo Launch; sistemas externos controlam a invalidação efetiva das credenciais entregues.
API administrativa e do cliente Launch
Estas interfaces ficam na origem Launch implantada pelo comerciante e não usam autenticação HMAC do serviço externo. Administradores usam as sessões existentes do site; requisições de escrita incluem X-CSRF-Token. Clientes só podem ler suas próprias compras.
| Métodos e caminhos | Finalidade |
|---|---|
GET /api/v1/admin/licensing/connections |
Retorna connections, bindings, mappings e default_provider; a lista contém apenas conexões personalizadas, preservando a interface administrativa original do Licentivo |
POST /api/v1/admin/licensing/connections |
Verificar e adicionar conexões personalizadas |
PUT /api/v1/admin/licensing/connections/{id} |
Editar nome, origens de ativação permitidas e configuração de assinatura; o endereço de recebimento existente permanece igual |
POST /api/v1/admin/licensing/connections/{id}/check |
Verificar novamente; last_check_error não nulo em HTTP 200 indica falha na verificação |
PUT /api/v1/admin/products/{id}/licensing |
Configurar o provedor do produto por ambiente, associando a conexão e os planos externos |
GET /api/v1/me/products/{id}/delivery?environment=sandbox |
O cliente atual lê a entrega personalizada da compra atualmente válida; autenticação por Cookie ou bearer de desktop |
GET /api/v1/licensing/documentation |
Este documento não exige login |
GET /api/v1/licensing/protocol |
O OpenAPI exigido pelos serviços personalizados não exige login |
Corpo para adicionar uma conexão personalizada:
{
"provider": "custom_http",
"name": "My licensing service",
"base_url": "https://licenses.example.com",
"environment": "sandbox",
"connection_ref": "merchant-01-sandbox",
"key_id": "key-1",
"signing_key": "<standard Base64 encoding of 32 to 128 random bytes>",
"allow_private_network": false,
"allow_local_http": false,
"activation_origins": ["https://activate.example.com"]
}
data contém a conexão com os dados sensíveis removidos; não retorna a chave de assinatura nem o ID do segredo. allow_local_http só permite localhost ou loopback no sandbox; HTTPS privado exige permissão administrativa explícita. Uma chave vazia só é permitida ao editar uma conexão existente. Licentivo continua sendo configurado por POST /api/v1/admin/connections/licentivo.
Corpo para configurar o produto:
{
"provider": "custom_http",
"environment": "sandbox",
"connection_id": "<id returned when creating the connection>",
"mappings": [
{
"version_id": "<Launch plan version UUID>",
"product_ref": "desktop-editor",
"plan_ref": "professional-yearly-v1"
}
]
}
Ao selecionar Licentivo, envie {"provider":"licentivo","environment":"sandbox","mappings":[]}; o mapeamento de produto e política mantém a interface original. A primeira troca converte a publicação existente em prévia, exigindo nova verificação e publicação pelo novo provedor. Os mapeamentos só podem incluir versões a adicionar ou conservar; omiti-los não apaga mapeamentos anteriores.
A entrega do cliente retorna o envelope data padrão com HTTP 200. Sem compra, retorna 404; produtos Licentivo retornam 409 LICENSING_DELIVERY_UNSUPPORTED; compra não sincronizada, suspensa, expirada ou integralmente reembolsada retorna 409 ENTITLEMENT_NOT_READY; indisponibilidade externa ou recibo divergente retorna 503. “Ver entrega da licença” na central do cliente verifica novamente a titularidade e o estado, sem incluir credenciais em URLs ou logs.
As interfaces Licentivo identity, device e integration-check de um produto personalizado retornam 409 CUSTOM_LICENSING_SDK_REQUIRED. A antiga /credential continua retornando 410. O aplicativo deve usar o SDK do comerciante, sem considerar o estado ready do Launch uma autorização de execução.
Exemplos e validação
O exemplo de adaptador leve em Python fornece uma estrutura de recebimento com assinatura, nonce, deduplicação de eventos e persistência de recibos; a autorização real precisa ser integrada ao sistema do comerciante.
O OpenAPI do protocolo personalizado e o OpenAPI do próprio Launch descrevem as duas direções separadamente. O destinatário não precisa implementar catálogos de produtos ou políticas nem interfaces de confiança de identidade do Licentivo.
A verificação local usa MySQL isolado, o adaptador HTTP real e serviços simulados de licenciamento e pagamento. Cobre perda de resposta, reinício, renovação, upgrade, reembolso, desativação de conta, recibos divergentes e isolamento da entrega. A validação de produção também exige o serviço de licenciamento e o aplicativo do comerciante. Launch não acrescenta registro de dispositivos, login compartilhado de conta nem capacidades offline.