# Woocommerce

> >-

- **Type:** Skill
- **Install:** `agentstack add skill-iwritec0de-wp-dev-woocommerce`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [iwritec0de](https://agentstack.voostack.com/s/iwritec0de)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [iwritec0de](https://github.com/iwritec0de)
- **Source:** https://github.com/iwritec0de/wp-dev/tree/main/skills/woocommerce

## Install

```sh
agentstack add skill-iwritec0de-wp-dev-woocommerce
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# WooCommerce Development Patterns

This skill covers WooCommerce development including CRUD objects, hooks, custom product types, checkout customization, payment gateways, shipping methods, and HPOS compatibility.

## HPOS Compatibility

WooCommerce High-Performance Order Storage (HPOS) replaces the `wp_posts` table for orders. **Always use CRUD methods** instead of post functions:

```php
// CORRECT (HPOS-compatible):
$order = wc_get_order( $order_id );
$order->get_meta( '_custom_field' );
$order->update_meta_data( '_custom_field', $value );
$order->save();

// WRONG (breaks with HPOS):
$order = get_post( $order_id );
get_post_meta( $order_id, '_custom_field', true );
update_post_meta( $order_id, '_custom_field', $value );
```

Declare HPOS compatibility in your plugin:

```php
add_action( 'before_woocommerce_init', function (): void {
    if ( class_exists( '\Automattic\WooCommerce\Utilities\FeaturesUtil' ) ) {
        \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility(
            'custom_order_tables',
            __FILE__,
            true
        );
    }
} );
```

## WooCommerce CRUD Objects

### WC_Product
```php
// Get a product:
$product = wc_get_product( $product_id );

// Product properties:
$product->get_name();
$product->get_price();
$product->get_regular_price();
$product->get_sale_price();
$product->get_sku();
$product->get_stock_quantity();
$product->is_in_stock();
$product->is_on_sale();
$product->get_type();         // 'simple', 'variable', 'grouped', 'external'
$product->get_category_ids();
$product->get_image_id();

// Create a product:
$product = new WC_Product_Simple();
$product->set_name( 'My Product' );
$product->set_regular_price( '29.99' );
$product->set_description( 'Product description.' );
$product->set_short_description( 'Short description.' );
$product->set_sku( 'MY-SKU-001' );
$product->set_manage_stock( true );
$product->set_stock_quantity( 100 );
$product->set_category_ids( array( 15, 22 ) );
$product->save();
$new_id = $product->get_id();
```

### WC_Order
```php
// Get an order:
$order = wc_get_order( $order_id );

// Order properties:
$order->get_id();
$order->get_status();
$order->get_total();
$order->get_subtotal();
$order->get_billing_email();
$order->get_billing_first_name();
$order->get_shipping_address_1();
$order->get_payment_method();
$order->get_items();
$order->get_customer_id();
$order->get_date_created();

// Order items:
foreach ( $order->get_items() as $item_id => $item ) {
    $product    = $item->get_product();
    $quantity   = $item->get_quantity();
    $subtotal   = $item->get_subtotal();
    $item_meta  = $item->get_meta( '_custom_key' );
}

// Update order:
$order->set_status( 'completed' );
$order->add_order_note( __( 'Order processed.', 'myplugin' ) );
$order->update_meta_data( '_myplugin_processed', true );
$order->save();

// Create an order programmatically:
$order = wc_create_order( array( 'customer_id' => $user_id ) );
$order->add_product( wc_get_product( $product_id ), 2 );
$order->set_address( $billing_address, 'billing' );
$order->calculate_totals();
$order->save();
```

### WC_Customer
```php
$customer = new WC_Customer( $user_id );
$customer->get_billing_email();
$customer->get_billing_first_name();
$customer->get_order_count();
$customer->get_total_spent();
```

## Key Hooks

### Cart Hooks
```php
// Modify cart item data:
add_filter( 'woocommerce_add_cart_item_data', function ( array $cart_item_data, int $product_id, int $variation_id ): array {
    if ( isset( $_POST['custom_option'] ) ) {
        $cart_item_data['custom_option'] = sanitize_text_field( wp_unslash( $_POST['custom_option'] ) );
    }
    return $cart_item_data;
}, 10, 3 );

// Validate before add to cart:
add_filter( 'woocommerce_add_to_cart_validation', function ( bool $passed, int $product_id, int $quantity ): bool {
    if ( $quantity > 10 ) {
        wc_add_notice( __( 'Maximum 10 items allowed.', 'myplugin' ), 'error' );
        return false;
    }
    return $passed;
}, 10, 3 );

// Modify cart item price:
add_action( 'woocommerce_before_calculate_totals', function ( WC_Cart $cart ): void {
    foreach ( $cart->get_cart() as $cart_item ) {
        if ( isset( $cart_item['custom_price'] ) ) {
            $cart_item['data']->set_price( $cart_item['custom_price'] );
        }
    }
} );
```

### Checkout Hooks
```php
// Add custom checkout field:
add_action( 'woocommerce_after_order_notes', function ( WC_Checkout $checkout ): void {
    woocommerce_form_field( 'delivery_notes', array(
        'type'        => 'textarea',
        'class'       => array( 'form-row-wide' ),
        'label'       => __( 'Delivery Notes', 'myplugin' ),
        'placeholder' => __( 'Special instructions...', 'myplugin' ),
        'required'    => false,
    ), $checkout->get_value( 'delivery_notes' ) );
} );

// Validate custom field:
add_action( 'woocommerce_checkout_process', function (): void {
    // Validation logic here.
} );

// Save custom field:
add_action( 'woocommerce_checkout_create_order', function ( WC_Order $order, array $data ): void {
    if ( ! empty( $_POST['delivery_notes'] ) ) {
        $order->update_meta_data(
            '_delivery_notes',
            sanitize_textarea_field( wp_unslash( $_POST['delivery_notes'] ) )
        );
    }
}, 10, 2 );
```

### Order Status Hooks
```php
// When order status changes:
add_action( 'woocommerce_order_status_changed', function ( int $order_id, string $old_status, string $new_status, WC_Order $order ): void {
    if ( 'completed' === $new_status ) {
        // Handle completion.
    }
}, 10, 4 );

// Specific status transition:
add_action( 'woocommerce_order_status_processing_to_completed', function ( int $order_id, WC_Order $order ): void {
    // Runs only on processing → completed.
}, 10, 2 );

// After payment complete:
add_action( 'woocommerce_payment_complete', function ( int $order_id ): void {
    $order = wc_get_order( $order_id );
    // Post-payment logic.
} );
```

## Custom Product Data Tab

```php
// Add tab:
add_filter( 'woocommerce_product_data_tabs', function ( array $tabs ): array {
    $tabs['myplugin_custom'] = array(
        'label'    => __( 'Custom Data', 'myplugin' ),
        'target'   => 'myplugin_custom_product_data',
        'class'    => array(),
        'priority' => 70,
    );
    return $tabs;
} );

// Tab content:
add_action( 'woocommerce_product_data_panels', function (): void {
    global $post;
    ?>
    
         '_myplugin_custom_field',
            'label'       => __( 'Custom Field', 'myplugin' ),
            'desc_tip'    => true,
            'description' => __( 'Enter a custom value.', 'myplugin' ),
        ) );

        woocommerce_wp_select( array(
            'id'      => '_myplugin_option',
            'label'   => __( 'Option', 'myplugin' ),
            'options' => array(
                ''       => __( 'Select...', 'myplugin' ),
                'opt_a'  => __( 'Option A', 'myplugin' ),
                'opt_b'  => __( 'Option B', 'myplugin' ),
            ),
        ) );

        woocommerce_wp_checkbox( array(
            'id'    => '_myplugin_enabled',
            'label' => __( 'Enable Feature', 'myplugin' ),
        ) );
        ?>
    
    update_meta_data( '_myplugin_custom_field',
            sanitize_text_field( wp_unslash( $_POST['_myplugin_custom_field'] ) ) );
    }

    if ( isset( $_POST['_myplugin_option'] ) ) {
        $product->update_meta_data( '_myplugin_option',
            sanitize_key( $_POST['_myplugin_option'] ) );
    }

    $product->update_meta_data( '_myplugin_enabled',
        isset( $_POST['_myplugin_enabled'] ) ? 'yes' : 'no' );

    $product->save();
} );
```

## Payment Gateway Skeleton

```php
class WC_Gateway_MyPlugin extends WC_Payment_Gateway {

