/**
  * Customise which actions are shown against a subscriptions order on the My Account page.
  *
  * @since 1.3
  */
 public static function filter_woocommerce_my_account_my_orders_actions($actions, $order)
 {
     if (WC_Subscriptions_Order::order_contains_subscription($order) || WC_Subscriptions_Renewal_Order::is_renewal($order)) {
         unset($actions['cancel']);
         if (is_numeric(get_post_meta($order->id, '_failed_order_replaced_by', true))) {
             unset($actions['pay']);
         }
         $original_order = WC_Subscriptions_Renewal_Order::get_parent_order($order);
         $order_items = WC_Subscriptions_Order::get_recurring_items($original_order);
         $first_order_item = reset($order_items);
         $product_id = WC_Subscriptions_Order::get_items_product_id($first_order_item);
         $subscription_key = WC_Subscriptions_Manager::get_subscription_key($original_order->id, $product_id);
         $subscription = WC_Subscriptions_Manager::get_users_subscription($original_order->customer_user, $subscription_key);
         if (empty($subscription) || !in_array($subscription['status'], array('on-hold', 'pending'))) {
             unset($actions['pay']);
         }
     }
     return $actions;
 }
 /**
  * Returns the string key for a subscription purchased in an order specified by $order_id
  * 
  * @param order_id int The ID of the order in which the subscription was purchased. 
  * @param product_id int The ID of the subscription product.
  * @return string The key representing the given subscription.
  * @since 1.0
  */
 public static function get_subscription_key($order_id, $product_id = '')
 {
     // If we have a child renewal order, we need the parent order's ID
     if (WC_Subscriptions_Renewal_Order::is_renewal($order_id, array('order_role' => 'child'))) {
         $order_id = WC_Subscriptions_Renewal_Order::get_parent_order_id($order_id);
     }
     if (empty($product_id)) {
         $order = new WC_Order($order_id);
         $order_items = WC_Subscriptions_Order::get_recurring_items($order);
         $first_order_item = reset($order_items);
         $product_id = WC_Subscriptions_Order::get_items_product_id($first_order_item);
     }
     $subscription_key = $order_id . '_' . $product_id;
     return apply_filters('woocommerce_subscription_key', $subscription_key, $order_id, $product_id);
 }
 /**
  * Restore renewal flag when cart is reset and modify Product object with
  * renewal order related info
  *
  * @since 1.3
  */
 public static function get_cart_item_from_session($session_data, $values, $key)
 {
     if (isset($values['subscription_renewal'])) {
         $session_data['subscription_renewal'] = $values['subscription_renewal'];
         // Need to get the original order price, not the current price
         $original_order_id = $values['subscription_renewal']['original_order'];
         $order_items = WC_Subscriptions_Order::get_recurring_items($original_order_id);
         $first_order_item = reset($order_items);
         $price = $first_order_item['subscription_recurring_amount'];
         /*
          * Modify the Cart $_product object. 
          * All the cart calculations and cart/checkout/mini-cart displays will use this object.
          * So by modifying it here, we take care of all those cases.
          */
         $_product = $session_data['data'];
         $_product->price = $price;
         // Don't carry over any sign up fee
         $_product->subscription_sign_up_fee = $_product->product_custom_fields['_subscription_sign_up_fee'][0] = 0;
         // Make sure the original subscription terms perisist
         if ('parent' == $session_data['subscription_renewal']['role']) {
             $_product->subscription_price = $_product->product_custom_fields['_subscription_price'][0] = $price;
             $_product->subscription_period = $_product->product_custom_fields['_subscription_period'][0] = $first_order_item['subscription_period'];
             $_product->subscription_period_interval = $_product->product_custom_fields['_subscription_period_interval'][0] = $first_order_item['subscription_interval'];
             $_product->subscription_trial_period = $_product->product_custom_fields['_subscription_trial_period'][0] = $first_order_item['subscription_trial_period'];
             $_product->subscription_length = $_product->product_custom_fields['_subscription_length'][0] = $first_order_item['subscription_length'];
             // Never give a free trial period again
             $_product->subscription_trial_length = $_product->product_custom_fields['_subscription_trial_length'][0] = 0;
         }
         $title = sprintf(__('Renewal of "%s"', 'woocommerce-subscriptions'), $_product->get_title());
         $_product->post->post_title = apply_filters('woocommerce_subscriptions_renewal_product_title', $title, $_product);
     }
     return $session_data;
 }
_e('Subscription', 'woocommerce-subscriptions');
?>
</th>
			<th scope="col" style="text-align:left; border: 1px solid #eee;"><?php 
_e('Start Date', 'woocommerce-subscriptions');
?>
</th>
			<th scope="col" style="text-align:left; border: 1px solid #eee;"><?php 
_e('End Date', 'woocommerce-subscriptions');
?>
</th>
		</tr>
	</thead>
	<tbody>
	<?php 
