WooCommerce Plugin Development
Overview
WooCommerce is an open-source e-commerce plugin for WordPress, powering over 36% of all online stores. It transforms a WordPress installation into a fully functional e-commerce platform with product management, cart, checkout, payment processing, shipping, and order management.
WooCommerce follows the WordPress extension model -- plugins extend core functionality through a hook-based architecture. WooCommerce adds its own extensive layer of hooks, filters, REST API endpoints, and abstract classes on top of WordPress's plugin system. Extensions can add payment gateways, shipping methods, product types, checkout fields, admin pages, reporting dashboards, and storefront modifications.
The platform has evolved significantly with the introduction of High-Performance Order Storage (HPOS), block-based checkout, and the WooCommerce Admin React-based interface. Modern WooCommerce plugin development must account for these architectural shifts while maintaining backward compatibility with classic implementations.
WooCommerce plugins are distributed through the WordPress.org plugin directory (free), the WooCommerce.com marketplace (premium), or independently through developer websites. All distribution channels require GPL-compatible licensing. The WordPress.org review process enforces security standards, coding quality, and adherence to plugin guidelines.
Plugin Structure
Every WooCommerce plugin starts with a main PHP file containing the WordPress plugin header. WooCommerce-specific headers declare version compatibility:
<?php
/**
* Plugin Name: My WooCommerce Plugin
* Plugin URI: https://example.com/my-woo-plugin
* Description: A brief description of the plugin.
* Version: 1.0.0
* Author: Developer Name
* Author URI: https://example.com
* License: GPL-2.0-or-later
* Text Domain: my-woo-plugin
* Domain Path: /languages
* Requires at least: 6.0
* Requires PHP: 7.4
* WC requires at least: 8.0
* WC tested up to: 9.4
* Woo: 12345:abc123def456
*/
The WC requires at least and WC tested up to headers tell WooCommerce which versions the plugin supports. The Woo header is required for plugins sold on WooCommerce.com marketplace.
Activation and Dependency Checks
Before initializing, verify that WooCommerce is active. Hook into plugins_loaded to check for the WooCommerce class, and display an admin notice if WooCommerce is missing. Never call WooCommerce functions at the top level of the plugin file -- always wait for the woocommerce_loaded action.
HPOS Feature Declaration
All new plugins must declare HPOS compatibility. Without this declaration, WooCommerce displays a compatibility warning in the admin:
add_action('before_woocommerce_init', function () {
if (class_exists(\Automattic\WooCommerce\Utilities\FeaturesUtil::class)) {
\Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility(
'custom_order_tables', __FILE__, true
);
}
});
For full plugin structure details including autoloading, directory layout, and initialization patterns, see references/architecture.md.
Hook System
WooCommerce's hook system is the primary mechanism for extending functionality. It builds on WordPress's core add_action() and add_filter() API.
Actions vs Filters
- Actions execute code at specific points in the WooCommerce lifecycle (order created, payment processed, email sent). They do not return values.
- Filters modify data as it passes through the system (change product price, alter checkout fields, modify email content). They receive a value and must return it (modified or not).
Priority System
Hooks accept a priority parameter (default 10). Lower numbers execute first. Use priorities strategically:
- 1-9 -- Execute before most other plugins.
- 10 -- Default priority. Most plugins use this.
- 11-99 -- Execute after most plugins. Useful for overriding other plugin behavior.
- 100+ -- Execute last. Use for final modifications.
Critical WooCommerce Hooks
The most commonly used hooks fall into categories:
| Category | Key Hooks |
|---|---|
| Product | woocommerce_product_options_general_product_data, woocommerce_process_product_meta |
| Cart | woocommerce_add_to_cart, woocommerce_cart_calculate_fees, woocommerce_before_calculate_totals |
| Checkout | woocommerce_checkout_process, woocommerce_checkout_order_processed, woocommerce_checkout_create_order |
| Order | woocommerce_new_order, woocommerce_order_status_changed, woocommerce_payment_complete |
| Admin | woocommerce_admin_order_data_after_billing_address, woocommerce_product_data_tabs |
woocommerce_email_before_order_table, woocommerce_email_order_details |
For a comprehensive categorized list with code examples, see references/hooks-and-filters.md.
Development Setup
Local WordPress Environments
Several tools provide local WordPress development environments:
- LocalWP (Local by Flywheel) -- GUI-based, one-click WordPress setup with SSL, mailbox, and live link sharing. Recommended for beginners.
- wp-env -- Official WordPress Docker-based environment. Define configuration in
.wp-env.jsonand runnpx @wordpress/env start. Ideal for CI/CD. - DDEV -- Docker-based PHP development environment with WordPress recipes.
- VVV (Varying Vagrant Vagrants) -- Vagrant-based, heavier but highly configurable.
WP-CLI
WP-CLI is the command-line interface for WordPress. Essential commands for WooCommerce development:
wp plugin activate my-woo-plugin
wp wc product list --user=admin
wp wc order create --customer_id=1 --user=admin
wp option get woocommerce_default_country
wp transient delete --all
WooCommerce Beta Tester
Install the WooCommerce Beta Tester plugin to test against upcoming WooCommerce releases. This catches compatibility issues before they reach production stores. Enable beta updates in WooCommerce > Settings > Advanced > WooCommerce.com and select the desired release channel (beta, release candidate, or nightly).
Debug Configuration
Enable WordPress debug mode in wp-config.php:
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', false);
define('SCRIPT_DEBUG', true);
Use wc_get_logger() for structured logging within WooCommerce context. Log files appear under wp-content/uploads/wc-logs/ and are viewable in WooCommerce > Status > Logs.
Recommended Developer Tools
- Query Monitor -- WordPress debugging plugin that shows database queries, hooks fired, HTTP requests, and PHP errors. Invaluable for identifying performance bottlenecks and hook execution order.
- WooCommerce Admin Test Helper -- Reset WooCommerce admin features and onboarding states for testing.
- Code Snippets -- Test hook callbacks and filters without creating a full plugin.
- Debug Bar -- Inspect WordPress internals, including cache hits and conditional functions.
For testing strategies including PHPUnit setup, E2E tests, and mocking patterns, see references/testing.md.
Key Integration Patterns
| Pattern | Base Class | Key Method(s) | Reference |
|---|---|---|---|
| Payment Gateway | WC_Payment_Gateway |
process_payment(), init_form_fields() |
payment-gateway.md |
| Shipping Method | WC_Shipping_Method |
calculate_shipping(), init_form_fields() |
shipping-method.md |
| Product Type | WC_Product |
get_type(), custom data stores |
architecture.md |
| Checkout Blocks | IntegrationInterface |
get_script_handles(), get_script_data() |
storefront-and-blocks.md |
| Admin Settings | WC_Integration |
init_form_fields(), process_admin_options() |
admin-extension.md |
| REST Endpoint | WC_REST_Controller |
register_routes(), get_items() |
rest-api.md |
Each integration pattern follows the same lifecycle: extend the abstract class, implement required methods, register with WooCommerce via the appropriate hook. WooCommerce discovers registered extensions through specific filter hooks -- woocommerce_payment_gateways for gateways, woocommerce_shipping_methods for shipping, and woocommerce_integrations for settings integrations. The registration hook fires during WooCommerce initialization, so the plugin must be loaded before that point.
For headless and external integrations, WooCommerce provides two REST APIs. The WC REST API v3 handles administrative operations (managing products, orders, customers) with consumer key/secret authentication. The Store API serves the block-based cart and checkout with session-based authentication. See references/rest-api.md for endpoint details, custom controller creation, and webhook configuration.
HPOS Compatibility
High-Performance Order Storage (HPOS) replaces the WordPress wp_posts and wp_postmeta tables with dedicated WooCommerce order tables. This dramatically improves query performance for stores with large order volumes.
Key Rules for HPOS Compatibility
- Never access
wp_postsorwp_postmetadirectly for order data. Use the WooCommerce CRUD API ($order->get_meta(),$order->update_meta_data()). - Never use
get_post_meta()orupdate_post_meta()on order IDs. These bypass HPOS entirely. - Use
OrderUtil::get_order_type()to determine if an ID is an order, notget_post_type(). - Query orders with
wc_get_orders(), notWP_Queryor direct SQL. - Declare HPOS support in the plugin header via
FeaturesUtil::declare_compatibility().
// Correct: HPOS-compatible order meta access
$order = wc_get_order($order_id);
$custom_value = $order->get_meta('_my_custom_field');
$order->update_meta_data('_my_custom_field', 'new_value');
$order->save();
// Wrong: Direct post meta (breaks HPOS)
$custom_value = get_post_meta($order_id, '_my_custom_field', true);
update_post_meta($order_id, '_my_custom_field', 'new_value');
WooCommerce Blocks
WooCommerce Blocks provide a modern, block-based cart and checkout experience built with React. The block-based checkout is now the default for new WooCommerce installations.
Extending Block Checkout
Register a checkout block integration by implementing Automattic\WooCommerce\Blocks\Integrations\IntegrationInterface:
use Automattic\WooCommerce\Blocks\Integrations\IntegrationInterface;
class My_Blocks_Integration implements IntegrationInterface {
public function get_name() { return 'my-plugin'; }
public function initialize() { /* register scripts */ }
public function get_script_handles() { return ['my-plugin-checkout-block']; }
public function get_editor_script_handles() { return []; }
public function get_script_data() { return ['ajaxUrl' => admin_url('admin-ajax.php')]; }
}
On the JavaScript side, use the @woocommerce/blocks-checkout package to register components in checkout slots:
import { registerCheckoutBlock } from '@woocommerce/blocks-checkout';
registerCheckoutBlock({
metadata: { name: 'my-plugin/custom-field', parent: ['woocommerce/checkout-fields-block'] },
component: MyCustomFieldBlock,
});
Store API
The WooCommerce Store API serves block-based cart and checkout. Extend it to expose custom data to checkout blocks. Use ExtendSchema to add data to cart/checkout API responses and register_endpoint_data to attach custom schemas.
For complete block integration patterns, see references/storefront-and-blocks.md.
Anti-Patterns
Avoid these common mistakes in WooCommerce plugin development:
Direct Database Access for Orders
Never query $wpdb->posts or $wpdb->postmeta for order data. This breaks HPOS compatibility and bypasses WooCommerce's data layer, cache, and hooks.
Bypassing WooCommerce CRUD API
Always use $order->get_total(), $order->get_billing_email(), $product->get_price() instead of accessing internal properties or post meta directly. The CRUD methods handle data stores, caching, and format conversions.
Hardcoding URLs
Never hardcode site URLs, asset URLs, or API endpoints. Use plugins_url(), admin_url(), wc_get_endpoint_url(), and rest_url() to generate URLs dynamically.
Ignoring Nonce Verification
All form submissions and AJAX requests must verify WordPress nonces with wp_verify_nonce() or check_ajax_referer(). Skipping nonce verification opens the plugin to CSRF attacks.
Loading Assets Everywhere
Enqueue scripts and styles only on pages where they are needed. Use is_checkout(), is_cart(), is_product(), and similar conditional functions to target specific pages.
Ignoring Block Checkout
Plugins that only support classic checkout (shortcode-based) are increasingly incompatible with new installations. Support both classic and block checkout flows.
Modifying Orders via Direct SQL
Never run UPDATE or INSERT queries against wp_posts, wp_postmeta, or the HPOS tables (wp_wc_orders, wp_wc_orders_meta) directly. Use wc_get_order() to retrieve orders and the CRUD methods to modify them. Direct SQL bypasses validation, caching, and hook triggers, leading to data inconsistency.
Not Sanitizing or Escaping
All user input flowing into the database must pass through sanitization functions (sanitize_text_field(), absint(), sanitize_email()). All output rendered in HTML must be escaped (esc_html(), esc_attr(), esc_url(), wp_kses_post()). Skipping sanitization or escaping is the most common reason for plugin rejection on WordPress.org.
Shipping and Tax Assumptions
Never assume a store uses a specific currency, tax mode, or shipping structure. Use WooCommerce helper functions (get_woocommerce_currency(), wc_tax_enabled(), wc_prices_include_tax()) to query the store configuration. Hardcoded assumptions break internationalization.
Data Handling and CRUD
WooCommerce provides a CRUD (Create, Read, Update, Delete) abstraction for all core data types. Use these APIs instead of direct WordPress functions:
| Object | Retrieve | Query |
|---|---|---|
| Product | wc_get_product($id) |
wc_get_products($args) |
| Order | wc_get_order($id) |
wc_get_orders($args) |
| Customer | new WC_Customer($id) |
get_users($args) |
| Coupon | new WC_Coupon($code) |
wc_get_orders(['type' => 'shop_coupon']) |
All CRUD objects share the same meta API:
$object->get_meta('_key'); // Read
$object->update_meta_data('_key', $v); // Write (in memory)
$object->save(); // Persist to database
$object->delete_meta_data('_key'); // Remove
Always call $object->save() after modifying data. Meta changes are batched in memory until saved, which reduces database writes.
For detailed architecture including autoloading, custom tables, template overrides, and WP-CLI commands, see references/architecture.md.
Reference Files
- Architecture -- Plugin file structure, autoloading, headers, HPOS declaration, activation hooks, settings registration
- Hooks and Filters -- Complete categorized hook reference with code examples and priority system
- REST API -- WC REST API v3, authentication, custom endpoints, Store API, batch operations
- Admin Extension -- Settings tabs, product data panels, order meta boxes, WooCommerce Admin pages
- Storefront and Blocks -- Block checkout extensions, Store API, template overrides, product page customization
- Checkout and Cart -- Checkout field customization, cart fees, validation, block checkout integration
- Payment Gateway -- WC_Payment_Gateway extension, tokenization, refunds, 3D Secure, webhook handling
- Shipping Method -- WC_Shipping_Method extension, rate calculation, shipping zones, tracking integration
- Testing -- PHPUnit setup, WC test helpers, E2E testing, WP-CLI testing, debug logging
- Publishing -- WordPress.org submission, readme.txt format, SVN workflow, WooCommerce.com marketplace
Example Files
- Basic Plugin -- Minimal complete WooCommerce plugin with HPOS support, dependency check, and settings page
- Payment Gateway -- Payment gateway skeleton with form fields, payment processing, webhooks, and refund support