Fundamentado em leitura directa do WooCommerce 11.0.1 real instalado em ecommerce.descomplicar.pt (nao no EMCP Tools Pro, cujo codigo Woo Integration esta ausente do build Free). Cobre REST API wc/v3 + classes CRUD nativas, capabilities reais por dominio, e tabelas de ability proposta para replica. Exclui SEO/Yoast/RankMath por pedido. Actualiza INDEX.md com a 12a entrada e totais revistos.
48 KiB
11 — WooCommerce: blueprint de abilities MCP (não é auditoria EMCP)
Fonte: leitura directa do código-fonte real do WooCommerce 11.0.1, instalado em
ecommerce.descomplicar.pt (/home/ealmeida/ecommerce.descomplicar.pt/wp-content/plugins/woocommerce/),
19-08-2026. Cruzado com docs/00-ARQUITECTURA.md (padrão emcp_tools_register_ability() /
normalize_result()) e docs/10-MODULOS-E-INVENTARIO-PRO.md (confirmação de que
EMCP_Tools_Woo_Integration está gated por class_exists(...) && EMCP_Tools_Woo_Integration::woo_active(),
sem module toggle dedicado — linha 470 desse documento). Exclui por completo SEO/Yoast/Rank
Math e licenciamento Freemius, que estão fora de âmbito.
0. Porque este documento é diferente dos outros 10
Os docs 01-10 desta série leem o código-fonte do plugin emcp-tools (a integração MCP em si) e
descrevem o que ele faz. Este documento não pode fazer isso para WooCommerce: a classe
EMCP_Tools_Woo_Integration é Pro-only e o seu ficheiro está fisicamente ausente da árvore Free
instalada (confirmado por tentativa de leitura nas sessões anteriores desta batch — só o
class_exists() gate e o comentário associado em class-ability-registrar.php são visíveis, ver
citação acima). Não há nada para auditar.
Em vez disso, este documento faz a pergunta inversa: dado o que o WooCommerce em si expõe —
REST API wc/v3 (mais wc/v4 emergente e wc-analytics para Store Analytics) e as classes CRUD
PHP nativas (WC_Product, WC_Order, WC_Coupon, WC_Customer) — que grupo de abilities MCP
faria sentido construir do zero, seguindo os padrões arquitecturais já documentados nesta série
(dispatcher de dois braços <domínio>-read/<domínio>-write, change ledger, normalize_result)?
Todas as tabelas de "ability proposta" abaixo são propostas próprias, fundamentadas em código real
lido nesta tarefa — nunca uma transcrição do que o EMCP Pro possa ou não fazer.
1. Panorama
O WooCommerce expõe duas superfícies programáticas distintas, nenhuma das quais é MCP:
- REST API
wc/v3— a API pública estável, versionada, autenticada por OAuth1/Basic Auth (consumer key/secret) ou cookie+nonce em contexto admin. Routing centralizado emincludes/rest-api/Server.php, classeAutomattic\WooCommerce\RestApi\Server(singleton, hookrest_api_init). Confirmado por leitura integral do ficheiro: quatro namespaces legado (wc/v1,wc/v2,wc/v3) mais umwc-telemetry, cada um com o seu próprio maparest_base => Controller_Class. A v3 estende sempre a v2, que estende sempre a v1 — cadeia de herança real (WC_REST_Products_Controller extends WC_REST_Products_V2_Controller, e assim por diante), não reimplementação por versão. wc/v4(namespace novo,Server.phplinhas ~78-82) — feature-gated porAutomattic\WooCommerce\Admin\Features\Features::is_enabled('rest-api-v4'), ainda em construção: só 12 recursos migrados (fulfillments,products,customers,order-notes,shipping-zones,orders,refunds,offline-payment-methods, mais 6 controllers desettings-*), implementados como classes PHP com namespace próprio (Automattic\WooCommerce\Internal\RestApi\Routes\V4\...), não a famíliaWC_REST_*_Controllerclássica. Não é a superfície recomendada para uma réplica hoje — instável e parcial; este documento fundamenta-se na v3, estável e completa.wc-analytics(Store Analytics) — namespace carregado só quando pedido (RestApiUtil::lazy_load_namespace('wc-analytics', ...),src/Admin/API/Init.php), com um subconjunto que reexporta os controllerswc/v3sob outro namespace (Orders extends \WC_REST_Orders_Controller) mais um conjunto próprio de controllers de relatório agregado (Automattic\WooCommerce\Admin\API\Reports\*). Detalhado na secção 6.- Classes CRUD PHP nativas —
WC_Product(+WC_Product_Variable/_Simple/etc.),WC_Order(+WC_Order_Item_Product/_Fee/_Shipping/_Coupon),WC_Coupon,WC_Customer. Todas herdam deWC_Data(padrãoget_prop()/set_prop()/save()) com um data store injectável —WC_Product_Data_Store_CPT, etc. — que faz a persistência real emwp_posts/wp_postmeta(produtos/encomendas legado) ou nas tabelas HPOS próprias quando ocustom_orders_tableestá activo (OrderUtil::custom_orders_table_usage_is_enabled(), referenciado emWC_REST_Orders_Controller::prepare_objects_query()).
A relação entre as duas superfícies é directa, não paralela: todos os controllers REST wc/v3
de CRUD (WC_REST_CRUD_Controller, base abstracta em
includes/rest-api/Controllers/Version2/class-wc-rest-crud-controller.php) implementam
save_object() construindo um objecto WC_Product/WC_Order/WC_Coupon via
prepare_object_for_database() e chamando ->save() — o REST é uma camada fina de
serialização/validação/permissões por cima das mesmas classes CRUD. Uma réplica MCP pode (e
deve) chamar as classes CRUD directamente (wc_get_product(), wc_get_products(),
wc_get_orders(), new WC_Coupon(), etc.) em vez de fazer loopback HTTP à REST API — evita
autenticação redundante, é mais rápido, e dá acesso a WP_Error/WC_Data_Exception nativos em
vez de ter de reparsear respostas JSON.
Nota de normalize_result (já documentada em docs/00-ARQUITECTURA.md §5): se uma réplica
optar por reutilizar WP_REST_Response::get_data() verbatim de rotas wc/v3 em vez de chamar as
classes CRUD directamente, várias rotas devolvem array de topo em vez de objecto — confirmado
nesta tarefa em WC_REST_CRUD_Controller::get_items() (devolve $objects, um array indexado, não
um mapa), e nos controllers de relatório (reports-products-totals, etc., citados no doc00). Sem
um wrapper {data: [...]}, clientes MCP estritos (Gemini/OpenAI strict function calling)
rejeitam a resposta.
2. Produtos
Controller REST: WC_REST_Products_Controller
(includes/rest-api/Controllers/Version3/class-wc-rest-products-controller.php, namespace = 'wc/v3', rest_base = 'products'), estende WC_REST_Products_V2_Controller. Classe CRUD
nativa: WC_Product (+ subclasses por tipo) e o data store
WC_Product_Data_Store_CPT
(includes/data-stores/class-wc-product-data-store-cpt.php). Capability WordPress:
capability_type = 'product', map_meta_cap = true (includes/class-wc-post-types.php:402) —
gera edit_product/edit_products/publish_products/read_private_products/delete_product
etc.; atribuídas a administrator e shop_manager no install (WC_Install::create_roles(),
includes/class-wc-install.php:2289-2340).
2.1 Schema real (campos confirmados por leitura de get_item_schema())
type (enum de wc_get_product_types(): simple, grouped, external, variable, mais os que
plugins registarem), status (publish/draft/pending/private/future/auto-draft/trash),
featured, catalog_visibility (visible/catalog/search/hidden), sku,
global_unique_id (GTIN/UPC/EAN/ISBN), regular_price/sale_price/date_on_sale_from(_gmt)/
date_on_sale_to(_gmt) (price e on_sale e price_html são readonly, calculados),
virtual/downloadable (+ downloads[]/download_limit/download_expiry),
external_url/button_text (só produtos external), tax_status/tax_class,
manage_stock/stock_quantity/stock_status/backorders/low_stock_amount/
sold_individually, weight/dimensions{length,width,height}, shipping_class,
upsell_ids[]/cross_sell_ids[], categories[]/tags[] (id ou {name} para criar on-the-fly),
images[] (featured + galeria, aceita URL externo — faz sideload via wc_rest_upload_image_from_url()),
attributes[]/default_attributes[] (só produtos variable), grouped_products[] (só grouped),
meta_data[]. Internal meta keys confirmados no data store (não expostos individualmente, mas
confirma o que manage_stock/etc. persistem):
_sku, _global_unique_id, _price, _regular_price, _sale_price,
_sale_price_dates_from(to), total_sales, _tax_status, _tax_class, _manage_stock,
_stock, _stock_status, _backorders, _low_stock_amount, _sold_individually, _weight,
_length/_width/_height, _upsell_ids, _crosssell_ids, _purchase_note,
_default_attributes, _product_attributes, _virtual, _downloadable
(class-wc-product-data-store-cpt.php, $internal_meta_keys).
2.2 Endpoints extra confirmados (além do CRUD standard)
POST /products/(id)/duplicate—duplicate_product()(class-wc-rest-products-controller.php,register_routes()): clona um produto viaWC_Admin_Duplicate_Product::product_duplicate(), status forçado adraft, nome com sufixo(copy).POST /products/batch— endpoint de lote (create/update/delete em array), definido na baseWC_REST_Products_V2_Controller::register_routes()(linhas ~216-227 do ficheiro V2); a v3 sobrepõebatch_items()só para diferir a contagem de termos (wp_defer_term_counting()) durante o lote inteiro, por performance quando muitos produtos partilham categorias/tags.GET /products/suggested-products— sugestões cacheadas (with_cache()wrapper), fora do âmbito de uma réplica inicial (é uma feature de UX do editor de produto, não de dados).POST /products/(id)/variations/generate— gera em massa todas as combinações de variações a partir dos atributos do produto pai, comdefault_valuesopcional edelete: boolpara apagar variações órfãs (class-wc-rest-product-variations-controller.php::register_routes()).
2.3 Tabela de ability proposta — Produtos
| Área | O que cobre | Endpoints/classes reais | Nível de risco/destructive |
|---|---|---|---|
woo-products-read |
Lista/procura produtos (nome, SKU parcial/exacto, search_name_or_sku), filtra por tipo/categoria/tag/classe-de-envio/atributo+termo/stock_status/on_sale/featured/gama de preço; devolve um produto/variação completo por ID |
WC_REST_Products_Controller::prepare_objects_query()/get_object() (wc/v3/products); wc_get_product()/wc_get_products() como alternativa nativa |
readonly, idempotente |
woo-products-write |
Cria/actualiza um produto: preço, stock (manage_stock/stock_quantity/backorders/low_stock_amount), estado, visibilidade, imagens (upload por URL), categorias/tags (cria on-the-fly), atributos + default_attributes (variáveis), upsell/cross-sell, downloads, campos COGS (Cost of Goods Sold, se activo) |
WC_REST_Products_Controller::prepare_object_for_database(); WC_Product::save() |
write, não-idempotente; nunca apaga dados existentes por omissão (merge de arrays como meta_data via MetaDataUtil::update()) |
woo-product-duplicate |
Clona um produto completo para rascunho ((copy)) |
duplicate_product() → WC_Admin_Duplicate_Product::product_duplicate() |
write, não-destrutivo (cria novo, não altera o original) |
woo-products-delete |
Envia para o lixo ou apaga definitivamente um produto (e cascata para as suas variações) | WC_REST_CRUD_Controller::delete_item() — trash se EMPTY_TRASH_DAYS > 0 e não force, senão apagar definitivo |
destructive quando force=true (irreversível); trash é recuperável |
woo-product-variations |
CRUD de uma variação individual + geração em massa de variações a partir de combinações de atributos, com opção de apagar variações não usadas | WC_REST_Product_Variations_Controller (wc/v3/products/<id>/variations[/generate]) |
write; generate com delete:true é destrutivo (apaga variações existentes não geradas) |
woo-product-taxonomy |
Lista/cria/edita/apaga categorias, tags, atributos globais, termos de atributo, e classes de envio | WC_REST_Product_Categories_Controller, ...Tags..., ...Attributes..., ...Attribute_Terms..., ...Shipping_Classes_Controller (todos wc/v3); capability manage_product_terms/edit_product_terms/delete_product_terms (wc_rest_check_manager_permissions('attributes',...) + wc_rest_check_product_term_permissions(), includes/wc-rest-functions.php:341 e :298) |
write nos termos; delete de um termo usado por produtos não apaga os produtos, só a associação |
woo-products-batch |
Cria/actualiza/apaga vários produtos numa só chamada (limite prático imposto pelo consumidor, WooCommerce não limita explicitamente) | Rota /products/batch, batch_items() (class-wc-rest-products-v2-controller.php) |
write multi-objecto; falha parcial por item — cada linha do resultado precisa de ser inspeccionada individualmente |
3. Encomendas
Controller REST: WC_REST_Orders_Controller
(includes/rest-api/Controllers/Version3/class-wc-rest-orders-controller.php, rest_base = 'orders'), estende WC_REST_Orders_V2_Controller. Classe CRUD nativa: WC_Order +
WC_Order_Item_Product/_Fee/_Shipping/_Coupon. Capability: capability_type = 'shop_order', map_meta_cap = true (includes/class-wc-post-types.php:462).
3.1 Estados reais (não inventados — via wc_get_order_statuses())
Confirmado em includes/wc-order-functions.php:104-118, mapa OrderInternalStatus::* filtrável
por wc_order_statuses:
Status (sem prefixo wc-) |
Label | Nota |
|---|---|---|
pending |
Pending payment | estado inicial antes de pagamento confirmado |
processing |
Processing | pagamento recebido, aguarda fulfilment — conta como "pago" (wc_get_is_paid_statuses()) |
on-hold |
On hold | pagamento offline pendente de confirmação manual |
completed |
Completed | fulfilment concluído — conta como "pago" |
cancelled |
Cancelled | |
refunded |
Refunded | |
failed |
Failed |
Além destes 7, WC_REST_Orders_Controller::get_collection_params() aceita também any e o
interno trash (Automattic\WooCommerce\Enums\OrderStatus::TRASH) para filtragem de listagem;
e existe ainda um estado interno checkout-draft (usado por save_object() como fallback quando
um WC_Data_Exception ocorre a meio da criação — não é um estado que se defina intencionalmente
via API, é um mecanismo de recuperação de falha de checkout).
3.2 Itens da encomenda e ciclo de escrita
A prop line_items/shipping_lines/fee_lines/coupon_lines cada uma aceita um array de linhas;
prepare_object_for_database() (class-wc-rest-orders-controller.php) itera cada item: se
item_is_null() ou quantity===0, remove-o (remove_item(), que valida que o id pertence
realmente à encomenda antes de o deixar remover — woocommerce_rest_invalid_item_id senão); caso
contrário set_item() (cria ou actualiza). Cupões são tratados à parte por
calculate_coupons() — valida cada código via WC_Discounts::is_coupon_valid(), remove todos
os cupões actuais e reaplica a lista completa recebida (não é um merge incremental — enviar
coupon_lines sem um código existente remove-o da encomenda). set_paid: true no request
chama payment_complete() se a encomenda ainda precisar de pagamento. manual_update: true marca
a nota de mudança de estado como "added by user" em vez de automática.
3.3 Devoluções (refunds)
Controller: WC_REST_Order_Refunds_Controller
(includes/rest-api/Controllers/Version3/class-wc-rest-order-refunds-controller.php,
wc/v3/orders/<order_id>/refunds). Criar um refund chama a função nativa wc_create_refund()
(includes/wc-order-functions.php:559) com order_id, amount, reason, line_items
(devolução parcial por item), refund_payment (tenta reembolso automático via gateway, se
suportado) e restock_items (repõe stock dos itens devolvidos). amount < 0 é rejeitado
explicitamente (woocommerce_rest_invalid_order_refund).
3.4 Notas da encomenda
Confirmado apenas por registo no router (Server.php, 'order-notes' => 'WC_REST_Order_Notes_Controller'
em wc/v1/wc/v2/wc/v3) — ficheiro não lido linha a linha nesta tarefa; é o mecanismo padrão
de comentários internos/ao cliente numa encomenda (WC_Order::add_order_note()).
3.5 Tabela de ability proposta — Encomendas
| Área | O que cobre | Endpoints/classes reais | Nível de risco/destructive |
|---|---|---|---|
woo-orders-read |
Lista/procura encomendas por estado (um ou vários, incl. any), por created_via, por número parcial (via wc-analytics/orders, ver §6), devolve uma encomenda completa (billing/shipping, itens, totais, meta) |
WC_REST_Orders_Controller::prepare_objects_query()/get_object() (wc/v3/orders); wc_get_orders() nativo |
readonly |
woo-orders-write |
Cria/edita encomenda: billing/shipping, line_items/shipping_lines/fee_lines (add/update/remove por linha), customer_id, set_paid, meta, dados COGS (se activo) |
prepare_object_for_database()/save_object() (WC_Order::save(), calculate_totals()) |
write, não-idempotente; remoção de linha por quantity:0 é irreversível sem novo pedido |
woo-order-status |
Muda o estado de uma encomenda (com opção manual_update para a nota registar como acção humana) |
WC_Order::set_status(), disparado de save_object() quando request['status'] presente |
write; transições como cancelled/refunded podem disparar hooks de terceiros (emails, restock) fora do controlo directo da ability |
woo-order-coupons |
Aplica/substitui a lista completa de cupões de uma encomenda (valida cada código antes de aplicar) | calculate_coupons(); WC_Discounts::is_coupon_valid(), WC_Order::apply_coupon()/remove_coupon() |
write; enviar sem um cupão existente remove-o — comportamento não-óbvio a documentar explicitamente no schema da ability |
woo-order-refunds |
Cria uma devolução total/parcial (por linha), com reembolso automático via gateway e reposição de stock opcionais | WC_REST_Order_Refunds_Controller::prepare_object_for_database() → wc_create_refund() (wc-order-functions.php:559) |
destructive-adjacente: pode mover dinheiro real através do gateway de pagamento se refund_payment:true |
woo-orders-delete |
Envia para o lixo ou apaga definitivamente uma encomenda | WC_REST_CRUD_Controller::delete_item() (mesma lógica trash/force que produtos) |
destructive quando force=true |
woo-order-notes |
Lê/adiciona notas internas ou ao cliente numa encomenda | WC_REST_Order_Notes_Controller (wc/v3/orders/<id>/notes) — registo confirmado em Server.php, corpo não lido |
write (notas ao cliente disparam email) |
woo-orders-batch |
Cria/actualiza/apaga várias encomendas numa só chamada | Herdado de WC_REST_CRUD_Controller/WC_REST_Posts_Controller (mesmo padrão /batch dos produtos) |
write multi-objecto |
4. Clientes
Controller REST: WC_REST_Customers_Controller
(includes/rest-api/Controllers/Version3/class-wc-rest-customers-controller.php), estende
WC_REST_Customers_V2_Controller. Diferença estrutural face a Produtos/Encomendas: um
"cliente" WooCommerce NÃO é um post type — é um utilizador WordPress com role customer.
Confirmado: a gate de permissões usa wc_rest_check_user_permissions()
(includes/wc-rest-functions.php:262), não wc_rest_check_post_permissions(). Classe CRUD
nativa: WC_Customer (includes/class-wc-customer.php), também WC_Data-based, mas persiste
em wp_users/wp_usermeta, não wp_posts.
4.1 Capabilities reais (não manage_woocommerce genérico)
wc_rest_check_user_permissions() mapeia: read→list_users, create→create_customers,
edit→edit_users, delete→delete_users. Nuance de segurança confirmada no código
(includes/wc-rest-functions.php:277-291): quando o utilizador actual tem a role shop_manager e
a acção é edit/delete, a gate é mais estrita que a capability WordPress crua — só é
permitida se o alvo tiver a role customer (filtrável via woocommerce_shop_manager_editable_roles)
ou for o próprio utilizador. Ou seja: um shop_manager não pode, por esta via, editar/apagar outro
administrator ou shop_manager, mesmo tendo edit_users/delete_users a nível WordPress. É
também multisite-aware desde a 9.4.0: Users::get_user_in_current_site() revoga a permissão se
o alvo não pertencer ao site actual.
4.2 Schema real (confirmado por get_item_schema())
email, first_name/last_name, role (readonly), username, password (write-only, contexto
edit), billing{first_name,last_name,company,address_1,address_2,city,state,postcode,country, email,phone}, shipping{...mesmos campos, sem email}, is_paying_customer (readonly),
avatar_url (readonly), meta_data[]. Sem histórico de encomendas embutido no schema base —
isso só existe agregado na variante Analytics (§6.4).
4.3 Tabela de ability proposta — Clientes
| Área | O que cobre | Endpoints/classes reais | Nível de risco/destructive |
|---|---|---|---|
woo-customers-read |
Lista/procura clientes (utilizadores role customer), devolve perfil completo (contactos, billing/shipping, is_paying_customer, meta) |
WC_REST_Customers_Controller (wc/v3/customers); new WC_Customer($user_id) nativo |
readonly |
woo-customers-write |
Cria/actualiza cliente: nome, email, username, password, endereços billing/shipping, meta | WC_REST_Customers_V2_Controller::prepare_object_for_database()/save_object(); WC_Customer::save() |
write; criação de utilizador WordPress real (não só dados WooCommerce) |
woo-customer-addresses |
Actualiza só billing ou só shipping de um cliente, sem tocar no resto do perfil | Subconjunto do schema billing/shipping acima — proposta de ability mais granular para evitar sobrescrever password/username sem intenção |
write |
woo-customers-delete |
Apaga um utilizador cliente | WC_REST_CRUD_Controller-equivalente para users (herdado de WP_REST_Users_Controller na prática do WordPress core, não uma classe própria WooCommerce) |
destructive, irreversível — apagar um utilizador WordPress apaga a conta de login, não só o "perfil de cliente" |
woo-customer-history |
Agrega orders_count, total_spend, avg_order_value, date_registered, date_last_active, date_last_order para um cliente |
Não é dado pela WC_Customer base — vem da tabela de analytics wp_wc_customer_lookup, populada por CustomersDataStore::update_registered_customer() (src/Admin/API/Reports/Customers/DataStore.php, colunas confirmadas linhas 50-93); requer feature analytics activa (§6) |
readonly; depende de wc-analytics estar carregado — não confundir com o schema base do cliente |
5. Cupões / Descontos
Controller REST: WC_REST_Coupons_Controller
(includes/rest-api/Controllers/Version3/class-wc-rest-coupons-controller.php), estende
WC_REST_Coupons_V2_Controller. Classe CRUD nativa: WC_Coupon
(includes/class-wc-coupon.php). Capability: capability_type = 'shop_coupon'
(includes/class-wc-post-types.php:525).
5.1 Tipos de desconto reais (via wc_get_coupon_types())
Confirmado em includes/wc-coupon-functions.php:21-31, filtrável por
woocommerce_coupon_discount_types:
discount_type |
Label | Comportamento (WC_Coupon::get_discount_amount()) |
|---|---|---|
percent |
Percentage discount | percentagem do valor do item/carrinho |
fixed_cart |
Fixed cart discount | valor fixo dividido proporcionalmente entre linhas por peso de subtotal (com/sem imposto conforme wc_prices_include_tax()) — o default ($this->data['discount_type'] = 'fixed_cart') |
fixed_product |
Fixed product discount | valor fixo por unidade de produto elegível, min(amount, preço) |
percent_product é aceite por retrocompatibilidade e silenciosamente convertido para percent
(set_discount_type_core(), includes/class-wc-coupon.php:600-608). Se discount_type==='percent'
e amount > 100, o WC_Coupon recusa a gravação (coupon_invalid_amount).
5.2 Campos de restrição confirmados (schema/data props do coupon)
Além de code/amount/discount_type/description, o WC_Coupon (visto nos props por omissão
e no método is_coupon_valid() referenciado por WC_REST_Orders_Controller::calculate_coupons())
gere: date_expires, usage_count/usage_limit/usage_limit_per_user/limit_usage_to_x_items,
individual_use, free_shipping, exclude_sale_items, restrições por produto/categoria
(include/exclude), por valor mínimo/máximo de carrinho, e por email de cliente
(customer_email/email_restrictions).
5.3 Tabela de ability proposta — Cupões
| Área | O que cobre | Endpoints/classes reais | Nível de risco/destructive |
|---|---|---|---|
woo-coupons-read |
Lista/procura cupões por código exacto (wc_get_coupon_id_by_code(), usado em prepare_objects_query()) ou estado; devolve um cupão completo (tipo de desconto, valor, restrições, uso) |
WC_REST_Coupons_Controller::prepare_objects_query() (wc/v3/coupons); new WC_Coupon($code) nativo |
readonly |
woo-coupons-write |
Cria/actualiza cupão: código, discount_type (3 valores válidos), amount, datas de expiração, free_shipping, individual_use, limites de uso, restrições produto/categoria/valor-mínimo/email |
WC_REST_Coupons_V2_Controller::prepare_object_for_database(); WC_Coupon::save() |
write; amount>100 com discount_type:percent é recusado pelo próprio WC_Coupon (validação de domínio, não HTTP) |
woo-coupons-delete |
Envia para o lixo ou apaga definitivamente um cupão | WC_REST_CRUD_Controller::delete_item() |
destructive quando force=true; cupão apagado deixa de validar em encomendas futuras mas não afecta descontos já aplicados a encomendas passadas |
woo-coupon-validate |
Valida um código de cupão contra uma encomenda/carrinho concreto (elegibilidade, expirado, limite atingido) sem o aplicar | WC_Discounts::is_coupon_valid(), usado internamente por calculate_coupons() — proposta de ability read-only exposta directamente, útil para um agente confirmar "este cupão ainda serve?" antes de o aplicar numa encomenda |
readonly |
6. Relatórios / Analytics (wc-analytics/*)
O namespace existe e é substancial — confirmado por leitura de
src/Admin/API/Init.php::rest_api_init_wc_analytics(). Carregado por
RestApiUtil::lazy_load_namespace('wc-analytics', ...) (só quando um pedido bate nesse namespace,
feature de performance — includes/wc-rest-functions.php::wc_rest_should_load_namespace()
confirma wc-analytics na lista de namespaces conhecidos que podem ficar por carregar).
6.1 Dois grupos de controllers
- Sempre registados (não dependem da feature
analytics):Notes,NoteActions,Coupons,Data/DataCountries/DataDownloadIPs,Orders,Products,ProductAttributes,ProductAttributeTerms,ProductCategories,ProductVariations,ProductReviews,ProductsLowInStock,SettingOptions,Taxes. Vários destes estendem directamente os controllerswc/v3— confirmado por leitura completa desrc/Admin/API/Orders.php:class Orders extends \WC_REST_Orders_Controller(namespace reescrito parawc-analytics), adicionando só um parâmetronumber(procura por número de encomenda parcial, via SQL directo à tabela HPOS ou meta legado) — herda a mesma gate de capabilityshop_orderdo §3, não uma nova. - Só quando
Features::is_enabled('analytics'):Customers,Leaderboards,Reports\Controller(catálogo de descoberta dos sub-relatórios) e umReports\*\Controllerpor domínio: Products, Variations, Revenue (/stats), Orders (/stats), Categories, Taxes, Coupons, Stock, Downloads, Customers, maisImport/Export,AnalyticsImports(estado de importações falhadas), ePerformanceIndicators(registado por último, agrega indicadores de todos os/statsjá registados).
6.2 Capability real para relatórios (não manage_woocommerce)
Confirmado em includes/rest-api/Controllers/Version1/class-wc-rest-reports-v1-controller.php:60-63
(base herdada por toda a família de reports, incl. o GenericController de que
Reports\Controller deriva): get_items_permissions_check() chama
wc_rest_check_manager_permissions('reports', 'read') → capability
view_woocommerce_reports (includes/wc-rest-functions.php:343), distinta de
manage_woocommerce. Um utilizador pode ter acesso de leitura a relatórios sem ter acesso de
gestão às definições da loja — desenho de menor privilégio já presente no core.
6.3 Data stores de agregação confirmados
src/Admin/API/Init.php::add_data_stores() regista, via o filtro woocommerce_data_stores:
report-revenue-stats, report-orders, report-orders-stats, report-products,
report-variations, report-products-stats, report-variations-stats, report-categories,
report-taxes, report-taxes-stats, report-coupons, report-coupons-stats,
report-downloads, report-downloads-stats, report-customers, report-customers-stats,
report-stock-stats. Cada um persiste em tabelas próprias sob wp_wc_order_stats,
wp_wc_customer_lookup, etc. (não wp_posts/wp_postmeta) — populadas por processamento
assíncrono (ReportsSync.php, src/Internal/Admin/), não em tempo real na escrita.
6.4 Tabela de ability proposta — Analytics
| Área | O que cobre | Endpoints/classes reais | Nível de risco/destructive |
|---|---|---|---|
woo-analytics-orders |
Lista encomendas com filtro por número parcial, além dos filtros standard de wc/v3/orders |
Automattic\WooCommerce\Admin\API\Orders extends \WC_REST_Orders_Controller |
readonly; mesma capability shop_order do §3, não view_woocommerce_reports |
woo-analytics-revenue-stats |
Séries temporais de receita, agregadas por intervalo (dia/semana/mês) | Automattic\WooCommerce\Admin\API\Reports\Revenue\Stats\Controller (wc-analytics/reports/revenue/stats); requer feature analytics |
readonly, view_woocommerce_reports |
woo-analytics-products-stats | -variations-stats | -categories-stats | -taxes-stats | -coupons-stats | -downloads-stats | -customers-stats | -stock-stats |
Relatórios detalhados + estatísticas agregadas por domínio (um par <domínio> + <domínio>/stats cada) |
Reports\{Products,Variations,Categories,Taxes,Coupons,Downloads,Customers,Stock}\Controller (+ \Stats\Controller respectivo) — 8 pares confirmados em Init.php::rest_api_init_wc_analytics() |
readonly, view_woocommerce_reports |
woo-analytics-customer-lookup |
Perfil de cliente enriquecido com orders_count/total_spend/avg_order_value/date_registered/date_last_active/date_last_order |
Reports\Customers\Controller + DataStore.php (colunas confirmadas §4.3) |
readonly |
woo-analytics-performance-indicators |
Endpoint agregador "dashboard" — pede vários indicadores de vários /stats numa só chamada em lote |
Reports\PerformanceIndicators\Controller (registado por último, depois de todos os /stats) |
readonly |
woo-analytics-low-stock |
Lista produtos abaixo do limiar de stock baixo (low_stock_amount/global), útil para alertas |
Automattic\WooCommerce\Admin\API\ProductsLowInStock — registado sempre-on (não depende da feature analytics) |
readonly |
Nota de prioridade para a réplica: dado que toda a família Reports\* depende da feature
analytics e de tabelas de agregação populadas por um processamento assíncrono próprio
(ReportsSync, não lido em detalhe nesta tarefa), replicar isto do zero (sem depender do
WooCommerce já as ter populado) seria um subsistema por si só — para uma réplica que corre sobre
um WooCommerce já instalado e com Analytics activo, a decisão certa é ler as tabelas/API já
populadas pelo WooCommerce, não reimplementar o pipeline de sync.
7. Webhooks
Classe CRUD nativa: WC_Webhook (includes/class-wc-webhook.php, estende
WC_Legacy_Webhook). Controller REST: WC_REST_Webhooks_Controller
(includes/rest-api/Controllers/Version3/class-wc-rest-webhooks-controller.php, muito fino — só
sobrepõe get_default_api_version() para wp_api_v3, herda todo o resto de
WC_REST_Webhooks_V2_Controller). Capability: wc_rest_check_manager_permissions('webhooks',...)
→ manage_woocommerce (includes/wc-rest-functions.php:349).
7.1 Modelo de dados real (confirmado por $data default em WC_Webhook)
status (active/paused/disabled — wc_get_webhook_statuses(),
includes/wc-webhook-functions.php), delivery_url, secret (para assinatura HMAC do payload),
name, topic (formato <resource>.<event>), hooks (hooks WordPress internos ligados a este
topic), resource/event (derivados do topic), failure_count, user_id (autor), api_version
(default 3), pending_delivery.
7.2 Validação de topic — não é livre texto
Confirmado em wc_is_webhook_valid_topic() (includes/wc-webhook-functions.php, linhas ~90-160):
recursos por omissão são coupon, customer, order, product (filtráveis via
woocommerce_valid_webhook_resources); eventos por omissão são created, updated, deleted,
restored, published (filtráveis via woocommerce_valid_webhook_events). Um par
recurso+evento ambos por omissão só é válido se tiver um hook WordPress realmente registado —
combinações "fantasma" como order.published ou customer.restored são explicitamente
rejeitadas mesmo sendo sintacticamente válidas, para não deixar o utilizador criar um webhook
"Active" que nunca dispara. Tópicos de acção livre (action.woocommerce_*/action.wc_*) são
sempre aceites, excepto uma pequena blocklist (action.woocommerce_login_credentials, etc.).
7.3 Entrega assíncrona por omissão
wc_webhook_execute_queue() (hook shutdown) enfileira entregas via WC()->queue()
(Action Scheduler), com deduplicação de 10 minutos por webhook — evita reentregas em rajada se
várias mutações do mesmo objecto ocorrerem no mesmo request. Entrega síncrona só se o filtro
woocommerce_webhook_deliver_async for forçado a false.
7.4 Tabela de ability proposta — Webhooks
| Área | O que cobre | Endpoints/classes reais | Nível de risco/destructive |
|---|---|---|---|
woo-webhooks-read |
Lista/lê webhooks configurados (URL, topic, estado, contagem de falhas) — nunca devolve o secret em texto claro (mascarar, seguindo o padrão já usado nesta base de código para access_token/credential noutras integrações) |
WC_REST_Webhooks_Controller (wc/v3/webhooks); wc_get_webhook() nativo |
readonly |
woo-webhooks-write |
Cria/actualiza um webhook: topic (validado contra wc_is_webhook_valid_topic()), delivery_url, status, secret (gerado automaticamente se omitido) |
WC_Webhook::save(); validação de topic em wc-webhook-functions.php |
write; um delivery_url malicioso pode ser usado para SSRF/exfiltração — reutilizar EMCP_Tools_Url_Guard já documentado em docs/05-REDIRECTS-SEARCH-LEDGER.md §6 para validar o destino antes de gravar |
woo-webhooks-delete |
Apaga um webhook | Herdado de WC_REST_CRUD_Controller |
destructive, irreversível (histórico de entregas fica órfão) |
woo-webhook-deliveries |
Lista o histórico de entregas de um webhook (tentativas, resposta HTTP, duração) — útil para diagnóstico "porque é que este webhook não chegou ao destino" | WC_REST_Webhook_Deliveries_V1_Controller/_V2_Controller (wc/v1/wc/v2 apenas — não existe em wc/v3, confirmado por ausência no mapa get_v3_controllers() de Server.php; uma réplica teria de ler directamente a tabela de log de entregas ou usar wc/v2) |
readonly |
8. Segurança / gating recomendado
Resumo das capabilities WordPress reais confirmadas nesta tarefa (não inventadas), por domínio:
| Domínio | capability_type do post type / mecanismo |
Capabilities geradas usadas pela REST API | Onde confirmado |
|---|---|---|---|
| Produtos | product (map_meta_cap: true) |
read_private_products, publish_products, edit_product, delete_product, edit_others_products (batch) |
class-wc-post-types.php:402; wc_rest_check_post_permissions() (wc-rest-functions.php:229) |
| Encomendas | shop_order (map_meta_cap: true) |
read_private_shop_orders, publish_shop_orders, edit_shop_order, delete_shop_order, edit_others_shop_orders |
class-wc-post-types.php:462; mesma função wc_rest_check_post_permissions() |
| Cupões | shop_coupon (map_meta_cap: true) |
read_private_shop_coupons, publish_shop_coupons, edit_shop_coupon, delete_shop_coupon |
class-wc-post-types.php:525 |
| Clientes | utilizador WordPress, não post type | list_users, create_customers, edit_users, delete_users + gate extra shop_manager-só-edita-customer |
wc_rest_check_user_permissions() (wc-rest-functions.php:262-291) |
| Categorias/Tags/Atributos de produto | taxonomia (product_cat/product_tag/atributos globais) |
manage_product_terms, edit_product_terms, delete_product_terms |
class-wc-post-types.php:129-132 (e repetido nas outras 3 taxonomias) |
Relatórios/Analytics (reports) |
genérico | view_woocommerce_reports (distinto de manage_woocommerce) |
wc_rest_check_manager_permissions() (wc-rest-functions.php:341-349); herdado por toda a família Reports\* via class-wc-rest-reports-v1-controller.php:60-63 |
| Definições/System Status/Shipping/Payment Gateways/Webhooks | genérico | manage_woocommerce |
mesma função, mesmo ficheiro |
Recomendações de gating para a réplica (aplicando o padrão já documentado em
docs/08-INTEGRACOES-TERCEIROS.md §0):
- Condição de registo do grupo inteiro:
class_exists('WooCommerce') && function_exists('wc_get_product')(ou equivalente) — WooCommerce activo, sem gate de licença própria (é um plugin gratuito de terceiros, ao contrário de ACF Pro/Meta Box Pro). - Gate coarse no MCP = a capability mais permissiva do domínio (
edit_productsparawoo-products-write,edit_users+create_customerspara clientes,manage_woocommercepara webhooks) — a gate fina por operação deve reflectir exactamente as capabilities reais da tabela acima, não ummanage_optionsgenérico que sobre-restringe (ex.: um utilizador comview_woocommerce_reportsmas semmanage_woocommercedeve conseguir chamarwoo-analytics-*-read, mas nãowoo-webhooks-write). - Nunca reexpor o
secretde um webhook em texto claro — seguir o mesmo padrão já aplicado aaccess_tokende contas sociais ecredentialde custom apps documentado nas descrições das ferramentas MCP existentes neste ecossistema. woo-order-couponsewoo-orders-writeprecisam de aviso explícito no schema sobre o comportamento "substituição total, não merge" decoupon_lines/line_items— é o tipo de comportamento não-óbvio que já causou confusão documentada noutras integrações desta série (ver ACFupdate-field-group, doc08 §1.3, para o precedente de "campos imutáveis" bem documentados no schema para evitar o mesmo erro).- Change ledger: todas as escritas write/delete desta secção devem passar pelo mesmo
Change_Log/Change_Recorderjá documentado emdocs/05-REDIRECTS-SEARCH-LEDGER.md§4 —WC_Product/WC_Order/WC_Couponexpõemget_data()(snapshot completo do objecto) antes de qualquersave(), o que torna trivial um novo tipo de rollbackwoo-product-before-image/woo-order-before-image/woo-coupon-before-imageno mesmo padrão dos 11 tipos já existentes. Clientes (utilizadores WordPress) já têm cobertura possível via o tipouser-create/post-fieldsequivalente, se a réplica tratar edição de perfil de cliente como uma variante de edição de utilizador.
9. Blueprint para réplica
9.1 Prioridade de construção (por valor/esforço, assumindo Fase 0 do doc00 já feita)
woo-products-read+woo-orders-read+woo-customers-read— três abilities read-only, zero risco, cobrem o caso de uso mais comum ("o agente precisa de ver o estado da loja"). Sem dependência de change ledger. Construir primeiro.woo-products-write/woo-orders-write/woo-coupons-write— o trio de escrita CRUD standard. Reaproveitar 1:1 o padrãoprepare_object_for_database()→->save()já visto nos 3 controllers reais (a lógica de mapearrequest[key] → $object->set_{key}()é directamente portável). Requer change ledger (before-image) já pronto.woo-order-status+woo-order-refunds— separados dewoo-orders-writede propósito: mudar estado e criar devolução são as duas acções de maior impacto de negócio numa encomenda (podem mover dinheiro real via gateway), e merecempermission_callback/confirm:truemais estritos do que uma edição de morada de entrega.woo-coupons-read/woo-coupon-validate— baixo risco, alto valor para agentes de apoio-ao-cliente ("este código ainda é válido?").woo-webhooks-read/woo-webhooks-write— depois de ter oEMCP_Tools_Url_Guardequivalente pronto (§8 nota 3), dado o vector SSRF real do campodelivery_url.woo-analytics-*— só depois de confirmar que a instalação alvo tem a featureanalyticsactiva e as tabelas de agregação já populadas; caso contrário, cair para computar as mesmas métricas por leitura directa de encomendas viawc_get_orders()comdate_query(mais lento, mas sem dependência de infra-estrutura extra).woo-product-taxonomy/woo-product-variations/woo-orders-batch/woo-products-batch— utilitários de produtividade, construir por último; nenhum introduz capability nova face às anteriores.
9.2 Reaproveitamento explícito dos padrões já documentados
normalize_result()(doc00 §5) é obrigatório aqui, mais do que em qualquer outro domínio desta série — confirmado nesta tarefa queWC_REST_CRUD_Controller::get_items()devolve sempre um array indexado (nunca um mapa), e vários controllers de relatório devolvem array de topo. Sem o wrapper, todas as abilities*-readdesta secção partem em clientes MCP estritos.- Dispatcher de dois braços
<domínio>-read/<domínio>-write(doc08 §0) é a forma certa de empacotar isto — WooCommerce tem demasiadas operações por domínio (produtos sozinho tem ~10 operações reais: list/get/create/update/delete/duplicate/batch/variations-CRUD/generate) para justificar uma ability por operação; seguir exactamente o mesmo catálogo de descoberta{operation?, arguments?}já usado por ACF/Meta Box/Forms/SEO. - Change ledger unificado (doc05 §4) —
WC_Product/WC_Order/WC_Couponsão todosWC_Datacomget_data()nativo, o que torna a captura de before-image mais simples aqui do que em qualquer outra integração de terceiros desta série (ACF precisa de reconstruir o estado a partir de field groups dispersos; aqui é um único array). EMCP_Tools_Url_Guard(doc05 §6) — reutilizar directamente para validardelivery_urlde webhooks antes de gravar; é exactamente o caso de uso para o qual a Camada 2 (validate()) foi desenhada (SSRF via campo controlado por utilizador que o próprio servidor depois contacta).
9.3 O que NÃO replicar sem decisão explícita
wc/v4— instável, parcial (12 de ~50 recursos), atrás de uma feature flag ainda em desenvolvimento pelo próprio WooCommerce. Uma réplica que se acople awc/v4hoje arrisca quebrar em cada minor release enquanto o WooCommerce a estabiliza. Usarwc/v3ou as classes CRUD nativas.- Reimplementar o pipeline de sync de Analytics — as tabelas
wp_wc_order_stats/wp_wc_customer_lookup/etc. já existem e são mantidas pelo próprio WooCommerce quando a featureanalyticsestá activa; uma réplica deve ler, nunca recalcular estas agregações do zero. woo-webhook-deliveriestal como existe emwc/v1/wc/v2— a WooCommerce já não o expõe emwc/v3; construir uma ability nova para isto implica decidir se se lê a tabela de log directamente via SQL (mais frágil a mudanças de schema interno) ou se se aceita a lacuna e se documenta como "não disponível na API estável".suggested-products— é uma feature de UX do editor de produto do wp-admin (cache de sugestões calculadas), não dados de domínio; sem valor claro para um agente MCP.
10. Fonte
Leitura directa (19-08-2026) do WooCommerce 11.0.1 instalado em ecommerce.descomplicar.pt
(/home/ealmeida/ecommerce.descomplicar.pt/wp-content/plugins/woocommerce/):
includes/rest-api/Server.php(integral — mapa dos 4+1 namespaces e todos os controllers registados)includes/rest-api/Controllers/Version3/class-wc-rest-crud-controller.php(integral)includes/rest-api/Controllers/Version3/class-wc-rest-products-controller.php(integral, incl. schema completo)includes/rest-api/Controllers/Version3/class-wc-rest-orders-controller.php(integral)includes/rest-api/Controllers/Version3/class-wc-rest-customers-controller.php(integral, incl. schema completo)includes/rest-api/Controllers/Version3/class-wc-rest-coupons-controller.php(integral)includes/rest-api/Controllers/Version3/class-wc-rest-product-variations-controller.php(cabeçalho +register_routes())includes/rest-api/Controllers/Version3/class-wc-rest-order-refunds-controller.php(cabeçalho +prepare_object_for_database())includes/rest-api/Controllers/Version3/class-wc-rest-webhooks-controller.php(integral)includes/rest-api/Controllers/Version2/class-wc-rest-products-v2-controller.php(excerto: rota/batch+batch_items())includes/wc-rest-functions.php(integral — todas as funçõeswc_rest_check_*_permissions)includes/class-wc-post-types.php(excertos:capability_typedeproduct/shop_order/shop_coupon,capabilitiesdas 4 taxonomias de produto)includes/class-wc-install.php(excertos:create_roles(), atribuição de capabilities ashop_manager/administrator)includes/wc-order-functions.php(excertos:wc_get_order_statuses(),wc_get_is_paid_statuses(), assinatura dewc_get_orders()/wc_create_refund())includes/wc-coupon-functions.php(excerto:wc_get_coupon_types())includes/class-wc-coupon.php(excertos: defaults,is_type(),get_discount_amount(),set_discount_type_core(),get_short_info())includes/class-wc-webhook.php(cabeçalho +$datadefault)includes/wc-webhook-functions.php(integral)includes/data-stores/class-wc-product-data-store-cpt.php(cabeçalho +$internal_meta_keys)includes/abstracts/abstract-wc-product.php(excertos:get_stock_status(),set_manage_stock(),set_stock_quantity(),backorders_allowed())includes/class-wc-customer.php(cabeçalho +$datadefault)includes/wc-product-functions.php(excerto: assinaturaswc_get_products()/wc_get_product())src/Admin/API/Init.php(integral — bootstrapwc-admin/wc-analytics, listas de controllers,add_data_stores())src/Admin/API/Orders.php(excerto:class Orders extends \WC_REST_Orders_Controller)src/Admin/API/Reports/Controller.php(excerto: catálogoget_items())src/Admin/API/Reports/GenericController.php(integral)src/Admin/API/Reports/Customers/DataStore.php(excertos: colunasorders_count/total_spend/avg_order_value/date_registered/date_last_active)includes/rest-api/Controllers/Version1/class-wc-rest-reports-v1-controller.php(excerto:register_routes()+get_items_permissions_check())src/Internal/Admin/Loader.php(excerto: import deReports\Orders\DataStore, confirma existência do namespaceAutomattic\WooCommerce\Admin\API\Reports)
Listagens de directório (sem leitura de conteúdo, só confirmação de existência/inventário):
includes/rest-api/Controllers/ (Version1-4 + Telemetry), includes/rest-api/Controllers/Version3/
(51 controllers), includes/rest-api/Controllers/Version4/ (2 ficheiros — wrapper fino),
src/Admin/API/ (44 ficheiros + 5 subdirectórios), src/Admin/API/Reports/ (13 subdirectórios de
domínio + 13 ficheiros base), src/Internal/Admin/ (confirma que Analytics.php/Coupons.php aí
não são routers REST, o router real é src/Admin/API/Init.php).
Cruzado com docs/00-ARQUITECTURA.md (padrão de registo de ability, normalize_result, nota
explícita sobre WP_REST_Response::get_data() de rotas WooCommerce), docs/05-REDIRECTS-SEARCH-LEDGER.md
§4 e §6 (change ledger, EMCP_Tools_Url_Guard), docs/08-INTEGRACOES-TERCEIROS.md §0 (padrão de
dispatcher de dois braços), e docs/10-MODULOS-E-INVENTARIO-PRO.md linha 470 (confirmação do gate
class_exists() && EMCP_Tools_Woo_Integration::woo_active(), sem module toggle dedicado — única
referência real ao EMCP Pro Woo Integration disponível nesta série, dado o código-fonte Pro não
estar acessível).