Skip to content

Instantly share code, notes, and snippets.

@igorbenic
Last active February 21, 2024 21:34
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save igorbenic/5700dc00398dc86b432364d3851b313b to your computer and use it in GitHub Desktop.
Save igorbenic/5700dc00398dc86b432364d3851b313b to your computer and use it in GitHub Desktop.
Selling Simple Products as WooCommerce Subscriptions - Parts of code from tutorials | Selling Simple Products as WooCommerce Subscriptions - Admin | https://www.ibenic.com/selling-simple-products-woocommerce-subscriptions-front | https://www.ibenic.com/selling-simple-products-woocommerce-subscriptions-admin
<?php
namespace Simple_Product_Subscriptions;
class Cart {
/**
* Cart Hooks
*/
public function hooks() {
// Hooks from before here....
// Add scheme data to cart items that can be purchased on a recurring basis.
add_filter( 'woocommerce_add_cart_item_data', array( __CLASS__, 'add_cart_item_data' ), 10, 3 );
// Load saved session data of cart items that can be purchased on a recurring basis.
add_filter( 'woocommerce_get_cart_item_from_session', array( __CLASS__, 'load_cart_item_data_from_session' ), 5, 2 );
}
// Functions from before are here
/**
* Add scheme data to cart items that can be purchased on a recurring basis.
*
* @param array $cart_item
* @param int $product_id
* @param int $variation_id
* @return array
*/
public static function add_cart_item_data( $cart_item, $product_id, $variation_id ) {
if ( isset( $_POST['sps'] ) ) {
$cart_item['sps'] = $_POST['sps'];
}
return $cart_item;
}
/**
* Load saved session data of cart items that can be pruchased on a recurring basis.
*
* @param array $cart_item
* @param array $item_session_values
* @return array
*/
public static function load_cart_item_data_from_session( $cart_item, $item_session_values ) {
if ( isset( $item_session_values[ 'sps' ] ) ) {
$cart_item[ 'sps' ] = $item_session_values[ 'sps' ];
}
return $cart_item;
}
}
<?php
namespace Simple_Product_Subscriptions;
class Cart {
/**
* Cart Hooks
*/
public function hooks() {
add_action( 'woocommerce_after_add_to_cart_button', array( __CLASS__, 'add_to_cart_button' ) );
// Other code will go here as well.
}
/**
* @param \WC_Product $product
*
* @return bool
*/
public static function has_subscription_option( $product ) {
return Product::get_subscription_interval( $product->get_id() ) && Product::get_subscription_period( $product->get_id() ) && Product::get_subscription_price( $product->get_id() );
}
/**
* Show the Subscription Add to Cart
*/
public static function add_to_cart_button() {
global $product;
if ( ! self::has_subscription_option($product) ) {
return;
}
$price = wc_price( Product::get_subscription_price($product->get_id()) ) . sprintf(esc_html__(' every %1$s', 'sps'), wcs_get_subscription_period_strings(Product::get_subscription_interval($product->get_id()), Product::get_subscription_period($product->get_id())));
$data = Product::get_subscription_interval($product->get_id()) . '_' . Product::get_subscription_period($product->get_id());
?>
<div>
Or
<br/>
<button data-sps="<?php echo esc_attr( $data ); ?>" type="submit" name="add-to-cart" data-product_id="<?php echo esc_attr( $product->get_id() ); ?>" value="<?php echo esc_attr( $product->get_id() ); ?>" class="add_to_cart_button button alt ajax_add_to_cart"><?php echo $price; ?></button>
</div>
<?php
}
}
<?php
namespace Simple_Product_Subscriptions;
class Cart {
/**
* Cart Hooks
*/
public function hooks() {
// Hooks from before are here
// Inspect product-level/cart-level session data and apply subscription schemes to cart items as needed.
add_action( 'woocommerce_cart_loaded_from_session', array( __CLASS__, 'apply_subscriptions' ), 5 );
// Inspect product-level/cart-level session data on add-to-cart and apply subscription schemes to cart items as needed. Then, recalculate totals.
add_action( 'woocommerce_add_to_cart', array( __CLASS__, 'apply_subscription_schemes_on_add_to_cart' ), 19, 6 );
}
// Methods from before are here
/**
* Inspect product-level/cart-level session data and apply subscription schemes on cart items as needed.
* Then, recalculate totals.
*
* @return void
*/
public static function apply_subscription_schemes_on_add_to_cart( $item_key, $product_id, $quantity, $variation_id, $variation, $item_data ) {
self::apply_subscriptions( WC()->cart );
}
/**
* Inspect product-level/cart-level session data and apply subscription schemes to cart items as needed.
*
* @param \WC_Cart $cart
* @return void
*/
public static function apply_subscriptions( $cart ) {
foreach ( $cart->cart_contents as $cart_item_key => $cart_item ) {
if ( isset( $cart_item['sps'] ) ) {
// Convert the product object to a subscription, if needed.
$cart->cart_contents[ $cart_item_key ] = self::apply_subscription( $cart->cart_contents[ $cart_item_key ] );
}
}
}
}
<?php
namespace Simple_Product_Subscriptions;
class Admin {
public function run() {
add_action( 'woocommerce_product_options_pricing', array( $this, 'display_subscription_options' ) );
add_action( 'woocommerce_process_product_meta', array( $this, 'save' ), 99, 2 );
}
// ... code for display_subscription_options before is here
/**
* Saving Subscription Prices
*
* @param $post_id
* @param $post
*/
public function save( $post_id, $post ) {
if ( isset( $_POST['_sp_subscription_interval'] ) ) {
Product::set_subscription_interval( $post_id, sanitize_text_field( $_POST['_sp_subscription_interval'] ) );
}
if ( isset( $_POST['_sp_subscription_period'\] ) ) {
Product::set_subscription_period( $post_id, sanitize_text_field( $_POST['_sp_subscription_period'] ) );
}
if ( isset( $_POST['_sp_subscription_price'] ) ) {
Product::set_subscription_price( $post_id, sanitize_text_field( $_POST['_sp_subscription_price'] ) );
}
}
}
<?php
namespace Simple_Product_Subscriptions;
class Cart {
/**
* Cart Hooks
*/
public function hooks() {
add_action( 'woocommerce_after_add_to_cart_button', array( __CLASS__, 'add_to_cart_button' ) );
// Add scheme data to cart items that can be purchased on a recurring basis.
add_filter( 'woocommerce_add_cart_item_data', array( __CLASS__, 'add_cart_item_data' ), 10, 3 );
// Load saved session data of cart items that can be purchased on a recurring basis.
add_filter( 'woocommerce_get_cart_item_from_session', array( __CLASS__, 'load_cart_item_data_from_session' ), 5, 2 );
// Inspect product-level/cart-level session data and apply subscription schemes to cart items as needed.
add_action( 'woocommerce_cart_loaded_from_session', array( __CLASS__, 'apply_subscriptions' ), 5 );
// Inspect product-level/cart-level session data on add-to-cart and apply subscription schemes to cart items as needed. Then, recalculate totals.
add_action( 'woocommerce_add_to_cart', array( __CLASS__, 'apply_subscription_schemes_on_add_to_cart' ), 19, 6 );
// Allow WCS to recognize any product as a subscription.
add_filter( 'woocommerce_is_subscription', array( __CLASS__, 'filter_is_subscription' ), 10, 3 );
add_filter( 'woocommerce_cart_item_price', array( __CLASS__, 'show_cart_item_subscription_options' ), 1000, 3 );
}
/**
* @param \WC_Product $product
*
* @return bool
*/
public static function has_subscription_option( $product ) {
return Product::get_subscription_interval( $product->get_id() ) && Product::get_subscription_period( $product->get_id() ) && Product::get_subscription_price( $product->get_id() );
}
/**
* Show the Subscription Add to Cart
*/
public static function add_to_cart_button() {
global $product;
if ( ! self::has_subscription_option($product) ) {
return;
}
$price = wc_price( Product::get_subscription_price($product->get_id()) ) . sprintf(esc_html__(' every %1$s', 'sps'), wcs_get_subscription_period_strings(Product::get_subscription_interval($product->get_id()), Product::get_subscription_period($product->get_id())));
$data = Product::get_subscription_interval($product->get_id()) . '_' . Product::get_subscription_period($product->get_id());
?>
<div>
Or
<br/>
<button data-sps="<?php echo esc_attr( $data ); ?>" type="submit" name="add-to-cart" data-product_id="<?php echo esc_attr( $product->get_id() ); ?>" value="<?php echo esc_attr( $product->get_id() ); ?>" class="add_to_cart_button button alt ajax_add_to_cart"><?php echo $price; ?></button>
</div>
<?php
}
/**
* Displays cart item options for purchasing a product once or creating a subscription from it.
*
* @param string $price
* @param array $cart_item
* @param string $cart_item_key
* @return string
*/
public static function show_cart_item_subscription_options( $price, $cart_item, $cart_item_key ) {
$product = $cart_item[ 'data' ];
$supports_args = array(
'cart_item' => $cart_item,
'cart_item_key' => $cart_item_key
);
$is_mini_cart = did_action( 'woocommerce_before_mini_cart' ) !== did_action( 'woocommerce_after_mini_cart' );
// Only show options in cart.
if ( ! is_cart() || $is_mini_cart ) {
return $price;
}
if ( ! self::is_subscription( $product ) ) {
return $price;
}
// Grab bare price without subscription details.
remove_filter( 'woocommerce_cart_product_price', array( 'WC_Subscriptions_Cart', 'cart_product_price' ), 10, 2 );
remove_filter( 'woocommerce_cart_item_price', array( __CLASS__, 'show_cart_item_subscription_options' ), 1000, 3 );
$price = wc_price( Product::get_subscription_price( $product->get_id() ) ) . sprintf(esc_html__(' every %1$s', 'jg-toolbox'), wcs_get_subscription_period_strings($product->get_meta( '_subscription_period_interval', true ), $product->get_meta( '_subscription_period', true )));
add_filter( 'woocommerce_cart_item_price', array( __CLASS__, 'show_cart_item_subscription_options' ), 1000, 3 );
add_filter( 'woocommerce_cart_product_price', array( 'WC_Subscriptions_Cart', 'cart_product_price' ), 10, 2 );
return $price;
}
/**
* Hooks onto 'woocommerce_is_subscription' to trick WCS into thinking it is dealing with a subscription-type product.
*
* @param boolean $is
* @param int $product_id
* @param \WC_Product $product
* @return boolean
*/
public static function filter_is_subscription( $is, $product_id, $product ) {
if ( ! $product ) {
return $is;
}
if ( self::is_subscription( $product ) ) {
$is = true;
}
return $is;
}
/**
* Determines if a subscription scheme is set on the product object.
*
* @param \WC_Product $product Product object to check.
* @return boolean Result of check.
*/
public static function is_subscription( $product ) {
return $product->get_meta( '_subscription_period', true ) && $product->get_meta( '_subscription_period_interval', true );
}
/**
* Delete object meta in use by the application layer.
* Note that the subscription state of a product object:
*
* 1. Cannot be persisted in the DB.
* 2. Is lost when the object is saved.
*
* This is intended behavior.
*
* @param \WC_Product $product
*/
public static function delete_runtime_meta( $product ) {
// Don't delete any subscription product-type meta :)
if ( ! self::is_subscription_product_type( $product ) ) {
$product->delete_meta_data( '_subscription_period' );
$product->delete_meta_data( '_subscription_period_interval' );
$product->delete_meta_data( '_subscription_price' );
}
}
/**
* Checks a product object to determine if it is a WCS subscription-type product.
*
* @param \WC_Product $product Product object to check.
* @return boolean Result of check.
*/
public static function is_subscription_product_type( $product ) {
return $product->is_type( array( 'subscription', 'subscription_variation', 'variable-subscription' ) );
}
/**
* Add scheme data to cart items that can be purchased on a recurring basis.
*
* @param array $cart_item
* @param int $product_id
* @param int $variation_id
* @return array
*/
public static function add_cart_item_data( $cart_item, $product_id, $variation_id ) {
if ( isset( $_POST['sps'] ) ) {
$cart_item['sps'] = $_POST['sps'];
}
return $cart_item;
}
/**
* Load saved session data of cart items that can be pruchased on a recurring basis.
*
* @param array $cart_item
* @param array $item_session_values
* @return array
*/
public static function load_cart_item_data_from_session( $cart_item, $item_session_values ) {
if ( isset( $item_session_values[ 'sps' ] ) ) {
$cart_item[ 'sps' ] = $item_session_values[ 'sps' ];
}
return $cart_item;
}
/**
* Inspect product-level/cart-level session data and apply subscription schemes on cart items as needed.
* Then, recalculate totals.
*
* @return void
*/
public static function apply_subscription_schemes_on_add_to_cart( $item_key, $product_id, $quantity, $variation_id, $variation, $item_data ) {
self::apply_subscriptions( WC()->cart );
}
/**
* Get Subscription Scheme
*
* @param $cart_item
*
* @return array|mixed
*/
public static function get_subscription_scheme( $cart_item ) {
return isset( $cart_item[ 'sps' ] ) ? $cart_item[ 'sps' ] : array();
}
/**
* Set the active subscription scheme. Key value should be:
*
* - string to activate a subscription scheme (valid key required);
* - false to indicate that the product is sold in a non-recurring manner; or
* - null to indicate that the susbcription state of the product is undefined.
*
* Note that the scheme set on the object may become invalid if 'set_subscription_schemes' or 'set_forced_subscription_scheme' are modified.
*
* @param \WC_Product $product Product object.
* @param string $key Identifier of subscription scheme to activate on object.
* @return boolean Action result.
*/
public static function set_subscription_scheme( $product, $scheme ) {
$period_value = explode( '_', $scheme );
$period = $period_value[1];
$interval = $period_value[0];
$product->add_meta_data( '_subscription_period', $period );
$product->add_meta_data( '_subscription_period_interval', $interval );
$product->add_meta_data( '_subscription_price', Product::get_subscription_price( $product->get_id() ) );
return true;
}
/**
* Applies a saved subscription key to a cart item.
*
* @param array $cart_item
* @return array
*/
public static function apply_subscription( $cart_item ) {
$scheme_to_apply = self::get_subscription_scheme( $cart_item );
if ( $scheme_to_apply ) {
self::set_subscription_scheme( $cart_item['data'], $scheme_to_apply );
$cart_item['data']->set_price(Product::get_subscription_price( $cart_item['data']->get_id() ) );
}
return apply_filters( 'sps_cart_item', $cart_item );
}
/**
* Inspect product-level/cart-level session data and apply subscription schemes to cart items as needed.
*
* @param \WC_Cart $cart
* @return void
*/
public static function apply_subscriptions( $cart ) {
foreach ( $cart->cart_contents as $cart_item_key => $cart_item ) {
if ( isset( $cart_item['sps'] ) ) {
// Convert the product object to a subscription, if needed.
$cart->cart_contents[ $cart_item_key ] = self::apply_subscription( $cart->cart_contents[ $cart_item_key ] );
}
}
}
}
<?php
namespace Simple_Product_Subscriptions;
class Cart {
// Code from before
/**
* Determines if a subscription scheme is set on the product object.
*
* @param \WC_Product $product Product object to check.
* @return boolean Result of check.
*/
public static function is_subscription( $product ) {
return $product->get_meta( '_subscription_period', true ) && $product->get_meta( '_subscription_period_interval', true );
}
/**
* Get Subscription Scheme
*
* @param $cart_item
*
* @return array|mixed
*/
public static function get_subscription_scheme( $cart_item ) {
return isset( $cart_item[ 'sps' ] ) ? $cart_item[ 'sps' ] : array();
}
/**
* Set the active subscription scheme. Key value should be:
*
* - string to activate a subscription scheme (valid key required);
* - false to indicate that the product is sold in a non-recurring manner; or
* - null to indicate that the susbcription state of the product is undefined.
*
* Note that the scheme set on the object may become invalid if 'set_subscription_schemes' or 'set_forced_subscription_scheme' are modified.
*
* @param \WC_Product $product Product object.
* @param string $key Identifier of subscription scheme to activate on object.
* @return boolean Action result.
*/
public static function set_subscription_scheme( $product, $scheme ) {
$period_value = explode( '_', $scheme );
$period = $period_value[1];
$interval = $period_value[0];
$product->add_meta_data( '_subscription_period', $period );
$product->add_meta_data( '_subscription_period_interval', $interval );
$product->add_meta_data( '_subscription_price', Product::get_subscription_price( $product->get_id() ) );
return true;
}
/**
* Applies a saved subscription key to a cart item.
*
* @param array $cart_item
* @return array
*/
public static function apply_subscription( $cart_item ) {
$scheme_to_apply = self::get_subscription_scheme( $cart_item );
if ( $scheme_to_apply ) {
self::set_subscription_scheme( $cart_item['data'], $scheme_to_apply );
// Set the temporary price to Cart item Product that is used for calculating totals.
$cart_item['data']->set_price(Product::get_subscription_price( $cart_item['data']->get_id() ) );
}
return apply_filters( 'sps_cart_item', $cart_item );
}
}
<?php
namespace Simple_Product_Subscriptions;
class Cart {
/**
* Cart Hooks
*/
public function hooks() {
// Hooks from before...
// Allow WCS to recognize any product as a subscription.
add_filter( 'woocommerce_is_subscription', array( __CLASS__, 'filter_is_subscription' ), 10, 3 );
add_filter( 'woocommerce_cart_item_price', array( __CLASS__, 'show_cart_item_subscription_options' ), 1000, 3 );
}
// Methods from before...
/**
* Displays cart item options for purchasing a product once or creating a subscription from it.
*
* @param string $price
* @param array $cart_item
* @param string $cart_item_key
* @return string
*/
public static function show_cart_item_subscription_options( $price, $cart_item, $cart_item_key ) {
$product = $cart_item[ 'data' ];
$supports_args = array(
'cart_item' => $cart_item,
'cart_item_key' => $cart_item_key
);
$is_mini_cart = did_action( 'woocommerce_before_mini_cart' ) !== did_action( 'woocommerce_after_mini_cart' );
// Only show options in cart.
if ( ! is_cart() || $is_mini_cart ) {
return $price;
}
if ( ! self::is_subscription( $product ) ) {
return $price;
}
// Grab bare price without subscription details.
remove_filter( 'woocommerce_cart_product_price', array( 'WC_Subscriptions_Cart', 'cart_product_price' ), 10, 2 );
remove_filter( 'woocommerce_cart_item_price', array( __CLASS__, 'show_cart_item_subscription_options' ), 1000, 3 );
$price = wc_price( Product::get_subscription_price( $product->get_id() ) ) . sprintf(esc_html__(' every %1$s', 'jg-toolbox'), wcs_get_subscription_period_strings($product->get_meta( '_subscription_period_interval', true ), $product->get_meta( '_subscription_period', true )));
add_filter( 'woocommerce_cart_item_price', array( __CLASS__, 'show_cart_item_subscription_options' ), 1000, 3 );
add_filter( 'woocommerce_cart_product_price', array( 'WC_Subscriptions_Cart', 'cart_product_price' ), 10, 2 );
return $price;
}
/**
* Hooks onto 'woocommerce_is_subscription' to trick WCS into thinking it is dealing with a subscription-type product.
*
* @param boolean $is
* @param int $product_id
* @param \WC_Product $product
* @return boolean
*/
public static function filter_is_subscription( $is, $product_id, $product ) {
if ( ! $product ) {
return $is;
}
if ( self::is_subscription( $product ) ) {
$is = true;
}
return $is;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment