Ir para o conteúdo
Voltar à documentação do Launch

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

  1. Implemente as quatro interfaces HTTP abaixo no seu serviço, com chaves de assinatura HMAC e identificadores independentes para cada conexão.
  2. 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”.
  3. Crie rascunhos de produto e plano comercial. Launch gerencia o tipo e o preço da compra; seu sistema gerencia as regras de licenciamento.
  4. 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.
  5. 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

  1. Implemente as quatro interfaces deste documento à frente do sistema de licenciamento existente, preservando sua lógica de licenças.
  2. 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.
  3. 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.
  4. Selecione essa conexão para o produto e mapeie a versão do plano comercial para product_ref e plan_ref externos. Os códigos externos podem ser strings próprias do comerciante; não precisam ser UUIDs Licentivo.
  5. Conclua a validação sandbox de pagamento, renovação, upgrade, reembolso e recuperação de falhas; depois publique a configuração live separadamente.
  6. Após verificar o pagamento, Launch salva o pedido, a cobertura comprada e as tarefas pendentes na mesma transação de banco de dados.
  7. O servidor envia um instantâneo completo da compra ao serviço personalizado; o serviço persiste o recebimento antes de confirmá-lo.
  8. O serviço de licenciamento aplica o estado desejado. Launch consulta o recibo do evento original; somente applied confirma a conclusão dessa sincronização.
  9. 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.
  10. 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; version usa 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-Nonce e X-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_CONFLICT para 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_FOUND explí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.