/**
  * Uses the details of an order to create a pending subscription on the customers account
  * for a subscription product, as specified with $product_id.
  *
  * @param $order mixed int | WC_Order The order ID or WC_Order object to create the subscription from.
  * @param $product_id int The ID of the subscription product on the order.
  * @param $args array An array of name => value pairs to customise the details of the subscription, including:
  * 			'start_date' A MySQL formatted date/time string on which the subscription should start, in UTC timezone
  * 			'expiry_date' A MySQL formatted date/time string on which the subscription should expire, in UTC timezone
  * @since 1.1
  */
 public static function create_pending_subscription_for_order($order, $product_id, $args = array())
 {
     if (!is_object($order)) {
         $order = new WC_Order($order);
     }
     if (!WC_Subscriptions_Product::is_subscription($product_id)) {
         return;
     }
     $args = wp_parse_args($args, array('start_date' => '', 'expiry_date' => ''));
     $subscription_key = self::get_subscription_key($order->id, $product_id);
     // In case the subscription exists already
     $subscription = self::get_users_subscription($order->customer_user, $subscription_key);
     // Adding a new subscription so set the start date/time to now
     if (!empty($args['start_date'])) {
         if (is_numeric($args['start_date'])) {
             $args['start_date'] = date('Y-m-d H:i:s', $args['start_date']);
         }
         $start_date = $args['start_date'];
     } else {
         $start_date = isset($subscription['start_date']) ? $subscription['start_date'] : gmdate('Y-m-d H:i:s');
     }
     // Adding a new subscription so set the expiry date/time from the order date
     if (!empty($args['expiry_date'])) {
         if (is_numeric($args['expiry_date'])) {
             $args['expiry_date'] = date('Y-m-d H:i:s', $args['expiry_date']);
         }
         $expiration = $args['expiry_date'];
     } else {
         $expiration = isset($subscription['expiry_date']) ? $subscription['expiry_date'] : WC_Subscriptions_Product::get_expiration_date($product_id, $start_date);
     }
     // Adding a new subscription so set the expiry date/time from the order date
     $trial_expiration = isset($subscription['trial_expiry_date']) ? $subscription['trial_expiry_date'] : WC_Subscriptions_Product::get_trial_expiration_date($product_id, $start_date);
     $failed_payments = isset($subscription['failed_payments']) ? $subscription['failed_payments'] : 0;
     $completed_payments = isset($subscription['completed_payments']) ? $subscription['completed_payments'] : array();
     $subscriptions[$subscription_key] = array('product_id' => $product_id, 'order_key' => $order->order_key, 'order_id' => $order->id, 'start_date' => $start_date, 'expiry_date' => $expiration, 'end_date' => 0, 'status' => 'pending', 'trial_expiry_date' => $trial_expiration, 'failed_payments' => $failed_payments, 'completed_payments' => $completed_payments);
     self::update_users_subscriptions($order->customer_user, $subscriptions);
     $product = WC_Subscriptions::get_product($product_id);
     // Set subscription status to active and log activation
     $order->add_order_note(sprintf(__('Pending subscription created for "%s".', WC_Subscriptions::$text_domain), $product->get_title()));
     do_action('pending_subscription_created_for_order', $order, $product_id);
 }
 /**
  * Calculate the first payment date for a synced subscription.
  *
  * The date is calculated in UTC timezone.
  *
  * @param WC_Product $product A subscription product.
  * @param string $type (optional) The format to return the first payment date in, either 'mysql' or 'timestamp'. Default 'mysql'.
  * @param string $from_date (optional) The date to calculate the first payment from in GMT/UTC timzeone. If not set, it will use the current date. This should not include any trial period on the product.
  * @since 1.5
  */
 public static function calculate_first_payment_date($product, $type = 'mysql', $from_date = '')
 {
     if (!is_object($product)) {
         $product = WC_Subscriptions::get_product($product);
     }
     if (!self::is_product_synced($product)) {
         return 0;
     }
     $period = WC_Subscriptions_Product::get_period($product);
     $trial_period = WC_Subscriptions_Product::get_trial_period($product);
     $trial_length = WC_Subscriptions_Product::get_trial_length($product);
     $from_date_param = $from_date;
     if (empty($from_date)) {
         $from_date = gmdate('Y-m-d H:i:s');
     }
     // If the subscription has a free trial period, the first payment should be synced to a day after the free trial
     if ($trial_length > 0) {
         $from_date = WC_Subscriptions_Product::get_trial_expiration_date($product, $from_date);
     }
     $from_timestamp = strtotime($from_date) + get_option('gmt_offset') * 3600;
     // Site time
     $payment_day = self::get_products_payment_day($product);
     if ('week' == $period) {
         // strtotime() only handles English, so can't use $wp_locale->weekday here
         $weekdays = array(1 => 'Monday', 2 => 'Tuesday', 3 => 'Wednesday', 4 => 'Thursday', 5 => 'Friday', 6 => 'Saturday', 7 => 'Sunday');
         // strtotime() will figure out if the day is in the future or today (see: https://gist.github.com/thenbrent/9698083)
         $first_payment_timestamp = strtotime($weekdays[$payment_day], $from_timestamp);
     } elseif ('month' == $period) {
         // strtotime() needs to know the month, so we need to determine if the specified day has occured this month yet or if we want the last day of the month (see: https://gist.github.com/thenbrent/9698083)
         if ($payment_day > 27) {
             // we actually want the last day of the month
             $payment_day = gmdate('t', $from_timestamp);
             $month = gmdate('F', $from_timestamp);
         } elseif (gmdate('j', $from_timestamp) > $payment_day) {
             // today is later than specified day in the from date, we need the next month
             $month = date('F', WC_Subscriptions::add_months($from_timestamp, 1));
         } else {
             // specified day is either today or still to come in the month of the from date
             $month = gmdate('F', $from_timestamp);
         }
         $first_payment_timestamp = strtotime("{$payment_day} {$month}", $from_timestamp);
     } elseif ('year' == $period) {
         // We can't use $wp_locale here because it is translated
         switch ($payment_day['month']) {
             case 1:
                 $month = 'January';
                 break;
             case 2:
                 $month = 'February';
                 break;
             case 3:
                 $month = 'March';
                 break;
             case 4:
                 $month = 'April';
                 break;
             case 5:
                 $month = 'May';
                 break;
             case 6:
                 $month = 'June';
                 break;
             case 7:
                 $month = 'July';
                 break;
             case 8:
                 $month = 'August';
                 break;
             case 9:
                 $month = 'September';
                 break;
             case 10:
                 $month = 'October';
                 break;
             case 11:
                 $month = 'November';
                 break;
             case 12:
                 $month = 'December';
                 break;
         }
         $first_payment_timestamp = strtotime("{$payment_day['day']} {$month}", $from_timestamp);
     }
     // Make sure the next payment is in the future and after the $from_date, as strtotime() will return the date this year for any day in the past when adding months or years (see: https://gist.github.com/thenbrent/9698083)
     if ('year' == $period || 'month' == $period) {
         // First make sure the day is in the past so that we don't end up jumping a month or year because of a few hours difference between now and the billing date
         if (gmdate('j', $first_payment_timestamp) < gmdate('j') && gmdate('n', $first_payment_timestamp) <= gmdate('n') && gmdate('Y', $first_payment_timestamp) <= gmdate('Y')) {
             $i = 1;
             // Then make sure the date and time of the payment is in the future
             while (($first_payment_timestamp < gmdate('U') || $first_payment_timestamp < $from_timestamp) && $i < 30) {
                 $first_payment_timestamp = strtotime("+ 1 {$period}", $first_payment_timestamp);
                 $i = $i + 1;
             }
         }
     }
     // We calculated a timestamp for midnight on the specific day in the site's timezone, let's push it to 3am to account for any daylight savings changes
     $first_payment_timestamp += 3 * HOUR_IN_SECONDS;
     // And convert it to the UTC equivalent of 3am on that day
     $first_payment_timestamp -= get_option('gmt_offset') * HOUR_IN_SECONDS;
     $first_payment = 'mysql' == $type && 0 != $first_payment_timestamp ? date('Y-m-d H:i:s', $first_payment_timestamp) : $first_payment_timestamp;
     return apply_filters('woocommerce_subscriptions_synced_first_payment_date', $first_payment, $product, $type, $from_date, $from_date_param);
 }
 /**
  * Returns the sign-up fee for a subscription, if it is a subscription.
  *
  * @param mixed $product A WC_Product object or product ID
  * @return float The value of the sign-up fee, or 0 if the product is not a subscription or the subscription has no sign-up fee
  * @since 1.0
  */
 public static function get_sign_up_fee($product)
 {
     if (!is_object($product)) {
         $product = WC_Subscriptions::get_product($product);
     }
     if (!self::is_subscription($product) || !isset($product->subscription_sign_up_fee) && empty($product->product_custom_fields['_subscription_sign_up_fee'][0])) {
         $subscription_sign_up_fee = 0;
     } else {
         $subscription_sign_up_fee = isset($product->subscription_sign_up_fee) ? $product->subscription_sign_up_fee : $product->product_custom_fields['_subscription_sign_up_fee'][0];
     }
     return apply_filters('woocommerce_subscriptions_product_sign_up_fee', $subscription_sign_up_fee, $product);
 }
/**
 * Update recurring tax for subscriptions
 * Not executed if Subscriptions plugin is not active
 *
 * @since 4.4
 * @return void
 */
function wootax_update_recurring_tax()
{
    global $wpdb;
    // Exit if subs is not active
    if (!WT_SUBS_ACTIVE) {
        return;
    }
    // Find date/time 12 hours from now
    $twelve_hours = mktime(date('H') + 12);
    $date = new DateTime(date('c', $twelve_hours));
    $date = $date->format('Y-m-d H:i:s');
    // Set up logger
    $logger = false;
    if (WT_LOG_REQUESTS) {
        $logger = class_exists('WC_Logger') ? new WC_Logger() : WC()->logger();
        $logger->add('wootax', 'Starting recurring tax update. Subscriptions with payments due before ' . $date . ' are being considered.');
    }
    // Get all scheduled "scheduled_subscription_payment" actions with post_date <= $twelve_hours
    $scheduled = $wpdb->get_results($wpdb->prepare("SELECT ID, post_content FROM {$wpdb->posts} WHERE post_status = %s AND post_title = %s AND post_date <= %s", "pending", "scheduled_subscription_payment", $date));
    // Update recurring totals if necessary
    if (count($scheduled) > 0) {
        // This will hold any warning messages that need to be sent to the admin
        $warnings = array();
        foreach ($scheduled as $action) {
            $temp_warnings = array();
            $show_warnings = false;
            // Run json_decode on post_content to extract user_id and subscription_key
            $args = json_decode($action->post_content);
            // Parse subscription_key to get order_id/product_id (format: ORDERID_PRODUCTID)
            $subscription_key = $args->subscription_key;
            $key_parts = explode('_', $subscription_key);
            $order_id = (int) $key_parts[0];
            $product_id = (int) $key_parts[1];
            if (get_post_status($order_id) == false) {
                continue;
                // Skip if the order no longer exists
            }
            // Determine if changes to subscription amounts are allowed by the current gateway
            $chosen_gateway = WC_Subscriptions_Payment_Gateways::get_payment_gateway(get_post_meta($order_id, '_recurring_payment_method', true));
            $manual_renewal = WC_Subscriptions_Order::requires_manual_renewal($order_id);
            $changes_supported = $chosen_gateway === false || $manual_renewal == 'true' || $chosen_gateway->supports('subscription_amount_changes') ? true : false;
            // Load order
            $order = WT_Orders::get_order($order_id);
            // Collect data for Lookup request
            $item_data = $type_array = array();
            // Add subscription
            $product = WC_Subscriptions::get_product($product_id);
            // Get order item ID
            $item_id = $wpdb->get_var("SELECT i.order_item_id FROM {$wpdb->prefix}woocommerce_order_items i LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta im ON im.order_item_id = i.order_item_id WHERE im.meta_key = '_product_id' AND im.meta_value = {$product_id} AND i.order_id = {$order_id}");
            // Get price
            $recurring_subtotal = $order->get_item_meta($item_id, '_recurring_line_subtotal');
            $regular_subtotal = $order->get_item_meta($item_id, '_line_subtotal');
            $price = $recurring_subtotal === '0' || !empty($recurring_subtotal) ? $recurring_subtotal : $regular_subtotal;
            // Special case: If _subscription_sign_up_fee is set and $price is equal to its value, fall back to product price
            if ($order->get_item_meta($item_id, '_subscription_sign_up_fee') == $price) {
                $price = $product->get_price();
            }
            $item_info = array('Index' => '', 'ItemID' => $item_id, 'Qty' => 1, 'Price' => $price, 'Type' => 'cart');
            $tic = get_post_meta($product_id, 'wootax_tic', true);
            if (!empty($tic) && $tic) {
                $item_info['TIC'] = $tic;
            }
            $item_data[] = $item_info;
            $type_array[$item_id] = 'cart';
            // Add recurring shipping items
            foreach ($order->order->get_items('recurring_shipping') as $item_id => $shipping) {
                $item_data[] = array('Index' => '', 'ItemID' => $item_id, 'TIC' => WT_SHIPPING_TIC, 'Qty' => 1, 'Price' => $shipping['cost'], 'Type' => 'shipping');
                $type_array[$item_id] = 'shipping';
            }
            // Reset "captured" meta so lookup always sent
            $captured = WT_Orders::get_meta($order_id, 'captured');
            WT_Orders::update_meta($order_id, 'captured', false);
            // Issue Lookup request
            $res = $order->do_lookup($item_data, $type_array, true);
            // Set "captured" back to original value
            WT_Orders::update_meta($order_id, 'captured', $captured);
            // If lookup is successful, use result to update recurring tax totals as described here: http://docs.woothemes.com/document/subscriptions/add-or-modify-a-subscription/#change-recurring-total
            if (is_array($res)) {
                // Find recurring tax item and determine original tax/shipping tax totals
                $wootax_item_id = $wpdb->get_var($wpdb->prepare("SELECT i.order_item_id FROM {$wpdb->prefix}woocommerce_order_items i LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta im ON im.order_item_id = i.order_item_id WHERE im.meta_key = %s AND im.meta_value = %d AND i.order_id = %d AND i.order_item_type = %s", "rate_id", WT_RATE_ID, $order_id, "recurring_tax"));
                $old_tax = empty($wootax_item_id) ? 0 : $order->get_item_meta($wootax_item_id, 'tax_amount');
                $old_shipping_tax = empty($wootax_item_id) ? 0 : $order->get_item_meta($wootax_item_id, 'shipping_tax_amount');
                // Find new tax/shipping tax totals
                // Update _recurring_line_tax meta for each item
                $tax = $shipping_tax = 0;
                foreach ($res as $item) {
                    $item_id = $item->ItemID;
                    $item_tax = $item->TaxAmount;
                    if ($type_array[$item_id] == 'shipping') {
                        $shipping_tax += $item_tax;
                    } else {
                        $tax += $item_tax;
                    }
                    if ($changes_supported) {
                        wc_update_order_item_meta($item_id, '_recurring_line_tax', $item_tax);
                        wc_update_order_item_meta($item_id, '_recurring_line_subtotal_tax', $item_tax);
                    } else {
                        $temp_warnings[] = 'Recurring tax for item #' . $item_id . ' changed to ' . wc_round_tax_total($item_tax);
                    }
                }
                // Update recurring tax if necessary
                if ($old_tax != $tax || $old_shipping_tax != $shipping_tax) {
                    if ($changes_supported) {
                        if (!empty($wootax_item_id)) {
                            wc_update_order_item_meta($wootax_item_id, 'tax_amount', $tax);
                            wc_update_order_item_meta($wootax_item_id, 'cart_tax', $tax);
                            wc_update_order_item_meta($wootax_item_id, 'shipping_tax_amount', $shipping_tax);
                            wc_update_order_item_meta($wootax_item_id, 'shipping_tax', $shipping_tax);
                        }
                        // Determine rounded difference in old/new tax totals
                        $tax_diff = $tax + $shipping_tax - ($old_tax + $old_shipping_tax);
                        $rounded_tax_diff = wc_round_tax_total($tax_diff);
                        // Set new recurring total by adding difference between old and new tax to existing total
                        $new_recurring_total = get_post_meta($order_id, '_order_recurring_total', true) + $rounded_tax_diff;
                        update_post_meta($order_id, '_order_recurring_total', $new_recurring_total);
                        if ($logger) {
                            $logger->add('wootax', 'Set recurring total for order ' . $order_id . ' to: ' . $new_recurring_total);
                        }
                    } else {
                        $temp_warnings[] = 'Total recurring tax changed from ' . wc_round_tax_total($old_tax) . ' to ' . wc_round_tax_total($tax);
                        $temp_warnings[] = 'Total recurring shipping tax changed from ' . wc_round_tax_total($old_shipping_tax) . ' to ' . wc_round_tax_total($shipping_tax);
                        $show_warnings = true;
                    }
                }
                // Add to warnings array if necessary
                if ($show_warnings) {
                    $warnings[$order_id] = $temp_warnings;
                }
            }
        }
        // Send out a single warning email to the admin if necessary
        // Ex: Email sent if a change in tax rates is detected and the gateway used by an order doesn't allow modification of sub. details
        if (count($warnings) > 0) {
            $email = wootax_get_notification_email();
            if (!empty($email) && is_email($email)) {
                $subject = 'WooTax Warning: Recurring Tax Totals Need To Be Updated';
                $message = 'Hello,' . "\r\n\r\n" . 'During a routine check on ' . date('m/d/Y') . ', WooTax discovered ' . count($warnings) . ' subscription orders whose recurring tax totals need to be updated. Unfortunately, the payment gateway(s) used for these orders does not allow subscription details to be altered, so the required changes must be implemented manually. All changes are listed below for your convenience.' . "\r\n\r\n";
                foreach ($warnings as $order_id => $errors) {
                    $message .= 'Order ' . $order_id . ': ' . "\r\n\r\n";
                    foreach ($errors as $error) {
                        $message .= '- ' . $error . "\r\n";
                    }
                    $message .= "\r\n\r\n";
                }
                $message .= 'For assistance, please contact the WooTax support team at sales@wootax.com.';
                wp_mail($email, $subject, $message);
            }
        }
    } else {
        if ($logger) {
            $logger->add('wootax', 'Ending recurring tax update. No subscriptions due before ' . $date . '.');
        }
    }
}
 /**
  * Uses the details of an order to create a pending subscription on the customers account
  * for a subscription product, as specified with $product_id.
  *
  * @param int|WC_Order $order The order ID or WC_Order object to create the subscription from.
  * @param int $product_id The ID of the subscription product on the order.
  * @param array $args An array of name => value pairs to customise the details of the subscription, including:
  * 			'start_date' A MySQL formatted date/time string on which the subscription should start, in UTC timezone
  * 			'expiry_date' A MySQL formatted date/time string on which the subscription should expire, in UTC timezone
  * @since 1.1
  */
 public static function create_pending_subscription_for_order($order, $product_id, $args = array())
 {
     if (!is_object($order)) {
         $order = new WC_Order($order);
     }
     if (!WC_Subscriptions_Product::is_subscription($product_id)) {
         return;
     }
     $args = wp_parse_args($args, array('start_date' => '', 'expiry_date' => ''));
     $subscription_key = self::get_subscription_key($order->id, $product_id);
     // In case the subscription exists already
     $subscription = self::get_subscription($subscription_key);
     if (!empty($subscription['variation_id'])) {
         $product_id = $subscription['variation_id'];
     } elseif (!empty($subscription['product_id'])) {
         $product_id = $subscription['product_id'];
     }
     // Adding a new subscription so set the start date/time to now
     if (!empty($args['start_date'])) {
         if (is_numeric($args['start_date'])) {
             $args['start_date'] = date('Y-m-d H:i:s', $args['start_date']);
         }
         $start_date = $args['start_date'];
     } else {
         $start_date = !empty($subscription['start_date']) ? $subscription['start_date'] : gmdate('Y-m-d H:i:s');
     }
     // Adding a new subscription so set the expiry date/time from the order date
     if (!empty($args['expiry_date'])) {
         if (is_numeric($args['expiry_date'])) {
             $args['expiry_date'] = date('Y-m-d H:i:s', $args['expiry_date']);
         }
         $expiration = $args['expiry_date'];
     } else {
         $expiration = !empty($subscription['expiry_date']) ? $subscription['expiry_date'] : WC_Subscriptions_Product::get_expiration_date($product_id, $start_date);
     }
     // Adding a new subscription so set the expiry date/time from the order date
     $trial_expiration = !empty($subscription['trial_expiry_date']) ? $subscription['trial_expiry_date'] : WC_Subscriptions_Product::get_trial_expiration_date($product_id, $start_date);
     $failed_payments = !empty($subscription['failed_payments']) ? $subscription['failed_payments'] : 0;
     $completed_payments = !empty($subscription['completed_payments']) ? $subscription['completed_payments'] : array();
     $order_item_id = WC_Subscriptions_Order::get_item_id_by_subscription_key($subscription_key);
     // Store the subscription details in item meta
     woocommerce_add_order_item_meta($order_item_id, '_subscription_start_date', $start_date, true);
     woocommerce_add_order_item_meta($order_item_id, '_subscription_expiry_date', $expiration, true);
     woocommerce_add_order_item_meta($order_item_id, '_subscription_trial_expiry_date', $trial_expiration, true);
     woocommerce_add_order_item_meta($order_item_id, '_subscription_failed_payments', $failed_payments, true);
     woocommerce_add_order_item_meta($order_item_id, '_subscription_completed_payments', $completed_payments, true);
     woocommerce_add_order_item_meta($order_item_id, '_subscription_status', 'pending', true);
     woocommerce_add_order_item_meta($order_item_id, '_subscription_end_date', 0, true);
     woocommerce_add_order_item_meta($order_item_id, '_subscription_suspension_count', 0, true);
     $product = WC_Subscriptions::get_product($product_id);
     // Set subscription status to active and log activation
     $order->add_order_note(sprintf(__('Pending subscription created for "%s".', 'woocommerce-subscriptions'), $product->get_title()));
     do_action('pending_subscription_created_for_order', $order, $product_id);
 }
 /**
  * Calculate recurring line taxes when a store manager clicks the "Calc Line Tax" button on the "Edit Order" page.
  *
  * Based on the @see woocommerce_calc_line_taxes() function.
  * @since 1.2.4
  * @return void
  */
 public static function calculate_recurring_line_taxes()
 {
     global $woocommerce, $wpdb;
     check_ajax_referer('woocommerce-subscriptions', 'security');
     $tax = new WC_Tax();
     $taxes = $tax_rows = $item_taxes = $shipping_taxes = $return = array();
     $item_tax = 0;
     $order_id = absint($_POST['order_id']);
     $country = strtoupper(esc_attr($_POST['country']));
     $state = strtoupper(esc_attr($_POST['state']));
     $postcode = strtoupper(esc_attr($_POST['postcode']));
     $tax_class = esc_attr($_POST['tax_class']);
     if (isset($_POST['city'])) {
         $city = sanitize_title(esc_attr($_POST['city']));
     }
     $shipping = $_POST['shipping'];
     $line_subtotal = isset($_POST['line_subtotal']) ? esc_attr($_POST['line_subtotal']) : 0;
     $line_total = isset($_POST['line_total']) ? esc_attr($_POST['line_total']) : 0;
     $product_id = '';
     if (isset($_POST['order_item_id'])) {
         $product_id = woocommerce_get_order_item_meta($_POST['order_item_id'], '_product_id');
     } elseif (isset($_POST['product_id'])) {
         $product_id = esc_attr($_POST['product_id']);
     }
     if (!empty($product_id) && WC_Subscriptions_Product::is_subscription($product_id)) {
         // Get product details
         $product = WC_Subscriptions::get_product($product_id);
         $item_tax_status = $product->get_tax_status();
         if ($item_tax_status == 'taxable') {
             $tax_rates = $tax->find_rates(array('country' => $country, 'state' => $state, 'postcode' => $postcode, 'city' => $city, 'tax_class' => $tax_class));
             $line_subtotal_taxes = $tax->calc_tax($line_subtotal, $tax_rates, false);
             $line_taxes = $tax->calc_tax($line_total, $tax_rates, false);
             $line_subtotal_tax = $tax->round(array_sum($line_subtotal_taxes));
             $line_tax = $tax->round(array_sum($line_taxes));
             if ($line_subtotal_tax < 0) {
                 $line_subtotal_tax = 0;
             }
             if ($line_tax < 0) {
                 $line_tax = 0;
             }
             $return = array('recurring_line_subtotal_tax' => $line_subtotal_tax, 'recurring_line_tax' => $line_tax);
             // Sum the item taxes
             foreach (array_keys($taxes + $line_taxes) as $key) {
                 $taxes[$key] = (isset($line_taxes[$key]) ? $line_taxes[$key] : 0) + (isset($taxes[$key]) ? $taxes[$key] : 0);
             }
         }
         // Now calculate shipping tax
         $matched_tax_rates = array();
         $tax_rates = $tax->find_rates(array('country' => $country, 'state' => $state, 'postcode' => $postcode, 'city' => $city, 'tax_class' => ''));
         if ($tax_rates) {
             foreach ($tax_rates as $key => $rate) {
                 if (isset($rate['shipping']) && $rate['shipping'] == 'yes') {
                     $matched_tax_rates[$key] = $rate;
                 }
             }
         }
         $shipping_taxes = $tax->calc_shipping_tax($shipping, $matched_tax_rates);
         $shipping_tax = $tax->round(array_sum($shipping_taxes));
         $return['recurring_shipping_tax'] = $shipping_tax;
         // Remove old tax rows
         $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->prefix}woocommerce_order_itemmeta WHERE order_item_id IN ( SELECT order_item_id FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = %d AND order_item_type = 'recurring_tax' )", $order_id));
         $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = %d AND order_item_type = 'recurring_tax'", $order_id));
         // Get tax rates
         $rates = $wpdb->get_results("SELECT tax_rate_id, tax_rate_country, tax_rate_state, tax_rate_name, tax_rate_priority FROM {$wpdb->prefix}woocommerce_tax_rates ORDER BY tax_rate_name");
         $tax_codes = array();
         foreach ($rates as $rate) {
             $code = array();
             $code[] = $rate->tax_rate_country;
             $code[] = $rate->tax_rate_state;
             $code[] = $rate->tax_rate_name ? sanitize_title($rate->tax_rate_name) : 'TAX';
             $code[] = absint($rate->tax_rate_priority);
             $tax_codes[$rate->tax_rate_id] = strtoupper(implode('-', array_filter($code)));
         }
         // Now merge to keep tax rows
         ob_start();
         foreach (array_keys($taxes + $shipping_taxes) as $key) {
             $item = array();
             $item['rate_id'] = $key;
             $item['name'] = $tax_codes[$key];
             $item['label'] = $tax->get_rate_label($key);
             $item['compound'] = $tax->is_compound($key) ? 1 : 0;
             $item['tax_amount'] = $tax->round(isset($taxes[$key]) ? $taxes[$key] : 0);
             $item['shipping_tax_amount'] = $tax->round(isset($shipping_taxes[$key]) ? $shipping_taxes[$key] : 0);
             if (!$item['label']) {
                 $item['label'] = $woocommerce->countries->tax_or_vat();
             }
             // Add line item
             $item_id = woocommerce_add_order_item($order_id, array('order_item_name' => $item['name'], 'order_item_type' => 'recurring_tax'));
             // Add line item meta
             if ($item_id) {
                 woocommerce_add_order_item_meta($item_id, 'rate_id', $item['rate_id']);
                 woocommerce_add_order_item_meta($item_id, 'label', $item['label']);
                 woocommerce_add_order_item_meta($item_id, 'compound', $item['compound']);
                 woocommerce_add_order_item_meta($item_id, 'tax_amount', $item['tax_amount']);
                 woocommerce_add_order_item_meta($item_id, 'shipping_tax_amount', $item['shipping_tax_amount']);
             }
             include plugin_dir_path(WC_Subscriptions::$plugin_file) . 'templates/admin/post-types/writepanels/order-tax-html.php';
         }
         $return['tax_row_html'] = ob_get_clean();
         echo json_encode($return);
     }
     die;
 }
 /**
  * Update recurring line taxes via AJAX
  * @see WC_Subscriptions_Order::calculate_recurring_line_taxes()
  * 
  * @since 4.4
  * @return JSON object with updated tax data
  */
 public static function ajax_update_recurring_tax()
 {
     global $wpdb;
     $woo_22_plus = version_compare(WOOCOMMERCE_VERSION, '2.2', '>=');
     check_ajax_referer('woocommerce-subscriptions', 'security');
     $order_id = absint($_POST['order_id']);
     $country = strtoupper(esc_attr($_POST['country']));
     // Step out of the way if the customer is not located in the US
     if ($country != 'US') {
         return;
     }
     $shipping = $_POST['shipping'];
     $line_subtotal = isset($_POST['line_subtotal']) ? esc_attr($_POST['line_subtotal']) : 0;
     $line_total = isset($_POST['line_total']) ? esc_attr($_POST['line_total']) : 0;
     // Set up WC_WooTax_Order object
     $order = self::get_order($order_id);
     // We only need to instantiate a WC_Tax object if we are using WooCommerce < 2.3
     if (!$woo_22_plus) {
         $tax = new WC_Tax();
     }
     $taxes = $shipping_taxes = array();
     $return = array();
     $item_data = array();
     $type_array = array();
     $product_id = '';
     if (isset($_POST['order_item_id'])) {
         $product_id = woocommerce_get_order_item_meta($_POST['order_item_id'], '_product_id');
     } elseif (isset($_POST['product_id'])) {
         $product_id = esc_attr($_POST['product_id']);
     }
     if (!empty($product_id) && WC_Subscriptions_Product::is_subscription($product_id)) {
         // Get product details
         $product = WC_Subscriptions::get_product($product_id);
         // Add product to items array
         $tic = get_post_meta($product->id, 'wootax_tic', true);
         $item_info = array('Index' => '', 'ItemID' => isset($_POST['order_item_id']) ? $_POST['order_item_id'] : $product_id, 'Qty' => 1, 'Price' => $line_subtotal > 0 ? $line_subtotal : $product->get_price(), 'Type' => 'cart');
         if (!empty($tic) && $tic) {
             $item_info['TIC'] = $tic;
         }
         $item_data[] = $item_info;
         $type_array[$_POST['order_item_id']] = 'cart';
         // Add shipping to items array
         if ($shipping > 0) {
             $item_data[] = array('Index' => '', 'ItemID' => WT_SHIPPING_ITEM, 'TIC' => WT_SHIPPING_TIC, 'Qty' => 1, 'Price' => $shipping, 'Type' => 'shipping');
             $type_array[WT_SHIPPING_ITEM] = 'shipping';
         }
         // Issue Lookup request
         $res = $order->do_lookup($item_data, $type_array, true);
         if (is_array($res)) {
             $return['recurring_shipping_tax'] = 0;
             $return['recurring_line_subtotal_tax'] = 0;
             $return['recurring_line_tax'] = 0;
             foreach ($res as $item) {
                 $item_id = $item->ItemID;
                 $item_tax = $item->TaxAmount;
                 if ($item_id == WT_SHIPPING_ITEM) {
                     $return['recurring_shipping_tax'] += $item_tax;
                 } else {
                     $return['recurring_line_subtotal_tax'] += $item_tax;
                     $return['recurring_line_tax'] += $item_tax;
                 }
             }
             $taxes[WT_RATE_ID] = $return['recurring_line_tax'];
             $shipping_taxes[WT_RATE_ID] = $return['recurring_shipping_tax'];
             // Get tax rates
             $tax_codes = array(WT_RATE_ID => apply_filters('wootax_rate_code', 'WOOTAX-RATE-DO-NOT-REMOVE'));
             // Remove old tax rows
             $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->prefix}woocommerce_order_itemmeta WHERE order_item_id IN ( SELECT order_item_id FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = %d AND order_item_type = 'recurring_tax' )", $order_id));
             $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = %d AND order_item_type = 'recurring_tax'", $order_id));
             // Now merge to keep tax rows
             ob_start();
             foreach (array_keys($taxes + $shipping_taxes) as $key) {
                 $item = array();
                 $item['rate_id'] = $key;
                 $item['name'] = $tax_codes[$key];
                 $item['label'] = $woo_22_plus ? WC_Tax::get_rate_label($key) : $tax->get_rate_label($key);
                 $item['compound'] = $woo_22_plus ? WC_Tax::is_compound($key) : $tax->is_compound($key) ? 1 : 0;
                 $item['tax_amount'] = wc_round_tax_total(isset($taxes[$key]) ? $taxes[$key] : 0);
                 $item['shipping_tax_amount'] = wc_round_tax_total(isset($shipping_taxes[$key]) ? $shipping_taxes[$key] : 0);
                 if (!$item['label']) {
                     $item['label'] = WC()->countries->tax_or_vat();
                 }
                 // Add line item
                 $item_id = woocommerce_add_order_item($order_id, array('order_item_name' => $item['name'], 'order_item_type' => 'recurring_tax'));
                 // Add line item meta
                 if ($item_id) {
                     woocommerce_add_order_item_meta($item_id, 'rate_id', $item['rate_id']);
                     woocommerce_add_order_item_meta($item_id, 'label', $item['label']);
                     woocommerce_add_order_item_meta($item_id, 'compound', $item['compound']);
                     woocommerce_add_order_item_meta($item_id, 'tax_amount', $item['tax_amount']);
                     woocommerce_add_order_item_meta($item_id, 'shipping_tax_amount', $item['shipping_tax_amount']);
                 }
                 include plugin_dir_path(WC_Subscriptions::$plugin_file) . 'templates/admin/post-types/writepanels/order-tax-html.php';
             }
             $return['tax_row_html'] = ob_get_clean();
             echo json_encode($return);
         }
     }
     die;
 }
 /**
  * Tells if a product is a subscription, provided that Subs is installed.
  *
  * @param  mixed    $product_id   product or id to check
  * @return boolean                true if Subs exists and product is a Sub
  */
 function is_subscription($product_id)
 {
     if (!class_exists('WC_Subscriptions')) {
         return false;
     }
     $is_subscription = false;
     if (is_object($product_id)) {
         $product_id = $product_id->id;
     }
     $post_type = get_post_type($product_id);
     if (in_array($post_type, array('product'))) {
         $product = WC_Subscriptions::get_product($product_id);
         if ($product->is_type(array('subscription'))) {
             $is_subscription = true;
         }
     }
     return apply_filters('woocommerce_is_subscription', $is_subscription, $product_id);
 }