foreach (WC_Subscriptions_Order::get_recurring_items($order) as $item) {
    ?>
			<tr>
				<td scope="row" style="text-align:left; border: 1px solid #eee;"><?php 
    echo $item['name'];
    ?>
</td>
				<td scope="row" style="text-align:left; border: 1px solid #eee;"><?php 
    echo date_i18n(woocommerce_date_format(), strtotime($item['subscription_start_date']));
    ?>
</td>
				<td scope="row" style="text-align:left; border: 1px solid #eee;"><?php 
    echo !empty($item['subscription_expiry_date']) ? date_i18n(woocommerce_date_format(), strtotime($item['subscription_expiry_date'])) : __('When Cancelled', 'woocommerce-subscriptions');
    ?>
			</tr>
	<?php 
 /**
  * Gets all the active and inactive subscriptions for a user, as specified by $user_id
  *
  * @param int $user_id (optional) The id of the user whose subscriptions you want. Defaults to the currently logged in user.
  * @param array $order_ids (optional) An array of post_ids of WC_Order objects as a way to get only subscriptions for certain orders. Defaults to null, which will return subscriptions for all orders.
  * @since 1.0
  */
 public static function get_users_subscriptions($user_id = 0, $order_ids = array())
 {
     global $wpdb;
     $subscriptions = array();
     if (empty($order_ids)) {
         $order_ids = WC_Subscriptions_Order::get_users_subscription_orders($user_id);
     }
     foreach ($order_ids as $order_id) {
         $items = WC_Subscriptions_Order::get_recurring_items($order_id);
         foreach ($items as $item) {
             $subscription_key = self::get_subscription_key($order_id, $item['product_id']);
             $subscriptions[$subscription_key] = self::get_subscription($subscription_key);
             // DRY over efficiency
         }
     }
     return apply_filters('woocommerce_users_subscriptions', $subscriptions, $user_id);
 }
 /**
  * Check if a payment is being made on a failed renewal order from 'My Account'. If so,
  * redirect the order into a cart/checkout payment flow.
  *
  * @since 1.3
  */
 public static function before_woocommerce_pay()
 {
     global $woocommerce;
     if (isset($_GET['pay_for_order']) && isset($_GET['order']) && isset($_GET['order_id'])) {
         // Pay for existing order
         $order_key = urldecode($_GET['order']);
         $order_id = absint($_GET['order_id']);
         $order = new WC_Order($order_id);
         $failed_order_replaced_by = get_post_meta($order_id, '_failed_order_replaced_by', true);
         if (is_numeric($failed_order_replaced_by)) {
             $woocommerce->add_error(sprintf(__('Sorry, this failed order has already been paid. See order %s.', WC_Subscriptions::$text_domain), $failed_order_replaced_by));
             wp_safe_redirect(get_permalink(woocommerce_get_page_id('myaccount')));
             exit;
         }
         if ($order->id == $order_id && $order->order_key == $order_key && in_array($order->status, array('pending', 'failed')) && WC_Subscriptions_Renewal_Order::is_renewal($order)) {
             // If order being paid is a parent order, get the original order, else query parent_order
             if (WC_Subscriptions_Renewal_Order::is_renewal($order_id, array('order_role' => 'parent'))) {
                 $role = 'parent';
                 $original_order = new WC_Order($order->order_custom_fields['_original_order'][0]);
             } elseif (WC_Subscriptions_Renewal_Order::is_renewal($order_id, array('order_role' => 'child'))) {
                 $role = 'child';
                 $original_order = WC_Subscriptions_Renewal_Order::get_parent_order($order_id);
             }
             $order_items = WC_Subscriptions_Order::get_recurring_items($original_order);
             $first_order_item = reset($order_items);
             $product_id = WC_Subscriptions_Order::get_items_product_id($first_order_item);
             $product = get_product($product_id);
             // Make sure we don't actually need the variation ID
             if ($product->is_type(array('variable-subscription'))) {
                 $item = WC_Subscriptions_Order::get_item_by_product_id($original_order, $product_id);
                 $variation_id = $item['variation_id'];
                 $variation = get_product($variation_id);
                 $variation_data = $variation->get_variation_attributes();
             } elseif ($product->is_type(array('subscription_variation'))) {
                 // Handle existing renewal orders incorrectly using variation_id as the product_id
                 $product_id = $product->id;
                 $variation_id = $product->get_variation_id();
                 $variation_data = $product->get_variation_attributes();
             } else {
                 $variation_id = '';
                 $variation_data = array();
             }
             $woocommerce->cart->empty_cart(true);
             $woocommerce->cart->add_to_cart($product_id, 1, $variation_id, $variation_data, array('subscription_renewal' => array('original_order' => $original_order->id, 'failed_order' => $order_id, 'role' => $role)));
             wp_safe_redirect($woocommerce->cart->get_checkout_url());
             exit;
         }
     }
 }
 /**
  * Creates a new order for renewing a subscription product based on the details of a previous order.
  *
  * No trial periods or sign up fees are applied to the renewal order. However, if the order has failed
  * payments and the store manager has set failed payments to be added to renewal orders, then the
  * orders totals will be set to include the outstanding balance.
  *
  * If the $new_order_role flag is set to 'parent', then the renewal order will supersede the existing 
  * order. The existing order and subscription associated with it will be cancelled. A new order and
  * subscription will be created. 
  *
  * If the $new_order_role flag is 'child', the $original_order will remain the master order for the
  * subscription and the new order is just for accepting a recurring payment on the subscription.
  *
  * Renewal orders have the same meta data as the original order. If the renewal order is set to be a 'child'
  * then any subscription related meta data will not be stored on the new order. This is to keep subscription
  * meta data associated only with the one master order for the subscription.
  *
  * @param $order WC_Order | int The WC_Order object or ID of the order for which the a new order should be created.
  * @param $product_id string The ID of the subscription product in the order which needs to be added to the new order.
  * @param $new_order_role string A flag to indicate whether the new order should become the master order for the subscription. Accepts either 'parent' or 'child'. Defaults to 'parent' - replace the existing order.
  * @since 1.2
  */
 public static function generate_renewal_order($original_order, $product_id, $new_order_role = 'parent')
 {
     global $wpdb;
     if (!is_object($original_order)) {
         $original_order = new WC_Order($original_order);
     }
     if (!WC_Subscriptions_Order::order_contains_subscription($original_order) || !WC_Subscriptions_Order::is_item_a_subscription($original_order, $product_id)) {
         return false;
     }
     if (self::is_renewal($original_order, 'child')) {
         $original_order = self::get_parent_order($original_order);
     }
     $renewal_order_key = uniqid('order_');
     // Create the new order
     $renewal_order_data = array('post_type' => 'shop_order', 'post_title' => sprintf(__('Subscription Renewal Order &ndash; %s', WC_Subscriptions::$text_domain), strftime(_x('%b %d, %Y @ %I:%M %p', 'Order date parsed by strftime', WC_Subscriptions::$text_domain))), 'post_status' => 'publish', 'ping_status' => 'closed', 'post_excerpt' => $original_order->customer_note, 'post_author' => 1, 'post_password' => $renewal_order_key);
     if ('child' == $new_order_role) {
         $renewal_order_data['post_parent'] = $original_order->id;
     }
     $renewal_order_id = wp_insert_post($renewal_order_data);
     // Set the order as pending
     wp_set_object_terms($renewal_order_id, 'pending', 'shop_order_status');
     // Set a unique key for this order
     update_post_meta($renewal_order_id, '_order_key', $renewal_order_key);
     $order_meta_query = "SELECT `meta_key`, `meta_value` FROM {$wpdb->postmeta} WHERE `post_id` = {$original_order->id} AND `meta_key` NOT IN ('_paid_date', '_completed_date', '_order_key', '_edit_lock', '_original_order')";
     // Superseding existing order so don't carry over payment details
     if ('parent' == $new_order_role) {
         $order_meta_query .= " AND `meta_key` NOT IN ('_payment_method', '_payment_method_title')";
     } else {
         $order_meta_query .= " AND `meta_key` NOT LIKE '_order_recurring_%'";
     }
     // Allow extensions to add/remove order meta
     $order_meta_query = apply_filters('woocommerce_subscriptions_renewal_order_meta_query', $order_meta_query, $original_order->id, $renewal_order_id, $new_order_role);
     // Carry all the required meta from the old order over to the new order
     $order_meta = $wpdb->get_results($order_meta_query, 'ARRAY_A');
     $order_meta = apply_filters('woocommerce_subscriptions_renewal_order_meta', $order_meta, $original_order->id, $renewal_order_id, $new_order_role);
     foreach ($order_meta as $meta_item) {
         add_post_meta($renewal_order_id, $meta_item['meta_key'], maybe_unserialize($meta_item['meta_value']), true);
     }
     $outstanding_balance = WC_Subscriptions_Order::get_outstanding_balance($original_order, $product_id);
     // If there are outstanding payment amounts, add them to the order, otherwise set the order details to the values of the recurring totals
     if ($outstanding_balance > 0 && 'yes' == get_option(WC_Subscriptions_Admin::$option_prefix . '_add_outstanding_balance')) {
         $failed_payment_multiplier = WC_Subscriptions_Order::get_failed_payment_count($original_order, $product_id);
     } else {
         $failed_payment_multiplier = 1;
     }
     // Set order totals based on recurring totals from the original order
     $cart_discount = $failed_payment_multiplier * get_post_meta($original_order->id, '_order_recurring_discount_cart', true);
     $order_discount = $failed_payment_multiplier * get_post_meta($original_order->id, '_order_recurring_discount_total', true);
     $order_shipping_tax = $failed_payment_multiplier * get_post_meta($original_order->id, '_order_recurring_shipping_tax_total', true);
     $order_tax = $failed_payment_multiplier * get_post_meta($original_order->id, '_order_recurring_tax_total', true);
     $order_total = $failed_payment_multiplier * get_post_meta($original_order->id, '_order_recurring_total', true);
     update_post_meta($renewal_order_id, '_cart_discount', $cart_discount);
     update_post_meta($renewal_order_id, '_order_discount', $order_discount);
     update_post_meta($renewal_order_id, '_order_shipping_tax', $order_shipping_tax);
     update_post_meta($renewal_order_id, '_order_tax', $order_tax);
     update_post_meta($renewal_order_id, '_order_total', $order_total);
     // Set order taxes based on recurring taxes from the original order
     $recurring_order_taxes = get_post_meta($original_order->id, '_order_recurring_taxes', true);
     foreach ($recurring_order_taxes as $index => $recurring_order_tax) {
         if (isset($recurring_order_tax['cart_tax']) && $recurring_order_tax['cart_tax'] > 0) {
             $recurring_order_taxes[$index]['cart_tax'] = $failed_payment_multiplier * $recurring_order_tax['cart_tax'];
         } else {
             $recurring_order_taxes[$index]['cart_tax'] = 0;
         }
         if (isset($recurring_order_tax['shipping_tax']) && $recurring_order_tax['shipping_tax'] > 0) {
             $recurring_order_taxes[$index]['shipping_tax'] = $failed_payment_multiplier * $recurring_order_tax['shipping_tax'];
         } else {
             $recurring_order_taxes[$index]['shipping_tax'] = 0;
         }
     }
     update_post_meta($renewal_order_id, '_order_taxes', $recurring_order_taxes);
     // Set line totals to be recurring line totals and remove the subscription/recurring related item meta from each order item
     $order_items = WC_Subscriptions_Order::get_recurring_items($original_order);
     // Allow extensions to add/remove items or item meta
     $order_items = apply_filters('woocommerce_subscriptions_renewal_order_items', $order_items, $original_order->id, $renewal_order_id, $product_id, $new_order_role);
     foreach ($order_items as $item_index => $order_item) {
         $item_meta = new WC_Order_Item_Meta($order_item['item_meta']);
         // Remove recurring line items and set item totals based on recurring line totals
         foreach ($item_meta->meta as $meta_index => $meta_item) {
             switch ($meta_item['meta_name']) {
                 case '_recurring_line_total':
                     $order_items[$item_index]['line_total'] = $failed_payment_multiplier * $meta_item['meta_value'];
                 case '_recurring_line_tax':
                     $order_items[$item_index]['line_tax'] = $failed_payment_multiplier * $meta_item['meta_value'];
                 case '_recurring_line_subtotal':
                     $order_items[$item_index]['line_subtotal'] = $failed_payment_multiplier * $meta_item['meta_value'];
                 case '_recurring_line_subtotal_tax':
                     $order_items[$item_index]['line_subtotal_tax'] = $failed_payment_multiplier * $meta_item['meta_value'];
                 case '_recurring_line_total':
                 case '_recurring_line_tax':
                 case '_recurring_line_subtotal':
                 case '_recurring_line_subtotal_tax':
                 case '_recurring_line_subtotal_tax':
                 case '_subscription_recurring_amount':
                 case '_subscription_sign_up_fee':
                 case '_subscription_period':
                 case '_subscription_interval':
                 case '_subscription_length':
                 case '_subscription_trial_length':
                 case '_subscription_trial_period':
                     if ('child' == $new_order_role) {
                         unset($item_meta->meta[$meta_index]);
                     }
                     break;
             }
         }
         if ('child' == $new_order_role) {
             $order_items[$item_index]['name'] = sprintf(__('Renewal of "%s" purchased in Order %s', WC_Subscriptions::$text_domain), $order_item['name'], $original_order->get_order_number());
         }
         $order_items[$item_index]['item_meta'] = $item_meta->meta;
     }
     // Save the item meta on the new order
     update_post_meta($renewal_order_id, '_order_items', $order_items);
     // Keep a record of the original order's ID on the renewal order
     update_post_meta($renewal_order_id, '_original_order', $original_order->id, true);
     $renewal_order = new WC_Order($renewal_order_id);
     if ('parent' == $new_order_role) {
         WC_Subscriptions_Manager::process_subscriptions_on_checkout($renewal_order_id);
         $original_order->add_order_note(sprintf(__('Order superseded by Renewal Order %s.', WC_Subscriptions::$text_domain), $renewal_order->get_order_number()));
     }
     do_action('woocommerce_subscriptions_renewal_order_created', $renewal_order, $original_order, $product_id, $new_order_role);
     return apply_filters('woocommerce_subscriptions_renewal_order_id', $renewal_order_id, $original_order, $product_id, $new_order_role);
 }
 /**
  * When a PayPal IPN messaged is received for a subscription transaction, 
  * check the transaction details and 
  *
  * @since 1.0
  */
 public static function process_paypal_ipn_request($transaction_details)
 {
     global $wpdb;
     $transaction_details = stripslashes_deep($transaction_details);
     if (!in_array($transaction_details['txn_type'], array('subscr_signup', 'subscr_payment', 'subscr_cancel', 'subscr_eot', 'subscr_failed', 'subscr_modify'))) {
         return;
     }
     if (empty($transaction_details['custom']) || empty($transaction_details['invoice'])) {
         return;
     }
     // Get the $order_id & $order_key with backward compatibility
     extract(self::get_order_id_and_key($transaction_details));
     $transaction_details['txn_type'] = strtolower($transaction_details['txn_type']);
     if (self::$debug) {
         self::$log->add('paypal', 'Subscription Transaction Type: ' . $transaction_details['txn_type']);
     }
     if (self::$debug) {
         self::$log->add('paypal', 'Subscription transaction details: ' . print_r($transaction_details, true));
     }
     $order = new WC_Order($order_id);
     // We have an invalid $order_id, probably because invoice_prefix has changed since the subscription was first created, so get the order by order key
     if (!isset($order->id)) {
         $order_id = function_exists('woocommerce_get_order_id_by_order_key') ? woocommerce_get_order_id_by_order_key($order_key) : $wpdb->get_var("SELECT post_id FROM {$wpdb->prefix}postmeta WHERE meta_key = '_order_key' AND meta_value = '{$order_key}'");
         $order = new WC_Order($order_id);
     }
     if ($order->order_key !== $order_key) {
         if (self::$debug) {
             self::$log->add('paypal', 'Subscription IPN Error: Order Key does not match invoice.');
         }
         return;
     }
     if ('paypal' != $order->recurring_payment_method) {
         if (self::$debug) {
             self::$log->add('paypal', 'IPN ignored, recurring payment method has changed.');
         }
         return;
     }
     if (isset($transaction_details['ipn_track_id'])) {
         // Make sure the IPN request has not already been handled
         $handled_ipn_requests = get_post_meta($order_id, '_paypal_ipn_tracking_ids', true);
         if (empty($handled_ipn_requests)) {
             $handled_ipn_requests = array();
         }
         // The 'ipn_track_id' is not a unique ID and is shared between different transaction types, so create a unique ID by prepending the transaction type
         $transaction_id = $transaction_details['txn_type'] . '_' . $transaction_details['ipn_track_id'];
         if (in_array($transaction_id, $handled_ipn_requests)) {
             if (self::$debug) {
                 self::$log->add('paypal', 'Subscription IPN Error: This IPN message has already been correctly handled.');
             }
             return;
         }
     }
     if (isset($transaction_details['subscr_id'])) {
         update_post_meta($order_id, 'PayPal Subscriber ID', $transaction_details['subscr_id']);
     }
     // Get the subscription this IPN message relates to
     $subscriptions_in_order = WC_Subscriptions_Order::get_recurring_items($order);
     $subscription_item = array_pop($subscriptions_in_order);
     $subscription_key = WC_Subscriptions_Manager::get_subscription_key($order->id, WC_Subscriptions_Order::get_items_product_id($subscription_item));
     $subscription = WC_Subscriptions_Manager::get_subscription($subscription_key);
     $is_first_payment = empty($subscription['completed_payments']) ? true : false;
     if ('switched' === $subscription['status']) {
         if (self::$debug) {
             self::$log->add('paypal', 'IPN ignored, subscription has been switched.');
         }
         return;
     }
     switch ($transaction_details['txn_type']) {
         case 'subscr_signup':
             // Store PayPal Details
             update_post_meta($order_id, 'Payer PayPal address', $transaction_details['payer_email']);
             update_post_meta($order_id, 'Payer PayPal first name', $transaction_details['first_name']);
             update_post_meta($order_id, 'Payer PayPal last name', $transaction_details['last_name']);
             $default_invoice_string = self::$paypal_settings['invoice_prefix'] . ltrim($order->get_order_number(), '#');
             // If the invoice ID doesn't match the default invoice ID and contains the string '-wcscpm-', the IPN is for a subscription payment method change
             if ($default_invoice_string != $transaction_details['invoice'] && false !== strpos($transaction_details['invoice'], '-wcscpm-')) {
                 $is_payment_change = true;
             } else {
                 $is_payment_change = false;
             }
             $switched_subscription_key = get_post_meta($order_id, '_switched_subscription_key', true);
             $no_initial_payment = 0 == WC_Subscriptions_Order::get_total_initial_payment($order) && WC_Subscriptions_Order::get_subscription_trial_length($order) > 0 ? true : false;
             // When there is a free trial & no initial payment amount, we need to mark the order as paid and activate the subscription
             if (!$is_payment_change && (!empty($switched_subscription_key) || $no_initial_payment)) {
                 $order->payment_complete();
                 WC_Subscriptions_Manager::activate_subscriptions_for_order($order);
             }
             // Payment completed
             if ($is_payment_change) {
                 $order->add_order_note(__('IPN subscription payment method changed.', WC_Subscriptions::$text_domain));
             } else {
                 $order->add_order_note(__('IPN subscription sign up completed.', WC_Subscriptions::$text_domain));
             }
             if (self::$debug) {
                 if ($is_payment_change) {
                     self::$log->add('paypal', 'IPN subscription payment method changed for order ' . $order_id);
                 } else {
                     self::$log->add('paypal', 'IPN subscription sign up completed for order ' . $order_id);
                 }
             }
             break;
         case 'subscr_payment':
             if ('completed' == strtolower($transaction_details['payment_status'])) {
                 // Store PayPal Details
                 update_post_meta($order_id, 'PayPal Transaction ID', $transaction_details['txn_id']);
                 update_post_meta($order_id, 'Payer PayPal first name', $transaction_details['first_name']);
                 update_post_meta($order_id, 'Payer PayPal last name', $transaction_details['last_name']);
                 update_post_meta($order_id, 'PayPal Payment type', $transaction_details['payment_type']);
                 // Subscription Payment completed
                 $order->add_order_note(__('IPN subscription payment completed.', WC_Subscriptions::$text_domain));
                 if (self::$debug) {
                     self::$log->add('paypal', 'IPN subscription payment completed for order ' . $order_id);
                 }
                 // First payment on order, process payment & activate subscription
                 if ($is_first_payment) {
                     $order->payment_complete();
                     WC_Subscriptions_Manager::activate_subscriptions_for_order($order);
                 } else {
                     // We don't need to reactivate the subscription because Subs didn't suspend it
                     remove_action('reactivated_subscription_paypal', __CLASS__ . '::reactivate_subscription_with_paypal', 10, 2);
                     WC_Subscriptions_Manager::process_subscription_payments_on_order($order);
                     add_action('reactivated_subscription_paypal', __CLASS__ . '::reactivate_subscription_with_paypal', 10, 2);
                 }
             } elseif ('failed' == strtolower($transaction_details['payment_status'])) {
                 // Subscription Payment completed
                 $order->add_order_note(__('IPN subscription payment failed.', WC_Subscriptions::$text_domain));
                 if (self::$debug) {
                     self::$log->add('paypal', 'IPN subscription payment failed for order ' . $order_id);
                 }
                 // First payment on order, don't generate a renewal order
                 if ($is_first_payment) {
                     remove_action('processed_subscription_payment_failure', 'WC_Subscriptions_Renewal_Order::generate_failed_payment_renewal_order', 10, 2);
                 }
                 WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($order);
             } else {
                 if (self::$debug) {
                     self::$log->add('paypal', 'IPN subscription payment notification received for order ' . $order_id . ' with status ' . $transaction_details['payment_status']);
                 }
             }
             break;
         case 'subscr_cancel':
             if ('true' == get_post_meta($order_id, '_wcs_changing_payment_from_paypal_to_paypal', true)) {
                 // The flag has served its purpose
                 delete_post_meta($order_id, '_wcs_changing_payment_from_paypal_to_paypal');
                 if (self::$debug) {
                     self::$log->add('paypal', 'IPN subscription cancellation request ignored as changing PayPal to PayPal, for order ' . $order_id);
                 }
             } else {
                 WC_Subscriptions_Manager::cancel_subscriptions_for_order($order);
                 // Subscription Cancellation Completed
                 $order->add_order_note(__('IPN subscription cancelled for order.', WC_Subscriptions::$text_domain));
                 if (self::$debug) {
                     self::$log->add('paypal', 'IPN subscription cancelled for order ' . $order_id);
                 }
             }
             break;
         case 'subscr_eot':
             // Subscription ended, either due to failed payments or expiration
             $subscription_length = WC_Subscriptions_Order::get_subscription_length($order);
             // PayPal fires the 'subscr_eot' notice immediately if a subscription is only for one billing period, so ignore the request when we only have one billing period
             if (1 != $subscription_length && $subscription_length != WC_Subscriptions_Order::get_subscription_interval($order)) {
                 if (self::$debug) {
                     self::$log->add('paypal', 'IPN subscription end-of-term for order ' . $order_id);
                 }
                 // Record subscription ended
                 $order->add_order_note(__('IPN subscription end-of-term for order.', WC_Subscriptions::$text_domain));
                 // Ended due to failed payments so cancel the subscription
                 if (gmdate('U') + 24 * 60 * 60 < strtotime(WC_Subscriptions_Manager::get_subscription_expiration_date(WC_Subscriptions_Manager::get_subscription_key($order->id), $order->customer_user))) {
                     WC_Subscriptions_Manager::cancel_subscriptions_for_order($order);
                 } else {
                     WC_Subscriptions_Manager::expire_subscriptions_for_order($order);
                 }
             }
             break;
         case 'subscr_failed':
             // Subscription sign up failed
             if (self::$debug) {
                 self::$log->add('paypal', 'IPN subscription payment failure for order ' . $order_id);
             }
             // Subscription Payment completed
             $order->add_order_note(__('IPN subscription payment failure.', WC_Subscriptions::$text_domain));
             // First payment on order, don't generate a renewal order
             if ($is_first_payment) {
                 remove_action('processed_subscription_payment_failure', 'WC_Subscriptions_Renewal_Order::generate_failed_payment_renewal_order', 10, 2);
             }
             WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($order_id);
             break;
     }
     // Store the transaction ID to avoid handling requests duplicated by PayPal
     if (isset($transaction_details['ipn_track_id'])) {
         $handled_ipn_requests[] = $transaction_id;
         update_post_meta($order_id, '_paypal_ipn_tracking_ids', $handled_ipn_requests);
     }
     // Prevent default IPN handling for subscription txn_types
     exit;
 }
 /**
  * Send an email notification when a subscription payment fails
  * @param WC_Order $order
  */
 public function payment_failed_for_order($order)
 {
     if (1 == get_option('fue_subscription_failure_notification', 0)) {
         // notification enabled
         $emails_string = get_option('fue_subscription_failure_notification_emails', '');
         if (empty($emails_string)) {
             return;
         }
         // get the product id to get the subscription string
         $order_items = WC_Subscriptions_Order::get_recurring_items($order);
         $first_order_item = reset($order_items);
         $product_id = WC_Subscriptions_Order::get_items_product_id($first_order_item);
         $subs_key = WC_Subscriptions_Manager::get_subscription_key($order->id, $product_id);
         $subject = sprintf(__('Subscription payment failed for Order %s'), $order->get_order_number());
         $message = sprintf(__('A subscription payment for the order %s has failed. The subscription has now been automatically put on hold.'), $order->get_order_number());
         $recipients = array();
         if (strpos($emails_string, ',') !== false) {
             $recipients = array_map('trim', explode(',', $emails_string));
         } else {
             $recipients = array($emails_string);
         }
         $scheduler = Follow_Up_Emails::instance()->scheduler;
         // FUE will use the billing_email by default. Remove the hook to stop it from changing the email
         remove_filter('fue_insert_email_order', array($scheduler, 'get_correct_email'));
         foreach ($recipients as $email) {
             $scheduler->queue_email(array('user_email' => $email, 'meta' => array('subscription_notification' => true, 'email' => $email, 'subject' => $subject, 'message' => $message), 'email_trigger' => 'After a subscription payment fails', 'order_id' => $order->id, 'product_id' => $product_id, 'send_on' => current_time('timestamp')), null, true);
         }
     }
 }
 /**
  * When a PayPal IPN messaged is received for a subscription transaction, 
  * check the transaction details and 
  *
  * @since 1.0
  */
 public static function process_paypal_ipn_request($transaction_details)
 {
     global $wpdb;
     $transaction_details = stripslashes_deep($transaction_details);
     if (!in_array($transaction_details['txn_type'], array('subscr_signup', 'subscr_payment', 'subscr_cancel', 'subscr_eot', 'subscr_failed', 'subscr_modify'))) {
         return;
     }
     // Get the $order_id & $order_key with backward compatibility
     extract(self::get_order_id_and_key($transaction_details));
     $transaction_details['txn_type'] = strtolower($transaction_details['txn_type']);
     if (self::$debug) {
         self::$log->add('paypal', 'Subscription Transaction Type: ' . $transaction_details['txn_type']);
     }
     if (self::$debug) {
         self::$log->add('paypal', 'Subscription transaction details: ' . print_r($transaction_details, true));
     }
     $order = new WC_Order($order_id);
     // We have an invalid $order_id, probably because invoice_prefix has changed since the subscription was first created, so get the order by order key
     if (!isset($order->id)) {
         $order_id = function_exists('woocommerce_get_order_id_by_order_key') ? woocommerce_get_order_id_by_order_key($order_key) : $wpdb->get_var("SELECT post_id FROM {$wpdb->prefix}postmeta WHERE meta_key = '_order_key' AND meta_value = '{$order_key}'");
         $order = new WC_Order($order_id);
     }
     if ($order->order_key !== $order_key) {
         if (self::$debug) {
             self::$log->add('paypal', 'Subscription IPN Error: Order Key does not match invoice.');
         }
         exit;
     }
     if ('paypal' != $order->recurring_payment_method) {
         if (self::$debug) {
             self::$log->add('paypal', 'IPN ignored, recurring payment method has changed.');
         }
         exit;
     }
     if (isset($transaction_details['ipn_track_id'])) {
         // Make sure the IPN request has not already been handled
         $handled_ipn_requests = get_post_meta($order_id, '_paypal_ipn_tracking_ids', true);
         if (empty($handled_ipn_requests)) {
             $handled_ipn_requests = array();
         }
         // The 'ipn_track_id' is not a unique ID and is shared between different transaction types, so create a unique ID by prepending the transaction type
         $ipn_id = $transaction_details['txn_type'] . '_' . $transaction_details['ipn_track_id'];
         if (in_array($ipn_id, $handled_ipn_requests)) {
             if (self::$debug) {
                 self::$log->add('paypal', 'Subscription IPN Error: This IPN message has already been correctly handled.');
             }
             exit;
         }
     }
     if (isset($transaction_details['txn_id'])) {
         // Make sure the IPN request has not already been handled
         $handled_transactions = get_post_meta($order_id, '_paypal_transaction_ids', true);
         if (empty($handled_transactions)) {
             $handled_transactions = array();
         }
         $transaction_id = $transaction_details['txn_id'];
         if (isset($transaction_details['txn_type'])) {
             $transaction_id .= '_' . $transaction_details['txn_type'];
         }
         // The same transaction ID is used for different payment statuses, so make sure we handle it only once. See: http://stackoverflow.com/questions/9240235/paypal-ipn-unique-identifier
         if (isset($transaction_details['payment_status'])) {
             $transaction_id .= '_' . $transaction_details['payment_status'];
         }
         if (in_array($transaction_id, $handled_transactions)) {
             if (self::$debug) {
                 self::$log->add('paypal', 'Subscription IPN Error: This transaction has already been correctly handled.');
             }
             exit;
         }
     }
     // Save the profile ID if it's not a cancellation/expiration request
     if (isset($transaction_details['subscr_id']) && !in_array($transaction_details['txn_type'], array('subscr_cancel', 'subscr_eot'))) {
         update_post_meta($order_id, 'PayPal Subscriber ID', $transaction_details['subscr_id']);
     }
     // Get the subscription this IPN message relates to
     $subscriptions_in_order = WC_Subscriptions_Order::get_recurring_items($order);
     $subscription_item = array_pop($subscriptions_in_order);
     $product_id = WC_Subscriptions_Order::get_items_product_id($subscription_item);
     $subscription_key = WC_Subscriptions_Manager::get_subscription_key($order->id, $product_id);
     $subscription = WC_Subscriptions_Manager::get_subscription($subscription_key);
     $is_first_payment = empty($subscription['completed_payments']) ? true : false;
     if ('switched' === $subscription['status']) {
         if (self::$debug) {
             self::$log->add('paypal', 'IPN ignored, subscription has been switched.');
         }
         exit;
     }
     switch ($transaction_details['txn_type']) {
         case 'subscr_signup':
             // Store PayPal Details
             update_post_meta($order_id, 'Payer PayPal address', $transaction_details['payer_email']);
             update_post_meta($order_id, 'Payer PayPal first name', $transaction_details['first_name']);
             update_post_meta($order_id, 'Payer PayPal last name', $transaction_details['last_name']);
             $default_invoice_string = self::$paypal_settings['invoice_prefix'] . ltrim($order->get_order_number(), '#');
             // If the invoice ID doesn't match the default invoice ID and contains the string '-wcscpm-', the IPN is for a subscription payment method change
             if ($default_invoice_string != $transaction_details['invoice'] && false !== strpos($transaction_details['invoice'], '-wcscpm-')) {
                 $is_payment_change = true;
             } else {
                 $is_payment_change = false;
             }
             $switched_subscription_key = get_post_meta($order_id, '_switched_subscription_key', true);
             $no_initial_payment = 0 == WC_Subscriptions_Order::get_total_initial_payment($order) && WC_Subscriptions_Order::get_subscription_trial_length($order) > 0 ? true : false;
             // When there is a free trial & no initial payment amount, we need to mark the order as paid and activate the subscription
             if (!$is_payment_change && (!empty($switched_subscription_key) || $no_initial_payment)) {
                 $order->payment_complete();
             }
             // Payment completed
             if ($is_payment_change) {
                 $old_payment_method = get_post_meta($order->id, '_old_recurring_payment_method', true);
                 // We need to cancel the subscription now that the method has been changed successfully
                 if ('paypal' == $old_payment_method) {
                     $profile_id = get_post_meta($order->id, '_old_paypal_subscriber_id', true);
                     self::cancel_subscription_with_paypal($order, $product_id, $profile_id);
                 }
                 $order->add_order_note(__('IPN subscription payment method changed.', 'woocommerce-subscriptions'));
             } else {
                 $order->add_order_note(__('IPN subscription sign up completed.', 'woocommerce-subscriptions'));
             }
             if (self::$debug) {
                 if ($is_payment_change) {
                     self::$log->add('paypal', 'IPN subscription payment method changed for order ' . $order_id);
                 } else {
                     self::$log->add('paypal', 'IPN subscription sign up completed for order ' . $order_id);
                 }
             }
             break;
         case 'subscr_payment':
             if ('completed' == strtolower($transaction_details['payment_status'])) {
                 // Store PayPal Details
                 update_post_meta($order_id, 'PayPal Transaction ID', $transaction_details['txn_id']);
                 update_post_meta($order_id, 'Payer PayPal first name', $transaction_details['first_name']);
                 update_post_meta($order_id, 'Payer PayPal last name', $transaction_details['last_name']);
                 update_post_meta($order_id, 'PayPal Payment type', $transaction_details['payment_type']);
                 // Subscription Payment completed
                 $order->add_order_note(__('IPN subscription payment completed.', 'woocommerce-subscriptions'));
                 if (self::$debug) {
                     self::$log->add('paypal', 'IPN subscription payment completed for order ' . $order_id);
                 }
                 // First payment on order, process payment & activate subscription
                 if ($is_first_payment) {
                     $order->payment_complete();
                     WC_Subscriptions_Manager::activate_subscriptions_for_order($order);
                     // Ignore the first IPN message if the PDT should have handled it (if it didn't handle it, it will have been dealt with as first payment), but set a flag to make sure we only ignore it once
                 } elseif (count($subscription['completed_payments']) == 1 && !empty(self::$paypal_settings['identity_token']) && 'true' != get_post_meta($order_id, '_paypal_first_ipn_ignored_for_pdt', true)) {
                     if (self::$debug) {
                         self::$log->add('paypal', 'IPN subscription payment ignored for order ' . $order_id . ' due to PDT previously handling the payment.');
                     }
                     update_post_meta($order_id, '_paypal_first_ipn_ignored_for_pdt', 'true');
                     // Process the payment if the subscription is active
                 } elseif (!in_array($subscription['status'], array('cancelled', 'expired', 'switched', 'trash'))) {
                     // We don't need to reactivate the subscription because Subs didn't suspend it
                     remove_action('reactivated_subscription_paypal', __CLASS__ . '::reactivate_subscription_with_paypal', 10, 2);
                     WC_Subscriptions_Manager::process_subscription_payments_on_order($order);
                     // Make sure the next payment date is sync with when PayPal processes the payments
                     WC_Subscriptions_Manager::set_next_payment_date($subscription_key, $order->customer_user);
                     add_action('reactivated_subscription_paypal', __CLASS__ . '::reactivate_subscription_with_paypal', 10, 2);
                 }
             } elseif ('failed' == strtolower($transaction_details['payment_status'])) {
                 // Subscription Payment completed
                 $order->add_order_note(__('IPN subscription payment failed.', 'woocommerce-subscriptions'));
                 if (self::$debug) {
                     self::$log->add('paypal', 'IPN subscription payment failed for order ' . $order_id);
                 }
                 // First payment on order, don't generate a renewal order
                 if ($is_first_payment) {
                     remove_action('processed_subscription_payment_failure', 'WC_Subscriptions_Renewal_Order::generate_failed_payment_renewal_order', 10, 2);
                 }
                 WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($order);
             } else {
                 if (self::$debug) {
                     self::$log->add('paypal', 'IPN subscription payment notification received for order ' . $order_id . ' with status ' . $transaction_details['payment_status']);
                 }
             }
             break;
         case 'subscr_cancel':
             // Make sure the subscription hasn't been linked to a new payment method
             if ($transaction_details['subscr_id'] != self::get_subscriptions_paypal_id($order)) {
                 if (self::$debug) {
                     self::$log->add('paypal', 'IPN subscription cancellation request ignored - new PayPal Profile ID linked to this subscription, for order ' . $order_id);
                 }
             } else {
                 WC_Subscriptions_Manager::cancel_subscriptions_for_order($order);
                 // Subscription Cancellation Completed
                 $order->add_order_note(__('IPN subscription cancelled for order.', 'woocommerce-subscriptions'));
                 if (self::$debug) {
                     self::$log->add('paypal', 'IPN subscription cancelled for order ' . $order_id);
                 }
             }
             break;
         case 'subscr_eot':
             // Subscription ended, either due to failed payments or expiration
             if (self::$debug) {
                 self::$log->add('paypal', 'IPN EOT request ignored for order ' . $order_id);
             }
             break;
         case 'subscr_failed':
             // Subscription sign up failed
             if (self::$debug) {
                 self::$log->add('paypal', 'IPN subscription payment failure for order ' . $order_id);
             }
             // Subscription Payment completed
             $order->add_order_note(__('IPN subscription payment failure.', 'woocommerce-subscriptions'));
             // First payment on order, don't generate a renewal order
             if ($is_first_payment) {
                 remove_action('processed_subscription_payment_failure', 'WC_Subscriptions_Renewal_Order::generate_failed_payment_renewal_order', 10, 2);
             }
             WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($order);
             break;
     }
     // Store the transaction IDs to avoid handling requests duplicated by PayPal
     if (isset($transaction_details['ipn_track_id'])) {
         $handled_ipn_requests[] = $ipn_id;
         update_post_meta($order_id, '_paypal_ipn_tracking_ids', $handled_ipn_requests);
     }
     if (isset($transaction_details['txn_id'])) {
         $handled_transactions[] = $transaction_id;
         update_post_meta($order_id, '_paypal_transaction_ids', $handled_transactions);
     }
     // Prevent default IPN handling for subscription txn_types
     exit;
 }
 /**
  * Check if a payment is being made on a failed renewal order from 'My Account'. If so,
  * redirect the order into a cart/checkout payment flow.
  *
  * @since 1.3
  */
 public static function before_woocommerce_pay()
 {
     global $woocommerce, $wp;
     if (isset($_GET['pay_for_order']) && (isset($_GET['order']) && isset($_GET['order_id']) || isset($_GET['key']) && isset($wp->query_vars['order-pay']))) {
         // Pay for existing order
         $order_key = isset($_GET['key']) ? $_GET['key'] : $_GET['order'];
         // WC 2.1 compatibility
         $order_id = isset($wp->query_vars['order-pay']) ? $wp->query_vars['order-pay'] : absint($_GET['order_id']);
         $order = new WC_Order($order_id);
         $failed_order_replaced_by = get_post_meta($order_id, '_failed_order_replaced_by', true);
         if (is_numeric($failed_order_replaced_by)) {
             WC_Subscriptions::add_notice(sprintf(__('Sorry, this failed order has already been paid. See order %s.', 'woocommerce-subscriptions'), $failed_order_replaced_by), 'error');
             wp_safe_redirect(get_permalink(woocommerce_get_page_id('myaccount')));
             exit;
         }
         if ($order->id == $order_id && $order->order_key == $order_key && in_array($order->status, array('pending', 'failed')) && WC_Subscriptions_Renewal_Order::is_renewal($order)) {
             // If order being paid is a parent order, get the original order, else query parent_order
             if (WC_Subscriptions_Renewal_Order::is_renewal($order_id, array('order_role' => 'parent'))) {
                 $role = 'parent';
                 $original_order = new WC_Order(WC_Subscriptions_Order::get_meta($order, 'original_order', false));
             } elseif (WC_Subscriptions_Renewal_Order::is_renewal($order_id, array('order_role' => 'child'))) {
                 $role = 'child';
                 $original_order = WC_Subscriptions_Renewal_Order::get_parent_order($order_id);
             }
             $order_items = WC_Subscriptions_Order::get_recurring_items($original_order);
             $first_order_item = reset($order_items);
             $product_id = WC_Subscriptions_Order::get_items_product_id($first_order_item);
             $product = get_product($product_id);
             $variation_id = '';
             $variation_data = array();
             // Display error message for deleted products
             if (false === $product) {
                 WC_Subscriptions::add_notice(self::$product_deleted_error_message, 'error');
                 // Make sure we don't actually need the variation ID
             } elseif ($product->is_type(array('variable-subscription'))) {
                 $item = WC_Subscriptions_Order::get_item_by_product_id($original_order, $product_id);
                 $variation_id = $item['variation_id'];
                 $variation = get_product($variation_id);
                 // Display error message for deleted product variations
                 if (false === $variation) {
                     WC_Subscriptions::add_notice(self::$product_deleted_error_message, 'error');
                     $variation_data = array();
                 } else {
                     $variation_data = $variation->get_variation_attributes();
                 }
             } elseif ($product->is_type(array('subscription_variation'))) {
                 // Handle existing renewal orders incorrectly using variation_id as the product_id
                 $product_id = $product->id;
                 $variation_id = $product->get_variation_id();
                 $variation_data = $product->get_variation_attributes();
             }
             $woocommerce->cart->empty_cart(true);
             $woocommerce->cart->add_to_cart($product_id, 1, $variation_id, $variation_data, array('subscription_renewal' => array('original_order' => $original_order->id, 'failed_order' => $order_id, 'role' => $role)));
             wp_safe_redirect($woocommerce->cart->get_checkout_url());
             exit;
         }
     }
 }
 /**
  * @param WC_Order $order
  */
 public function payment_failed_for_order($order)
 {
     if (1 == get_option('fue_subscription_failure_notification', 0)) {
         // notification enabled
         $emails_string = get_option('fue_subscription_failure_notification_emails', '');
         if (empty($emails_string)) {
             return;
         }
         // get the product id to get the subscription string
         $order_items = WC_Subscriptions_Order::get_recurring_items($order);
         $first_order_item = reset($order_items);
         $product_id = WC_Subscriptions_Order::get_items_product_id($first_order_item);
         $subs_key = WC_Subscriptions_Manager::get_subscription_key($order->id, $product_id);
         $subject = sprintf(__('Subscription payment failed for Order %s'), $order->get_order_number());
         $message = sprintf(__('A subscription payment for the order %s has failed. The subscription has now been automatically put on hold.'), $order->get_order_number());
         $recipients = array();
         if (strpos($emails_string, ',') !== false) {
             $recipients = array_map('trim', explode(',', $emails_string));
         } else {
             $recipients = array($emails_string);
         }
         foreach ($recipients as $email) {
             FUE::mail($email, $subject, $message);
         }
     }
 }
 /**
  * Override the default PayPal standard args in WooCommerce for subscription purchases when
  * automatic payments are enabled and when the recurring order totals is over $0.00 (because
  * PayPal doesn't support subscriptions with a $0 recurring total, we need to circumvent it and
  * manage it entirely ourselves.)
  *
  * Based on the HTML Variables documented here: https://developer.paypal.com/webapps/developer/docs/classic/paypal-payments-standard/integration-guide/Appx_websitestandard_htmlvariables/#id08A6HI00JQU
  *
  * @since 1.0
  */
 public static function paypal_standard_subscription_args($paypal_args)
 {
     extract(self::get_order_id_and_key($paypal_args));
     $order = new WC_Order($order_id);
     if ($cart_item = WC_Subscriptions_Cart::cart_contains_failed_renewal_order_payment() || false !== WC_Subscriptions_Renewal_Order::get_failed_order_replaced_by($order_id)) {
         $renewal_order = $order;
         $order = WC_Subscriptions_Renewal_Order::get_parent_order($renewal_order);
         $order_contains_failed_renewal = true;
     } else {
         $order_contains_failed_renewal = false;
     }
     if ($order_contains_failed_renewal || WC_Subscriptions_Order::order_contains_subscription($order) && WC_Subscriptions_Order::get_recurring_total($order) > 0 && 'yes' !== get_option(WC_Subscriptions_Admin::$option_prefix . '_turn_off_automatic_payments', 'no')) {
         // Only one subscription allowed in the cart when PayPal Standard is active
         $product = $order->get_product_from_item(array_pop(WC_Subscriptions_Order::get_recurring_items($order)));
         // It's a subscription
         $paypal_args['cmd'] = '_xclick-subscriptions';
         if (count($order->get_items()) > 1) {
             foreach ($order->get_items() as $item) {
                 if ($item['qty'] > 1) {
                     $item_names[] = $item['qty'] . ' x ' . self::paypal_item_name($item['name']);
                 } elseif ($item['qty'] > 0) {
                     $item_names[] = self::paypal_item_name($item['name']);
                 }
             }
             $paypal_args['item_name'] = self::paypal_item_name(sprintf(__('Order %s', 'woocommerce-subscriptions'), $order->get_order_number() . " - " . implode(', ', $item_names)));
         } else {
             $paypal_args['item_name'] = self::paypal_item_name($product->get_title());
         }
         $unconverted_periods = array('billing_period' => WC_Subscriptions_Order::get_subscription_period($order), 'trial_period' => WC_Subscriptions_Order::get_subscription_trial_period($order));
         $converted_periods = array();
         // Convert period strings into PayPay's format
         foreach ($unconverted_periods as $key => $period) {
             switch (strtolower($period)) {
                 case 'day':
                     $converted_periods[$key] = 'D';
                     break;
                 case 'week':
                     $converted_periods[$key] = 'W';
                     break;
                 case 'year':
                     $converted_periods[$key] = 'Y';
                     break;
                 case 'month':
                 default:
                     $converted_periods[$key] = 'M';
                     break;
             }
         }
         $price_per_period = WC_Subscriptions_Order::get_recurring_total($order);
         $subscription_interval = WC_Subscriptions_Order::get_subscription_interval($order);
         $subscription_length = WC_Subscriptions_Order::get_subscription_length($order);
         $subscription_installments = $subscription_length / $subscription_interval;
         $is_payment_change = WC_Subscriptions_Change_Payment_Gateway::$is_request_to_change_payment;
         $is_switch_order = WC_Subscriptions_Switcher::order_contains_subscription_switch($order->id);
         $is_synced_subscription = WC_Subscriptions_Synchroniser::order_contains_synced_subscription($order->id);
         $sign_up_fee = $is_payment_change ? 0 : WC_Subscriptions_Order::get_sign_up_fee($order);
         $initial_payment = $is_payment_change ? 0 : WC_Subscriptions_Order::get_total_initial_payment($order);
         if ($is_payment_change) {
             // Add a nonce to the order ID to avoid "This invoice has already been paid" error when changing payment method to PayPal when it was previously PayPal
             $paypal_args['invoice'] = $paypal_args['invoice'] . '-wcscpm-' . wp_create_nonce();
         } elseif ($order_contains_failed_renewal) {
             // Set the invoice details to the original order's invoice but also append a special string and this renewal orders ID so that we can match it up as a failed renewal order payment later
             $paypal_args['invoice'] = self::$invoice_prefix . ltrim($order->get_order_number(), '#') . '-wcsfrp-' . $renewal_order->id;
             $paypal_args['custom'] = serialize(array($order->id, $order->order_key));
         }
         if ($order_contains_failed_renewal) {
             $sign_up_fee = 0;
             $initial_payment = $renewal_order->get_total();
             // Initial payment can be left in case the customer is purchased other products with the payment
             $subscription_trial_length = 0;
             $subscription_installments = max($subscription_installments - WC_Subscriptions_Manager::get_subscriptions_completed_payment_count(WC_Subscriptions_Manager::get_subscription_key($order_id, $product->id)), 0);
             // If we're changing the payment date or switching subs, we need to set the trial period to the next payment date & installments to be the number of installments left
         } elseif ($is_payment_change || $is_switch_order || $is_synced_subscription) {
             $subscription_key = WC_Subscriptions_Manager::get_subscription_key($order_id, $product->id);
             // Give a free trial until the next payment date
             if ($is_switch_order) {
                 $next_payment_timestamp = get_post_meta($order->id, '_switched_subscription_first_payment_timestamp', true);
             } elseif ($is_synced_subscription) {
                 $next_payment_timestamp = WC_Subscriptions_Synchroniser::calculate_first_payment_date($product, 'timestamp');
             } else {
                 $next_payment_timestamp = WC_Subscriptions_Manager::get_next_payment_date($subscription_key, $order->user_id, 'timestamp');
             }
             // When the subscription is on hold
             if ($next_payment_timestamp != false && !empty($next_payment_timestamp)) {
                 $trial_until = self::calculate_trial_periods_until($next_payment_timestamp);
                 $subscription_trial_length = $trial_until['first_trial_length'];
                 $converted_periods['trial_period'] = $trial_until['first_trial_period'];
                 $second_trial_length = $trial_until['second_trial_length'];
                 $second_trial_period = $trial_until['second_trial_period'];
             } else {
                 $subscription_trial_length = 0;
             }
             // If is a payment change, we need to account for completed payments on the number of installments owing
             if ($is_payment_change && $subscription_length > 0) {
                 $subscription_installments -= WC_Subscriptions_Manager::get_subscriptions_completed_payment_count($subscription_key);
             }
         } else {
             $subscription_trial_length = WC_Subscriptions_Order::get_subscription_trial_length($order);
         }
         if ($subscription_trial_length > 0) {
             // Specify a free trial period
             if ($is_switch_order || $is_synced_subscription || $initial_payment != $sign_up_fee) {
                 $paypal_args['a1'] = $initial_payment > 0 ? $initial_payment : 0;
             } else {
                 $paypal_args['a1'] = $sign_up_fee > 0 ? $sign_up_fee : 0;
                 // Maybe add the sign up fee to the free trial period
             }
             // Trial period length
             $paypal_args['p1'] = $subscription_trial_length;
             // Trial period
             $paypal_args['t1'] = $converted_periods['trial_period'];
             // We need to use a second trial period before we have more than 90 days until the next payment
             if (isset($second_trial_length) && $second_trial_length > 0) {
                 $paypal_args['a2'] = 0.01;
                 // Alas, although it's undocumented, PayPal appears to require a non-zero value in order to allow a second trial period
                 $paypal_args['p2'] = $second_trial_length;
                 $paypal_args['t2'] = $second_trial_period;
             }
         } elseif ($sign_up_fee > 0 || $initial_payment != $price_per_period) {
             // No trial period, so charge sign up fee and per period price for the first period
             if ($subscription_installments == 1) {
                 $param_number = 3;
             } else {
                 $param_number = 1;
             }
             $paypal_args['a' . $param_number] = $initial_payment;
             // Sign Up interval
             $paypal_args['p' . $param_number] = $subscription_interval;
             // Sign Up unit of duration
             $paypal_args['t' . $param_number] = $converted_periods['billing_period'];
         }
         // We have a recurring payment
         if (!isset($param_number) || $param_number == 1) {
             // Subscription price
             $paypal_args['a3'] = $price_per_period;
             // Subscription duration
             $paypal_args['p3'] = $subscription_interval;
             // Subscription period
             $paypal_args['t3'] = $converted_periods['billing_period'];
         }
         // Recurring payments
         if ($subscription_installments == 1 || $sign_up_fee > 0 && $subscription_trial_length == 0 && $subscription_installments == 2) {
             // Non-recurring payments
             $paypal_args['src'] = 0;
         } else {
             $paypal_args['src'] = 1;
             if ($subscription_installments > 0) {
                 if ($sign_up_fee > 0 && $subscription_trial_length == 0) {
                     // An initial period is being used to charge a sign-up fee
                     $subscription_installments--;
                 }
                 $paypal_args['srt'] = $subscription_installments;
             }
         }
         // Don't reattempt failed payments, instead let Subscriptions handle the failed payment
         $paypal_args['sra'] = 0;
         // Force return URL so that order description & instructions display
         $paypal_args['rm'] = 2;
     }
     return $paypal_args;
 }
        echo $total['label'];
        ?>
</th>
					<td class="product-total"><?php 
        echo $total['value'];
        ?>
</td>
				</tr>
				<?php 
    }
}
?>
		</tfoot>
		<tbody>
			<?php 
