} $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; }
/** * 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 – %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); }
/** * 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 →</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 & 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’t register you… 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 – %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 – %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); } }