UI はローカライズ済みです。スキルの紹介と説明文書は選択した言語へ AI が自動翻訳し、コード・コマンド・パス・専門用語は原文のままです。原作者の文書が優先されます。
スキルを探す
デザイン · SKILL.md

woocommerce-developer

woocommerce-developer

Use when building or modifying WooCommerce themes, customizing checkout or product pages, working with WooCommerce hooks and templates, integrating payment gateways, using the WooCommerce REST API, or writing any PHP code that touches WooCommerce. Includes mandatory security patterns.

Independent GitHub snapshot · Automated classification · Original documentation governs

スキルの説明

GitHub

name: woocommerce-developer description: Use when building or modifying WooCommerce themes, customizing checkout or product pages, working with WooCommerce hooks and templates, integrating payment gateways, using the WooCommerce REST API, or writing any PHP code that touches WooCommerce. Includes mandatory security patterns.

WooCommerce Developer

Overview

WooCommerce powers 36%+ of all online stores. This skill covers template customization, hooks/filters, REST API, checkout (block and classic), performance, analytics integration, and mandatory security patterns. Every code pattern must follow the security rules in this skill — no exceptions.

Template System

Override Hierarchy

WooCommerce templates live in wp-content/plugins/woocommerce/templates/. Override by copying to your theme:

yourtheme/woocommerce/single-product.php          → Single product page
yourtheme/woocommerce/archive-product.php          → Shop/category pages
yourtheme/woocommerce/cart/cart.php                 → Cart page
yourtheme/woocommerce/checkout/form-checkout.php    → Classic checkout
yourtheme/woocommerce/content-product.php           → Product card in loops

Rules:

  • Copy the template file, don't modify the plugin original
  • Keep the @version docblock comment — WooCommerce warns when templates are outdated
  • Prefer hooks over template overrides when possible (less maintenance)
  • Block checkout (default since WC 8.3+) does NOT use form-checkout.php — it uses the block editor

Block vs Classic

FeatureBlock-Based (New Default)Classic (Shortcode)
Cart<!-- wp:woocommerce/cart -->[woocommerce_cart]
Checkout<!-- wp:woocommerce/checkout -->[woocommerce_checkout]
CustomizationSlot/Fill API, Inner Blocks, Additional Fields APIPHP hooks and filters
Template fileNot used (block renders)woocommerce/checkout/form-checkout.php

Recommendation: New stores should use block checkout. Existing stores with heavy classic customization may keep classic until migration is practical.

Key Hooks

See hooks-reference.md for the complete hook map. Most important:

Product Page

HookLocationPriority
woocommerce_before_single_product_summaryBefore title/price area10
woocommerce_single_product_summaryTitle, price, excerpt, add-to-cart5-60
woocommerce_after_single_product_summaryTabs, related products10-15
woocommerce_product_tabs (filter)Add/remove/reorder product tabs10

Checkout

HookLocation
woocommerce_before_checkout_formBefore entire form
woocommerce_checkout_before_customer_detailsBefore billing/shipping fields
woocommerce_checkout_fields (filter)Add/remove/reorder checkout fields
woocommerce_checkout_order_processedAfter order created, before payment

Order Processing

HookWhen
woocommerce_new_orderOrder created
woocommerce_order_status_changedAny status transition
woocommerce_payment_completePayment received
woocommerce_order_status_completedOrder marked complete

REST API (v3)

Base URL: https://yoursite.com/wp-json/wc/v3/

EndpointMethodsAuth Required
/productsGET, POST, PUT, DELETEYes
/products/{id}/variationsGET, POST, PUT, DELETEYes
/ordersGET, POST, PUT, DELETEYes
/customersGET, POST, PUT, DELETEYes
/couponsGET, POST, PUT, DELETEYes
/reports/salesGETYes

Auth methods: Consumer key/secret (query string or Basic Auth), Application Passwords (WP 5.6+), OAuth 1.0a.

Batch operations: POST /products/batch with create, update, delete arrays. Max 100 objects per batch.

Rate limits: Store API default 25 requests per 10 seconds. REST API has no built-in limit (server-dependent).

Headless Storefront (Store API)

When a decoupled front-end (see modern-frontend) renders the storefront, WooCommerce stays authoritative for all commerce invariants; the front-end renders and collects input. This section defines the WooCommerce side.

Store API vs admin REST — pick by caller.

  • Store API (/wp-json/wc/store/v1/...) — the customer-facing cart/checkout API. Public, nonce-protected, session-aware. This is what a storefront uses for products, cart, and checkout.
  • Admin REST v3 (/wp-json/wc/v3/..., consumer key/secret) — the back-office API (order management, product CRUD, reports). It is NOT for the browser: its keys are admin credentials and must live server-side only.
  • A storefront that reaches for admin REST to render a cart is a security bug waiting to happen — use the Store API.

Cart, session & nonce — SAME-ORIGIN by default. The Store API keeps cart state in a session tied to cookies and a Nonce header. Reverse-proxy / rewrite the storefront's Store-API calls to the WordPress origin so those cookies stay first-party. A cross-domain decoupled front-end (front-end on one domain, WordPress on another) breaks cart persistence: third-party cookies are blocked by CORS credential rules and by Safari ITP, so the shopper's cart silently empties. Same-origin proxying is the default pattern, not an optimization.

