Cobre: auditoria de ligacao/licencas por site, ligar sites a conta FULL, reactivacao de plugins premium, e diagnostico de licencas fora da FULL (fabricante directo, CodeCanyon). Baseada em leitura directa do codigo do plugin full-customer v4.1.3 durante sessao real de auditoria da frota WP.
9.3 KiB
name, description
| name | description |
|---|---|
| full-cliente | Gestão do plugin FULL.Cliente (broker de licenças WordPress premium, api.full.services) via WP-CLI no servidor CWP. Cobre auditoria de ligação e licenças por site, ligar/desligar sites à conta FULL, reactivação de plugins premium (Elementor Pro, Elements Kit, Rank Math Pro, etc.), e diagnóstico de licenças que NÃO passam pela FULL (fabricante directo, CodeCanyon). Usar quando "full cliente", "full.services", "plugin premium sem licença", "elementor pro expired", "ligar site à full", "reactivar licença", "quantos sites têm licença full", "quem consome esta licença". |
/full-cliente — Broker de licenças WordPress premium
O plugin full-customer (nome visível "FULL.Cliente") está instalado em cada
site WP da frota. Substitui o updater nativo de cada plugin premium que gere
por um Plugin Update Checker apontado ao servidor da FULL, e serve como
corretor de licenças compradas pela Descomplicar® — uma conta partilhada
(it@descomplicar.pt) por toda a frota.
Fonte: código lido directamente em wp-content/plugins/full-customer/
(v4.1.3), servidor server.descomplicar.pt (SSH porta 9443). Namespace REST
local: fc/v1. API externa: https://api.full.services/wp-json/fc-ai.
Regra de ouro: "geridos" ≠ "comprado"
Duas fontes dizem coisas diferentes, e as duas são reais:
| Endpoint | O que mostra | Quando usar |
|---|---|---|
plugin-repository/updates |
Plugins com o updater ACTUALMENTE substituído pela FULL neste site | "Este plugin está a receber updates pela FULL agora?" |
account/licenses-extract |
Ficha real de licenças da conta: comprado/disponível/domínios em uso, por produto | "Esta licença é da FULL?" — fonte de verdade |
Nunca concluir "não é FULL" só pela ausência em updates. Um produto pode
estar comprado e a consumir licença em vários domínios (licenses-extract) e
mesmo assim estar fora do catálogo activo (plugin-repository/all) — o mesmo
padrão do elementor-pro expired/"retirado dos geridos". Verificar sempre
as duas antes de classificar algo como "fora da FULL". (Erro cometido e
corrigido em sessão real: "Elements Kit" tinha 13 licenças compradas, 2
disponíveis, mas estava fora do catálogo activo — parecia "sem FULL" olhando
só para updates.)
Consultas (leitura livre, sem GATE) — sempre via wp eval-file seguro
Aspas aninhadas partem o PHP através de ssh. Usar sempre base64 no stdin:
B64=$(printf '%s' '<?php
// codigo PHP aqui, so aspas simples PHP normais
' | base64 -w0)
timeout 200 ssh -p 9443 -o BatchMode=yes root@server.descomplicar.pt \
"P=/opt/alt/php-fpm82/usr/bin/php; W=/usr/local/bin/wp
echo $B64 | base64 -d | \$P -d memory_limit=512M -d error_reporting=0 \$W eval-file - --allow-root --path=/home/USER/SITE 2>&1"
O site está ligado?
<?php
global $wpdb;
$r = $wpdb->get_row("SELECT user_id, meta_value FROM {$wpdb->usermeta} WHERE meta_key='fc/connection-email' AND meta_value!='' LIMIT 1", ARRAY_A);
echo $r ? "ligado: {$r['meta_value']} (user {$r['user_id']})" : "NAO LIGADO";
Atalho sem PHP: wp option get _full_customer-connection_email --allow-root --path=$S
— só devolve valor se o site ainda não migrou (ver secção "Porque é que um
site pode não estar ligado").
Extracto de licenças da conta (fonte de verdade)
<?php
$x = fcDashboardAPI("GET", "account/licenses-extract");
$items = isset($x["data"]["name"]) ? [$x["data"]] : ($x["data"] ?? []);
foreach ($items as $v) {
echo $v["name"] . " | comprado=" . $v["purchased"] . " | disponivel=" . $v["available"]
. " | dominios=" . implode(", ", $v["domainsInUse"] ?? []) . "\n";
}
Requer o site já estar ligado (usa o token do site). Corre em qualquer site ligado, não só na conta de referência.
Para contar sites únicos com licença activa: agregar domainsInUse de todos
os produtos, ignorar entradas "Site excluído", desduplicar por domínio.
Catálogo activo vs geridos neste site
<?php
$catalogo = fcDashboardAPI("GET", "plugin-repository/all")["data"] ?? [];
$geridos = fcDashboardAPI("GET", "plugin-repository/updates")["data"] ?? [];
echo "catalogo activo: " . count($catalogo) . " | geridos neste site: " . count($geridos) . "\n";
Ligar um site à conta FULL (grátis — não consome licença)
Mecanismo real (Actions/ConnectAccount.php::handleConnection): regista o par
(email, site) no servidor FULL via account/connect; só se aceite grava
fc/connection-email no usermeta do utilizador WP que executa a acção.
Pré-requisitos:
- GATE 5.1 — mutação em produção. Pedir autorização
(
~/.claude/hooks/infra-auth.sh server, válida 60 min) antes de escrever. - Um utilizador admin local (
manage_options). Preferir oit@descomplicar.ptlocal se existir (fica auditável).
B64=$(printf '%s' '<?php
$ca = new \FC\Actions\ConnectAccount();
$result = $ca->handleConnection("it@descomplicar.pt");
echo json_encode($result) . "\n";
echo "isConnected: " . (\FC\User::instance()->isConnected() ? "yes" : "no") . "\n";
' | base64 -w0)
# --user=<ID do admin> e OBRIGATORIO: sem ele User::instance() nao esta
# "logged in" e o setMeta() silencia sem gravar nada (ver Erros Comuns).
echo $B64 | base64 -d | php -d memory_limit=512M wp eval-file - --user=<ID> --allow-root --path=$S
Verificar sempre de forma independente, fora da sessão do eval:
wp user meta get <ID> fc/connection-email --allow-root --path=$S. Não
confiar só no success:true da resposta.
Porque é que um site "v4.1.3" pode não estar ligado
Services/Connection.php::autoConnection() corre em cada admin_init e tenta
migrar sozinho: lê o option legado _full_customer-connection_email; se tiver
valor, liga e apaga o legado (connectionMode = 'upgrade'). Se esse option
já estava vazio antes da actualização do plugin, nunca houve nada para
migrar — o site nunca esteve ligado, nem na geração antiga. Confirmar com
wp option get _full_customer-connection_email (vazio = nunca ligado).
Reactivar a licença de um plugin (CUSTA — consome licença e abre ticket)
Nunca fazer sem autorização explícita. Cada reactivate consome uma
licença disponível e abre um ticket Zendesk do lado da FULL, que fica em fila
— não é instantâneo.
Fluxo (Actions/PluginReactivate.php):
plugin-repository/<slug>/reactivate— consome a licença, abre ticket;plugin-repository/<slug>/license— busca a chave;LocalLicenseProcessor::process($slug, $license, $step, $state)— aplica localmente (multi-step para alguns);plugin-repository/<slug>/license/confirm+do_action('fc/updates/invalidate').
Processador por produto (Services/LocalLicenseProcessor.php) — nem todos
são one-shot:
| Produto (slug) | Fluxo |
|---|---|
elementorPro |
OAuth multi-step com a Elementor: fetch_consent → submit_consent → finalize, cookies de sessão entre passos. Não dá por WP-CLI puro. |
essentialAddons |
OTP (check_otp) |
astra, rankMath, seoPress, perfmatters, acfPRO, wpRocket, jetThemeCore, ultimateAddons |
Uma chamada, sem steps |
Não existe forma de libertar uma licença de um domínio
Procurado exaustivamente no código (grep -rniE "release|remove.?domain|deactivate.?domain|revoke|free.?license")
— zero mecanismo. Actions/DisconnectAccount.php só desliga a ligação
LOCAL (setConnectionEmail('')); o próprio texto confirma: "suas ativações e
licenças seguem funcionando normalmente". As únicas acções por produto
(PluginActivationManager.php) são "Reativar", "Renovar" e "Solicitar ajuda"
— e "ajuda" é acção de frontend, sem rota REST própria. Para libertar um
domínio (ex.: site descomissionado que ainda consome uma licença comprada), a
única via é abrir um pedido de suporte à FULL — não há atalho técnico.
Licenças que NÃO são da FULL (não confundir)
| Fonte | Como identificar | Exemplo real |
|---|---|---|
| Fabricante directo | Option própria com license_key, formato do produto (não FULL) |
elementskit_options.license_key (WPMET), _ff_fluentform_pro_license_key (WPManageNinja) |
| CodeCanyon (compra única) | Zero option de licença, zero envato/purchase_code no código — ficheiro entregue sem phone-home, por desenho |
kivicare-pro |
Confirmar sempre as duas possibilidades (FULL via licenses-extract E licença
própria do fabricante) antes de classificar um plugin como "sem licença" — um
produto pode estar em ambas, nenhuma, ou só numa.
Erros comuns
| Sintoma | Causa | Solução |
|---|---|---|
| "X não é FULL" (conclusão errada) | Só verificado em plugin-repository/updates, faltou account/licenses-extract |
Verificar sempre as duas fontes (ver "Regra de ouro") |
ConnectAccount devolve success:true mas usermeta continua vazio |
Faltou --user=<ID> no wp eval-file — User::instance() não estava "logged in", setMeta() silencia |
Sempre passar --user=<ID admin> |
Erro de sintaxe PHP no eval-file |
Aspas duplas aninhadas via ssh | Base64 no stdin (ver topo desta skill) |
| Domínio "morto" que na verdade está vivo | DNS aponta para o servidor mas é conta partilhada com outros sites | Confirmar siteurl real (wp option get siteurl) antes de declarar morto — nunca só presença/ausência de pasta |