Pular para o conteúdo

Catálogo de demonstração. Produtos, criadores, preços e avaliações são fictícios. Recursos gratuitos podem ser adquiridos de verdade; itens pagos não são cobrados nem simulados. Ver o que já funciona

Primeiros passos

Do registro do plugin à primeira licença ativada, sem precisar de uma venda real.

1. Tenha uma conta de vendedor aprovada

O licenciamento é um serviço para quem vende. Se a sua conta ainda não é de vendedor, o pedido é feito em /vender. A aprovação é manual.

2. Registre o plugin

Em /seller/licensing/plugins/new, informe nome, apelido e plataformas. O registro gera um Product ID no formato lsp_a1b2c3d4e5f6a7b8c9d0e1f2.

O Product ID é imutável

Ele vai dentro do .jar que você distribui. Nenhuma operação do painel ou da API o altera — trocar o identificador de um plugin já distribuído derrubaria todos os servidores que rodam aquela versão. Escolha o registro certo da primeira vez.

O Product ID é público. Pode ficar no código à vista: quem o lê não consegue emitir nem autorizar nada com ele.

3. Configure a política

Em /seller/licensing/plugins/[id]/settings. As decisões que importam:

  • Vínculo. O padrão é INSTALLATION, e é o recomendado. Vincular por IP quebra em IP dinâmico, CGNAT e hospedagem compartilhada — cenários legítimos e comuns.
  • Instalações simultâneas. Quantos servidores a mesma chave cobre ao mesmo tempo.
  • Prazo. Vitalícia, por período ou avaliação.
  • Tolerância offline. Quantas horas o plugin segue funcionando sem alcançar a API. Setenta e duas horas é um bom padrão: cobre um fim de semana inteiro de indisponibilidade.
Mudar a política depois não altera as licenças já emitidas: cada uma guarda uma cópia do que valia no momento da venda. É o que impede uma mudança de regra de quebrar a licença de quem já comprou.

4. Associe o produto do marketplace

Com um produto associado, toda compra confirmada emite a licença automaticamente — o comprador vê a chave em /dashboard/licenses logo depois do pedido. Sem associação, você emite à mão.

Cada produto só pode estar em um registro. A plataforma recusa a segunda associação: dois registros apontando para o mesmo produto emitiriam duas licenças na mesma compra.

5. Integre o SDK

build.gradle.kts
dependencies {
    // implementation, NÃO compileOnly: a biblioteca vai DENTRO do seu .jar.
    implementation("gg.lodestone:lodestone-licensing:1.0.0")
}

tasks.shadowJar {
    // Obrigatório. Sem relocação, dois plugins com o SDK colidem no
    // classloader do servidor e o segundo roda contra a versão do primeiro.
    relocate("com.lodestone.licensing", "com.seupacote.libs.licensing")
    minimize()
}
java
public final class SeuPlugin extends JavaPlugin {

    private static final String PRODUCT_ID = "lsp_a1b2c3d4e5f6a7b8c9d0e1f2";

    private LodestoneLicensing licensing;
    private PlatformSupport.MainThreadDispatcher mainThread;
    private final AtomicBoolean premium = new AtomicBoolean(false);

    @Override
    public void onEnable() {
        saveDefaultConfig();

        String key = getConfig().getString("license.key", "");
        if (key.isBlank()) {
            getLogger().severe("Preencha license.key no config.yml.");
            getServer().getPluginManager().disablePlugin(this);
            return;
        }

        mainThread = PlatformSupport.dispatcher(this);

        licensing = LodestoneLicensing.builder()
                .productId(PRODUCT_ID)
                .licenseKey(key)
                .dataFolder(getDataFolder().toPath())
                .platform(PlatformSupport.detect().id())
                .serverVersion(PlatformSupport.serverVersion())
                .listener(new LicenseListener() {
                    @Override public void onAuthorized(LicenseResult r) {
                        mainThread.run(() -> premium.set(true));
                    }
                    @Override public void onDenied(LicenseResult r) {
                        mainThread.run(() -> {
                            premium.set(false);
                            getLogger().warning(r.message());
                        });
                    }
                    @Override public void onGrace(LicenseResult r) {
                        // Plataforma fora do ar: SEGUE ligado.
                        mainThread.run(() -> premium.set(true));
                    }
                    @Override public void onOffline(LicenseResult r) {
                        mainThread.run(() -> premium.set(false));
                    }
                })
                .build();

        // O boot NÃO espera a rede.
        licensing.activateAsync();
    }

    @Override
    public void onDisable() {
        if (licensing != null) licensing.close();
    }
}

Quatro regras que o exemplo demonstra

  • O boot não espera a rede. Um servidor que trava 15 segundos no onEnable() porque a rede do cliente está ruim é um chamado de suporte garantido.
  • Os recursos começam desligados. Ligar primeiro e desligar depois entrega o plugin a quem bloquear a nossa API no firewall.
  • Os callbacks voltam para a thread principal. Eles chegam na thread do SDK; tocar a API do Bukkit de lá é erro na maioria das versões.
  • onGrace mantém tudo ligado. É a situação “a plataforma caiu” — e o cliente não tem culpa disso.

6. Teste sem uma venda real

Emita uma licença de teste na tela de licenças do plugin, marcando licença de teste. Ela não conta para o limite do plano.

config.yml
license:
  key: "LSL-XXXXX-XXXXX-XXXXX-XXXXX"
  endpoint: "http://localhost:3000/api/v1/licensing"
  test-mode: true   # sem isto, a licença de teste é RECUSADA

Licença de teste não autoriza produção

Por três caminhos independentes: a API recusa com test_license_in_production, o documento assinado sai com test: true, e o SDK recusa quando testMode está desligado. Isso impede que uma chave do Playground escape para o cliente final.

Para exercitar os cenários de falha — expirada, revogada, limite atingido, resposta adulterada, API fora do ar — use o Playground. Ele roda contra o motor real, não contra uma simulação.

7. Publique

Gere o .jar com o SDK sombreado e relocado, e publique o produto no marketplace. A partir da primeira compra confirmada, a licença é emitida sozinha.

Nunca coloque uma chave de API no .jar

A chave de API do desenvolvedor gerencia plugins e licenças — quem a tiver age como você. O plugin não precisa dela: ele autentica com a chave de licença do próprio cliente e verifica a resposta com uma chave pública. Um .jar é um zip.