Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save pareshsojitra/a2c9cdb9d3b7e77e6a5096f3054fc638 to your computer and use it in GitHub Desktop.
Save pareshsojitra/a2c9cdb9d3b7e77e6a5096f3054fc638 to your computer and use it in GitHub Desktop.
Add a trial subscription product to the cart during order processing.
<?php
/**
* When a trial product is purchased we add a subscription product the order
* directly before order processing.
*
* This subscription order charges after 14 days if customer does not cancel.
*/
class Nano_Trial_Subscription {
/**
* The ID for our WooCommerce trial product.
* (Trial Kit)
*
* @since 2.2.0
* @access public
* @var int
*/
public $trial_product_id = 9344;
/**
* The ID for the subscription product.
* This product has a trial set, so it won't charge or ship until 14 days out.
*
* @since 2.2.0
* @access public
* @var int
*/
public $subscription_product_id = 9507;
/**
* The URL for the subscription product.
* (Home Bundle)
*
* @since 2.2.0
* @access public
* @var string
*/
public $subscription_product_url = '/product/for-the-home-bundle/';
/**
* Init the class.
*
* @since 2.2.0
*/
public function init() {
// The trial product requires additional cart and checkout rules.
add_filter( 'woocommerce_add_to_cart_validation', array( $this, 'trial_product_cart_filtering' ), 100, 2 );
// Filters the add to cart message if the trial product is added.
add_filter( 'wc_add_to_cart_message_html', array( $this, 'trial_add_to_cart_message_html' ), 100, 2 );
// Adds a field key for trial_agree_to_subscription so it is available in $data for later hooks.
add_filter( 'woocommerce_checkout_fields' , array( $this, 'trial_agree_to_subscription_field_key' ) );
// Adds an "agree to subscription" checkbox to the checkout page.
add_action( 'woocommerce_review_order_before_submit', array( $this, 'trial_agree_to_subscription_checkbox' ) );
// Saves the value of the "agree to subscription" checkbox to order meta.
add_action( 'woocommerce_checkout_update_order_meta', array( $this, 'save_agree_to_subscription_meta' ), 100, 2 );
// Adds the subscription if customer agreed to trial_subscription_accept.
// This meta field is only on orders that include the trial product.
add_action( 'woocommerce_checkout_create_order', array( $this, 'check_trial_subscription_accept' ), 100, 2 );
}
/**
* Helper function to check if cart contains a specific $product_id.
*
* @since 2.2.0
* @param int $product_id
* @return boolean
*/
function nano_cart_contains( $product_id ) {
$products = WC()->cart->get_cart_contents();
foreach ( $products as $product => $values ) {
$cart_product_id = $values['data']->get_id();
if ( $product_id === $cart_product_id ) {
return true;
}
}
return false;
}
/**
* Restricts which items can be added to cart:
*
* 1) The $trial_product should not be added to cart if existing items are in the cart.
* 2) Other products should not be added to cart if $trial_product is already in cart.
*
* @since 2.2.0
* @param boolean $passed_validation
* @param int $product_id
* @return boolean
*/
function trial_product_cart_filtering( $passed_validation, $product_id ) {
// If cart is empty, no need for additional validation.
if ( WC()->cart->is_empty() ) {
return $passed_validation;
}
// If trial product is being added to cart, existing products should be removed.
// In product details, make sure "Enable this to only allow one of this item to be bought in a single order" is also checked.
if ( $this->trial_product_id === $product_id ) {
$notice = sprintf(
// Translators: Placeholder is for cart url.
__( 'Trial Product can not be purchased with other products. Please empty your <a href="%s">existing cart</a>.' ),
esc_url( wc_get_cart_url() )
);
wc_add_notice( $notice, 'notice' );
return false;
}
// If trial product is in cart, additional products should not be added.
if ( $this->nano_cart_contains( $this->trial_product_id ) ) {
$notice = sprintf(
// Translators: Placeholder is for cart url.
__( 'The Trial Product can not be ordered with other items.<br> Please remove the Trial Product from <a href="%s">your cart</a> if you wish to make other purchases.' ),
esc_url( wc_get_cart_url() )
);
wc_add_notice( $notice, 'notice' );
return false;
}
// Trial product is not in cart or being added.
return $passed_validation;
}
/**
* Strips out the "Shop" link from add_to_cart message if trial product is added.
*
* @since 2.2.0
* @param string $message
* @param array $products
* @return string
*/
public function trial_add_to_cart_message_html( $message, $products ) {
// ID of the trial product
$trial_product_id = 9344;
// Check cart for trial product
foreach ( $products as $product_id => $qty ) {
if ( $this->trial_product_id === $product_id ) {
// Strips out the "Shop" link/button from the message.
$message = preg_replace( '/<a href=\"(.*?)\">(.*?)<\/a>/', '', $message );
// Adds an additional notice about the subscription product
$notice = sprintf(
__( 'After 14 days this order will convert into a subscription for the <a href="%s">Home Bundle</a>. You will be reminded before renewal and can cancel at any time.' ),
esc_url( home_url( $this->subscription_product_url ) )
);
return $message . '<br><br>' . $notice;
}
}
// No changes needed, return default $message.
return $message;
}
public function trial_agree_to_subscription_field_key( $fields ) {
$fields['order']['trial_subscription_accept']['type'] = 'hidden';
return $fields;
}
/**
* Adds an "agree to subscription" checkbox on checkout.
*
* @since 2.2.0
* @return void
*/
public function trial_agree_to_subscription_checkbox() {
if ( $this->nano_cart_contains( $this->trial_product_id ) ) : ?>
<p class="form-row woocommerce-validated">
<?php /* Hidden input ensures a value will be posted even if visible input is unchecked. */ ?>
<input type="hidden" name="trial_subscription_accept" value="0">
<label>
<input id="trial-subscription-accept" name="trial_subscription_accept" type="checkbox" value="1" checked>
<span>I'd like this order to convert into a <a href="<?php echo esc_url( home_url( $this->subscription_product_url ) ); ?> ">Home Bundle</a> subscription after 14 days unless I cancel.</span>
</label>
</p>
<?php endif;
}
/**
* Saves the meta value of "agree to subscription" checkbox on checkout.
*
* @since 2.2.0
* @return void
*/
public function save_agree_to_subscription_meta( $order_id, $data ) {
$order = wc_get_order( $order_id );
// The "trial-subscription-accept variable" should only exist on orders with the trial product.
if ( isset( $_POST['trial_subscription_accept'] ) ) :
if ( intval( $_POST['trial_subscription_accept'] ) ) {
$order->update_meta_data( 'trial_subscription_accept', 1 );
} else {
$order->update_meta_data( 'trial_subscription_accept', 0 );
}
$order->save();
endif;
}
/**
* Runs after a payment completes (i.e. complete checkout + payment processed).
* If the order contains the trial product, we'll create the subscription order.
*
* @since 2.2.0
* @param int $order_id
* @return void
*/
public function check_trial_subscription_accept( $order, $data ) {
// Meta data for subscription approval should only be on orders that contain the trial product.
$trial_subscription_accept = $order->get_meta( 'trial_subscription_accept' );
if ( 1 === intval( $data['trial_subscription_accept'] ) ) {
error_log( 'Scheduling subscription order.' );
$this->create_subscription_order( $order );
}
}
/**
* Create a subscription order with data from the completed trial product order.
*
* @since 2.2.0
* @param object $order
* @return void
*/
public function create_subscription_order( $order ) {
// Adds the subscription product to completed order.
$product = wc_get_product( $this->subscription_product_id );
$quantity = 1;
$line_items = array(
'subtotal' => '0.00',
'total' => '0.00'
);
$order->add_product( $product, $quantity, $line_items );
WC()->cart->add_to_cart( $this->subscription_product_id, $quantity, array() );
}
}
// Let's go!
$nano_trial_subscription = new Nano_Trial_Subscription();
$nano_trial_subscription->init();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment