Building a Custom WooCommerce Product Type from Scratch

WooCommerce’s product type system is fully extensible — you can create a custom product type by extending WC_Product, registering a product type label, and adding a custom tab to the product data meta box. This is how extensions like subscriptions, bundles, and gift cards are built.

Problem: WooCommerce ships with Simple, Variable, Grouped, and External product types — none of which map cleanly to a subscription box, a digital download with seat licences, or a service product with custom booking fields.

Solution: Create a custom product type by extending WC_Product, registering it with the product_type_selector filter, and adding a custom admin tab via woocommerce_product_data_tabs. Override get_type() and any relevant method — is_purchasable(), get_price() — to control how the product behaves in the cart and checkout.

The examples below define a "Donation" custom product type that lets the customer enter any amount, register it with WooCommerce, add it to the product type dropdown, and handle its price in the cart.

session?->get( 'donation_amount_' . $this->get_id() ) ?? '0';
    }

    public function is_virtual(): bool {
        return true;  // no shipping required
    }
}

// Register the class for this product type
add_filter( 'woocommerce_product_class', function( string $classname, string $product_type ): string {
    if ( 'donation' === $product_type ) {
        return 'WC_Product_Donation';
    }
    return $classname;
}, 10, 2 );

// Add 'Donation' to the product type dropdown in the admin
add_filter( 'product_type_selector', function( array $types ): array {
    $types['donation'] = __( 'Donation', 'myplugin' );
    return $types;
} );

// Load the class early so WooCommerce can find it
add_action( 'plugins_loaded', function() {
    require_once plugin_dir_path( __FILE__ ) . 'class-product-donation.php';
} );

Add a custom product data tab and handle the donation amount in the cart:

get_type() ) return;
    ?>
    
get_type() ) return $valid; $amount = floatval( wp_unslash( $_POST['donation_amount'] ?? 0 ) ); if ( $amount < 1 ) { wc_add_notice( __( 'Please enter a donation amount of at least $1.', 'myplugin' ), 'error' ); return false; } return $valid; }, 10, 2 ); // Store the amount in the cart item data and set the price add_filter( 'woocommerce_add_cart_item_data', function( array $data, int $product_id ): array { $product = wc_get_product( $product_id ); if ( $product && 'donation' === $product->get_type() ) { $data['donation_amount'] = floatval( wp_unslash( $_POST['donation_amount'] ?? 0 ) ); } return $data; }, 10, 2 ); add_filter( 'woocommerce_get_cart_item_from_session', function( array $item ): array { if ( isset( $item['donation_amount'] ) ) { $item['data']->set_price( $item['donation_amount'] ); } return $item; } );

NOTE: Custom product types integrate fully with WooCommerce reports, order emails, and the REST API as long as the class extends WC_Product and implements get_type(). For complex types like subscriptions, also implement needs_shipping(), get_stock_status(), and is_in_stock().

Leave Comment

Your email address will not be published. Required fields are marked *