Nesse artigo você verá como criar um sistema para controle de licenças/chave de uso para seus Temas e Plugins Premium. Trabalhar com recursos exclusivos é o grande diferencial dos plugins que são pagos ou possuem alguns recursos grátis, porém suas versões mais sofisticadas são limitadas e liberadas mediante pagamento ou algum outro tipo de ativação (como cadastro de email, por exemplo).

A fim de tornar viável esse tipo de validação, a partir de agora você vai trabalhar na criação de um plugin cujos recursos somente estarão habilitados após validação de uma chave de uso (License key). Para isso será preciso desenvolver o sistema em duas frentes: cliente e servidor. Entenda por cliente o plugin em si que o usuário terá acesso; e por servidor a aplicação que irá gerenciar as requisições do cliente.

Validação de Licença para Plugins

O plugin funcionará do seguinte modo: Ao ativar o plugin no WordPress, tal como qualquer outro plugin, será habilitada uma página personalizada solicitando a inserção de uma chave de uso. Somente após essa chave ter sido cadastrada e validada pelo servidor é que os demais recursos do plugin passarão a funcionar.

Para cuidar do tratamento das informações e processos a serem executados no lado servidor, você desenvolverá um novo Tema, ou então poderá configurar algum Tema que já esteja sendo utilizado. A comunicação entre cliente e servidor será feita através de requisições de cabeçalho.

Lado cliente

Antes mesmo de iniciar os estudos, tenha em mente que nesse projeto não será trabalhado especificamente um dado recurso, como por exemplo integração com API’s externas, galerias de imagens ou qualquer outra funcionalidade para seu WordPress; mas sim será criado o ambiente responsável por blindar, tornar seus recursos disponíveis apenas para aqueles a quem você desejar.

Os códigos que serão apresentados devem ser usados em conjunto aos códigos de outros plugins e/ou temas; desse modo você consegue aplicar a técnica para limitar o acesso de qualquer recurso. Para efeitos didáticos sempre que a instrução se referir a plugin, trata-se do lado cliente da aplicação; e quando alguma referência citar o tema, trata-se do lado servidor.

Dado as explicações, o lado cliente (plugin) será responsável pela criação de uma página administrativa. Como foi dito anteriormente, para que o conteúdo dessa página seja exibido é preciso que o usuário tenha definido sua chave de uso, do contrário apenas é apresentado a ele um formulário com o campo para inserção da mesma.

Quando o usuário do plugin submeter esse formulário com a chave ela será validada pelo servidor (tema). De acordo com a resposta o recurso que está sendo protegido será ativado ou não; caso não seja a tela para inserção da chave será exibida toda vez que o usuário tentar acessá-lo.

Lado servidor

A esse lado do sistema ficará a responsabilidade de receber os dados do cliente, processá-los e retornar aquilo que está sendo solicitado. Para que isso seja possível e também aumente o nível de segurança do mesmo, você desenvolverá também um controle das chaves de uso, sendo que cada usuário possuirá sua própria chave.

Tenha em mente que o plugin somente funcionará corretamente caso o tema (servidor) também esteja operando como previsto, ambos os recursos devem estar alinhados com relação as ações a serem executadas. Portanto fique atento a nomenclatura de ações e parâmetros transferidos de uma área a outra de seu sistema.

Criação de um Plugin Premium (Cliente)

Crie dois arquivos PHP para dar início ao plugin. O primeiro deles, arquivo principal, será responsável pela comunicação com o servidor e o outro trará as opções visíveis no painel de controle do WordPress (Dashboard). Cada arquivo possuirá uma classe que na instrução serão chamados respectivamente de Plugin (classe principal) e Plugin_Admin (classe secundária, de apoio).

Inicie a classe principal definindo as variáveis de controle $url para identificar a URL do servidor, para onde as requisições serão enviadas; e $token para armazenar a chave válida do usuário.

<?php 

/**
 *
 * Plugin Name: Plugin restrito
 * 
 */
class Plugin
{
    private static $url, $token;
    public static function setup()
    {
    }
}

add_action( 'plugins_loaded', array( 'Plugin', 'setup' ) );

if ( is_admin() )
    require_once 'plugin_admin.php';