    public function __construct() {
        $this->id                 = 'myplugin_gateway';
        $this->method_title       = __( 'My Payment Gateway', 'myplugin' );
        $this->method_description = __( 'Accept payments via My Gateway.', 'myplugin' );
        $this->has_fields         = true;
        $this->supports           = array( 'products', 'refunds' );

        $this->init_form_fields();
        $this->init_settings();

        $this->title   = $this->get_option( 'title' );
        $this->enabled = $this->get_option( 'enabled' );

        add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ) );
    }

    public function init_form_fields(): void {
        $this->form_fields = array(
            'enabled' => array(
                'title'   => __( 'Enable/Disable', 'myplugin' ),
                'type'    => 'checkbox',
                'label'   => __( 'Enable My Gateway', 'myplugin' ),
                'default' => 'no',
            ),
            'title' => array(
                'title'   => __( 'Title', 'myplugin' ),
                'type'    => 'text',
                'default' => __( 'My Gateway', 'myplugin' ),
            ),
            'api_key' => array(
                'title' => __( 'API Key', 'myplugin' ),
                'type'  => 'password',
            ),
        );
    }

    public function process_payment( $order_id ): array {
        $order = wc_get_order( $order_id );

        // Process payment with external API...

        $order->payment_complete( $transaction_id );
        $order->add_order_note( sprintf(
            /* translators: %s: transaction ID */
            __( 'Payment completed. Transaction ID: %s', 'myplugin' ),
            $transaction_id
        ) );

        WC()->cart->empty_cart();

        return array(
            'result'   => 'success',
            'redirect' => $this->get_return_url( $order ),
        );
    }

    public function process_refund( $order_id, $amount = null, $reason = '' ): bool {
        $order = wc_get_order( $order_id );
        // Process refund with external API...
        return true; // or WP_Error
    }
}