$recurring_order_items = WC_Subscriptions_Order::get_recurring_items($order);
if (sizeof($recurring_order_items) > 0) {
    foreach ($recurring_order_items as $item) {
        echo '
						<tr>
							<td class="product-name">' . $item['name'] . '</td>
							<td class="product-quantity">' . $item['qty'] . '</td>
							<td class="product-subtotal">' . $order->get_formatted_line_subtotal($item) . '</td>
						</tr>';
    }
}
?>
		</tbody>
	</table>

	<div id="payment">
 /**
  * When a PayPal IPN messaged is received for a subscription transaction, 
  * check the transaction details and 
  *
  * @since 1.0
  */
 public static function process_paypal_ipn_request($transaction_details)
 {
     if (!in_array($transaction_details['txn_type'], array('subscr_signup', 'subscr_payment', 'subscr_cancel', 'subscr_eot', 'subscr_failed', 'subscr_modify'))) {
         return;
     }
     if (empty($transaction_details['custom']) || empty($transaction_details['invoice'])) {
         return;
     }
     // Get the $order_id & $order_key with backward compatibility
     extract(self::get_order_id_and_key($transaction_details));
     $transaction_details['txn_type'] = strtolower($transaction_details['txn_type']);
     if (self::$debug) {
         self::$log->add('paypal', 'Subscription Transaction Type: ' . $transaction_details['txn_type']);
     }
     if (self::$debug) {
         self::$log->add('paypal', 'Subscription transaction details: ' . print_r($transaction_details, true));
     }
     $order = new WC_Order($order_id);
     // We have an invalid $order_id, probably because invoice_prefix has changed since the subscription was first created, so get the
     if (!isset($order->id)) {
         $order_id = function_exists('woocommerce_get_order_id_by_order_key') ? woocommerce_get_order_id_by_order_key($order_key) : $wpdb->get_var("SELECT post_id FROM {$wpdb->prefix}postmeta WHERE meta_key = '_order_key' AND meta_value = '{$order_key}'");
         $order = new WC_Order($order_id);
     }
     if ($order->order_key !== $order_key) {
         if (self::$debug) {
             self::$log->add('paypal', 'Subscription IPN Error: Order Key does not match invoice.');
         }
         return;
     }
     switch ($transaction_details['txn_type']) {
         case 'subscr_signup':
             // Store PayPal Details
             update_post_meta($order_id, 'Payer PayPal address', $transaction_details['payer_email']);
             update_post_meta($order_id, 'Payer PayPal first name', $transaction_details['first_name']);
             update_post_meta($order_id, 'Payer PayPal last name', $transaction_details['last_name']);
             update_post_meta($order_id, 'PayPal Subscriber ID', $transaction_details['subscr_id']);
             // Payment completed
             $order->add_order_note(__('IPN subscription sign up completed.', WC_Subscriptions::$text_domain));
             if (self::$debug) {
                 self::$log->add('paypal', 'IPN subscription sign up completed for order ' . $order_id);
             }
             // When there is a free trial & no initial payment amount, we need to mark the order as paid and activate the subscription
             if (0 == WC_Subscriptions_Order::get_total_initial_payment($order) && WC_Subscriptions_Order::get_subscription_trial_length($order) > 0) {
                 $order->payment_complete();
                 WC_Subscriptions_Manager::activate_subscriptions_for_order($order);
             }
             break;
         case 'subscr_payment':
             if ('completed' == strtolower($transaction_details['payment_status'])) {
                 // Store PayPal Details
                 update_post_meta($order_id, 'PayPal Transaction ID', $transaction_details['txn_id']);
                 update_post_meta($order_id, 'Payer PayPal first name', $transaction_details['first_name']);
                 update_post_meta($order_id, 'Payer PayPal last name', $transaction_details['last_name']);
                 update_post_meta($order_id, 'PayPal Payment type', $transaction_details['payment_type']);
                 // Subscription Payment completed
                 $order->add_order_note(__('IPN subscription payment completed.', WC_Subscriptions::$text_domain));
                 if (self::$debug) {
                     self::$log->add('paypal', 'IPN subscription payment completed for order ' . $order_id);
                 }
                 $subscriptions_in_order = WC_Subscriptions_Order::get_recurring_items($order);
                 $subscription_item = array_pop($subscriptions_in_order);
                 $subscription_key = WC_Subscriptions_Manager::get_subscription_key($order->id, $subscription_item['id']);
                 $subscription = WC_Subscriptions_Manager::get_subscription($subscription_key, $order->customer_user);
                 // First payment on order, process payment & activate subscription
                 if (empty($subscription['completed_payments'])) {
                     $order->payment_complete();
                     WC_Subscriptions_Manager::activate_subscriptions_for_order($order);
                 } else {
                     WC_Subscriptions_Manager::process_subscription_payments_on_order($order);
                 }
             } elseif ('failed' == strtolower($transaction_details['payment_status'])) {
                 // Subscription Payment completed
                 $order->add_order_note(__('IPN subscription payment failed.', WC_Subscriptions::$text_domain));
                 if (self::$debug) {
                     self::$log->add('paypal', 'IPN subscription payment failed for order ' . $order_id);
                 }
                 WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($order);
             } else {
                 if (self::$debug) {
                     self::$log->add('paypal', 'IPN subscription payment notification received for order ' . $order_id . ' with status ' . $transaction_details['payment_status']);
                 }
             }
             break;
         case 'subscr_cancel':
             if (self::$debug) {
                 self::$log->add('paypal', 'IPN subscription cancelled for order ' . $order_id);
             }
             // Subscription Payment completed
             $order->add_order_note(__('IPN subscription cancelled for order.', WC_Subscriptions::$text_domain));
             WC_Subscriptions_Manager::cancel_subscriptions_for_order($order);
             break;
         case 'subscr_eot':
             // Subscription ended, either due to failed payments or expiration
             // PayPal fires the 'subscr_eot' notice immediately if a subscription is only for one billing period, so ignore the request when we only have one billing period
             if (1 != WC_Subscriptions_Order::get_subscription_length($order)) {
                 if (self::$debug) {
                     self::$log->add('paypal', 'IPN subscription end-of-term for order ' . $order_id);
                 }
                 // Record subscription ended
                 $order->add_order_note(__('IPN subscription end-of-term for order.', WC_Subscriptions::$text_domain));
                 // Ended due to failed payments so cancel the subscription
                 if (time() < strtotime(WC_Subscriptions_Manager::get_subscription_expiration_date(WC_Subscriptions_Manager::get_subscription_key($order->id), $order->customer_user))) {
                     WC_Subscriptions_Manager::cancel_subscriptions_for_order($order);
                 } else {
                     WC_Subscriptions_Manager::expire_subscriptions_for_order($order);
                 }
             }
             break;
         case 'subscr_failed':
             // Subscription sign up failed
             if (self::$debug) {
                 self::$log->add('paypal', 'IPN subscription sign up failure for order ' . $order_id);
             }
             // Subscription Payment completed
             $order->add_order_note(__('IPN subscription sign up failure.', WC_Subscriptions::$text_domain));
             WC_Subscriptions_Manager::failed_subscription_sign_ups_for_order($order);
             break;
     }
     // Prevent default IPN handling for subscription txn_types
     exit;
 }