-
-
Save pareshsojitra/c2e8f563ceca870b0f750df60d9e5119 to your computer and use it in GitHub Desktop.
Creates a subscription in the background when a trial product is purchased.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* When a trial product is purchased we create an order for a subscription product. | |
* | |
* 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. | |
* (Home Bundle) | |
* | |
* @since 2.2.0 | |
* @access public | |
* @var int | |
*/ | |
public $subscription_product_id = 6247; | |
/** | |
* 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 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 ); | |
// Creates the pending subscription if trial product purchase was completed. | |
add_action( 'woocommerce_payment_complete', array( $this, 'check_payment_for_trial_product' ), 100, 1 ); | |
// Action hook for creating the subscription. | |
add_action( 'nano_create_subscription_order', array( $this, 'create_subscription_order' ) ); | |
} | |
/** | |
* 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; | |
} | |
/** | |
* 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_payment_for_trial_product( $order_id ) { | |
// Get the order that was just placed. | |
$order = wc_get_order( $order_id ); | |
// 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( $trial_subscription_accept ) ) { | |
error_log( 'Scheduling subscription order.' ); | |
$this->create_subscription_order( $order_id ); | |
/* | |
* Ideally create_subscription_order would be scheduled. | |
* Current code throws a notice from Stripe extension. | |
$timestamp = time() + 60; // Generates timestamp 60 seconds in the future | |
wc_schedule_single_action( $timestamp, 'nano_create_subscription_order', array( $order_id ) ); | |
*/ | |
} | |
} | |
/** | |
* Create a subscription order with data from the completed trial product order. | |
* | |
* @since 2.2.0 | |
* @param int $order_id | |
* @return void | |
*/ | |
public function create_subscription_order( $trial_order_id ) { | |
error_log( 'Creating subscription order.' ); | |
// Get order data from the original "Trial Kit" purchase. | |
$trial_order = wc_get_order( $trial_order_id ); | |
$trial_order_data = $trial_order->get_data(); | |
// All customers should have a user account automatically created. | |
$user_id = $trial_order->get_user_id(); | |
// Creates the new order object. | |
$order = wc_create_order( | |
array( | |
'customer_id' => $user_id, | |
'payment_method' => $trial_order_data['payment_method'], | |
'payment_method_title' => $trial_order_data['payment_method_title'], | |
) | |
); | |
// Adds the product to the new order. | |
$product = wc_get_product( $this->subscription_product_id ); | |
$quantity = 1; | |
$args = array(); | |
// Sets the address. | |
$order->add_product( $product, $quantity, $args ); | |
$order->set_address( $trial_order_data['billing'], 'billing' ); | |
$order->set_address( $trial_order_data['shipping'], 'shipping' ); | |
// Order totals are not calculated ($order->calculate_totals). | |
// This makes it so the first order is for 0.00. | |
// Get Stripe payment data. | |
// @TODO Can this be set in data store using CRUD methods? | |
$stripe_customer_id = get_post_meta( $trial_order_id, '_stripe_customer_id', true ); | |
$stripe_card_id = get_post_meta( $trial_order_id, '_stripe_card_id', true ); | |
// Add Stripe data to new order. | |
if ( $stripe_customer_id ) { | |
$order->update_meta_data( '_stripe_customer_id', $stripe_customer_id ); | |
} | |
if ( $stripe_card_id ) { | |
$order->update_meta_data( '_stripe_card_id', $stripe_card_id ); | |
} | |
$stripe = new WC_Gateway_Stripe(); | |
$stripe->process_payment( $order->get_id() ); | |
// Adds the subscription data to the new order. | |
$period = WC_Subscriptions_Product::get_period( $product ); | |
$interval = WC_Subscriptions_Product::get_interval( $product ); | |
// Create the subscription. | |
$subscription = wcs_create_subscription( | |
array( | |
'order_id' => $order->get_id(), | |
'billing_period' => $period, | |
'billing_interval' => $interval, | |
) | |
); | |
// Set trial_end date. | |
$default_start_date = wcs_get_datetime_utc_string( wcs_get_objects_property( $order, 'date_created' ) ); | |
$start_date_time = wcs_date_to_time( $default_start_date ); // Convert to timestamp. | |
$trial_end = wcs_add_time( 14, 'days', $start_date_time ); // Add 14 days to $start_date_time. | |
$trial_end = date( 'Y-m-d H:i:s', $trial_end ); // Convert to date format. | |
$subscription->update_dates( array( 'trial_end' => $trial_end ) ); | |
// Add the product. | |
$subscription->add_product( $product, $quantity, $args ); | |
$subscription->calculate_totals(); | |
// Activate the subscription. | |
WC_Subscriptions_Manager::activate_subscriptions_for_order( $order ); | |
} | |
} | |
// 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