}
 $order_item['line_tax'] = isset($order_item['line_tax']) ? $order_item['line_tax'] : 0;
 $order_item['line_total'] = isset($order_item['line_total']) ? $order_item['line_total'] : 0;
 $order_item['line_subtotal_tax'] = isset($order_item['line_subtotal_tax']) ? $order_item['line_subtotal_tax'] : 0;
 $order_item['line_subtotal'] = isset($order_item['line_subtotal']) ? $order_item['line_subtotal'] : 0;
 $item_id = woocommerce_add_order_item($order_item_row->post_id, array('order_item_name' => $order_item['name'], 'order_item_type' => 'line_item'));
 // Add line item meta
 if ($item_id) {
     woocommerce_add_order_item_meta($item_id, '_qty', absint($order_item['qty']));
     woocommerce_add_order_item_meta($item_id, '_tax_class', $order_item['tax_class']);
     woocommerce_add_order_item_meta($item_id, '_product_id', $order_item['id']);
     woocommerce_add_order_item_meta($item_id, '_variation_id', $order_item['variation_id']);
     woocommerce_add_order_item_meta($item_id, '_line_subtotal', woocommerce_format_decimal($order_item['line_subtotal']));
     woocommerce_add_order_item_meta($item_id, '_line_subtotal_tax', woocommerce_format_decimal($order_item['line_subtotal_tax']));
     woocommerce_add_order_item_meta($item_id, '_line_total', woocommerce_format_decimal($order_item['line_total']));
     woocommerce_add_order_item_meta($item_id, '_line_tax', woocommerce_format_decimal($order_item['line_tax']));
     $meta_rows = array();
     // Insert meta
     if (!empty($order_item['item_meta'])) {
         foreach ($order_item['item_meta'] as $key => $meta) {
             // Backwards compatibility
             if (is_array($meta) && isset($meta['meta_name'])) {
                 $meta_rows[] = '(' . $item_id . ',"' . esc_sql($meta['meta_name']) . '","' . esc_sql($meta['meta_value']) . '")';
             } else {
                 $meta_rows[] = '(' . $item_id . ',"' . esc_sql($key) . '","' . esc_sql($meta) . '")';
             }
         }
     }
     // Insert meta rows at once
     if (sizeof($meta_rows) > 0) {
         $wpdb->query($wpdb->prepare("\r\n\t\t\t\t\tINSERT INTO {$wpdb->prefix}woocommerce_order_itemmeta ( order_item_id, meta_key, meta_value )\r\n\t\t\t\t\tVALUES " . implode(',', $meta_rows) . ";\r\n\t\t\t\t", $order_item_row->post_id));
 /**
  * Save shipping and tax options
  */
 public function wc_setup_shipping_taxes_save()
 {
     check_admin_referer('wc-setup');
     $woocommerce_calc_shipping = isset($_POST['woocommerce_calc_shipping']) ? 'yes' : 'no';
     $woocommerce_calc_taxes = isset($_POST['woocommerce_calc_taxes']) ? 'yes' : 'no';
     update_option('woocommerce_calc_shipping', $woocommerce_calc_shipping);
     update_option('woocommerce_calc_taxes', $woocommerce_calc_taxes);
     update_option('woocommerce_prices_include_tax', sanitize_text_field($_POST['woocommerce_prices_include_tax']));
     if ('yes' === $woocommerce_calc_shipping && !empty($_POST['shipping_cost_domestic'])) {
         // Delete existing settings if they exist
         delete_option('woocommerce_flat_rate_settings');
         // Init rate and settings
         $shipping_method = new WC_Shipping_Flat_Rate();
         $costs = array();
         $costs[] = woocommerce_format_decimal(sanitize_text_field($_POST['shipping_cost_domestic']));
         if ($item_cost = sanitize_text_field($_POST['shipping_cost_domestic_item'])) {
             $costs[] = $item_cost . ' * [qty]';
         }
         $shipping_method->settings['cost'] = implode(' + ', array_filter($costs));
         $shipping_method->settings['enabled'] = 'yes';
         $shipping_method->settings['type'] = 'order';
         update_option($shipping_method->plugin_id . $shipping_method->id . '_settings', $shipping_method->settings);
     }
     if ('yes' === $woocommerce_calc_shipping && !empty($_POST['shipping_cost_international'])) {
         // Delete existing settings if they exist
         delete_option('woocommerce_international_delivery_settings');
         // Init rate and settings
         $shipping_method = new WC_Shipping_International_Delivery();
         $costs = array();
         $costs[] = woocommerce_format_decimal(sanitize_text_field($_POST['shipping_cost_international']));
         if ($item_cost = sanitize_text_field($_POST['shipping_cost_international_item'])) {
             $costs[] = $item_cost . ' * [qty]';
         }
         $shipping_method->settings['cost'] = implode(' + ', array_filter($costs));
         $shipping_method->settings['enabled'] = 'yes';
         $shipping_method->settings['type'] = 'order';
         update_option($shipping_method->plugin_id . $shipping_method->id . '_settings', $shipping_method->settings);
     }
     if ('yes' === $woocommerce_calc_taxes && !empty($_POST['woocommerce_import_tax_rates'])) {
         $locale_info = (include WC()->plugin_path() . '/i18n/locale-info.php');
         $tax_rates = array();
         $country = WC()->countries->get_base_country();
         $state = WC()->countries->get_base_state();
         if (isset($locale_info[$country])) {
             if (isset($locale_info[$country]['tax_rates'][$state])) {
                 $tax_rates = $locale_info[$country]['tax_rates'][$state];
             } elseif (isset($locale_info[$country]['tax_rates'][''])) {
                 $tax_rates = $locale_info[$country]['tax_rates'][''];
             }
             if (isset($locale_info[$country]['tax_rates']['*'])) {
                 $tax_rates = array_merge($locale_info[$country]['tax_rates']['*'], $tax_rates);
             }
         }
         if ($tax_rates) {
             $loop = 0;
             foreach ($tax_rates as $rate) {
                 $tax_rate = array('tax_rate_country' => $rate['country'], 'tax_rate_state' => $rate['state'], 'tax_rate' => $rate['rate'], 'tax_rate_name' => $rate['name'], 'tax_rate_priority' => isset($rate['priority']) ? absint($rate['priority']) : 1, 'tax_rate_compound' => 0, 'tax_rate_shipping' => $rate['shipping'] ? 1 : 0, 'tax_rate_order' => $loop++, 'tax_rate_class' => '');
                 WC_Tax::_insert_tax_rate($tax_rate);
             }
         }
     }
     wp_redirect($this->get_next_step_link());
     exit;
 }
Example #3
0
/**
 * Creates a sub order
 *
 * @param int $parent_order
 * @param int $seller_id
 * @param array $seller_products
 */
function dokan_create_seller_order($parent_order, $seller_id, $seller_products)
{
    $order_data = apply_filters('woocommerce_new_order_data', array('post_type' => 'shop_order', 'post_title' => sprintf(__('Order – %s', 'woocommerce'), strftime(_x('%b %d, %Y @ %I:%M %p', 'Order date parsed by strftime', 'woocommerce'))), 'post_status' => 'wc-pending', 'ping_status' => 'closed', 'post_excerpt' => isset($posted['order_comments']) ? $posted['order_comments'] : '', 'post_author' => $seller_id, 'post_parent' => $parent_order->id, 'post_password' => uniqid('order_')));
    $order_id = wp_insert_post($order_data);
    if ($order_id && !is_wp_error($order_id)) {
        $order_total = $order_tax = 0;
        $product_ids = array();
        do_action('woocommerce_new_order', $order_id);
        // now insert line items
        foreach ($seller_products as $item) {
            $order_total += (double) $item['line_total'];
            $order_tax += (double) $item['line_tax'];
            $product_ids[] = $item['product_id'];
            $item_id = wc_add_order_item($order_id, array('order_item_name' => $item['name'], 'order_item_type' => 'line_item'));
            if ($item_id) {
                wc_add_order_item_meta($item_id, '_qty', $item['qty']);
                wc_add_order_item_meta($item_id, '_tax_class', $item['tax_class']);
                wc_add_order_item_meta($item_id, '_product_id', $item['product_id']);
                wc_add_order_item_meta($item_id, '_line_subtotal', $item['line_subtotal']);
                wc_add_order_item_meta($item_id, '_line_total', $item['line_total']);
                wc_add_order_item_meta($item_id, '_line_tax', $item['line_tax']);
                wc_add_order_item_meta($item_id, '_line_subtotal_tax', $item['line_subtotal_tax']);
            }
        }
        // foreach
        $bill_ship = array('_billing_country', '_billing_first_name', '_billing_last_name', '_billing_company', '_billing_address_1', '_billing_address_2', '_billing_city', '_billing_state', '_billing_postcode', '_billing_email', '_billing_phone', '_shipping_country', '_shipping_first_name', '_shipping_last_name', '_shipping_company', '_shipping_address_1', '_shipping_address_2', '_shipping_city', '_shipping_state', '_shipping_postcode');
        // save billing and shipping address
        foreach ($bill_ship as $val) {
            $order_key = ltrim($val, '_');
            update_post_meta($order_id, $val, $parent_order->{$order_key});
        }
        // calculate the total
        $order_in_total = $order_total + $shipping_cost + $order_tax;
        // set order meta
        update_post_meta($order_id, '_payment_method', $parent_order->payment_method);
        update_post_meta($order_id, '_payment_method_title', $parent_order->payment_method_title);
        update_post_meta($order_id, '_order_shipping', woocommerce_format_decimal($shipping_cost));
        update_post_meta($order_id, '_cart_discount', '0');
        update_post_meta($order_id, '_order_tax', woocommerce_format_decimal($order_tax));
        update_post_meta($order_id, '_order_shipping_tax', '0');
        update_post_meta($order_id, '_order_total', woocommerce_format_decimal($order_in_total));
        update_post_meta($order_id, '_order_key', apply_filters('woocommerce_generate_order_key', uniqid('order_')));
        update_post_meta($order_id, '_customer_user', $parent_order->customer_user);
        update_post_meta($order_id, '_order_currency', get_post_meta($parent_order->id, '_order_currency', true));
        update_post_meta($order_id, '_prices_include_tax', $parent_order->prices_include_tax);
        update_post_meta($order_id, '_customer_ip_address', get_post_meta($parent_order->id, '_customer_ip_address', true));
        update_post_meta($order_id, '_customer_user_agent', get_post_meta($parent_order->id, '_customer_user_agent', true));
        do_action('dokan_checkout_update_order_meta', $order_id, $seller_id);
    }
    // if order
}
 /**
  * create_order function.
  *
  * @access public
  * @return void
  */
 public function create_order()
 {
     global $woocommerce, $wpdb;
     // Give plugins the opportunity to create an order themselves
     $order_id = apply_filters('woocommerce_create_order', null, $this);
     if (is_numeric($order_id)) {
         return $order_id;
     }
     // Create Order (send cart variable so we can record items and reduce inventory). Only create if this is a new order, not if the payment was rejected.
     $order_data = apply_filters('woocommerce_new_order_data', array('post_type' => 'shop_order', 'post_title' => sprintf(__('Order – %s', 'woocommerce'), strftime(_x('%b %d, %Y @ %I:%M %p', 'Order date parsed by strftime', 'woocommerce'))), 'post_status' => 'publish', 'ping_status' => 'closed', 'post_excerpt' => isset($this->posted['order_comments']) ? $this->posted['order_comments'] : '', 'post_author' => 1, 'post_password' => uniqid('order_')));
     // Insert or update the post data
     $create_new_order = true;
     if ($woocommerce->session->order_awaiting_payment > 0) {
         $order_id = absint($woocommerce->session->order_awaiting_payment);
         /* Check order is unpaid by getting its status */
         $terms = wp_get_object_terms($order_id, 'shop_order_status', array('fields' => 'slugs'));
         $order_status = isset($terms[0]) ? $terms[0] : 'pending';
         // Resume the unpaid order if its pending
         if ($order_status == 'pending' || $order_status == 'failed') {
             // Update the existing order as we are resuming it
             $create_new_order = false;
             $order_data['ID'] = $order_id;
             wp_update_post($order_data);
             // Clear the old line items - we'll add these again in case they changed
             $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 )", $order_id));
             $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = %d", $order_id));
             // Trigger an action for the resumed order
             do_action('woocommerce_resume_order', $order_id);
         }
     }
     if ($create_new_order) {
         $order_id = wp_insert_post($order_data);
         if (is_wp_error($order_id)) {
             throw new MyException('Error: Unable to create order. Please try again.');
         } else {
             do_action('woocommerce_new_order', $order_id);
         }
     }
     // Store user data
     if ($this->checkout_fields['billing']) {
         foreach ($this->checkout_fields['billing'] as $key => $field) {
             update_post_meta($order_id, '_' . $key, $this->posted[$key]);
             // User
             if ($this->customer_id && !empty($this->posted[$key])) {
                 update_user_meta($this->customer_id, $key, $this->posted[$key]);
                 // Special fields
                 switch ($key) {
                     case "billing_email":
                         if (!email_exists($this->posted[$key])) {
                             wp_update_user(array('ID' => $this->customer_id, 'user_email' => $this->posted[$key]));
                         }
                         break;
                     case "billing_first_name":
                         wp_update_user(array('ID' => $this->customer_id, 'first_name' => $this->posted[$key]));
                         break;
                     case "billing_last_name":
                         wp_update_user(array('ID' => $this->customer_id, 'last_name' => $this->posted[$key]));
                         break;
                 }
             }
         }
     }
     if ($this->checkout_fields['shipping'] && ($woocommerce->cart->needs_shipping() || get_option('woocommerce_require_shipping_address') == 'yes')) {
         foreach ($this->checkout_fields['shipping'] as $key => $field) {
             $postvalue = false;
             if ($this->posted['shiptobilling']) {
                 if (isset($this->posted[str_replace('shipping_', 'billing_', $key)])) {
                     $postvalue = $this->posted[str_replace('shipping_', 'billing_', $key)];
                     update_post_meta($order_id, '_' . $key, $postvalue);
                 }
             } else {
                 $postvalue = $this->posted[$key];
                 update_post_meta($order_id, '_' . $key, $postvalue);
             }
             // User
             if ($postvalue && $this->customer_id) {
                 update_user_meta($this->customer_id, $key, $postvalue);
             }
         }
     }
     // Save any other user meta
     if ($this->customer_id) {
         do_action('woocommerce_checkout_update_user_meta', $this->customer_id, $this->posted);
     }
     // Store the line items to the new/resumed order
     foreach ($woocommerce->cart->get_cart() as $cart_item_key => $values) {
         $_product = $values['data'];
         // Add line item
         $item_id = woocommerce_add_order_item($order_id, array('order_item_name' => $_product->get_title(), 'order_item_type' => 'line_item'));
         // Add line item meta
         if ($item_id) {
             woocommerce_add_order_item_meta($item_id, '_qty', apply_filters('woocommerce_stock_amount', $values['quantity']));
             woocommerce_add_order_item_meta($item_id, '_tax_class', $_product->get_tax_class());
             woocommerce_add_order_item_meta($item_id, '_product_id', $values['product_id']);
             woocommerce_add_order_item_meta($item_id, '_variation_id', $values['variation_id']);
             woocommerce_add_order_item_meta($item_id, '_line_subtotal', woocommerce_format_decimal($values['line_subtotal'], 4));
             woocommerce_add_order_item_meta($item_id, '_line_total', woocommerce_format_decimal($values['line_total'], 4));
             woocommerce_add_order_item_meta($item_id, '_line_tax', woocommerce_format_decimal($values['line_tax'], 4));
             woocommerce_add_order_item_meta($item_id, '_line_subtotal_tax', woocommerce_format_decimal($values['line_subtotal_tax'], 4));
             // Store variation data in meta so admin can view it
             if ($values['variation'] && is_array($values['variation'])) {
                 foreach ($values['variation'] as $key => $value) {
                     woocommerce_add_order_item_meta($item_id, esc_attr(str_replace('attribute_', '', $key)), $value);
                 }
             }
             // Add line item meta for backorder status
             if ($_product->backorders_require_notification() && $_product->is_on_backorder($values['quantity'])) {
                 woocommerce_add_order_item_meta($item_id, apply_filters('woocommerce_backordered_item_meta_name', __('Backordered', 'woocommerce'), $cart_item_key, $order_id), $values['quantity'] - max(0, $_product->get_total_stock()));
             }
             // Allow plugins to add order item meta
             do_action('woocommerce_add_order_item_meta', $item_id, $values);
         }
     }
     // Store fees
     foreach ($woocommerce->cart->get_fees() as $fee) {
         $item_id = woocommerce_add_order_item($order_id, array('order_item_name' => $fee->name, 'order_item_type' => 'fee'));
         if ($fee->taxable) {
             woocommerce_add_order_item_meta($item_id, '_tax_class', $fee->tax_class);
         } else {
             woocommerce_add_order_item_meta($item_id, '_tax_class', '0');
         }
         woocommerce_add_order_item_meta($item_id, '_line_total', woocommerce_format_decimal($fee->amount));
         woocommerce_add_order_item_meta($item_id, '_line_tax', woocommerce_format_decimal($fee->tax));
     }
     // Store tax rows
     foreach (array_keys($woocommerce->cart->taxes + $woocommerce->cart->shipping_taxes) as $key) {
         $item_id = woocommerce_add_order_item($order_id, array('order_item_name' => $woocommerce->cart->tax->get_rate_code($key), 'order_item_type' => 'tax'));
         // Add line item meta
         if ($item_id) {
             woocommerce_add_order_item_meta($item_id, 'rate_id', $key);
             woocommerce_add_order_item_meta($item_id, 'label', $woocommerce->cart->tax->get_rate_label($key));
             woocommerce_add_order_item_meta($item_id, 'compound', absint($woocommerce->cart->tax->is_compound($key) ? 1 : 0));
             woocommerce_add_order_item_meta($item_id, 'tax_amount', woocommerce_clean(isset($woocommerce->cart->taxes[$key]) ? $woocommerce->cart->taxes[$key] : 0));
             woocommerce_add_order_item_meta($item_id, 'shipping_tax_amount', woocommerce_clean(isset($woocommerce->cart->shipping_taxes[$key]) ? $woocommerce->cart->shipping_taxes[$key] : 0));
         }
     }
     // Store coupons
     if ($applied_coupons = $woocommerce->cart->get_applied_coupons()) {
         foreach ($applied_coupons as $code) {
             $item_id = woocommerce_add_order_item($order_id, array('order_item_name' => $code, 'order_item_type' => 'coupon'));
             // Add line item meta
             if ($item_id) {
                 woocommerce_add_order_item_meta($item_id, 'discount_amount', isset($woocommerce->cart->coupon_discount_amounts[$code]) ? $woocommerce->cart->coupon_discount_amounts[$code] : 0);
             }
         }
     }
     // Store meta
     if ($this->shipping_method) {
         update_post_meta($order_id, '_shipping_method', $this->shipping_method->id);
         update_post_meta($order_id, '_shipping_method_title', $this->shipping_method->label);
     }
     if ($this->payment_method) {
         update_post_meta($order_id, '_payment_method', $this->payment_method->id);
         update_post_meta($order_id, '_payment_method_title', $this->payment_method->get_title());
     }
     update_post_meta($order_id, '_order_shipping', woocommerce_format_total($woocommerce->cart->shipping_total));
     update_post_meta($order_id, '_order_discount', woocommerce_format_total($woocommerce->cart->get_order_discount_total()));
     update_post_meta($order_id, '_cart_discount', woocommerce_format_total($woocommerce->cart->get_cart_discount_total()));
     update_post_meta($order_id, '_order_tax', woocommerce_clean($woocommerce->cart->tax_total));
     update_post_meta($order_id, '_order_shipping_tax', woocommerce_clean($woocommerce->cart->shipping_tax_total));
     update_post_meta($order_id, '_order_total', woocommerce_format_total($woocommerce->cart->total));
     update_post_meta($order_id, '_order_key', apply_filters('woocommerce_generate_order_key', uniqid('order_')));
     update_post_meta($order_id, '_customer_user', absint($this->customer_id));
     update_post_meta($order_id, '_order_currency', get_woocommerce_currency());
     update_post_meta($order_id, '_prices_include_tax', get_option('woocommerce_prices_include_tax'));
     update_post_meta($order_id, '_customer_ip_address', isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : $_SERVER['REMOTE_ADDR']);
     update_post_meta($order_id, '_customer_user_agent', isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '');
     // Let plugins add meta
     do_action('woocommerce_checkout_update_order_meta', $order_id, $this->posted);
     // Order status
     wp_set_object_terms($order_id, 'pending', 'shop_order_status');
     return $order_id;
 }
 /**
  * When creating an order at checkout, if the order is for renewing a subscription from a failed
  * payment, hijack the order creation to make a renewal order - not a vanilla WooCommerce order.
  *
  * @since 1.3
  */
 public static function filter_woocommerce_create_order($order_id, $checkout_object)
 {
     global $woocommerce;
     $cart_item = WC_Subscriptions_Cart::cart_contains_subscription_renewal();
     if ($cart_item && 'child' == $cart_item['subscription_renewal']['role']) {
         $product_id = $cart_item['product_id'];
         $failed_order_id = $cart_item['subscription_renewal']['failed_order'];
         $original_order_id = $cart_item['subscription_renewal']['original_order'];
         $role = $cart_item['subscription_renewal']['role'];
         $renewal_order_args = array('new_order_role' => $role, 'checkout_renewal' => true, 'failed_order_id' => $failed_order_id);
         $renewal_order_id = WC_Subscriptions_Renewal_Order::generate_renewal_order($original_order_id, $product_id, $renewal_order_args);
         $original_order = new WC_Order($original_order_id);
         $customer_id = $original_order->customer_user;
         // Save posted billing address fields to both renewal and original order
         if ($checkout_object->checkout_fields['billing']) {
             foreach ($checkout_object->checkout_fields['billing'] as $key => $field) {
                 update_post_meta($renewal_order_id, '_' . $key, $checkout_object->posted[$key]);
                 update_post_meta($original_order->id, '_' . $key, $checkout_object->posted[$key]);
                 // User
                 if ($customer_id && !empty($checkout_object->posted[$key])) {
                     update_user_meta($customer_id, $key, $checkout_object->posted[$key]);
                     // Special fields
                     switch ($key) {
                         case "billing_email":
                             if (!email_exists($checkout_object->posted[$key])) {
                                 wp_update_user(array('ID' => $customer_id, 'user_email' => $checkout_object->posted[$key]));
                             }
                             break;
                         case "billing_first_name":
                             wp_update_user(array('ID' => $customer_id, 'first_name' => $checkout_object->posted[$key]));
                             break;
                         case "billing_last_name":
                             wp_update_user(array('ID' => $customer_id, 'last_name' => $checkout_object->posted[$key]));
                             break;
                     }
                 }
             }
         }
         // Save posted shipping address fields to both renewal and original order
         if ($checkout_object->checkout_fields['shipping'] && ($woocommerce->cart->needs_shipping() || get_option('woocommerce_require_shipping_address') == 'yes')) {
             foreach ($checkout_object->checkout_fields['shipping'] as $key => $field) {
                 $postvalue = false;
                 if (isset($checkout_object->posted['shiptobilling']) && $checkout_object->posted['shiptobilling'] || isset($checkout_object->posted['ship_to_different_address']) && $checkout_object->posted['ship_to_different_address']) {
                     if (isset($checkout_object->posted[str_replace('shipping_', 'billing_', $key)])) {
                         $postvalue = $checkout_object->posted[str_replace('shipping_', 'billing_', $key)];
                         update_post_meta($renewal_order_id, '_' . $key, $postvalue);
                         update_post_meta($original_order->id, '_' . $key, $postvalue);
                     }
                 } elseif (isset($checkout_object->posted[$key])) {
                     $postvalue = $checkout_object->posted[$key];
                     update_post_meta($renewal_order_id, '_' . $key, $postvalue);
                     update_post_meta($original_order->id, '_' . $key, $postvalue);
                 }
                 // User
                 if ($postvalue && $customer_id) {
                     update_user_meta($customer_id, $key, $postvalue);
                 }
             }
         }
         if ($checkout_object->posted['payment_method']) {
             $available_gateways = $woocommerce->payment_gateways->get_available_payment_gateways();
             if (isset($available_gateways[$checkout_object->posted['payment_method']])) {
                 $payment_method = $available_gateways[$checkout_object->posted['payment_method']];
                 $payment_method->validate_fields();
                 update_post_meta($renewal_order_id, '_payment_method', $payment_method->id);
                 update_post_meta($renewal_order_id, '_payment_method_title', $payment_method->get_title());
             }
         }
         // Set the shipping method for WC < 2.1
         if ($checkout_object->posted['shipping_method'] && method_exists($woocommerce->shipping, 'get_available_shipping_methods')) {
             $available_shipping_methods = $woocommerce->shipping->get_available_shipping_methods();
             if (isset($available_shipping_methods[$checkout_object->posted['shipping_method']])) {
                 $shipping_method = $available_shipping_methods[$checkout_object->posted['shipping_method']];
                 update_post_meta($renewal_order_id, '_shipping_method', $shipping_method->id);
                 update_post_meta($renewal_order_id, '_shipping_method_title', $shipping_method->label);
             }
         }
         if (isset($failed_order_id)) {
             $failed_order = new WC_Order($failed_order_id);
             if ($failed_order->status == 'failed') {
                 update_post_meta($failed_order_id, '_failed_order_replaced_by', $renewal_order_id);
             }
         }
         // Store fees, any new fees on this order should be applied now
         foreach ($woocommerce->cart->get_fees() as $fee) {
             $item_id = woocommerce_add_order_item($renewal_order_id, array('order_item_name' => $fee->name, 'order_item_type' => 'fee'));
             if ($fee->taxable) {
                 woocommerce_add_order_item_meta($item_id, '_tax_class', $fee->tax_class);
             } else {
                 woocommerce_add_order_item_meta($item_id, '_tax_class', '0');
             }
             woocommerce_add_order_item_meta($item_id, '_line_total', woocommerce_format_decimal($fee->amount));
             woocommerce_add_order_item_meta($item_id, '_line_tax', woocommerce_format_decimal($fee->tax));
         }
         // Store tax rows
         foreach (array_keys($woocommerce->cart->taxes + $woocommerce->cart->shipping_taxes) as $key) {
             $item_id = woocommerce_add_order_item($renewal_order_id, array('order_item_name' => WC_Tax::get_rate_code($key), 'order_item_type' => 'tax'));
             // Add line item meta
             if ($item_id) {
                 woocommerce_add_order_item_meta($item_id, 'rate_id', $key);
                 woocommerce_add_order_item_meta($item_id, 'label', WC_Tax::get_rate_label($key));
                 woocommerce_add_order_item_meta($item_id, 'compound', absint(WC_Tax::is_compound($key) ? 1 : 0));
                 woocommerce_add_order_item_meta($item_id, 'tax_amount', woocommerce_format_decimal(isset($woocommerce->cart->taxes[$key]) ? $woocommerce->cart->taxes[$key] : 0));
                 woocommerce_add_order_item_meta($item_id, 'shipping_tax_amount', woocommerce_format_decimal(isset($woocommerce->cart->shipping_taxes[$key]) ? $woocommerce->cart->shipping_taxes[$key] : 0));
             }
         }
         // Store shipping for all packages on this order (as this can differ between each order), WC 2.1
         if (method_exists($woocommerce->shipping, 'get_packages')) {
             $packages = $woocommerce->shipping->get_packages();
             foreach ($packages as $i => $package) {
                 if (isset($package['rates'][$checkout_object->shipping_methods[$i]])) {
                     $method = $package['rates'][$checkout_object->shipping_methods[$i]];
                     $item_id = woocommerce_add_order_item($renewal_order_id, array('order_item_name' => $method->label, 'order_item_type' => 'shipping'));
                     if ($item_id) {
                         woocommerce_add_order_item_meta($item_id, 'method_id', $method->id);
                         woocommerce_add_order_item_meta($item_id, 'cost', woocommerce_format_decimal($method->cost));
                         do_action('woocommerce_add_shipping_order_item', $renewal_order_id, $item_id, $i);
                     }
                 }
             }
         }
         update_post_meta($renewal_order_id, '_order_shipping', WC_Subscriptions::format_total($woocommerce->cart->shipping_total));
         update_post_meta($renewal_order_id, '_cart_discount', WC_Subscriptions::format_total($woocommerce->cart->get_cart_discount_total()));
         update_post_meta($renewal_order_id, '_order_tax', WC_Subscriptions::format_total($woocommerce->cart->tax_total));
         update_post_meta($renewal_order_id, '_order_shipping_tax', WC_Subscriptions::format_total($woocommerce->cart->shipping_tax_total));
         update_post_meta($renewal_order_id, '_order_total', WC_Subscriptions::format_total($woocommerce->cart->total));
         // WC < 2.3, set deprecated after tax discount total
         if (WC_Subscriptions::is_woocommerce_pre('2.3')) {
             update_post_meta($renewal_order_id, '_order_discount', WC_Subscriptions::format_total($woocommerce->cart->get_total_discount()));
         }
         update_post_meta($renewal_order_id, '_order_currency', get_woocommerce_currency());
         update_post_meta($renewal_order_id, '_prices_include_tax', get_option('woocommerce_prices_include_tax'));
         update_post_meta($renewal_order_id, '_customer_ip_address', isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : $_SERVER['REMOTE_ADDR']);
         update_post_meta($renewal_order_id, '_customer_user_agent', isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '');
         update_post_meta($renewal_order_id, '_checkout_renewal', 'yes');
         // Return the new order's ID to prevent WC creating an order
         $order_id = $renewal_order_id;
     }
     return $order_id;
 }
 /**
  * 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 $args['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 $args['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 WC_Order|int $order The WC_Order object or ID of the order for which the a new order should be created.
  * @param string $product_id The ID of the subscription product in the order which needs to be added to the new order.
  * @param array $args (optional) An array of name => value flags:
  *         '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.
  *         'checkout_renewal' bool Indicates if invoked from an interactive cart/checkout session. Default false.
  *         'failed_order_id' int For checkout_renewal true, indicates order id being replaced
  * @since 1.2
  */
 public static function generate_renewal_order($original_order, $product_id, $args = array())
 {
     global $wpdb, $woocommerce;
     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_subscription($original_order, $product_id)) {
         return false;
     }
     if (self::is_renewal($original_order, array('order_role' => 'child'))) {
         $original_order = self::get_parent_order($original_order);
     }
     if (!is_array($args)) {
         _deprecated_argument(__CLASS__ . '::' . __FUNCTION__, '1.3', __('Third parameter is now an array of name => value pairs. Use array( "new_order_role" => "parent" ) instead.', WC_Subscriptions::$text_domain));
         $args = array('new_order_role' => $args);
     }
     $args = wp_parse_args($args, array('new_order_role' => 'parent', 'checkout_renewal' => false));
     $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);
     $create_new_order = true;
     if ('child' == $args['new_order_role']) {
         $renewal_order_data['post_parent'] = $original_order->id;
     }
     if (true === $args['checkout_renewal']) {
         $renewal_order_id = null;
         if ($woocommerce->session->order_awaiting_payment > 0) {
             $renewal_order_id = absint($woocommerce->session->order_awaiting_payment);
         } elseif (isset($args['failed_order_id'])) {
             $failed_order_id = $args['failed_order_id'];
             /* Check order is unpaid by getting its status */
             $terms = wp_get_object_terms($failed_order_id, 'shop_order_status', array('fields' => 'slugs'));
             $order_status = isset($terms[0]) ? $terms[0] : 'pending';
             /* If paying on a pending order, we are resuming */
             if ($order_status == 'pending') {
                 $renewal_order_id = $failed_order_id;
             }
         }
         if ($renewal_order_id) {
             /* Check order is unpaid by getting its status */
             $terms = wp_get_object_terms($renewal_order_id, 'shop_order_status', array('fields' => 'slugs'));
             $order_status = isset($terms[0]) ? $terms[0] : 'pending';
             // Resume the unpaid order if its pending
             if ($order_status == 'pending' || $order_status == 'failed') {
                 // Update the existing order as we are resuming it
                 $create_new_order = false;
                 $renewal_order_data['ID'] = $renewal_order_id;
                 wp_update_post($renewal_order_data);
                 // Clear the old line items - we'll add these again in case they changed
                 $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 )", $renewal_order_id));
                 $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = %d", $renewal_order_id));
             }
         }
     }
     if ($create_new_order) {
         $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);
     if (true === $args['checkout_renewal']) {
         $checkout_object = $woocommerce->checkout;
         $customer_id = $original_order->customer_user;
         // Save posted billing fields to both renewal and original order
         if ($checkout_object->checkout_fields['billing']) {
             foreach ($checkout_object->checkout_fields['billing'] as $key => $field) {
                 update_post_meta($renewal_order_id, '_' . $key, $checkout_object->posted[$key]);
                 update_post_meta($original_order->id, '_' . $key, $checkout_object->posted[$key]);
                 // User
                 if ($customer_id && !empty($checkout_object->posted[$key])) {
                     update_user_meta($customer_id, $key, $checkout_object->posted[$key]);
                     // Special fields
                     switch ($key) {
                         case "billing_email":
                             if (!email_exists($checkout_object->posted[$key])) {
                                 wp_update_user(array('ID' => $customer_id, 'user_email' => $checkout_object->posted[$key]));
                             }
                             break;
                         case "billing_first_name":
                             wp_update_user(array('ID' => $customer_id, 'first_name' => $checkout_object->posted[$key]));
                             break;
                         case "billing_last_name":
                             wp_update_user(array('ID' => $customer_id, 'last_name' => $checkout_object->posted[$key]));
                             break;
                     }
                 }
             }
         }
         // Save posted shipping fields to both renewal and original order
         if ($checkout_object->checkout_fields['shipping'] && ($woocommerce->cart->needs_shipping() || get_option('woocommerce_require_shipping_address') == 'yes')) {
             foreach ($checkout_object->checkout_fields['shipping'] as $key => $field) {
                 $postvalue = false;
                 if ($checkout_object->posted['shiptobilling']) {
                     if (isset($checkout_object->posted[str_replace('shipping_', 'billing_', $key)])) {
                         $postvalue = $checkout_object->posted[str_replace('shipping_', 'billing_', $key)];
                         update_post_meta($renewal_order_id, '_' . $key, $postvalue);
                         update_post_meta($original_order->id, '_' . $key, $postvalue);
                     }
                 } else {
                     $postvalue = $checkout_object->posted[$key];
                     update_post_meta($renewal_order_id, '_' . $key, $postvalue);
                     update_post_meta($original_order->id, '_' . $key, $postvalue);
                 }
                 // User
                 if ($postvalue && $customer_id) {
                     update_user_meta($customer_id, $key, $postvalue);
                 }
             }
         }
     }
     $order_meta_query = "SELECT `meta_key`, `meta_value`\n\t\t\t\t\t\t\t FROM {$wpdb->postmeta}\n\t\t\t\t\t\t\t WHERE `post_id` = {$original_order->id}\n\t\t\t\t\t\t\t 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' == $args['new_order_role'] || true === $args['checkout_renewal']) {
         $order_meta_query .= " AND `meta_key` NOT IN ('_payment_method', '_payment_method_title', '_recurring_payment_method', '_recurring_payment_method_title', '_shipping_method', '_shipping_method_title', '_recurring_shipping_method', '_recurring_shipping_method_title')";
     } else {
         $order_meta_query .= " AND `meta_key` NOT LIKE '_order_recurring_%' AND `meta_key` NOT IN ('_payment_method', '_payment_method_title', '_recurring_payment_method', '_recurring_payment_method_title', '_shipping_method', '_shipping_method_title', '_recurring_shipping_method', '_recurring_shipping_method_title')";
     }
     // 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, $args['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, $args['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 (true === $args['checkout_renewal']) {
         $failed_payment_multiplier = 1;
         update_post_meta($renewal_order_id, '_order_shipping', woocommerce_format_total($woocommerce->cart->shipping_total));
         update_post_meta($renewal_order_id, '_order_discount', woocommerce_format_total($woocommerce->cart->get_order_discount_total()));
         update_post_meta($renewal_order_id, '_cart_discount', woocommerce_format_total($woocommerce->cart->get_cart_discount_total()));
         update_post_meta($renewal_order_id, '_order_tax', woocommerce_format_total($woocommerce->cart->tax_total));
         update_post_meta($renewal_order_id, '_order_shipping_tax', woocommerce_format_total($woocommerce->cart->shipping_tax_total));
         update_post_meta($renewal_order_id, '_order_total', woocommerce_format_total($woocommerce->cart->total));
         update_post_meta($renewal_order_id, '_checkout_renewal', 'yes');
     } else {
         // 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', 'no')) {
             $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_shipping = $failed_payment_multiplier * get_post_meta($original_order->id, '_order_recurring_shipping_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_shipping', $order_shipping);
         update_post_meta($renewal_order_id, '_order_tax', $order_tax);
         update_post_meta($renewal_order_id, '_order_total', $order_total);
         update_post_meta($renewal_order_id, '_shipping_method', $original_order->recurring_shipping_method);
         update_post_meta($renewal_order_id, '_shipping_method_title', $original_order->recurring_shipping_method_title);
         // Apply the recurring shipping & payment methods to child renewal orders
         if ('child' == $args['new_order_role']) {
             update_post_meta($renewal_order_id, '_payment_method', $original_order->recurring_payment_method);
             update_post_meta($renewal_order_id, '_payment_method_title', $original_order->recurring_payment_method_title);
         }
     }
     // Set order taxes based on recurring taxes from the original order
     $recurring_order_taxes = WC_Subscriptions_Order::get_recurring_taxes($original_order);
     foreach ($recurring_order_taxes as $index => $recurring_order_tax) {
         if (function_exists('woocommerce_update_order_item_meta')) {
             // WC 2.0+
             $item_ids = array();
             $item_ids[] = woocommerce_add_order_item($renewal_order_id, array('order_item_name' => $recurring_order_tax['name'], 'order_item_type' => 'tax'));
             // Also set recurring taxes on parent renewal orders
             if ('parent' == $args['new_order_role']) {
                 $item_ids[] = woocommerce_add_order_item($renewal_order_id, array('order_item_name' => $recurring_order_tax['name'], 'order_item_type' => 'recurring_tax'));
             }
             // Add line item meta
             foreach ($item_ids as $item_id) {
                 woocommerce_add_order_item_meta($item_id, 'compound', absint(isset($recurring_order_tax['compound']) ? $recurring_order_tax['compound'] : 0));
                 woocommerce_add_order_item_meta($item_id, 'tax_amount', woocommerce_clean($failed_payment_multiplier * $recurring_order_tax['tax_amount']));
                 woocommerce_add_order_item_meta($item_id, 'shipping_tax_amount', woocommerce_clean($failed_payment_multiplier * $recurring_order_tax['shipping_tax_amount']));
                 if (isset($recurring_order_tax['rate_id'])) {
                     woocommerce_add_order_item_meta($item_id, 'rate_id', $recurring_order_tax['rate_id']);
                 }
                 if (isset($recurring_order_tax['label'])) {
                     woocommerce_add_order_item_meta($item_id, 'label', $recurring_order_tax['label']);
                 }
             }
         } else {
             // WC 1.x
             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;
             }
             // Inefficient but keeps WC 1.x code grouped together
             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, $args['new_order_role']);
     foreach ($order_items as $item_index => $order_item) {
         $item_meta = new WC_Order_Item_Meta($order_item['item_meta']);
         // WC 2.0+ order item structure - as of WC 2.0 item_meta is stored as $key => meta pairs, not 'meta_name'/'meta_value'
         if (function_exists('woocommerce_add_order_item_meta')) {
             if ('child' == $args['new_order_role']) {
                 $renewal_order_item_name = sprintf(__('Renewal of "%s" purchased in Order %s', WC_Subscriptions::$text_domain), $order_item['name'], $original_order->get_order_number());
             } else {
                 $renewal_order_item_name = $order_item['name'];
             }
             // Create order line item on the renewal order
             $recurring_item_id = woocommerce_add_order_item($renewal_order_id, array('order_item_name' => $renewal_order_item_name, 'order_item_type' => 'line_item'));
             // Remove recurring line items and set item totals based on recurring line totals
             foreach ($item_meta->meta as $meta_key => $meta) {
                 // $meta is an array, so the item needs to be extracted from $meta[0] (just like order meta on a WC Order)
                 $meta_value = $meta[0];
                 // Map line item totals based on recurring line totals
                 switch ($meta_key) {
                     case '_recurring_line_total':
                         woocommerce_delete_order_item_meta($recurring_item_id, '_line_total');
                         woocommerce_add_order_item_meta($recurring_item_id, '_line_total', woocommerce_format_decimal($failed_payment_multiplier * $meta_value));
                         break;
                     case '_recurring_line_tax':
                         woocommerce_delete_order_item_meta($recurring_item_id, '_line_tax');
                         woocommerce_add_order_item_meta($recurring_item_id, '_line_tax', woocommerce_format_decimal($failed_payment_multiplier * $meta_value));
                         break;
                     case '_recurring_line_subtotal':
                         woocommerce_delete_order_item_meta($recurring_item_id, '_line_subtotal');
                         woocommerce_add_order_item_meta($recurring_item_id, '_line_subtotal', woocommerce_format_decimal($failed_payment_multiplier * $meta_value));
                         break;
                     case '_recurring_line_subtotal_tax':
                         woocommerce_delete_order_item_meta($recurring_item_id, '_line_subtotal_tax');
                         woocommerce_add_order_item_meta($recurring_item_id, '_line_subtotal_tax', woocommerce_format_decimal($failed_payment_multiplier * $meta_value));
                         break;
                     default:
                         break;
                 }
                 // Copy over line item meta data, with some parent/child role based exceptions for recurring amounts
                 $copy_to_renewal_item = true;
                 switch ($meta_key) {
                     case '_recurring_line_total':
                     case '_recurring_line_tax':
                     case '_recurring_line_subtotal':
                     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_period':
                     case '_subscription_end_date':
                     case '_subscription_expiry_date':
                     case '_subscription_start_date':
                     case '_subscription_status':
                     case '_subscription_completed_payments':
                         if ('child' == $args['new_order_role']) {
                             $copy_to_renewal_item = false;
                         }
                         break;
                     case '_subscription_trial_length':
                         // We never want to duplicate free trials on renewal orders
                         $copy_to_renewal_item = false;
                         break;
                     case '_subscription_suspension_count':
                         // We want to reset some values for the new order
                     // We want to reset some values for the new order
                     case '_subscription_trial_expiry_date':
                     case '_subscription_failed_payments':
                         $copy_to_renewal_item = false;
                         $meta_value = 0;
                         break;
                     default:
                         break;
                 }
                 // Copy existing item over to new recurring order item
                 if ($copy_to_renewal_item) {
                     woocommerce_add_order_item_meta($recurring_item_id, $meta_key, $meta_value);
                 }
             }
         } else {
             // WC 1.x order item structure
             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' == $args['new_order_role']) {
                             unset($item_meta->meta[$meta_index]);
                         }
                         break;
                     case '_subscription_trial_length':
                         // We never want to duplicate free trials on renewal orders
                         if ('child' == $args['new_order_role']) {
                             unset($item_meta->meta[$meta_index]);
                         } else {
                             $item_meta->meta[$meta_index] = 0;
                         }
                         break;
                 }
                 if ('child' == $args['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' == $args['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, $args['new_order_role']);
     return apply_filters('woocommerce_subscriptions_renewal_order_id', $renewal_order_id, $original_order, $product_id, $args['new_order_role']);
 }
 /**
  * Version 1.2 introduced a massive change to the order meta data schema. This function goes
  * through and upgrades the existing data on all orders to the new schema.
  *
  * The upgrade process is timeout safe as it keeps a record of the orders upgraded and only
  * deletes this record once all orders have been upgraded successfully. If operating on a huge
  * number of orders and the upgrade process times out, only the orders not already upgraded
  * will be upgraded in future requests that trigger this function.
  *
  * @since 1.2
  */
 private static function upgrade_database_to_1_2()
 {
     global $wpdb;
     set_transient('wc_subscriptions_is_upgrading', 'true', 60 * 2);
     // Get IDs only and use a direct DB query for efficiency
     $orders_to_upgrade = $wpdb->get_col("SELECT ID FROM {$wpdb->posts} WHERE post_type = 'shop_order' AND post_parent = 0");
     $upgraded_orders = get_option('wcs_1_2_upgraded_order_ids', array());
     // Transition deprecated subscription status if we aren't in the middle of updating orders
     if (empty($upgraded_orders)) {
         $wpdb->query($wpdb->prepare("UPDATE {$wpdb->usermeta} SET meta_value = replace( meta_value, 's:9:\"suspended\"', 's:7:\"on-hold\"' ) WHERE meta_key LIKE %s", '%_' . WC_Subscriptions_Manager::$users_meta_key));
         $wpdb->query($wpdb->prepare("UPDATE {$wpdb->usermeta} SET meta_value = replace( meta_value, 's:6:\"failed\"', 's:9:\"cancelled\"' ) WHERE meta_key LIKE %s", '%_' . WC_Subscriptions_Manager::$users_meta_key));
     }
     $orders_to_upgrade = array_diff($orders_to_upgrade, $upgraded_orders);
     // Upgrade all _sign_up_{field} order meta to new order data format
     foreach ($orders_to_upgrade as $order_id) {
         $order = new WC_Order($order_id);
         // Manually check if a product in an order is a subscription, we can't use WC_Subscriptions_Order::order_contains_subscription( $order ) because it relies on the new data structure
         $contains_subscription = false;
         foreach ($order->get_items() as $order_item) {
             if (WC_Subscriptions_Product::is_subscription(WC_Subscriptions_Order::get_items_product_id($order_item))) {
                 $contains_subscription = true;
                 break;
             }
         }
         if (!$contains_subscription) {
             continue;
         }
         $trial_lengths = WC_Subscriptions_Order::get_meta($order, '_order_subscription_trial_lengths', array());
         $trial_length = array_pop($trial_lengths);
         $has_trial = !empty($trial_length) && $trial_length > 0 ? true : false;
         $sign_up_fee_total = WC_Subscriptions_Order::get_meta($order, '_sign_up_fee_total', 0);
         // Create recurring_* meta data from existing cart totals
         $cart_discount = $order->get_cart_discount();
         update_post_meta($order_id, '_order_recurring_discount_cart', $cart_discount);
         $order_discount = $order->get_order_discount();
         update_post_meta($order_id, '_order_recurring_discount_total', $order_discount);
         $order_shipping_tax = get_post_meta($order_id, '_order_shipping_tax', true);
         update_post_meta($order_id, '_order_recurring_shipping_tax_total', $order_shipping_tax);
         $order_tax = get_post_meta($order_id, '_order_tax', true);
         // $order->get_total_tax() includes shipping tax
         update_post_meta($order_id, '_order_recurring_tax_total', $order_tax);
         $order_total = $order->get_total();
         update_post_meta($order_id, '_order_recurring_total', $order_total);
         // Set order totals to include sign up fee fields, if there was a sign up fee on the order and a trial period (other wise, the recurring totals are correct)
         if ($sign_up_fee_total > 0) {
             // Order totals need to be changed to be equal to sign up fee totals
             if ($has_trial) {
                 $cart_discount = WC_Subscriptions_Order::get_meta($order, '_sign_up_fee_discount_cart', 0);
                 $order_discount = WC_Subscriptions_Order::get_meta($order, '_sign_up_fee_discount_total', 0);
                 $order_tax = WC_Subscriptions_Order::get_meta($order, '_sign_up_fee_tax_total', 0);
                 $order_total = $sign_up_fee_total;
             } else {
                 // No trial, sign up fees need to be added to order totals
                 $cart_discount += WC_Subscriptions_Order::get_meta($order, '_sign_up_fee_discount_cart', 0);
                 $order_discount += WC_Subscriptions_Order::get_meta($order, '_sign_up_fee_discount_total', 0);
                 $order_tax += WC_Subscriptions_Order::get_meta($order, '_sign_up_fee_tax_total', 0);
                 $order_total += $sign_up_fee_total;
             }
             update_post_meta($order_id, '_order_total', $order_total);
             update_post_meta($order_id, '_cart_discount', $cart_discount);
             update_post_meta($order_id, '_order_discount', $order_discount);
             update_post_meta($order_id, '_order_tax', $order_tax);
         }
         // Make sure we get order taxes in WC 1.x format
         if (false == self::$is_wc_version_2) {
             $order_taxes = $order->get_taxes();
         } else {
             $order_tax_row = $wpdb->get_row($wpdb->prepare("\n\t\t\t\t\tSELECT * FROM {$wpdb->postmeta}\n\t\t\t\t\tWHERE meta_key = '_order_taxes_old'\n\t\t\t\t\tAND post_id = %s\n\t\t\t\t\t", $order_id));
             $order_taxes = (array) maybe_unserialize($order_tax_row->meta_value);
         }
         // Set recurring taxes to order taxes, if using WC 2.0, this will be migrated to the new format in @see self::upgrade_to_latest_wc()
         update_post_meta($order_id, '_order_recurring_taxes', $order_taxes);
         $sign_up_fee_taxes = WC_Subscriptions_Order::get_meta($order, '_sign_up_fee_taxes', array());
         // Update order taxes to include sign up fee taxes
         foreach ($sign_up_fee_taxes as $index => $sign_up_tax) {
             if ($has_trial && $sign_up_fee_total > 0) {
                 // Order taxes need to be set to the same as the sign up fee taxes
                 if (isset($sign_up_tax['cart_tax']) && $sign_up_tax['cart_tax'] > 0) {
                     $order_taxes[$index]['cart_tax'] = $sign_up_tax['cart_tax'];
                 }
             } elseif (!$has_trial && $sign_up_fee_total > 0) {
                 // Sign up fee taxes need to be added to order taxes
                 if (isset($sign_up_tax['cart_tax']) && $sign_up_tax['cart_tax'] > 0) {
                     $order_taxes[$index]['cart_tax'] += $sign_up_tax['cart_tax'];
                 }
             }
         }
         if (false == self::$is_wc_version_2) {
             // Doing it right: updated Subs *before* updating WooCommerce, the WooCommerce updater will take care of data migration
             update_post_meta($order_id, '_order_taxes', $order_taxes);
         } else {
             // Doing it wrong: updated Subs *after* updating WooCommerce, need to store in WC2.0 tax structure
             $index = 0;
             $new_order_taxes = $order->get_taxes();
             foreach ($new_order_taxes as $item_id => $order_tax) {
                 $index = $index + 1;
                 if (!isset($order_taxes[$index]['label']) || !isset($order_taxes[$index]['cart_tax']) || !isset($order_taxes[$index]['shipping_tax'])) {
                     continue;
                 }
                 // Add line item meta
                 if ($item_id) {
                     woocommerce_update_order_item_meta($item_id, 'compound', absint(isset($order_taxes[$index]['compound']) ? $order_taxes[$index]['compound'] : 0));
                     woocommerce_update_order_item_meta($item_id, 'tax_amount', woocommerce_clean($order_taxes[$index]['cart_tax']));
                     woocommerce_update_order_item_meta($item_id, 'shipping_tax_amount', woocommerce_clean($order_taxes[$index]['shipping_tax']));
                 }
             }
         }
         /* Upgrade each order item to use new Item Meta schema */
         $order_subscription_periods = WC_Subscriptions_Order::get_meta($order_id, '_order_subscription_periods', array());
         $order_subscription_intervals = WC_Subscriptions_Order::get_meta($order_id, '_order_subscription_intervals', array());
         $order_subscription_lengths = WC_Subscriptions_Order::get_meta($order_id, '_order_subscription_lengths', array());
         $order_subscription_trial_lengths = WC_Subscriptions_Order::get_meta($order_id, '_order_subscription_trial_lengths', array());
         $order_items = $order->get_items();
         foreach ($order_items as $index => $order_item) {
             $product_id = WC_Subscriptions_Order::get_items_product_id($order_item);
             $item_meta = new WC_Order_Item_Meta($order_item['item_meta']);
             $subscription_interval = isset($order_subscription_intervals[$product_id]) ? $order_subscription_intervals[$product_id] : 1;
             $subscription_length = isset($order_subscription_lengths[$product_id]) ? $order_subscription_lengths[$product_id] : 0;
             $subscription_trial_length = isset($order_subscription_trial_lengths[$product_id]) ? $order_subscription_trial_lengths[$product_id] : 0;
             $subscription_sign_up_fee = WC_Subscriptions_Order::get_meta($order, '_cart_contents_sign_up_fee_total', 0);
             if ($sign_up_fee_total > 0) {
                 // Discounted price * Quantity
                 $sign_up_fee_line_total = WC_Subscriptions_Order::get_meta($order, '_cart_contents_sign_up_fee_total', 0);
                 $sign_up_fee_line_tax = WC_Subscriptions_Order::get_meta($order, '_sign_up_fee_tax_total', 0);
                 // Base price * Quantity
                 $sign_up_fee_line_subtotal = WC_Subscriptions_Order::get_meta($order, '_cart_contents_sign_up_fee_total', 0) + WC_Subscriptions_Order::get_meta($order, '_sign_up_fee_discount_cart', 0);
                 $sign_up_fee_propotion = $sign_up_fee_line_total > 0 ? $sign_up_fee_line_subtotal / $sign_up_fee_line_total : 0;
                 $sign_up_fee_line_subtotal_tax = WC_Subscriptions_Manager::get_amount_from_proportion(WC_Subscriptions_Order::get_meta($order, '_sign_up_fee_tax_total', 0), $sign_up_fee_propotion);
                 if ($has_trial) {
                     // Set line item totals equal to sign up fee totals
                     $order_item['line_subtotal'] = $sign_up_fee_line_subtotal;
                     $order_item['line_subtotal_tax'] = $sign_up_fee_line_subtotal_tax;
                     $order_item['line_total'] = $sign_up_fee_line_total;
                     $order_item['line_tax'] = $sign_up_fee_line_tax;
                 } else {
                     // No trial period, sign up fees need to be added to order totals
                     $order_item['line_subtotal'] += $sign_up_fee_line_subtotal;
                     $order_item['line_subtotal_tax'] += $sign_up_fee_line_subtotal_tax;
                     $order_item['line_total'] += $sign_up_fee_line_total;
                     $order_item['line_tax'] += $sign_up_fee_line_tax;
                 }
             }
             // Upgrading with WC 1.x
             if (method_exists($item_meta, 'add')) {
                 $item_meta->add('_subscription_period', $order_subscription_periods[$product_id]);
                 $item_meta->add('_subscription_interval', $subscription_interval);
                 $item_meta->add('_subscription_length', $subscription_length);
                 $item_meta->add('_subscription_trial_length', $subscription_trial_length);
                 $item_meta->add('_subscription_recurring_amount', $order_item['line_subtotal']);
                 // WC_Subscriptions_Product::get_price() would return a price without filters applied
                 $item_meta->add('_subscription_sign_up_fee', $subscription_sign_up_fee);
                 // Set recurring amounts for the item
                 $item_meta->add('_recurring_line_total', $order_item['line_total']);
                 $item_meta->add('_recurring_line_tax', $order_item['line_tax']);
                 $item_meta->add('_recurring_line_subtotal', $order_item['line_subtotal']);
                 $item_meta->add('_recurring_line_subtotal_tax', $order_item['line_subtotal_tax']);
                 $order_item['item_meta'] = $item_meta->meta;
                 $order_items[$index] = $order_item;
             } else {
                 // Ignoring all advice, upgrading 4 months after version 1.2 was released, and doing it with WC 2.0 installed
                 woocommerce_add_order_item_meta($index, '_subscription_period', $order_subscription_periods[$product_id]);
                 woocommerce_add_order_item_meta($index, '_subscription_interval', $subscription_interval);
                 woocommerce_add_order_item_meta($index, '_subscription_length', $subscription_length);
                 woocommerce_add_order_item_meta($index, '_subscription_trial_length', $subscription_trial_length);
                 woocommerce_add_order_item_meta($index, '_subscription_trial_period', $order_subscription_periods[$product_id]);
                 woocommerce_add_order_item_meta($index, '_subscription_recurring_amount', $order_item['line_subtotal']);
                 woocommerce_add_order_item_meta($index, '_subscription_sign_up_fee', $subscription_sign_up_fee);
                 // Calculated recurring amounts for the item
                 woocommerce_add_order_item_meta($index, '_recurring_line_total', $order_item['line_total']);
                 woocommerce_add_order_item_meta($index, '_recurring_line_tax', $order_item['line_tax']);
                 woocommerce_add_order_item_meta($index, '_recurring_line_subtotal', $order_item['line_subtotal']);
                 woocommerce_add_order_item_meta($index, '_recurring_line_subtotal_tax', $order_item['line_subtotal_tax']);
                 if ($sign_up_fee_total > 0) {
                     // Order totals have changed
                     woocommerce_update_order_item_meta($index, '_line_subtotal', woocommerce_format_decimal($order_item['line_subtotal']));
                     woocommerce_update_order_item_meta($index, '_line_subtotal_tax', woocommerce_format_decimal($order_item['line_subtotal_tax']));
                     woocommerce_update_order_item_meta($index, '_line_total', woocommerce_format_decimal($order_item['line_total']));
                     woocommerce_update_order_item_meta($index, '_line_tax', woocommerce_format_decimal($order_item['line_tax']));
                 }
             }
         }
         // Save the new meta on the order items for WC 1.x (the API functions already saved the data for WC2.x)
         if (false == self::$is_wc_version_2) {
             update_post_meta($order_id, '_order_items', $order_items);
         }
         $upgraded_orders[] = $order_id;
         update_option('wcs_1_2_upgraded_order_ids', $upgraded_orders);
     }
     // Remove the lock on upgrading
     delete_transient('wc_subscriptions_is_upgrading');
 }
 /**
  * Get transaction rate.
  *
  * @return float
  */
 public function get_transaction_rate()
 {
     $rate = isset($this->gateway->transaction_rate) ? $this->gateway->transaction_rate : 7;
     return woocommerce_format_decimal($rate);
 }
Example #9
0
 /**
  * Process the checkout after the confirm order button is pressed
  *
  * @access public
  * @return void
  */
 function process_checkout()
 {
     global $wpdb, $woocommerce;
     if (!defined('WOOCOMMERCE_CHECKOUT')) {
         define('WOOCOMMERCE_CHECKOUT', true);
     }
     $woocommerce->verify_nonce('process_checkout');
     do_action('woocommerce_before_checkout_process');
     if (sizeof($woocommerce->cart->get_cart()) == 0) {
         $woocommerce->add_error(sprintf(__('Sorry, your session has expired. <a href="%s">Return to homepage &rarr;</a>', 'woocommerce'), home_url()));
     }
     do_action('woocommerce_checkout_process');
     // Checkout fields (not defined in checkout_fields)
     $this->posted['shiptobilling'] = isset($_POST['shiptobilling']) ? 1 : 0;
     $this->posted['terms'] = isset($_POST['terms']) ? 1 : 0;
     $this->posted['createaccount'] = isset($_POST['createaccount']) ? 1 : 0;
     $this->posted['payment_method'] = isset($_POST['payment_method']) ? woocommerce_clean($_POST['payment_method']) : '';
     $this->posted['shipping_method'] = isset($_POST['shipping_method']) ? woocommerce_clean($_POST['shipping_method']) : '';
     // Ship to billing only option
     if ($woocommerce->cart->ship_to_billing_address_only()) {
         $this->posted['shiptobilling'] = 1;
     }
     // Update customer shipping and payment method to posted method
     $_SESSION['_chosen_shipping_method'] = $this->posted['shipping_method'];
     $_SESSION['_chosen_payment_method'] = $this->posted['payment_method'];
     // Note if we skip shipping
     $skipped_shipping = false;
     // Get validation class
     $validation = $woocommerce->validation();
     // Get posted checkout_fields and do validation
     foreach ($this->checkout_fields as $fieldset_key => $fieldset) {
         // Skip shipping if its not needed
         if ($fieldset_key == 'shipping' && ($woocommerce->cart->ship_to_billing_address_only() || $this->posted['shiptobilling'] || !$woocommerce->cart->needs_shipping() && get_option('woocommerce_require_shipping_address') == 'no')) {
             $skipped_shipping = true;
             continue;
         }
         foreach ($fieldset as $key => $field) {
             if (!isset($field['type'])) {
                 $field['type'] = 'text';
             }
             // Get Value
             switch ($field['type']) {
                 case "checkbox":
                     $this->posted[$key] = isset($_POST[$key]) ? 1 : 0;
                     break;
                 default:
                     $this->posted[$key] = isset($_POST[$key]) ? woocommerce_clean($_POST[$key]) : '';
                     break;
             }
             // Hook to allow modification of value
             $this->posted[$key] = apply_filters('woocommerce_process_checkout_field_' . $key, $this->posted[$key]);
             // Validation: Required fields
             if (isset($field['required']) && $field['required'] && empty($this->posted[$key])) {
                 $woocommerce->add_error('<strong>' . $field['label'] . '</strong> ' . __('is a required field.', 'woocommerce'));
             }
             if (!empty($this->posted[$key])) {
                 // Special handling for validation and formatting
                 switch ($key) {
                     case "billing_postcode":
                     case "shipping_postcode":
                         $validate_against = $key == 'billing_postcode' ? 'billing_country' : 'shipping_country';
                         $this->posted[$key] = strtoupper(str_replace(' ', '', $this->posted[$key]));
                         if (!$validation->is_postcode($this->posted[$key], $_POST[$validate_against])) {
                             $woocommerce->add_error('<strong>' . $field['label'] . '</strong> ' . sprintf(__('(%s) is not a valid postcode/ZIP.', 'woocommerce'), $this->posted[$key]));
                         } else {
                             $this->posted[$key] = $validation->format_postcode($this->posted[$key], $_POST[$validate_against]);
                         }
                         break;
                     case "billing_state":
                     case "shipping_state":
                         // Get valid states
                         $validate_against = $key == 'billing_state' ? 'billing_country' : 'shipping_country';
                         $valid_states = $woocommerce->countries->get_states($_POST[$validate_against]);
                         if ($valid_states) {
                             $valid_state_values = array_flip(array_map('strtolower', $valid_states));
                         }
                         // Convert value to key if set
                         if (isset($valid_state_values[strtolower($this->posted[$key])])) {
                             $this->posted[$key] = $valid_state_values[strtolower($this->posted[$key])];
                         }
                         // Only validate if the country has specific state options
                         if ($valid_states && sizeof($valid_states) > 0) {
                             if (!in_array($this->posted[$key], array_keys($valid_states))) {
                                 $woocommerce->add_error('<strong>' . $field['label'] . '</strong> ' . __('is not valid. Please enter one of the following:', 'woocommerce') . ' ' . implode(', ', $valid_states));
                             }
                         }
                         break;
                     case "billing_phone":
                         $this->posted[$key] = $validation->format_phone($this->posted[$key]);
                         if (!$validation->is_phone($this->posted[$key])) {
                             $woocommerce->add_error('<strong>' . $field['label'] . '</strong> ' . __('is not a valid number.', 'woocommerce'));
                         }
                         break;
                     case "billing_email":
                         $this->posted[$key] = strtolower($this->posted[$key]);
                         if (!$validation->is_email($this->posted[$key])) {
                             $woocommerce->add_error('<strong>' . $field['label'] . '</strong> ' . __('is not a valid email address.', 'woocommerce'));
                         }
                         break;
                 }
             }
         }
     }
     // Update customer location to posted location so we can correctly check available shipping methods
     if (isset($this->posted['billing_country'])) {
         $woocommerce->customer->set_country($this->posted['billing_country']);
     }
     if (isset($this->posted['billing_state'])) {
         $woocommerce->customer->set_state($this->posted['billing_state']);
     }
     if (isset($this->posted['billing_postcode'])) {
         $woocommerce->customer->set_postcode($this->posted['billing_postcode']);
     }
     // Shipping Information
     if (!$skipped_shipping) {
         // Update customer location to posted location so we can correctly check available shipping methods
         if (isset($this->posted['shipping_country'])) {
             $woocommerce->customer->set_shipping_country($this->posted['shipping_country']);
         }
         if (isset($this->posted['shipping_state'])) {
             $woocommerce->customer->set_shipping_state($this->posted['shipping_state']);
         }
         if (isset($this->posted['shipping_postcode'])) {
             $woocommerce->customer->set_shipping_postcode($this->posted['shipping_postcode']);
         }
     } else {
         // Update customer location to posted location so we can correctly check available shipping methods
         if (isset($this->posted['billing_country'])) {
             $woocommerce->customer->set_shipping_country($this->posted['billing_country']);
         }
         if (isset($this->posted['billing_state'])) {
             $woocommerce->customer->set_shipping_state($this->posted['billing_state']);
         }
         if (isset($this->posted['billing_postcode'])) {
             $woocommerce->customer->set_shipping_postcode($this->posted['billing_postcode']);
         }
     }
     // Update cart totals now we have customer address
     $woocommerce->cart->calculate_totals();
     // Handle accounts
     if (is_user_logged_in()) {
         $this->creating_account = false;
     } elseif (!empty($this->posted['createaccount'])) {
         $this->creating_account = true;
     } elseif ($this->must_create_account) {
         $this->creating_account = true;
     } else {
         $this->creating_account = false;
     }
     if ($this->creating_account) {
         if (get_option('woocommerce_registration_email_for_username') == 'no') {
             if (empty($this->posted['account_username'])) {
                 $woocommerce->add_error(__('Please enter an account username.', 'woocommerce'));
             }
             // Check the username
             if (!validate_username($this->posted['account_username'])) {
                 $woocommerce->add_error(__('Invalid email/username.', 'woocommerce'));
             } elseif (username_exists($this->posted['account_username'])) {
                 $woocommerce->add_error(__('An account is already registered with that username. Please choose another.', 'woocommerce'));
             }
         } else {
             $this->posted['account_username'] = $this->posted['billing_email'];
         }
         // Validate passwords
         if (empty($this->posted['account_password'])) {
             $woocommerce->add_error(__('Please enter an account password.', 'woocommerce'));
         }
         if ($this->posted['account_password-2'] !== $this->posted['account_password']) {
             $woocommerce->add_error(__('Passwords do not match.', 'woocommerce'));
         }
         // Check the e-mail address
         if (email_exists($this->posted['billing_email'])) {
             $woocommerce->add_error(__('An account is already registered with your email address. Please login.', 'woocommerce'));
         }
     }
     // Terms
     if (!isset($_POST['woocommerce_checkout_update_totals']) && empty($this->posted['terms']) && woocommerce_get_page_id('terms') > 0) {
         $woocommerce->add_error(__('You must accept our Terms &amp; Conditions.', 'woocommerce'));
     }
     if ($woocommerce->cart->needs_shipping()) {
         // Shipping Method
         $available_methods = $woocommerce->shipping->get_available_shipping_methods();
         if (!isset($available_methods[$this->posted['shipping_method']])) {
             $woocommerce->add_error(__('Invalid shipping method.', 'woocommerce'));
         }
     }
     if ($woocommerce->cart->needs_payment()) {
         // Payment Method
         $available_gateways = $woocommerce->payment_gateways->get_available_payment_gateways();
         if (!isset($available_gateways[$this->posted['payment_method']])) {
             $woocommerce->add_error(__('Invalid payment method.', 'woocommerce'));
         } else {
             $available_gateways[$this->posted['payment_method']]->validate_fields();
         }
         // Payment Method Field Validation
     }
     // Action after validation
     do_action('woocommerce_after_checkout_validation', $this->posted);
     if (!isset($_POST['woocommerce_checkout_update_totals']) && $woocommerce->error_count() == 0) {
         $user_id = get_current_user_id();
         while (1) {
             // Create customer account and log them in
             if ($this->creating_account && !$user_id) {
                 $reg_errors = new WP_Error();
                 do_action('woocommerce_register_post', $this->posted['account_username'], $this->posted['billing_email'], $reg_errors);
                 $errors = apply_filters('woocommerce_registration_errors', $reg_errors, $this->posted['account_username'], $this->posted['billing_email']);
                 // if there are no errors, let's create the user account
                 if (!$reg_errors->get_error_code()) {
                     $user_pass = esc_attr($this->posted['account_password']);
                     $user_id = wp_create_user($this->posted['account_username'], $user_pass, $this->posted['billing_email']);
                     if (!$user_id) {
                         $woocommerce->add_error('<strong>' . __('ERROR', 'woocommerce') . '</strong>: ' . __('Couldn&#8217;t register you&hellip; please contact us if you continue to have problems.', 'woocommerce'));
                         break;
                     }
                     // Change role
                     wp_update_user(array('ID' => $user_id, 'role' => 'customer'));
                     // Action
                     do_action('woocommerce_created_customer', $user_id);
                     // send the user a confirmation and their login details
                     $mailer = $woocommerce->mailer();
                     $mailer->customer_new_account($user_id, $user_pass);
                     // set the WP login cookie
                     $secure_cookie = is_ssl() ? true : false;
                     wp_set_auth_cookie($user_id, true, $secure_cookie);
                 } else {
                     $woocommerce->add_error($reg_errors->get_error_message());
                     break;
                 }
             }
             // Create Order (send cart variable so we can record items and reduce inventory). Only create if this is a new order, not if the payment was rejected last time.
             $_tax = new WC_Tax();
             $order_data = array('post_type' => 'shop_order', 'post_title' => sprintf(__('Order &ndash; %s', 'woocommerce'), strftime(_x('%b %d, %Y @ %I:%M %p', 'Order date parsed by strftime', 'woocommerce'))), 'post_status' => 'publish', 'ping_status' => 'closed', 'post_excerpt' => $this->posted['order_comments'], 'post_author' => 1, 'post_password' => uniqid('order_'));
             // Cart items
             $order_items = array();
             foreach ($woocommerce->cart->get_cart() as $cart_item_key => $values) {
                 $_product = $values['data'];
                 // Store any item meta data - item meta class lets plugins add item meta in a standardized way
                 $item_meta = new WC_Order_Item_Meta();
                 $item_meta->new_order_item($values);
                 // Store variation data in meta so admin can view it
                 if ($values['variation'] && is_array($values['variation'])) {
                     foreach ($values['variation'] as $key => $value) {
                         $item_meta->add(esc_attr(str_replace('attribute_', '', $key)), $value);
                     }
                 }
                 // Store backorder status
                 if ($_product->backorders_require_notification() && $_product->is_on_backorder($values['quantity'])) {
                     $item_meta->add(__('Backordered', 'woocommerce'), $values['quantity'] - max(0, $_product->get_total_stock()));
                 }
                 $order_items[] = apply_filters('new_order_item', array('id' => $values['product_id'], 'variation_id' => $values['variation_id'], 'name' => $_product->get_title(), 'qty' => (int) $values['quantity'], 'item_meta' => $item_meta->meta, 'line_subtotal' => woocommerce_format_decimal($values['line_subtotal']), 'line_subtotal_tax' => woocommerce_format_decimal($values['line_subtotal_tax']), 'line_total' => woocommerce_format_decimal($values['line_total']), 'line_tax' => woocommerce_format_decimal($values['line_tax']), 'tax_class' => $_product->get_tax_class()), $values);
             }
             // Check order items for errors
             do_action('woocommerce_check_new_order_items', $order_items);
             if ($woocommerce->error_count() > 0) {
                 break;
             }
             // Insert or update the post data
             $create_new_order = true;
             if (isset($_SESSION['order_awaiting_payment']) && $_SESSION['order_awaiting_payment'] > 0) {
                 $order_id = (int) $_SESSION['order_awaiting_payment'];
                 /* Check order is unpaid by getting its status */
                 $terms = wp_get_object_terms($order_id, 'shop_order_status', array('fields' => 'slugs'));
                 $order_status = isset($terms[0]) ? $terms[0] : 'pending';
                 if ($order_status == 'pending') {
                     // Resume the unpaid order
                     $order_data['ID'] = $order_id;
                     wp_update_post($order_data);
                     do_action('woocommerce_resume_order', $order_id);
                     $create_new_order = false;
                 }
             }
             if ($create_new_order) {
                 $order_id = wp_insert_post($order_data);
                 if (is_wp_error($order_id)) {
                     $woocommerce->add_error('Error: Unable to create order. Please try again.');
                     break;
                 } else {
                     // Inserted successfully
                     do_action('woocommerce_new_order', $order_id);
                 }
             }
             // Get better formatted shipping method (title)
             $shipping_method = $this->posted['shipping_method'];
             if (isset($available_methods[$this->posted['shipping_method']])) {
                 $shipping_method = $available_methods[$this->posted['shipping_method']]->label;
             }
             // Get better formatted payment method (title/label)
             $payment_method = $this->posted['payment_method'];
             if (isset($available_gateways[$this->posted['payment_method']])) {
                 $payment_method = $available_gateways[$this->posted['payment_method']]->get_title();
             }
             // UPDATE ORDER META
             // Save billing and shipping first, also save to user meta if logged in
             if ($this->checkout_fields['billing']) {
                 foreach ($this->checkout_fields['billing'] as $key => $field) {
                     // Post
                     update_post_meta($order_id, '_' . $key, $this->posted[$key]);
                     // User
                     if ($user_id > 0 && !empty($this->posted[$key])) {
                         update_user_meta($user_id, $key, $this->posted[$key]);
                         // Special fields
                         switch ($key) {
                             case "billing_email":
                                 if (!email_exists($this->posted[$key])) {
                                     wp_update_user(array('ID' => $user_id, 'user_email' => $this->posted[$key]));
                                 }
                                 break;
                             case "billing_first_name":
                                 wp_update_user(array('ID' => $user_id, 'first_name' => $this->posted[$key]));
                                 break;
                             case "billing_last_name":
                                 wp_update_user(array('ID' => $user_id, 'last_name' => $this->posted[$key]));
                                 break;
                         }
                     }
                 }
             }
             if ($this->checkout_fields['shipping'] && ($woocommerce->cart->needs_shipping() || get_option('woocommerce_require_shipping_address') == 'yes')) {
                 foreach ($this->checkout_fields['shipping'] as $key => $field) {
                     if ($this->posted['shiptobilling']) {
                         $field_key = str_replace('shipping_', 'billing_', $key);
                         // Post
                         update_post_meta($order_id, '_' . $key, $this->posted[$field_key]);
                     } else {
                         // Post
                         update_post_meta($order_id, '_' . $key, $this->posted[$key]);
                         // User
                         if ($user_id > 0) {
                             update_user_meta($user_id, $key, $this->posted[$key]);
                         }
                     }
                 }
             }
             // Save any other user meta
             if ($user_id) {
                 do_action('woocommerce_checkout_update_user_meta', $user_id, $this->posted);
             }
             // Prepare order taxes for storage
             $order_taxes = array();
             foreach (array_keys($woocommerce->cart->taxes + $woocommerce->cart->shipping_taxes) as $key) {
                 $is_compound = $woocommerce->cart->tax->is_compound($key) ? 1 : 0;
                 $cart_tax = isset($woocommerce->cart->taxes[$key]) ? $woocommerce->cart->taxes[$key] : 0;
                 $shipping_tax = isset($woocommerce->cart->shipping_taxes[$key]) ? $woocommerce->cart->shipping_taxes[$key] : 0;
                 $order_taxes[] = array('label' => $woocommerce->cart->tax->get_rate_label($key), 'compound' => $is_compound, 'cart_tax' => woocommerce_format_total($cart_tax), 'shipping_tax' => woocommerce_format_total($shipping_tax));
             }
             // Save other order meta fields
             update_post_meta($order_id, '_shipping_method', $this->posted['shipping_method']);
             update_post_meta($order_id, '_payment_method', $this->posted['payment_method']);
             update_post_meta($order_id, '_shipping_method_title', $shipping_method);
             update_post_meta($order_id, '_payment_method_title', $payment_method);
             update_post_meta($order_id, '_order_shipping', woocommerce_format_total($woocommerce->cart->shipping_total));
             update_post_meta($order_id, '_order_discount', woocommerce_format_total($woocommerce->cart->get_order_discount_total()));
             update_post_meta($order_id, '_cart_discount', woocommerce_format_total($woocommerce->cart->get_cart_discount_total()));
             update_post_meta($order_id, '_order_tax', woocommerce_format_total($woocommerce->cart->tax_total));
             update_post_meta($order_id, '_order_shipping_tax', woocommerce_format_total($woocommerce->cart->shipping_tax_total));
             update_post_meta($order_id, '_order_total', woocommerce_format_total($woocommerce->cart->total));
             update_post_meta($order_id, '_order_key', apply_filters('woocommerce_generate_order_key', uniqid('order_')));
             update_post_meta($order_id, '_customer_user', (int) $user_id);
             update_post_meta($order_id, '_order_items', $order_items);
             update_post_meta($order_id, '_order_taxes', $order_taxes);
             update_post_meta($order_id, '_order_currency', get_woocommerce_currency());
             update_post_meta($order_id, '_prices_include_tax', get_option('woocommerce_prices_include_tax'));
             // Store technical customer details in meta
             $customer_ip = isset($_SERVER['HTTP_X_FORWARD_FOR']) ? $_SERVER['HTTP_X_FORWARD_FOR'] : $_SERVER['REMOTE_ADDR'];
             $customer_user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
             update_post_meta($order_id, __('Customer IP Address', 'woocommerce'), $customer_ip);
             update_post_meta($order_id, __('Customer UA', 'woocommerce'), $customer_user_agent);
             // Let plugins add meta
             do_action('woocommerce_checkout_update_order_meta', $order_id, $this->posted);
             // Order status
             wp_set_object_terms($order_id, 'pending', 'shop_order_status');
             // Discount code meta
             if ($applied_coupons = $woocommerce->cart->get_applied_coupons()) {
                 update_post_meta($order_id, 'coupons', implode(', ', $applied_coupons));
                 if (empty($order)) {
                     $order = new WC_Order($order_id);
                 }
                 $order->add_order_note(sprintf(__('Coupon Code Used: %s', 'woocommerce'), implode(', ', $applied_coupons)));
             }
             // Order is saved
             do_action('woocommerce_checkout_order_processed', $order_id, $this->posted);
             // Prevent timeout
             @set_time_limit(0);
             // Process payment
             if ($woocommerce->cart->needs_payment()) {
                 // Store Order ID in session so it can be re-used after payment failure
                 $_SESSION['order_awaiting_payment'] = $order_id;
                 // Process Payment
                 $result = $available_gateways[$this->posted['payment_method']]->process_payment($order_id);
                 // Redirect to success/confirmation/payment page
                 if ($result['result'] == 'success') {
                     $result = apply_filters('woocommerce_payment_successful_result', $result);
                     if (is_ajax()) {
                         echo json_encode($result) . '<!--WC_END-->';
                         exit;
                     } else {
                         wp_redirect($result['redirect']);
                         exit;
                     }
                 }
             } else {
                 if (empty($order)) {
                     $order = new WC_Order($order_id);
                 }
                 // No payment was required for order
                 $order->payment_complete();
                 // Empty the Cart
                 $woocommerce->cart->empty_cart();
                 // Get redirect
                 $return_url = get_permalink(woocommerce_get_page_id('thanks'));
                 $return_url = add_query_arg('key', $order->order_key, add_query_arg('order', $order->id, $return_url));
                 // Redirect to success/confirmation/payment page
                 if (is_ajax()) {
                     echo json_encode(array('result' => 'success', 'redirect' => apply_filters('woocommerce_checkout_no_payment_needed_redirect', $return_url, $order))) . '<!--WC_END-->';
                     exit;
                 } else {
                     wp_safe_redirect(apply_filters('woocommerce_checkout_no_payment_needed_redirect', $return_url, $order));
                     exit;
                 }
             }
             // Break out of loop
             break;
         }
     }
     // If we reached this point then there were errors
     if (is_ajax()) {
         ob_start();
         $woocommerce->show_messages();
         $messages = ob_get_clean();
         echo json_encode(array('result' => 'failure', 'messages' => $messages, 'refresh' => isset($_SESSION['refresh_totals']) ? 'true' : 'false')) . '<!--WC_END-->';
         unset($_SESSION['refresh_totals']);
         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 $args['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 $args['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 WC_Order|int $order The WC_Order object or ID of the order for which the a new order should be created.
  * @param string $product_id The ID of the subscription product in the order which needs to be added to the new order.
  * @param array $args (optional) An array of name => value flags:
  *         '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.
  *         'checkout_renewal' bool Indicates if invoked from an interactive cart/checkout session and certain order items are not set, like taxes, shipping as they need to be set in teh calling function, like @see WC_Subscriptions_Checkout::filter_woocommerce_create_order(). Default false.
  *         'failed_order_id' int For checkout_renewal true, indicates order id being replaced
  * @since 1.2
  */
 public static function generate_renewal_order($original_order, $product_id, $args = array())
 {
     global $wpdb, $woocommerce;
     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_subscription($original_order, $product_id)) {
         return false;
     }
     if (self::is_renewal($original_order, array('order_role' => 'child'))) {
         $original_order = self::get_parent_order($original_order);
     }
     if (!is_array($args)) {
         _deprecated_argument(__CLASS__ . '::' . __FUNCTION__, '1.3', __('Third parameter is now an array of name => value pairs. Use array( "new_order_role" => "parent" ) instead.', 'woocommerce-subscriptions'));
         $args = array('new_order_role' => $args);
     }
     $args = wp_parse_args($args, array('new_order_role' => 'parent', 'checkout_renewal' => false));
     $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', 'woocommerce-subscriptions'), strftime(_x('%b %d, %Y @ %I:%M %p', 'Order date parsed by strftime', 'woocommerce-subscriptions'))), 'ping_status' => 'closed', 'post_excerpt' => $original_order->customer_note, 'post_author' => 1, 'post_password' => $renewal_order_key);
     $create_new_order = true;
     if (!WC_Subscriptions::is_woocommerce_pre_2_2()) {
         // WC 2.2 order status
         $renewal_order_data['post_status'] = 'wc-pending';
     } else {
         $renewal_order_data['post_status'] = 'publish';
     }
     if ('child' == $args['new_order_role']) {
         $renewal_order_data['post_parent'] = $original_order->id;
     }
     if (true === $args['checkout_renewal']) {
         $renewal_order_id = null;
         if ($woocommerce->session->order_awaiting_payment > 0) {
             $renewal_order_id = absint($woocommerce->session->order_awaiting_payment);
         } elseif (isset($args['failed_order_id'])) {
             $failed_order_id = $args['failed_order_id'];
             if (WC_Subscriptions::is_woocommerce_pre_2_2()) {
                 // WC 2.1  - need to use taxonomy
                 /* Check order is unpaid by getting its status */
                 $terms = wp_get_object_terms($failed_order_id, 'shop_order_status', array('fields' => 'slugs'));
                 $order_status = isset($terms[0]) ? $terms[0] : 'pending';
             } else {
                 // WC 2.2+
                 $failed_order = wc_get_order($failed_order_id);
                 $order_status = $failed_order->get_status();
             }
             /* If paying on a pending order, we are resuming */
             if ($order_status == 'pending') {
                 $renewal_order_id = $failed_order_id;
             }
         }
         if ($renewal_order_id) {
             if (WC_Subscriptions::is_woocommerce_pre_2_2()) {
                 // WC 2.1  - need to use taxonomy
                 /* Check order is unpaid by getting its status */
                 $terms = wp_get_object_terms($renewal_order_id, 'shop_order_status', array('fields' => 'slugs'));
                 $order_status = isset($terms[0]) ? $terms[0] : 'pending';
             } else {
                 // WC 2.2+
                 $existing_order = wc_get_order($renewal_order_id);
                 $order_status = $existing_order->get_status();
             }
             // Resume the unpaid order if its pending
             if ($order_status == 'pending' || $order_status == 'failed') {
                 // Update the existing order as we are resuming it
                 $create_new_order = false;
                 $renewal_order_data['ID'] = $renewal_order_id;
                 wp_update_post($renewal_order_data);
                 // Clear the old line items - we'll add these again in case they changed
                 $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 )", $renewal_order_id));
                 $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = %d", $renewal_order_id));
             }
         }
     }
     if ($create_new_order) {
         $renewal_order_id = wp_insert_post($renewal_order_data);
     }
     if (WC_Subscriptions::is_woocommerce_pre_2_2()) {
         // WC 2.1 order status
         // 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`\n\t\t\t\t\t\t\t FROM {$wpdb->postmeta}\n\t\t\t\t\t\t\t WHERE `post_id` = {$original_order->id}\n\t\t\t\t\t\t\t AND `meta_key` NOT IN ('_paid_date', '_completed_date', '_order_key', '_edit_lock', '_original_order', '_wc_points_earned', '_transaction_id')";
     // Superseding existing order so don't carry over payment details
     if ('parent' == $args['new_order_role'] || true === $args['checkout_renewal']) {
         $order_meta_query .= " AND `meta_key` NOT IN ('_payment_method', '_payment_method_title', '_recurring_payment_method', '_recurring_payment_method_title', '_shipping_method', '_shipping_method_title', '_recurring_shipping_method', '_recurring_shipping_method_title')";
     } else {
         $order_meta_query .= " AND `meta_key` NOT LIKE '_order_recurring_%' AND `meta_key` NOT IN ('_payment_method', '_payment_method_title', '_recurring_payment_method', '_recurring_payment_method_title', '_shipping_method', '_shipping_method_title', '_recurring_shipping_method', '_recurring_shipping_method_title')";
     }
     // 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, $args['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, $args['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);
     $failed_payment_multiplier = 1;
     if (false == $args['checkout_renewal']) {
         // 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', 'no')) {
             $failed_payment_multiplier = WC_Subscriptions_Order::get_failed_payment_count($original_order, $product_id);
         }
         // Set order totals based on recurring totals from the original order
         $cart_discount = $failed_payment_multiplier * wc_format_decimal(get_post_meta($original_order->id, '_order_recurring_discount_cart', true));
         $order_discount = $failed_payment_multiplier * wc_format_decimal(get_post_meta($original_order->id, '_order_recurring_discount_total', true));
         $order_shipping_tax = $failed_payment_multiplier * wc_format_decimal(get_post_meta($original_order->id, '_order_recurring_shipping_tax_total', true));
         $order_shipping = $failed_payment_multiplier * wc_format_decimal(get_post_meta($original_order->id, '_order_recurring_shipping_total', true));
         $order_tax = $failed_payment_multiplier * wc_format_decimal(get_post_meta($original_order->id, '_order_recurring_tax_total', true));
         $order_total = $failed_payment_multiplier * wc_format_decimal(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_shipping', $order_shipping);
         update_post_meta($renewal_order_id, '_order_tax', $order_tax);
         update_post_meta($renewal_order_id, '_order_total', $order_total);
         // Set shipping for orders created with WC 2.0.n (or when we are using WC 2.0.n)
         if (WC_Subscriptions::is_woocommerce_pre_2_1() || isset($original_order->recurring_shipping_method)) {
             update_post_meta($renewal_order_id, '_shipping_method', $original_order->recurring_shipping_method);
             update_post_meta($renewal_order_id, '_shipping_method_title', $original_order->recurring_shipping_method_title);
             // Also set recurring shipping as it's a parent renewal order
             if ('parent' == $args['new_order_role']) {
                 update_post_meta($renewal_order_id, '_recurring_shipping_method', $original_order->recurring_shipping_method);
                 update_post_meta($renewal_order_id, '_recurring_shipping_method_title', $original_order->recurring_shipping_method_title);
             }
         }
         // Apply the recurring shipping & payment methods to child renewal orders
         if ('child' == $args['new_order_role']) {
             update_post_meta($renewal_order_id, '_payment_method', $original_order->recurring_payment_method);
             update_post_meta($renewal_order_id, '_payment_method_title', $original_order->recurring_payment_method_title);
         }
         // Set order taxes based on recurring taxes from the original order
         $recurring_order_taxes = WC_Subscriptions_Order::get_recurring_taxes($original_order);
         foreach ($recurring_order_taxes as $index => $recurring_order_tax) {
             $item_ids = array();
             $item_ids[] = woocommerce_add_order_item($renewal_order_id, array('order_item_name' => $recurring_order_tax['name'], 'order_item_type' => 'tax'));
             // Also set recurring taxes on parent renewal orders
             if ('parent' == $args['new_order_role']) {
                 $item_ids[] = woocommerce_add_order_item($renewal_order_id, array('order_item_name' => $recurring_order_tax['name'], 'order_item_type' => 'recurring_tax'));
             }
             // Add line item meta
             foreach ($item_ids as $item_id) {
                 woocommerce_add_order_item_meta($item_id, 'compound', absint(isset($recurring_order_tax['compound']) ? $recurring_order_tax['compound'] : 0));
                 woocommerce_add_order_item_meta($item_id, 'tax_amount', $failed_payment_multiplier * WC_Subscriptions::format_total($recurring_order_tax['tax_amount']));
                 woocommerce_add_order_item_meta($item_id, 'shipping_tax_amount', $failed_payment_multiplier * WC_Subscriptions::format_total($recurring_order_tax['shipping_tax_amount']));
                 if (isset($recurring_order_tax['rate_id'])) {
                     woocommerce_add_order_item_meta($item_id, 'rate_id', $recurring_order_tax['rate_id']);
                 }
                 if (isset($recurring_order_tax['label'])) {
                     woocommerce_add_order_item_meta($item_id, 'label', $recurring_order_tax['label']);
                 }
             }
         }
         // Set up shipping items on renewal order
         $recurring_shipping_items = WC_Subscriptions_Order::get_recurring_shipping_methods($original_order);
         foreach ($recurring_shipping_items as $recurring_shipping_item_id => $recurring_shipping_item) {
             $item_ids = array();
             $item_ids[] = woocommerce_add_order_item($renewal_order_id, array('order_item_name' => $recurring_shipping_item['name'], 'order_item_type' => 'shipping'));
             // Also set recurring shipping as it's a parent renewal order
             if ('parent' == $args['new_order_role']) {
                 $item_ids[] = woocommerce_add_order_item($renewal_order_id, array('order_item_name' => $recurring_shipping_item['name'], 'order_item_type' => 'recurring_shipping'));
             }
             // Add shipping item meta
             foreach ($item_ids as $item_id) {
                 woocommerce_add_order_item_meta($item_id, 'method_id', $recurring_shipping_item['method_id']);
                 woocommerce_add_order_item_meta($item_id, 'cost', $failed_payment_multiplier * WC_Subscriptions::format_total($recurring_shipping_item['cost']));
                 // Set line taxes for shipping
                 if (isset($recurring_shipping_item['taxes'])) {
                     $taxes = maybe_unserialize($recurring_shipping_item['taxes']);
                 } else {
                     // try to determine recurring shipping taxes from original order's shipping taxes
                     $taxes = array();
                     foreach ($original_order->get_shipping_methods() as $original_shipping_item_id => $original_shipping_item) {
                         // We need to use the amount of the same shipping method as this one
                         if ($recurring_shipping_item['method_id'] != $original_shipping_item['method_id']) {
                             continue;
                         }
                         // If we don't have taxes on the matching shipping item, there's nothing we can do
                         if (!isset($original_shipping_item['taxes'])) {
                             break;
                         }
                         $original_shipping_item['taxes'] = maybe_unserialize($original_shipping_item['taxes']);
                         // Make sure we account for any amount on the original original shipping not applied to renewals by determining what proportion of the initial amount the recurring total represents
                         if (0 != $original_shipping_item['cost'] && $recurring_shipping_item['cost'] != $original_shipping_item['cost']) {
                             $recurring_ratio = $recurring_shipping_item['cost'] / $original_shipping_item['cost'];
                             foreach ($original_shipping_item['taxes'] as $tax_id => $tax_amount) {
                                 $original_shipping_item['taxes'][$tax_id] = $recurring_ratio * $tax_amount;
                             }
                         }
                         foreach ($original_shipping_item['taxes'] as $tax_id => $tax_amount) {
                             $taxes[$tax_id] = WC_Subscriptions::format_total($failed_payment_multiplier * $tax_amount);
                         }
                     }
                 }
                 woocommerce_add_order_item_meta($item_id, 'taxes', $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, $args['new_order_role']);
     if (true === $args['checkout_renewal']) {
         $cart_items = $woocommerce->cart->get_cart();
     }
     foreach ($order_items as $item_index => $order_item) {
         $renewal_order_item_name = apply_filters('woocommerce_subscriptions_renewal_order_item_name', $order_item['name'], $order_item, $original_order);
         // Create order line item on the renewal order
         $recurring_item_id = woocommerce_add_order_item($renewal_order_id, array('order_item_name' => $renewal_order_item_name, 'order_item_type' => 'line_item'));
         if (true === $args['checkout_renewal']) {
             $cart_item = array();
             foreach ($cart_items as $item) {
                 if ($item['product_id'] == $order_item['product_id'] && (empty($order_item['variation_id']) || $item['variation_id'] == $order_item['variation_id'])) {
                     $cart_item = $item;
                 }
             }
             if (!empty($cart_item)) {
                 woocommerce_update_order_item_meta($recurring_item_id, '_line_total', woocommerce_format_decimal($cart_item['line_total']));
                 woocommerce_update_order_item_meta($recurring_item_id, '_line_tax', woocommerce_format_decimal($cart_item['line_tax']));
                 woocommerce_update_order_item_meta($recurring_item_id, '_line_subtotal', woocommerce_format_decimal($cart_item['line_subtotal']));
                 woocommerce_update_order_item_meta($recurring_item_id, '_line_subtotal_tax', woocommerce_format_decimal($cart_item['line_subtotal_tax']));
                 if (is_object($cart_item['data'])) {
                     woocommerce_update_order_item_meta($recurring_item_id, '_tax_class', $cart_item['data']->get_tax_class());
                 }
             }
             $cart_items = $woocommerce->cart->get_cart();
         }
         $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_key => $meta) {
             // $meta is an array, so the item needs to be extracted from $meta[0] (just like order meta on a WC Order)
             $meta_value = maybe_unserialize($meta[0]);
             if (false === $args['checkout_renewal']) {
                 // Already set earlier
                 // Map line item totals based on recurring line totals
                 switch ($meta_key) {
                     case '_recurring_line_total':
                         woocommerce_update_order_item_meta($recurring_item_id, '_line_total', $failed_payment_multiplier * woocommerce_format_decimal($meta_value));
                         break;
                     case '_recurring_line_tax':
                         woocommerce_update_order_item_meta($recurring_item_id, '_line_tax', $failed_payment_multiplier * woocommerce_format_decimal($meta_value));
                         break;
                     case '_recurring_line_subtotal':
                         woocommerce_update_order_item_meta($recurring_item_id, '_line_subtotal', $failed_payment_multiplier * woocommerce_format_decimal($meta_value));
                         break;
                     case '_recurring_line_subtotal_tax':
                         woocommerce_update_order_item_meta($recurring_item_id, '_line_subtotal_tax', $failed_payment_multiplier * woocommerce_format_decimal($meta_value));
                         break;
                     case '_line_tax_data':
                         // Copy line tax data if the order doesn't have a _recurring_line_tax_data (for backward compatibility)
                         if (!array_key_exists('_recurring_line_tax_data', $item_meta->meta)) {
                             $line_total = $item_meta->meta['_line_total'][0];
                             $recurring_line_total = $item_meta->meta['_recurring_line_total'][0];
                             // There will only be recurring tax data if the recurring amount is > 0 and we can only retroactively calculate recurring amount from initial amoutn if it is > 0
                             if ($line_total > 0 && $recurring_line_total > 0) {
                                 // Make sure we account for any sign-up fees by determining what proportion of the initial amount the recurring total represents
                                 $recurring_ratio = $recurring_line_total / $line_total;
                                 $recurring_tax_data = array();
                                 $tax_data_keys = array('total', 'subtotal');
                                 foreach ($tax_data_keys as $tax_data_key) {
                                     foreach ($meta_value[$tax_data_key] as $tax_index => $tax_value) {
                                         // Use total tax amount for both total and subtotal because we don't want any initial discounts to be applied to recurring amounts
                                         $total_tax_amount = $meta_value['total'][$tax_index];
                                         $recurring_tax_data[$tax_data_key][$tax_index] = woocommerce_format_decimal($failed_payment_multiplier * ($recurring_ratio * $total_tax_amount));
                                     }
                                 }
                             } else {
                                 $recurring_tax_data = array('total' => array(), 'subtotal' => array());
                             }
                             woocommerce_update_order_item_meta($recurring_item_id, '_line_tax_data', $recurring_tax_data);
                         }
                         break;
                     case '_recurring_line_tax_data':
                         $recurring_tax_data = array();
                         $tax_data_keys = array('total', 'subtotal');
                         foreach ($tax_data_keys as $tax_data_key) {
                             foreach ($meta_value[$tax_data_key] as $tax_index => $tax_value) {
                                 $recurring_tax_data[$tax_data_key][$tax_index] = woocommerce_format_decimal($failed_payment_multiplier * $tax_value);
                             }
                         }
                         woocommerce_update_order_item_meta($recurring_item_id, '_line_tax_data', $recurring_tax_data);
                         break;
                     default:
                         break;
                 }
             }
             // Copy over line item meta data, with some parent/child role based exceptions for recurring amounts
             $copy_to_renewal_item = true;
             switch ($meta_key) {
                 case '_recurring_line_total':
                 case '_recurring_line_tax':
                 case '_recurring_line_subtotal':
                 case '_recurring_line_subtotal_tax':
                 case '_recurring_line_tax_data':
                 case '_line_tax_data':
                 case '_subscription_recurring_amount':
                 case '_subscription_sign_up_fee':
                 case '_subscription_period':
                 case '_subscription_interval':
                 case '_subscription_length':
                 case '_subscription_trial_period':
                 case '_subscription_end_date':
                 case '_subscription_expiry_date':
                 case '_subscription_start_date':
                 case '_subscription_status':
                 case '_subscription_completed_payments':
                     if ('child' == $args['new_order_role']) {
                         $copy_to_renewal_item = false;
                     }
                     break;
                 case '_subscription_trial_length':
                     // We never want to duplicate free trials on renewal orders
                     $copy_to_renewal_item = false;
                     break;
                 case '_subscription_suspension_count':
                     // We want to reset some values for the new order
                 // We want to reset some values for the new order
                 case '_subscription_trial_expiry_date':
                 case '_subscription_failed_payments':
                     $copy_to_renewal_item = false;
                     $meta_value = 0;
                     break;
                 default:
                     break;
             }
             // Copy existing item over to new recurring order item
             if ($copy_to_renewal_item) {
                 woocommerce_add_order_item_meta($recurring_item_id, $meta_key, $meta_value);
             }
         }
     }
     if (false == $args['checkout_renewal']) {
         // Add fees
         foreach ($original_order->get_fees() as $item_id => $order_fee) {
             if (!isset($order_fee['recurring_line_total'])) {
                 continue;
             }
             $item_id = woocommerce_add_order_item($renewal_order_id, array('order_item_name' => $order_fee['name'], 'order_item_type' => 'fee'));
             woocommerce_add_order_item_meta($item_id, '_tax_class', $order_fee['tax_class']);
             woocommerce_add_order_item_meta($item_id, '_line_total', WC_Subscriptions::format_total($order_fee['recurring_line_total']));
             woocommerce_add_order_item_meta($item_id, '_line_tax', WC_Subscriptions::format_total($order_fee['recurring_line_tax']));
         }
     }
     // 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' == $args['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.', 'woocommerce-subscriptions'), $renewal_order->get_order_number()));
     }
     do_action('woocommerce_subscriptions_renewal_order_created', $renewal_order, $original_order, $product_id, $args['new_order_role']);
     return apply_filters('woocommerce_subscriptions_renewal_order_id', $renewal_order_id, $original_order, $product_id, $args['new_order_role']);
 }
 /**
  * Format decimal numbers ready for DB storage
  *
  * @param type $number
  * @param type $dp
  * @param type $trim_zeros
  * @return type
  */
 public static function wc_format_decimal($number, $dp = false, $trim_zeros = false)
 {
     if (self::is_wc_2_1()) {
         return wc_format_decimal($number, $dp, $trim_zeros);
     } else {
         return woocommerce_format_decimal($number, $dp, $trim_zeros);
     }
 }