Checkout boundary — what must stay WooCommerce-owned. Payment authorization, tax calculation, inventory/stock decrement, coupon validation, and order totals are computed and enforced by WooCommerce. The front-end submits the order through the Store API and renders the result — it never computes a price, tax, or "in stock" verdict it then trusts. Never let the client be the source of truth for money or stock.

Price & stock freshness. Cache product data for speed, but treat cached price/stock as display-only and re-validate at add-to-cart and at checkout against WooCommerce — the authoritative check happens server-side, so a stale cached price cannot be honored.

Webhook-driven revalidation. Fire WooCommerce webhooks (product / order updated) to the front-end's revalidation endpoint so static/ISR product pages refresh on price/stock change; tag by product ID to revalidate only affected routes.

Boundary: commerce invariants (cart/session, checkout, payment/tax/inventory authority) stay here; the storefront UI and rendering live in modern-frontend; experience and journey decisions live in audience-experience-design.

HPOS (High-Performance Order Storage)

WooCommerce moved from wp_posts/wp_postmeta to dedicated order tables (wp_wc_orders, wp_wc_orders_meta). HPOS is now the default.

Rules:

  • Use wc_get_order() and $order->get_*() methods — NEVER query wp_posts for orders
  • Use wc_get_orders() instead of WP_Query for order queries
  • Use $order->update_meta_data() / $order->get_meta() for custom order data
  • Run compatibility check before enabling: wp wc hpos status

Security (MANDATORY)

The Iron Rule

Every piece of WooCommerce code MUST follow these patterns. No exceptions. No shortcuts.

Input Sanitization — Which Function When

ContextFunctionExample
Plain textsanitize_text_field()Names, addresses
Emailsanitize_email()Customer email
URLesc_url_raw() (for DB) / esc_url() (for output)Return URLs
Integerabsint() or intval()Product IDs, quantities
Textareasanitize_textarea_field()Order notes
HTML (limited)wp_kses_post()Rich text descriptions
Filenamesanitize_file_name()Uploads

Output Escaping — Every Echo Must Escape

ContextFunction
HTML contentesc_html()
HTML attributesesc_attr()
URLs in href/srcesc_url()
JavaScript stringsesc_js()
Translation + escapeesc_html__(), esc_attr__()

Rule: If it came from user input, database, or external source → escape it on output. Always.

Database Queries — Always Prepare

// CORRECT
$results = $wpdb->get_results(
    $wpdb->prepare(
        "SELECT * FROM {$wpdb->prefix}wc_orders WHERE customer_id = %d AND status = %s",
        $customer_id,
        $status
    )
);

// NEVER — SQL injection vulnerability
$results = $wpdb->get_results("SELECT * FROM wp_wc_orders WHERE customer_id = $customer_id");

Nonce Verification

Every form submission and AJAX handler MUST verify a nonce.

// In form:
wp_nonce_field('my_action', 'my_nonce');

// In handler:
if (!wp_verify_nonce($_POST['my_nonce'], 'my_action')) {
    wp_die('Security check failed');
}

// AJAX handler:
check_ajax_referer('my_action', 'nonce');

Capability Checks

// Before any admin/privileged action:
if (!current_user_can('manage_woocommerce')) {
    wp_die('Unauthorized');
}

Payment Security

  • NEVER store raw card numbers, CVVs, or full card data — PCI DSS violation
  • Use tokenization (Stripe, PayPal handle this)
  • Store only: last 4 digits, card brand, expiry month/year, token reference
  • Payment forms: use hosted iframes or redirect (never direct POST to your server)
  • SCA (Strong Customer Authentication) required for UK/EU transactions

Code Review Checklist

Before any WooCommerce code is committed, verify:

  • All $_GET/$_POST/$_REQUEST values sanitized before use
  • All output escaped with appropriate esc_*() function
  • All DB queries use $wpdb->prepare() with placeholders
  • All form handlers verify nonces
  • All privileged actions check current_user_can()
  • No hardcoded API keys, secrets, or passwords
  • No eval(), extract(), unserialize() on user data
  • File uploads validate type, size, and use wp_handle_upload()
  • REST API endpoints check permissions in permission_callback
  • No raw card data stored anywhere

Performance

Cart Fragments (Biggest WooCommerce Performance Issue)

WooCommerce loads wc-cart-fragments.js on every page — fires AJAX on every load to update cart count. Adds 300-800ms to every page.

Fix: Disable on non-cart pages:

add_action('wp_enqueue_scripts', function() {
    if (!is_cart() && !is_checkout()) {
        wp_dequeue_script('wc-cart-fragments');
    }
});

Trade-off: Cart count in header won't update dynamically on those pages. Implement custom lightweight cart count if needed.

Query Optimization

  • Use wc_get_products() instead of WP_Query for products (HPOS-compatible)
  • Use wc_get_orders() instead of get_posts() for orders
  • Limit related products to 4 (woocommerce_output_related_products_args filter)
  • Use transients for expensive queries (product counts, aggregations)

Caching Exclusions

Never cache: cart, checkout, my-account pages. WooCommerce sets DONOTCACHEPAGE constant on these. Ensure your caching plugin respects it.

Feed Generation at Scale

Large catalogues (10k+ products) break when a product feed (Google Shopping, Meta, or an AI-crawler product feed) is built synchronously on a page request — PHP max_execution_time and memory limits kill it mid-run.

  • Generate feeds with Action Scheduler (bundled with WooCommerce) in chunked batches (200-500 products each), never in one request. Paginate with wc_get_products() and free objects between batches.
  • Guard each batch: cap peak memory, and avoid loading full product objects when only feed fields are needed.
  • Cache the artifact, not the request — write the finished feed to a static file (or object storage) and serve that; regenerate off-peak via a real system cron / Action Scheduler, not on the visitor's request.
  • Write to a temp file and atomically rename so a crawler never reads a half-written feed.

Search Offload

Default WordPress search is a LIKE scan on wp_posts; adding WooCommerce attribute/meta search layers on meta_query joins. Both degrade badly past a few thousand products or with high-cardinality attributes.

  • Offload to an external index — ElasticPress (Elasticsearch/OpenSearch) or Algolia — once search/filter latency or relevance is the bottleneck.
  • Keep WooCommerce as the source of truth; treat the index as a rebuildable projection re-synced on product save/delete. Never let the index hold authoritative price or order data.
  • Security: store index API keys in environment variables or wp_options, never in theme files or committed code; for any client-side calls use a scoped search-only key (e.g. Algolia's search-only API key), never the admin key.
  • For high-cardinality attribute filtering specifically (not free-text search), see woocommerce-faceted-navigation — the facet layer covers the wc_product_attributes_lookup boundary and when to reach for an external index.

Analytics Integration (GA4)

Recommended method: GTM4WP plugin → Google Tag Manager → GA4

To design and run experiments on these events (A/B testing, sample-size and statistical-validity guards, cache-safe variation delivery), see ecommerce-cro-experimentation — it references this event-mapping table rather than duplicating it.

E-commerce Event Mapping

WooCommerce ActionGA4 EventKey Parameters
Product page viewview_itemitem_id, item_name, price
Add to cartadd_to_cartitem_id, quantity, value
Remove from cartremove_from_cartitem_id, quantity
View cartview_cartitems[], value
Begin checkoutbegin_checkoutitems[], value, coupon
Shipping method chosenadd_shipping_infoitems[], value, shipping_tier
Payment method chosenadd_payment_infoitems[], value, payment_type
Purchasepurchasetransaction_id, value, tax, shipping, items[]

⚠️ Express wallets break this funnel — instrument them explicitly

An Apple Pay / Google Pay / Shop Pay / PayPal express purchase started from the product or cart page never passes through the checkout page, so begin_checkout, add_shipping_info and add_payment_info never fire. Only add_to_cart (sometimes not even that) and purchase do.

This matters more than it sounds: wallets are roughly 65% of mobile conversions. A funnel readout built on the table above will show those buyers appearing at purchase out of nowhere, and will compute a checkout-completion rate that is silently wrong for about two-thirds of mobile orders — usually presenting as an implausibly low begin_checkout → purchase rate that gets "fixed" by optimising a checkout page most mobile buyers never see.

DoWhy
Fire begin_checkout when the express-wallet sheet opens, not only on the checkout pageRestores the funnel's entry event for wallet buyers
Stamp every checkout + purchase event with a checkout_type parameter (classic | express_wallet) and register it as a custom dimensionLets you segment the two funnels instead of averaging them into nonsense
Set payment_type on add_payment_info (Apple Pay, Google Pay, PayPal, card)The only way to size the wallet share you are missing
Analyse classic and express funnels separatelyThey have different step counts; a blended completion rate describes neither
QA on a real mobile device with a live walletWallet sheets frequently do not fire in desktop emulation or GTM Preview

Clarity Integration

Install via official WordPress plugin or add tracking script to header.php. Tag purchases with custom tags: window.clarity("set", "order_id", orderId)

Version Requirements (2026)

ComponentMinimumRecommended
PHP8.28.2+
WordPress6.6Latest
WooCommerce9.xLatest stable
MySQL5.7 / MariaDB 10.48.0+ / 10.6+

Anti-Patterns

Don'tWhy
Query wp_posts for ordersBreaks with HPOS. Use wc_get_orders()
Use WP_Query for productsUse wc_get_products() for forward compatibility
Echo unsanitized user inputXSS vulnerability. Always escape output
Skip nonce verificationCSRF vulnerability. Every form/AJAX needs it
Store card data in order metaPCI DSS violation. Use tokenization
Override templates when hooks sufficeTemplates need maintenance on WC updates; hooks don't
Leave cart fragments on all pages300-800ms performance hit per page
Use extract() or eval()Security vulnerabilities. Never use with user data
Hardcode API keys in theme filesUse wp_options or environment variables
Skip capability checks on admin actionsPrivilege escalation vulnerability
保存したスキル