Perceba a incorporação da classe de apoio apenas quando necessário, ou seja, quando o usuário estiver dentro do painel de controle. Como as propriedades da classe (as variáveis $url e $token) foram criadas como privadas, você precisará de métodos setters e getters para acessá-las. Uma vez que o método setup do plugin é disparado sempre que o plugin é carregado, dentro dele serão atribuídas as propriedades e também criada a chamada para a página administrativa.

public static function setup()
{
    self::set_url();
    self::set_token();
		
    add_action( 'admin_menu', array( 'Plugin_Admin', 'admin_menu' ) );
}

public static function get_token()
{
    return self::$token;
}

public static function set_token()
{
    self::$token = get_option( 'plugin_token' );
} 

private static function set_url()
{
    self::$url = 'http://dominio-servidor.com.br';
}

O método get_token permitirá que você utilize o valor do token (chave de uso) também na classe auxiliar. Perceba que o valor da chave ficará armazenado em uma option e a URL corresponde ao endereço que criaremos posteriormente no tema.

Página Administrativa

Você precisa agora, no arquivo auxiliar do plugin, criar as rotinas para uso do mesmo dentro do painel de controle. Dentro do setup foi criada uma chamada para a criação de uma página administrativa personalizada através do filtro admin_menu; crie agora a classe e o método para que isso ocorra:

<?php

class Plugin_Admin
{
    public static function admin_menu()
    {
        add_menu_page( 'Plugin', 'Plugin', 'administrator', 'plugin-admin-page', array( 'Plugin_Admin', 'admin_page' ) );
    }
    
    public static function admin_page()
    {
        $error = self::process();
        
        $token = Plugin::get_token(); ?>
        
        <div class="wrap">
            <h2>Plugin</h2>
            <?php 
            if ( !empty( $_POST ) ) {
                if ( $error ) {
                    $c = 'error';
                    $msg = $error;
                } else {
                    $c = 'updated';
                    $msg = 'Dados atualizados com sucesso!';
                }
                printf(
                    '<div class="%s below-h2"><p>%s</p></div>',
                    $c, $msg
                );
            }			
            if ( !$token ) { ?>
            <form method="post">
	        <?php wp_nonce_field( 'plugin_hash', '_plugin_admin_page' );?>
	        <input type="hidden" name="_plugin_action" value="license" />
	        <table class="form-table">
	            <tbody>
	                <tr>
	                    <th><label for="pugin-token">Chave de uso: </label></th>
	                    <td><input type="text" id="plugin-token" name="_license" size="30" /></td>
	                </tr>
	            </tbody>
	        </table>
	        <input type="submit" class="button button-primary" value="Salvar" />
	    </form>
        <?php } ?>
    </div>
    <?php
}

Dentro da página administrativa ocorre a validação do envio, dentro do método self::process. Abaixo você pode perceber a mensagem de erro ou de confirmação de alguma alteração sendo exibida de acordo com a formatação das mensagens exibidas pelo WordPress no Dashboard. A condicional if ( !$token ) força o plugin a exibir sempre o formulário de um campo só caso tenha ocorrido algum problema ao definir o token para o cliente.

Ainda no trecho de código acima, perceba o campo oculto plugin_action, através do valor por ele armazenado você definirá quais rotinas o arquivo principal deverá executar. Se o campo não estiver preenchido ou possuir um valor inválido, nada será executado.

private static function process()
{
     $action = ( isset( $_POST[ '_plugin_action' ] ) ) ? sanitize_text_field( $_POST[ '_plugin_action' ] ) : false;

     if ( !$action )
          return 'Nenhuma ação válida...';

     if ( !wp_verify_nonce( $_POST[ '_plugin_admin_page' ], 'plugin_hash' ) )
          return 'Não foi possível processar sua requisição...';

     return Plugin::do_action( $action );
}

Comunicação Cliente e Servidor

Criado a tela administrativa do plugin, resta agora realizar o processamento da chave enviada a fim de liberar os recursos caso essa seja válida. Esse tratamento você fará através de três métodos da classe principal do plugin: prepare_action, build_action e do_action. Assim como a nomenclatura sugere, será feito respectivamente, a preparação dos dados para envio ao servidor, a construção dessa requisição ao mesmo e finalmente o recebimento das respostas e execução das rotinas pelo plugin. A chamada dessas funções inicia-se com o método Plugin::do_action( $action ), como visto anteriormente.

