Subscription Toggle in WooCommerce

In WooCommerce subscription products and standard products can’t be combined. For example, if you’d like to offer customers the option to purchase coffee as a one-time sale or as a convenient monthly subscription, you’ll need to create two separate products on the backend (even though it’s essentially the same product and SKU).

If you’re SEO focused, this might be a concern in terms of duplicate content and splitting page rank. For customers, this also isn’t a great experience. If a customer lands on the one-time product page, they might not know about the subscription option (and vicea versa).

A better example of subscription user experience is Target. If a product offers a subscription option, there’s a radio button toggle with a discount clearly highlighted. Turns out, with a little work, this is also possible to do in WooCommerce.

How to Implement a Subscription Toggle in WooCommerce

Here’s an example from a WooCommerce site. As you can see, the price and button text change when the radio button is toggled. When a customer clicks the “Subscribe” or “Add to Cart” button, the appropriate product is added to the cart.

This implementation still uses two products (a standard and subscription), but additional markup and javascript is added to standard product so that it can pull in pricing and update the cart for the linked subscription product.

Setting Up Product Links

WooCommerce products already have a tab for “Linked Products”, so this is where I’ve added an additional field for “Linked Subscription”.

The following code adds a field for selecting a linked subscription product + sanitizing/saving the input:

/**
* Adds an option under "Linked Products" on the product edit page.
*/
function example_product_options_related() {
global $post;
$meta_id = 'linked_subscription_id';
?>
<div class="options_group">
<p class="form-field">
<label for="<?php echo $meta_id; ?>"><?php _e( 'Linked Subscription', 'example' ); ?></label>
<select class="wc-product-search" style="width: 50%;" id="<?php echo $meta_id; ?>" name="<?php echo $meta_id; ?>[]" data-placeholder="<?php esc_attr_e( 'Search for a product&hellip;', 'example' ); ?>" data-action="woocommerce_json_search_products_and_variations" data-exclude="<?php echo intval( $post->ID ); ?>">
<?php
$product_ids = get_post_meta( $post->ID, $meta_id, true );
foreach ( $product_ids as $product_id ) {
$product = wc_get_product( $product_id );
if ( is_object( $product ) ) {
echo '<option value="' . esc_attr( $product_id ) . '"' . selected( true, true, false ) . '>' . wp_kses_post( $product->get_formatted_name() ) . '</option>';
}
}
?>
</select>
<?php echo wc_help_tip( __( 'Enables customer to choose subscription option from product page.', 'example' ) ); ?>
</p>
</div>
<?php }
add_action( 'woocommerce_product_options_related', 'example_product_options_related' );
/**
* Sanitizes and save the linked subscription product id
*/
function example_process_linked_subscription_meta( $post_id ) {
if ( ! ( isset( $_POST['woocommerce_meta_nonce'], $_POST[$meta_id] ) || wp_verify_nonce( sanitize_key( $_POST['woocommerce_meta_nonce'] ), 'woocommerce_save_data' ) ) ) {
return false;
}
$meta_id = 'linked_subscription_id';
$linked_subscription_id = isset( $_POST[$meta_id] ) ? array_map( 'intval', (array) $_POST[$meta_id] ) : array();
update_post_meta( $post_id, $meta_id, $linked_subscription_id );
}
add_action( 'woocommerce_process_product_meta', 'example_process_linked_subscription_meta' );

Displaying the Toggle

The code below is what displays the subscription toggle for products that have a linked subscription. This will likely require some minor tweaks to work correctly with your site (as markup might be different and styling might be needed)- but it should at least get you set in the right direction.

This method works by querying the linked product to get the pricing information and product ID. Then these attributes are swapped out with javascript when someone toggles between products (the discount is hardcoded at the moment, but could also be made to be dynamic).

/**
* Adds a subscription toggle option for products that have a linked subscription.
* Including javascript inline for clarity.
*/
function example_add_subscription_option() {
global $product;
$display = false;
$linked_id = $product->get_meta( 'linked_subscription_id' );
if ( isset( $linked_id ) && $linked_id[0] ) {
$linked_product = wc_get_product( $linked_id[0] );
// If linked object is an actual product, set $display to true
if ( is_object( $linked_product ) ) {
$display = true;
}
}
?>
<?php
// Display the subscription toggle markup
if ( $display ) :
$onetime = array(
'id' => $product->get_id(),
'label' => 'One Time Order',
'button' => 'Add to Cart',
'price_html' => $product->get_price_html()
);
$subscription = array(
'id' => $linked_product->get_id(),
'label' => 'Subscribe and Save 15% (cancel anytime)',
'button' => 'Subscribe',
'price_html' => $linked_product->get_price_html()
);
?>
<div class="subscription-toggle">
<div class="radio-select">
<fieldset>
<div class="option option-onetime-order" data-product="<?php echo $onetime['id']; ?>" data-button="<?php echo $onetime['button']; ?>" data-price="<?php echo esc_html( $onetime['price_html'] ); ?>">
<input class="sub-switch" checked="checked" name="onetime-order" type="radio" />
<label for="onetime-order"><?php echo $onetime['label']; ?></label>
</div>
<div class="option option-subscription" data-product="<?php echo $subscription['id']; ?>" data-button="<?php echo $subscription['button']; ?>" data-price="<?php echo esc_html( $subscription['price_html'] ); ?>">
<input class="sub-switch" name="subscription-order" type="radio" />
<label name="subscription-order"><?php echo $subscription['label']; ?></label>
</div>
</fieldset>
</div>
</div>
<script>
jQuery(document).ready(function($) {
$('.subscription-toggle .option').on( 'click', function() {
$('.subscription-toggle input').prop('checked', false);
var $option = $(this).find('input');
var product = $(this).data('product');
var button = $(this).data('button');
var price = $(this).data('price');
$option.prop('checked', true);
$('.single_add_to_cart_button').text(button);
$('.price').html(price);
$('input[name=add-to-cart]').val(product);
});
});
</script>
<?php endif; ?>
<?php }
add_action( 'woocommerce_single_product_summary', 'example_add_subscription_option', 25 );

Last Steps

  • 301 redirect all the subscription products to the standard product
  • Filter the linked subscription products so they don’t display in your shop archives or search

Conclusions

Although creating two separate products requires a bit of additional effort by admins, I doubt many stores offer subscriptions for more than a few items and this method should perfectly address that use case. Have suggestions to improve the code? Leave them below or comment on the gist on GitHub.

About Devin

I'm a WordPress developer based in Austin, Texas. I run a little theme shop called DevPress and work for a startup called Nano. Find me on twitter @devinsays.

Leave a Reply