// Register:
add_filter( 'woocommerce_payment_gateways', function ( array $gateways ): array {
    $gateways[] = 'WC_Gateway_MyPlugin';
    return $gateways;
} );
```

## Shipping Method Skeleton

```php
class WC_Shipping_MyPlugin extends WC_Shipping_Method {

    public function __construct( $instance_id = 0 ) {
        $this->id                 = 'myplugin_shipping';
        $this->instance_id        = absint( $instance_id );
        $this->method_title       = __( 'My Shipping', 'myplugin' );
        $this->method_description = __( 'Custom shipping method.', 'myplugin' );
        $this->supports           = array( 'shipping-zones', 'instance-settings' );

        $this->init();
    }

    public function init(): void {
        $this->init_form_fields();
        $this->init_settings();
        $this->title = $this->get_option( 'title' );

        add_action( 'woocommerce_update_options_shipping_' . $this->id, array( $this, 'process_admin_options' ) );
    }

    public function calculate_shipping( $package = [] ): void {
        $rate = array(
            'id'    => $this->get_rate_id(),
            'label' => $this->title,
            'cost'  => '10.00',
        );

        $this->add_rate( $rate );
    }
}

add_action( 'woocommerce_shipping_init', function (): void {
    // Class defined above or included here.
} );

add_filter( 'woocommerce_shipping_methods', function ( array $methods ): array {
    $methods['myplugin_shipping'] = 'WC_Shipping_MyPlugin';
    return $methods;
} );
```

## WooCommerce REST API Extensions

```php
add_action( 'rest_api_init', function (): void {
    register_rest_route( 'myplugin/v1', '/orders/(?P\d+)/custom-data', array(
        'methods'             => 'GET',
        'callback'            => function ( WP_REST_Request $request ): WP_REST_Response|WP_Error {
            $order = wc_get_order( $request['id'] );
            if ( ! $order ) {
                return new WP_Error( 'not_found', __( 'Order not found', 'myplugin' ), array( 'status' => 404 ) );
            }
            return rest_ensure_response( array(
                'custom_field' => $order->get_meta( '_myplugin_custom_field' ),
            ) );
        },
        'permission_callback' => function (): bool {
            return current_user_can( 'manage_woocommerce' );
        },
    ) );
} );

// Extend existing WC REST API responses:
add_filter( 'woocommerce_rest_prepare_product_object', function ( WP_REST_Response $response, WC_Product $product, WP_REST_Request $request ): WP_REST_Response {
    $response->data['myplugin_custom'] = $product->get_meta( '_myplugin_custom_field' );
    return $response;
}, 10, 3 );
```

## WooCommerce Email Customization

```php
class WC_Email_MyPlugin_Notification extends WC_Email {

    public function __construct() {
        $this->id             = 'myplugin_notification';
        $this->title          = __( 'Custom Notification', 'myplugin' );
        $this->description    = __( 'Sent when a custom event occurs.', 'myplugin' );
        $this->heading        = __( 'Custom Notification', 'myplugin' );
        $this->subject        = __( '[{site_title}]: Custom Notification', 'myplugin' );
        $this->template_base  = MYPLUGIN_PATH . 'templates/';
        $this->template_html  = 'emails/custom-notification.php';
        $this->template_plain = 'emails/plain/custom-notification.php';
        $this->customer_email = true;

        parent::__construct();
    }

    public function trigger( int $order_id ): void {
        $this->setup_locale();
        $order = wc_get_order( $order_id );
        if ( $order ) {
            $this->object    = $order;
            $this->recipient = $order->get_billing_email();
            $this->send( $this->get_recipient(), $this->get_subject(), $this->get_content(), $this->get_headers(), $this->get_attachments() );
        }
        $this->restore_locale();
    }

    public function get_content_html(): string {
        return wc_get_template_html( $this->template_html, array(
            'order'         => $this->object,
            'email_heading' => $this->get_heading(),
            'email'         => $this,
        ), '', $this->template_base );
    }
}

add_filter( 'woocommerce_email_classes', function ( array $emails ): array {
    $emails['WC_Email_MyPlugin_Notification'] = new WC_Email_MyPlugin_Notification();
    return $emails;
} );
```

For the complete WooCommerce hook reference, see `references/woo-hooks.md`.

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [iwritec0de](https://github.com/iwritec0de)
- **Source:** [iwritec0de/wp-dev](https://github.com/iwritec0de/wp-dev)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-iwritec0de-wp-dev-woocommerce
- Seller: https://agentstack.voostack.com/s/iwritec0de
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
