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().