Custom Post Type WooCommerce Integration – Sell Any CPT with WooCommerce
Custom Post Type WooCommerce Integration makes any WordPress custom post type (CPT) purchasable through WooCommerce — without recreating each post as a separate WooCommerce product. If you already store content as custom post types (courses, events, bookings, listings, real estate, cars, books, directories, or digital downloads), you normally cannot sell it directly with WooCommerce. The usual workaround is to duplicate every post as a WooCommerce product and keep the two in sync by hand. This plugin removes that step. It connects your existing CPT to WooCommerce so a single post can be added to the cart, checked out, and paid for using the WooCommerce systems you already trust. Connect the post type, set a price (or map an existing meta key), and drop a shortcode where you want the price and add-to-cart button to appear. Your custom content becomes sellable, while WooCommerce continues to handle the cart, checkout, orders, payment gateways, coupons, taxes, and emails. The problem this plugin solves Custom post types hold your content, but WooCommerce only sells WooCommerce products. Bridging the two normally means maintaining duplicate records — one CPT post for display and one product for selling. That duplication causes mismatched prices, broken stock counts, and extra maintenance. This plugin lets a CPT post be the sellable item. There is no second copy to manage, no data migration, and no code required. What you can do Sell any custom post type through WooCommerce. Use the standard WooCommerce cart and checkout for your CPT content. Process CPT purchases as normal WooCommerce orders. Accept payment through any WooCommerce payment gateway (Stripe, PayPal, and others). Apply WooCommerce coupons, taxes, and order emails to CPT purchases. Use WooCommerce shipping where the product type supports it. Reuse prices you already store in ACF, JetEngine, Meta Box, or any custom meta key. Avoid creating and syncing duplicate WooCommerce products. Two ways to use it Full shop experience — make CPT archive pages look like the WooCommerce shop and CPT single pages look like WooCommerce product pages, with product categories and tags. Checkout and payments only — keep your custom landing pages exactly as designed and use WooCommerce purely as a secure checkout and payment processor. Documentation: Getting started guide | Pro version: CPT WooCommerce Integration Pro Traditional WooCommerce Workflow vs This Plugin Traditional workflow Create your content as a custom post type. Manually recreate the same item as a WooCommerce product. Keep price, stock, and details in sync across both copies. Maintain two records forever. With this plugin Create your content as a custom post type. Connect the post type and set (or map) a price. Sell the post directly — WooCommerce handles cart, checkout, and orders. The difference: no duplicate products, no manual syncing, and one source of truth for each item. Why use this plugin No duplicate products — a CPT post becomes the sellable item, so there is nothing to copy or sync. Sell almost anything — courses, events, bookings, listings, digital files, donations, services, and other custom content. Use existing meta fields — already storing prices in ACF, JetEngine, Meta Box, or custom meta? Map the meta key and the plugin reads it directly. WooCommerce as checkout only — keep custom landing pages and use WooCommerce just for secure payments. No coding required — configure everything from a clean, React-powered admin interface. Theme and builder friendly — shortcodes work in any theme, template, widget, or page builder. Free Features Integrate unlimited custom post types with WooCommerce. Add a regular price meta field to any CPT. Add a sale price meta field to any CPT. Fallback regular price — map an existing meta key (works well with ACF, JetEngine, and Meta Box values). Fallback sale price — reuse a meta key you already have. Shortcode for the price: [cptwooint_price/] Shortcode for the add-to-cart button: [cptwooint_cart_button/] Shortcode for the short description: [cptwooint_short_description/] Shortcode for WooCommerce notices: [cptwooint_wc_all_notices/] Make CPT archive pages look like the WooCommerce shop page. Make CPT single pages look like the WooCommerce product page. Use WooCommerce product categories with your CPT. Use WooCommerce product tags with your CPT. Customize the add-to-cart button color, background, and size from settings. Pro Features The Pro version includes every free feature plus: Quick Edit and Bulk Edit for CPT prices and stock directly from the post list. Full product-type support — Simple, Variable, External/Affiliate, and Grouped. Display CPT posts on the WooCommerce shop page alongside regular products. Sale price scheduling with start and end dates. Inventory management — SKU, stock quantity, stock status, low-stock alerts, backorders, sold individually, and purchase limits. Shipping data — weight, dimensions, and shipping classes. Product gallery images with a Swiper slider (vertical/horizontal thumbnails, RTL, auto-height, and zoom). Downloadable products — file delivery, download limits, and expiry dates. Customer downloads from the WooCommerce My Account page. Upsells and cross-sells for CPTs. Product attributes — a full taxonomy-based attribute management interface for CPTs. Product reviews and ratings — 1 to 5 stars, verified purchase badge, HPOS-compatible, with rating distribution. Structured data / JSON-LD schema — Product, Review, BreadcrumbList, Website, and Order markup for richer search results. Additional shortcodes — [cptwooint_sku], [cptwooint_attributes], [cptwooint_gallery], [cptwooint_gallery_with_variation], and [cptwooint_upsell_products]. WooCommerce Bookings integration (sell bookable CPTs). Easy Booking for WooCommerce integration. LearnPress review compatibility (sell and review courses). Extra Product Options (Themecomplete EPO) support. Automatic license updates from the WordPress dashboard. Learn more about Pro Use Cases Online courses — sell LearnPress or custom course post types through WooCommerce. Bookings and appointments — sell bookable post types (BA Book Everything in free; WooCommerce Bookings and Easy Booking in Pro). Events and tickets — sell event post types with WooCommerce checkout. Directories and classifieds — charge for listings built with JetEngine or other CPT tools. Real estate and properties — sell or accept payment on property listings. Cars and vehicles — turn vehicle listings into purchasable items. Books and digital downloads — sell files with download control (Pro). Memberships and services — sell access or service posts. Donations — set a fixed amount on a page or post and collect it through WooCommerce checkout. Payments for landing pages — keep a custom design and use WooCommerce only for the transaction. Supported Integrations LearnPress — sell courses through WooCommerce. Documentation Advanced Custom Fields (ACF) — read prices stored in ACF fields by mapping the meta key. JetEngine — CPT and meta integration, including variation panels. BA Book Everything — sell bookings and appointments. Documentation Meta Box — reuse price values stored in Meta Box fields via meta-key mapping. Custom Post Type UI — register a CPT and connect it right away. Extra Product Options for WooCommerce (Free and Pro). WooCommerce Bookings (Pro). Easy Booking for WooCommerce (Pro). Works with Elementor, Visual Composer, Gutenberg, and most page builders. Compatible wherever standard WooCommerce hooks are respected. Quick Start — Four Steps Install and activate Custom Post Type WooCommerce Integration. Open WC Integration in the WordPress admin sidebar. Select your custom post type under “Select Post Type”. Enable the default price meta field — or map your existing meta keys. Your CPT is now WooCommerce-ready. Already storing prices in meta fields? If your CPT already stores prices via ACF, JetEngine, Meta Box, or another plugin: Select the regular price meta key from the dropdown. Optionally select a sale price meta key. Save settings — the plugin reads prices straight from your existing data, with no migration. Displaying the price and cart button Use these shortcodes in pages, posts, single templates, page builders, or widgets: [cptwooint_price/] — the formatted price. [cptwooint_cart_button/] — the add-to-cart button. [cptwooint_short_description/] — the short description. [cptwooint_wc_all_notices/] — WooCommerce notices (errors, success, warnings).
Top keywords
- woocommerce48×3.85%
- post26×2.08%
- cpt24×1.92%
- meta22×1.76%
- custom20×1.60%
- price18×1.44%
- product16×1.28%
- sell14×1.12%
- cptwooint13×1.04%
- type12×0.96%
- custom post11×0.88%
- post type11×0.88%
Text Modules
Use the new Text Modules custom post type and display a text module by either shortcode or widget. Have you ever wanted to use some pieces of text information more than once? For instance, contact information such as a postal address? Or some slogan, motto or claim? This is exactly when Text Modules kicks in. Usage This plugin registers a simple post type for text modules. A text module can be accessed either via shortcode (by means of the text module’s ID or slug) or via a new Tex Modules widget. Filters In order to customize certain aspects of the plugin, it provides you with several filters. For each of these, a short description as well as a code example on how to alter the default behavior is given below. Just put the according code snippet in your theme’s functions.php file or your customization plugin, or to some other appropriate place. text_modules_after_widget_content This filter lets you alter the HTML after the widget content. /** * Filter the HTML after the widget content. * * @param string $after_widget_content Some HTML after the widget content. */ add_filter( 'text_modules_after_widget_content', function() { return ' '; } ); text_modules_before_widget_content This filter lets you alter the HTML before the widget content. /** * Filter the HTML before the widget content. * * @param string $before_widget_content Some HTML before the widget content. */ add_filter( 'text_modules_before_widget_content', function() { return ' '; } ); text_modules_post_type Yes, you can alter the post type (slug). /** * Filter the post type. * * @param string $post_type Post type. */ add_filter( 'text_modules_post_type', function() { return 'exotic_stuff'; } ); text_modules_post_type_args If you want to alter a specific post type argument but you can’t find a fitting filter, there’s text_modules_post_type_args, which provides you with the complete args array. /** * Filter the post type args. * * @param array $args Post type args. */ add_filter( 'text_modules_post_type_args', function( $args ) { // Use hierarchical external content $args[ 'hierarchical' ] = TRUE; return $args; } ); text_modules_post_type_description The post type description can be customized by using the text_modules_post_type_description filter. /** * Filter the post type description. * * @param string $description Post type description. */ add_filter( 'text_modules_post_type_description', function() { // Provide a description return 'Simple post type for text modules.'; } ); text_modules_post_type_labels In case you don’t like the labels, easily adapt them to your liking. /** * Filter the post type labels. * * @param array $labels Post type labels. */ add_filter( 'text_modules_post_type_labels', function( $labels ) { // A little more horror, please... $labels[ 'not_found' ] = 'ZOMG, no text module found!!1!!1!!oneone!!!1!eleven!1!'; return $labels; } ); text_modules_post_type_supports This filter provides you with the post type supports. /** * Filter the post type supports. * * @param array $supports Post type supports. */ add_filter( 'text_modules_post_type_supports', function( $supports ) { // Let's add revisions for our post type if ( ! in_array( 'revisions', $supports ) ) { $supports[] = 'revisions'; } return $supports; } ); text_modules_shortcode_apply_do_shortcode By default, do_shortcode() will be called on the shortcode output. Of course, you can change that. /** * Filter if the shortcode should apply do_shortcode() to the output. * * @param bool $do_shortcode Should the shortcode apply do_shortcode()? */ add_filter( 'text_modules_shortcode_apply_do_shortcode', '__return_false' ); text_modules_shortcode_callback In case you would like to adapt how the shortcode data is handled, you can provide your own shortcode callback. This can either be a string holding the function name, or an array with either a class name or an object, and the according method. /** * Filter the shortcode callback. * * @param array|string $callback Shortcode callback. */ add_filter( 'text_modules_shortcode_callback', function() { return 'my_text_modules_shortcode_callback'; } ); text_modules_shortcode_id_attribute_name This filter lets you alter the shortcode’s ‘id’ attribute name. /** * Filter the 'id' shortcode attribute name. * * @param string $name Attribute name. */ add_filter( 'text_modules_shortcode_id_attribute_name', function() { return 'post_id'; } ); text_modules_shortcode_output This filter lets you alter the shortcode output. The second parameter holds the shortcode attributes array. /** * Filter the shortcode output. * * @param string $output Shortcode output. * @param array $atts Shortcode attributes array. */ add_filter( 'text_modules_shortcode_output', function( $output ) { return $output . ' Over and out.'; } ); text_modules_shortcode_query_args Also, there’s text_modules_shortcode_query_args, which provides you with the complete args array for the shortcode’s query. /** * Filter the shortcode query args. * * @param array $args Shortcode query args. */ add_filter( 'text_modules_shortcode_query_args', function( $args ) { // Exclude some text modules by ID $args[ 'post__not_in' ] = array( 4, 8, 15, 16, 23, 42 ); return $args; } ); text_modules_shortcode_slug_attribute_name This filter lets you alter the shortcode’s ‘slug’ attribute name. /** * Filter the 'slug' shortcode attribute name. * * @param string $name Attribute name. */ add_filter( 'text_modules_shortcode_slug_attribute_name', function() { return 'post_slug'; } ); text_modules_shortcode_tag This filter lets you alter the shortcode’s tag. /** * Filter the shortcode tag. * * @param string $shortcode_tag Shortcode tag. */ add_filter( 'text_modules_shortcode_tag', function() { return 'text_block'; } ); text_modules_shortcode_use_slug By default, text modules are being queried by their post ID first. Of course, you can change that and use the post slug instead. /** * Filter if the shortcode (query) should use the post slug instead of the post ID. * * @param bool $use_slug Use slug instead of ID? */ add_filter( 'text_modules_shortcode_use_slug', '__return_true' ); text_modules_widget_form_query_args Also, there’s text_modules_widget_form_query_args, which provides you with the complete args array for the widget form’s query. /** * Filter the widget form query args. * * @param array $args Query args. */ add_filter( 'text_modules_widget_form_query_args', function( $args ) { // Exclude some text modules by ID $args[ 'post__not_in' ] = array( 4, 8, 15, 16, 23, 42 ); return $args; } ); Contribution To contribute to this plugin, please see its GitHub repository. If you have a feature request, or if you have developed the feature already, please feel free to use the Issues and/or Pull Requests section. Of course, you can also provide me with translations if you would like to use the plugin in another not yet included language.