    public static function do_action( $action )
    {   
        $prepare = self::prepare_action( $action );
        if ( !is_array( $prepare ) )
            return 'Ação inválida!';

De acordo com a ação enviada, o método prepare_action construirá em um array as opções a serem enviadas ao servidor. Se o retorno do método não for um array, logo a ação enviada não está prevista de ocorrer, portanto inválida.

Vale lembrar que a chamada desses métodos de comunicação entre o plugin e tema ocorre somente quando os dados estão sendo transferidos via $_POST pelo plugin. Com esse comportamento você consegue obter as informações enviadas pelo usuário via formulários (como é caso da chave de uso).

    private static function prepare_action( $action )
    {
        $prepare = false;
        switch ( $action )
        {
            case 'license':
                $license = sanitize_text_field( $_POST[ '_license' ] );
                $prepare = array(
                    'action'         => 'activate',
                    'license'        => $license
                );
                break;
        }
        return $prepare;
    }

Em continuidade as rotinas do método do_action, logo após disparar e verificar o retorno do método de preparação, você precisa disparar o método build_action responsável por executar a requisição externa e construir/formatar a resposta para o plugin.

    public static function do_action( $action )
    {   
        // ...
        $error = false;
        $r = self::build_action( $prepare );
        
        if ( isset( $r->action ) ) {
            // continua...
        } else {
            $error = 'Nao foi possivel realizar a requisição!';
        }
        return $error;
    }

O retorno do método build_action, quando em correto funcionamento, sempre será um objeto. Esse objeto por sua vez sempre trará consigo uma ação, portanto caso esse comportamento não se configure, ocorreu um erro na comunicação das partes. Ao final da verificação o erro é retornado ao plugin, caso algo tenha dado errado.

Requisições Remotas

Para realizar a requisição externa será empregada a HTTP API do WordPress. Você deverá agora enviar para a URL que foi definida na inicialização do plugin os dados preparados pelo método prepare_action.

    private static function build_action( $args )
    {
        $r = wp_remote_post( 
            self::$url,
	    array(
		'method' 		=> 'POST',
		'timeout' 		=> 60,
		'httpversion' 		=> 1.0,
		'blocking' 		=> true,
		'body' 			=> $args
	    )
	);

        return ( isset( $r[ 'body' ] ) ) ? json_decode( $r[ 'body' ] ) : false;
    }

Você verá nos tópicos seguintes como essa resposta será obtida, porém no momento basta deixar claro que o retorno virá em formato JSON. Por essa razão a resposta é decodificada e transformada em um objeto de fácil manipulação pelo PHP.

De volta ao método do_action, faça o fechamento do mesmo identificando qual ação foi retornada pelo servidor e o que essa ação deverá fazer no plugin. Como o propósito desse recurso é apenas a ativação do mesmo, considere apenas a ação de registrar a chave de uso ou então na exibição de algum erro (vindo do servidor) ao usuário.

        if ( isset( $r->action ) ) {
            switch ( $r->action ) {
                case 'license':
                    update_option( 'plugin_token', $r->args );
                    break;
                case 'error':                  
                    $error = $r->error_message;
                    break;
            } 

Os erros obtidos podem ser os mais variados, inclusive nessa simples ação de ativar o recurso; como por exemplo chave inválida, chave não encontrada, erro de conexão, etc. Alguns desses veremos logo mais na construção do Tema, o lado servidor da aplicação.

Criação do Sistema de Licenças (Servidor)

Com o plugin estruturado, chegou a hora de programar todo o comportamento do servidor de sua aplicação. Como foi dito desde o início do desenvolvimento desse sistema, as ações no servidor serão controladas por um Tema.

Caso você crie um tema novo, você apenas trabalhará com o arquivo de funções (functions.php) e um arquivo auxiliar (nos exemplos server.php). Caso queira incorporar essas rotinas a um tema pronto, recomenda-se inserir as rotinas do arquivo de funções em um terceiro arquivo com o propósito de mater seu código organizado.

No arquivo functions.php você definirá a criação de uma chave para um usuário, como consultar essas chaves e também (essencial ao funcionamento do sistema) criará a URL que o plugin deverá requisitar. Por outro lado no arquivo server.php (auxiliar) estarão todas as rotinas de processos do servidor.

URL Personalizada

Você certamente se lembra que o plugin dispara as requisições para uma URL fixa do servidor. Pois bem, a primeira modificação que você fará no tema será a criação dessa URL. Fique a vontade para determinar qualquer endereço, a estrutura usada abaixo visa principalmente isso; que esse endereço não necessite estar vinculado a uma página ou qualquer outro tipo de conteúdo do WordPress.


<?php

include_once( TEMPLATEPATH . '/src/server.php' );

add_action( 'after_setup_theme' , 'custom_setup' );

function custom_setup()
{
    add_filter( 'rewrite_rules_array',          'custom_url' );
    add_filter( 'query_vars',                   'custom_query_vars' );
    add_action( 'template_redirect' ,           'custom_redirect' );
}

A partir da função custom_setup de configuração do Tema é que serão disparados os ganchos para os recursos do servidor. Nesse primeiro momento, a criação de uma URL nova e independente dos recursos do WordPress passa pelos filtros da Rewrite API e na ação de redirecionamento de arquivos.


function custom_url( $rules )
{
    $rules_add = array(
        "plugin/?$" => 'index.php?custom=plugin'
    );
    return array_merge( $rules_add, $rules );
}

function custom_query_vars( $qv )
{
    array_push( $qv, 'custom' );
    return $qv;
}

function custom_redirect()
{
    $custom = get_query_var( 'custom' );
    if ( $custom == 'plugin' ) {
        Server::run();
        exit;
    }
}

Observe nos callbacks dos ganchos que o endereço criado foi http://[site-url]/plugin/, o que significa que no arquivo principal do plugin esse mesmo endereço deve ser informado para que as requisições cheguem ao tema como previsto. Durante a verificação de redirecionamento, caso o Tema detecte que o endereço requisitado diz respeito a uma requisição do plugin, então ele executa o método principal da classe Server (presente no arquivo server.php) e interrompe a execução de códigos.

License key (Chave de uso)

A manipulação das chaves de uso do recurso será feita através de diferentes chaves entre os usuários, ou seja, cada usuário terá sua própria chave. A criação e a atribuição da chave acontecerão de modo automático quando um novo usuário for cadastrado no site. Para efeitos de gerenciamento, o tema ainda exibirá na listagem e dentro do perfil dos usuários a chave de cada um, caso exista.

Para que isso seja possível, ainda no arquivo de funções do tema – mais especificamente dentro da função de setup – atribua os ganchos necessários para que isso ocorra:


function custom_setup()
{
    // …
    add_action( 'user_register',                'custom_license_add' );
    
    add_action( 'edit_user_profile',            'custom_license_show' );
    add_filter( 'manage_users_columns',         'custom_license_col' );
    add_action( 'manage_users_custom_column',   'custom_license_col_show' , 10, 3 );
}

Nas funções de callback dos ganchos as instruções são (respeitando a ordem de inserção em custom_setup, de cima para baixo) para adicionar a chave, exibir a chave no perfil do usuário, criar a nova coluna na listagem dos usuários e por fim exibir a chave na coluna recém-criada.


function custom_license_add( $user_id )
{
    if ( $user_id ) {
        $hash = Server::generate_license();
        update_user_meta( $user_id , 'license_key' , $hash );
    }
}

function custom_license_show( $user )
{
    $license = get_user_meta( $user->ID, 'license_key', true);
    if ( $license ) {
    ?>
    <table class="form-table">
        <tbody>
            <tr id="license-key" class="user-license-wrap">
                <th>
                    <label>Chave de uso</label>
                </th>
                <td>
                    <label><?php echo $license; ?></label>
                </td>
            </tr>
        </tbody>
    </table>
    <?php
    }
}

function custom_license_col( $cols )
{
    $cols[ 'license_key' ] = 'Chave de uso';
    return $cols;
}

function custom_license_col_show( $value, $col, $user_id )
{
    $license = get_user_meta( $user_id , 'license_key' , true );
    if ( $col == 'license_key' )
        return $license;

    return $value;
}

Talvez você queira permitir a edição da chave quando aberta no perfil do usuário, nesse caso basta inserir um campo de texto em custom_license_show (ao invés de exibir a chave de modo direto) e em seguida com os filtros edit_user_profile_update (outros usuários) e personal_options_update (seu próprio perfil) faça o tratamento das informações enviadas via $_POST para atualizar o valor da chave armazenada no banco com update_user_meta.

Estrutura e validação da chave

Com o arquivo de funções preparado para receber e manipular as chaves de acesso dos usuários, agora você começará o trabalho de desenvolvimento da classe Server. No functions.php você já fez a chamada dos métodos generate_license e run; agora você deverá criá-los na nova classe do Tema.


<?php

class Server
{
    
    public static function generate_license()
    {
        $code = '';
        $chars = 'abcdefghijklmnopqrstuvxywz0123456789';
        $len = strlen( $chars )-1;
        
        for ( $i=0; $i<32; $i++ ) {
            $code .= $chars[ mt_rand( 0, $len ) ];
        }
        
        return $code;
    }

}

A começar pelo método responsável por gerar a chave de uso do recurso, o exemplo apenas cria uma sequência de letras e números de 32 posições. Dentro desse método sinta-se livre para criar sua própria estrutura de chave; siga por exemplo o modelo de meios de pagamento que usam caracteres em grupos separados por hífens sendo cada grupo referente a uma certa informação ou então criar seu próprio sistema de criptografia baseado no ID do usuário, são muitas as possibilidades.

Depois de definir qual será a técnica para aumentar a segurança de sua chave de acesso, crie também os métodos para validar e verificar a existência da mesma. Para tais funções foram criados respectivamente os métodos key_format e key_exists como demonstrado a seguir:


    private static function key_format( $key )
    {
        return ( strlen( $key ) == 32 );
    }
    
    private static function key_exists( $key )
    {
        global $wpdb;
        return (int) $wpdb->get_var(
            $wpdb->prepare(
                "SELECT user_id FROM {$wpdb->usermeta} " .
                "WHERE meta_key = 'license_key' AND meta_value = %s",
                $key
            )
        );
    }

Novamente, como foi dito logo mais acima, temos nesses dois métodos você encontra apenas um exemplo de como validar o formato da chave enviada e a presença da mesma em banco de dados. Para aprimorar essa validação, agora em especial sobre a presença da chave no banco de dados você pode idealizar um código que possa verificar chave e usuário, ou seja, se a chave pertence ao usuário que a está usando. Outra limitação muito utilizada trata-se de limitar o acesso por domínios; nesse caso você precisa receber além da chave o domínio da origem da requisição para então validar a transferência dos dados.

Rotinas de Servidor

Você definiu anterior que as ações do servidor devem ocorrer a partir de uma URL específica que dispara o método run da classe Server. Dentro desse método é preciso receber as requisições via $_POST do plugin, processá-las e então retornar as devidas respostas.


    public static function run()
    {       
        if ( !empty( $_POST ) ) {
            $values = self::validate( $_POST );
            $data = self::build_action( $values );
        } else {
            $data = array(
                'status'        => 'error',
                'error_code'    => 'invalid-request',
                'error_message' => 'Somente as requisições feitas a partir do plugin serão processadas...'
            );
        }
        
	echo json_encode( $data );
        exit;
    }

Por se tratar de uma URL que não será indexada e não será divulgada abertamente aos clientes finais, entende-se que todo acesso a mesma deve se tratar de requisições do plugin e portanto devem vir acompanhadas das devidas informações.

Note que você fará uso de dois métodos auxiliares, validate e build_action. O primeiro será responsável por receber a requisição $_POST e verificar se as informações requeridas estão presentes e corretas, do modo previsto. O outro por sua vez trabalhará com os dados formatados, pois você já confirmou no método anterior que eles estariam certos, executará a ação solicitada e construirá a resposta a ser retornada ao cliente. Ao final, toda resposta do servidor será dada em formato JSON, sendo ela um erro ou não.

Validação de Licenças

Veja abaixo a estrutura montada para validar as informações recebidas pelo servidor. A principal informação que o tema trata é o valor passado em action. Assim como o utilizar AJAX com o WordPress, você deve sempre definir na requisição do plugin uma ação para o servidor executar. No exemplo está sendo verificado apenas a validação da chave de acesso; quando você incrementar o plugin com outras ações basta incluí-las no método get_valid_actions que possui todas as ações válidas do sistema e em seguida criar as verificações das variáveis em um novo case da instrução switch.

A separação dos processos em métodos permite a você maior organização e legibilidade do código. Perceba como a verificação da chave enviada fica bem mais otimizada com os métodos específicos. Cada condicional determina um erro específico, logo fica mais fácil identificar o problema da requisição quando você estiver trabalhando no lado cliente.


    private static function validate( $r )
    {
        if ( !isset( $r[ 'action' ] ) || !in_array( $r[ 'action' ], self::get_valid_actions() ) )
            $r = array(
                'action' => 'none',
                'status' => 'no-action'
            );
        
        $r[ 'status' ] = 'success';
        switch ( $r[ 'action' ] )
        {
            case 'activate':
                if ( !self::key_format( $r[ 'license' ] ) )
                    $r[ 'status' ] = 'invalid-key!';
                else if ( !self::key_exists( $r[ 'license' ] ) )
                    $r[ 'status' ] = 'key-not-found';
                break;
        }
        return $r;
    }
    
    private static function get_valid_actions()
    {
        return array( 'activate' );
    }

Nesse ponto você já está pronto para retornar a resposta para o cliente, no caso o plugin; e será isso que essa instrução fará a seguir. No entanto abre-se um parênteses aqui pois antes de chamar o método build_action que construirá a resposta ao cliente, você poderá criar um outro método (exemplo do_action) para executar rotinas no servidor. Desse modo você amplia a funcionalidade do recurso de somente consulta para todas operações que desejar, como permitir ao plugin inserir, alterar ou remover informações do servidor de acordo com a action e demais valores passados via requisição HTTP.

Respostas do Gerenciador de Licenças

Com a validação feita, processos executados, agora você fará o retorno das informações ao plugin com build_action. Acima você pôde ver que a resposta da validação ocorre pela chave status do array passado como parâmetro, logo será usado esse valor para identificar se a validação foi executada sem nenhum erro.


    private static function build_action( $r )
    {        
        if ( $r[ 'status' ] == 'success' ) {
            switch ( $r[ 'action' ] )
            {
                case 'activate':
                    $args = $r[ 'license' ];
                    break;
            }
  
            $r = array(
                'status'        => 'success',
                'action'        => $r[ 'action' ],
                'args'          => $args
            );
        } else {
            $r = array(
                'status'        => 'error',
                'error_code'    => $r[ 'status' ],
                'error_message' => self::get_error( $r[ 'status' ] )
            ); 
        }
        
        return $r;
    }
    
    private static function get_error( $status )
    {
        $error_msg = array(
            'invalid-key'       => 'Licença inválida',
            'key-not-found'     => 'Licença não localizada',
            'no-action'         => 'Ação não permitida'
        );
        return ( isset( $error_msg[ $status ] ) ) ? $error_msg[ $status ] : false;
    }

Perceba que foi montado um array específico para cada situação, de erro e acerto. Em caso de acerto, os dados estavam corretos e a ação ocorreu no servidor, é retornado o devido status juntamente com a ação executada e alguns argumentos. Esses argumentos são informações que você está enviando de volta ao plugin. No exemplo a chave de uso é até desnecessária pelo fato do plugin a ter, afinal foi através dele que a chave foi enviada; porém a estrutura foi mantida para que você possa visualizar a técnica usada e futuramente possa transitar com as informações facilmente entre cliente e servidor.

Em caso de erro anteriormente foi passada apenas uma chave com o erro específico. Isso foi feito dessa maneira para centralizar em um único método get_error todas as mensagens de erro que o sistema possa oferecer; assim quando for preciso atualizar alguma mensagem você encontrará sem nenhuma dificuldade.

Com isso você já tem em mãos o recurso em funcionamento! Como foi abordado desde o início a proposta dessa trilha de conhecimento é de te apresentar como criar essa limitação de acesso aos seus plugins e temas. Mas você pode ainda fortalecer essa proteção e também estender ainda mais a funcionalidade do mesmo.

Proteja seus Plugins e Temas

Você certamente já deve ter feito download de temas e plugins prontos para estudo (se não fez, deveria rs). Isso somente é possível porque uma vez que você possui o recurso, você também tem acesso a seu código fonte. Apesar disso ser óbvio é importante frisar pois esse é o ponto que define o tipo de proteção que você deseja oferecer em seu recurso.

Exemplificando, veja que no sistema trabalhado você criou um plugin cuja validação da chave ocorre em um tema onde o detentor do plugin não possui acesso. A validação da chave poderia ocorrer inteiramente dentro do plugin, no entanto bastaria um pouco de conhecimento em PHP e um acesso rápido ao código fonte para remover as condicionais da chave e assim liberar o uso do recurso.

Uma alternativa (a título de curiosidade e abertura de possibilidades para estudo) para resolução desse problema seria os obfuscadores de código onde o código final ficaria criptografado e quando requisitado pelo servidor passaria por uma camada de descriptografia antes de ser executado; mas ainda assim estaria sujeito a ação de terceiros, pois tanto código quanto sistema para descriptografar o mesmo estariam de posse do cliente.

Aplicando uma criptografia em seu código, ele teria o lado negativo de demorar um pouco mais para ser processado por conta da camada adicional que carrega consigo, porém já limitaria o acesso ao código fonte da grande maioria de curiosos, sendo apenas possível de ser descoberto por programadores qualificados para tal função.

Comunicação entre Cliente e Servidor

Utilizando a técnica aqui demonstrada de requisições a um servidor esse problema de liberação e acesso ao código é completamente resolvida, basta que você limite ao cliente apenas os códigos comuns para uso do recurso e que em seu servidor você mantenha as rotinas mais importantes que não deseja compartilhar.

Ainda tem um outro cuidado a ser feito com relação a técnica usada: em toda requisição você deve enviar a chave de uso. Sim, porque se você apenas verifica a chave na ativação da mesma fica muito simples remover a condicional que verifica se a chave está no banco de dados e usar as demais requisições do plugin sem grandes dificuldades.

O ideal seria você inserir uma validação primária da chave também no plugin para que o usuário não crie a opção manualmente em seu banco de dados e no servidor você mantém a verificação completa da mesma. Novamente é possível que alguém descubra o padrão que você usou (letras e números, quantidades de caracteres, etc), porém quanto mais você dificulta, menores são as chances de alguém desvendar seu sistema e ter acesso indevido a ele.

Amplie suas Possibilidades com Callbacks

Para finalizar o recurso uma dica bônus que ajudará e muito a você ocultar as rotinas do plugin (por mais simples que sejam) e ao mesmo tempo ampliar as possibilidades de intereção em ambas as direções (cliente e servidor, servidor e cliente). Trata-se do uso de callbacks.

No método build_action do servidor você retornou ao cliente um valor de retorno, o qual foi chamado de argumentos. Seguindo a mesma linha de raciocínio você pode passar dentro do retorno um valor chamado callback que definirá qual função deve ser executada pelo cliente. Para facilitar a compreensão desse processo veja no exemplo abaixo como ficaria o retorno do servidor para inserir uma opção (tabela wp_options) no cliente:

        private static function build_action( $r )
        {        
            // ...
            $r = array(
                'status'        => 'success',
                'action'        => 'add-option',
                'args'          => 'Exemplo de valor para a opção'
                'callback'      => 'update_option'
            );

Em seguida o cliente processará esse callback dentro do arquivo principal do plugin, no método do_action:

        public static function do_action( $action )
        {   
        // ...
            switch ( $r->action ) {
                case 'add-option':
		call_user_func( $r->callback, 'opt-name', $r->args );
		break;

Olhe como um simples update_option fica bem protegido! Você pode ainda verificar se a função ou método existe antes de chamá-lo a fim de evitar imprevistos com function_exists e method_exists respectivamente.

A partir desse recurso as possibilidades de desenvolvimento são inúmeras e você já está apto a montar qualquer estrutura de controle privativo para seus temas e plugins!!