--- name: full-cliente description: 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: ```bash B64=$(printf '%s' '&1" ``` ### O site está ligado? ```php 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 handleConnection("it@descomplicar.pt"); echo json_encode($result) . "\n"; echo "isConnected: " . (\FC\User::instance()->isConnected() ? "yes" : "no") . "\n"; ' | base64 -w0) # --user= 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= --allow-root --path=$S ``` **Verificar sempre de forma independente**, fora da sessão do eval: `wp user meta get 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`): 1. `plugin-repository//reactivate` — consome a licença, abre ticket; 2. `plugin-repository//license` — busca a chave; 3. `LocalLicenseProcessor::process($slug, $license, $step, $state)` — aplica localmente (multi-step para alguns); 4. `plugin-repository//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=` no `wp eval-file` — `User::instance()` não estava "logged in", `setMeta()` silencia | Sempre passar `--user=` | | 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 |