WooCommerce Checkout Security: Preventing Purchases of Non-Published Products

The WooCommerce support forum recently highlighted a critical checkout anomaly: the ability for customers to complete purchases for products that are no longer in a "published" state. This issue, raised in the topic "WooCommerce checkout allowed on non published products," points to a potential gap in WooCommerce's default checkout validation process, which can have significant implications for store owners.

Addressing the WooCommerce Checkout Anomaly: Non-Published Products and Purchase Prevention

The original forum discussion brought to light a specific scenario: "After the products are added to cart, when the products status changes to anything from published, then the checkout is supposed to be blocked. However, the checkout succeeds." This observation, tested on a fresh WooCommerce instance with a default theme, indicates that WooCommerce, by default, might not re-validate a product's 'published' status at the final checkout stage, especially if the product was added to the cart when it was published.

The Core Problem: Product Status and Checkout Integrity

For many store owners, controlling product visibility and availability is fundamental. Products are often set to 'draft,' 'pending review,' 'private,' or even 'trash' for various reasons: inventory management, product updates, temporary unavailability, or pre-launch staging. The expectation is clear: if a product is not 'published,' it should not be purchasable. Allowing checkout for such products can lead to:

  • Inventory Discrepancies: Selling products that are not physically available or intended for sale.
  • Customer Dissatisfaction: Orders placed for products that cannot be fulfilled.
  • Operational Headaches: Manual order cancellations and refunds.
  • Data Inconsistencies: Sales data for products that should not have been sold.

Understanding WooCommerce Product Statuses

WooCommerce leverages WordPress's post status system for products. Key statuses include:

  • Published: Publicly visible and available for purchase.
  • Draft: Not visible to customers, still being worked on.
  • Pending Review: Awaiting approval before publishing.
  • Private: Visible only to logged-in users with appropriate permissions.
  • Trash: Deleted products, but recoverable.

The user in the forum correctly assumed that any status other than 'published' should prevent a purchase, particularly at the point of checkout.

The Solution: Enforcing 'Published' Status at Checkout

To address this, we need to introduce a custom validation layer that checks the status of all products in the cart immediately before the checkout process is completed. WooCommerce provides several hooks for this purpose, and woocommerce_check_cart_items is an excellent choice as it fires early in the checkout validation process.

Step-by-Step Instructions: Preventing Checkout for Non-Published Products

To implement this crucial validation, you will need to add custom PHP code to your WooCommerce setup. This code will iterate through each item in the customer's cart and verify that its corresponding product has a 'publish' status. If any product does not meet this criterion, the checkout will be blocked, and an informative error message will be displayed.

1. Access Your WordPress Site Files

You can do this via SFTP/FTP client or through your hosting provider's file manager.

2. Locate or Create Your Custom Code Location

It's best practice to add custom code either to your theme's functions.php file (if using a child theme) or, preferably, within a custom plugin for better maintainability and update safety.

  • Child Theme (Recommended for theme-specific functions): Navigate to wp-content/themes/your-child-theme/ and open functions.php.
  • Custom Plugin (Recommended for site-wide functionality): Create a new folder in wp-content/plugins/ (e.g., wc-checkout-enhancements), and inside it, create a PHP file (e.g., wc-checkout-enhancements.php). Add the necessary plugin header comments.

3. Add the Custom Validation Code

Insert the following PHP code block into your chosen file:


/**
 * Prevent checkout if any product in the cart is not "published".
 *
 * @param bool $valid True if the cart items are valid, false otherwise.
 * @param array $errors An array of WC_Validation_Error objects.
 * @return bool
 */
function custom_woocommerce_validate_cart_items_status( $valid, $errors ) {
    if ( is_admin() && ! defined( 'DOING_AJAX' ) ) {
        return $valid; // Skip validation in admin area unless it's an AJAX request
    }

    foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
        $_product = apply_filters( 'woocommerce_cart_item_product', $cart_item['data'], $cart_item, $cart_item_key );

        if ( ! $_product ) {
            continue; // Product data not found, skip
        }

        // Get the actual product object to check its status
        $product_id = $_product->get_id();
        $product_status = get_post_status( $product_id );

        // If the product is not published, add an error and invalidate checkout
        if ( 'publish' !== $product_status ) {
            $product_title = $_product->get_name();
            $error_message = sprintf(
                __( 'The product “%s” is no longer available for purchase. Please remove it from your cart to proceed.', 'your-text-domain' ),
                esc_html( $product_title )
            );
            wc_add_notice( $error_message, 'error' );
            $valid = false;
        }
    }
    return $valid;
}
add_filter( 'woocommerce_check_cart_items', 'custom_woocommerce_validate_cart_items_status', 10, 2 );

4. Save Changes and Test

Save the file and then thoroughly test your checkout process. Try adding a product to your cart, then changing its status to 'draft' or 'private' in the WordPress admin, and then attempting to check out. You should see the error message and be prevented from completing the purchase.

Important Considerations and Best Practices

  • Child Theme or Custom Plugin: Always use a child theme for functions.php modifications or a custom plugin. Directly editing a parent theme's functions.php will result in your changes being lost during theme updates.
  • User Experience: The error message provided is generic. You might want to customize it further to provide more specific instructions or links back to the cart page.
  • Caching: If you use caching plugins, clear your cache after implementing the code to ensure the changes take effect immediately.
  • Testing: Always test custom code on a staging environment before deploying to a live site.
  • Text Domain: Remember to replace 'your-text-domain' with your theme's or plugin's actual text domain for proper internationalization.

Conclusion

The issue highlighted in the WooCommerce support forum topic "WooCommerce checkout allowed on non published products" underscores the importance of robust validation throughout the e-commerce funnel. While WooCommerce provides a solid foundation, specific business logic, like preventing the purchase of non-published items, often requires custom enhancements. By implementing the provided code snippet, store owners can ensure greater control over their product catalog and deliver a more consistent and reliable shopping experience, preventing unintended sales and subsequent administrative overhead.

Start with the tools

Explore migration tools

See options, compare methods, and pick the path that fits your store.

Explore migration tools