/** * When an order is added or updated from the admin interface, check if a subscription product * has been manually added to the order or the details of the subscription have been modified, * and create/update the subscription as required. * * Save subscription order meta items * * @param int $post_id The ID of the post which is the WC_Order object. * @param Object $post The post object of the order. * @since 1.1 */ public static function pre_process_shop_order_meta($post_id, $post) { global $woocommerce, $wpdb; $order_contains_subscription = false; $order = new WC_Order($post_id); $existing_product_ids = array(); foreach ($order->get_items() as $existing_item) { $existing_product_ids[] = self::get_items_product_id($existing_item); } $product_ids = array(); if (isset($_POST['order_item_id'])) { foreach ($_POST['order_item_id'] as $order_item_id) { $product_ids[$order_item_id] = woocommerce_get_order_item_meta($order_item_id, '_product_id'); } } // Check if there are new subscription products to be added, or the order already has a subscription item foreach (array_merge($product_ids, $existing_product_ids) as $order_item_id => $product_id) { $is_existing_item = false; if (in_array($product_id, $existing_product_ids)) { $is_existing_item = true; } // If this is a new item and it's a subscription product, we have a subscription if (!$is_existing_item && WC_Subscriptions_Product::is_subscription($product_id)) { $order_contains_subscription = true; } // If this is an existing item and it's a subscription item, we have a subscription if ($is_existing_item && self::is_item_subscription($order, $product_id)) { $order_contains_subscription = true; } } if (!$order_contains_subscription) { return $post_id; } $existing_payment_method = get_post_meta($post_id, '_recurring_payment_method', true); $chosen_payment_method = isset($_POST['_recurring_payment_method']) ? stripslashes($_POST['_recurring_payment_method']) : ''; // For subscriptions manually added using paypal, make sure the _paypal_first_ipn_ignored_for_pdt is set so that IPN notifications are never ignored if (isset($_POST['_payment_method']) && 'paypal' == $_POST['_payment_method'] && 'true' != get_post_meta($post_id, '_paypal_first_ipn_ignored_for_pdt', true)) { update_post_meta($post_id, '_paypal_first_ipn_ignored_for_pdt', 'true'); } // If the recurring payment method is changing, or it isn't set make sure we have correct manual payment flag set if (isset($_POST['_recurring_payment_method']) || empty($existing_payment_method) && ($chosen_payment_method != $existing_payment_method || empty($chosen_payment_method))) { $payment_gateways = $woocommerce->payment_gateways->payment_gateways(); // Make sure the subscription is cancelled with the current gateway if (!empty($existing_payment_method) && isset($payment_gateways[$existing_payment_method]) && $payment_gateways[$existing_payment_method]->supports('subscriptions')) { foreach ($product_ids as $product_id) { WC_Subscriptions_Payment_Gateways::trigger_gateway_cancelled_subscription_hook(absint($_POST['customer_user']), WC_Subscriptions_Manager::get_subscription_key($post_id, $product_id)); } } if (!empty($chosen_payment_method) && isset($payment_gateways[$chosen_payment_method]) && $payment_gateways[$chosen_payment_method]->supports('subscriptions')) { $manual_renewal = 'false'; } else { $manual_renewal = 'true'; } update_post_meta($post_id, '_wcs_requires_manual_renewal', $manual_renewal); if (!empty($chosen_payment_method)) { update_post_meta($post_id, '_recurring_payment_method', stripslashes($_POST['_recurring_payment_method'])); } } // Make sure the recurring order totals are correct update_post_meta($post_id, '_order_recurring_discount_total', WC_Subscriptions::format_total($_POST['_order_recurring_discount_total'])); update_post_meta($post_id, '_order_recurring_total', WC_Subscriptions::format_total($_POST['_order_recurring_total'])); // Update fields for WC < 2.1 if (WC_Subscriptions::is_woocommerce_pre('2.1')) { // Also allow updates to the recurring payment method's title if (isset($_POST['_recurring_payment_method_title'])) { update_post_meta($post_id, '_recurring_payment_method_title', stripslashes($_POST['_recurring_payment_method_title'])); } else { // it's been deleted update_post_meta($post_id, '_recurring_payment_method_title', ''); } if (isset($_POST['_order_recurring_discount_cart'])) { update_post_meta($post_id, '_order_recurring_discount_cart', stripslashes($_POST['_order_recurring_discount_cart'])); } else { // it's been deleted update_post_meta($post_id, '_order_recurring_discount_cart', 0); } if (isset($_POST['_order_recurring_tax_total'])) { // WC < 2.1 update_post_meta($post_id, '_order_recurring_tax_total', stripslashes($_POST['_order_recurring_tax_total'])); } else { // it's been deleted update_post_meta($post_id, '_order_recurring_tax_total', 0); } if (isset($_POST['_order_recurring_shipping_tax_total'])) { update_post_meta($post_id, '_order_recurring_shipping_tax_total', stripslashes($_POST['_order_recurring_shipping_tax_total'])); } else { // it's been deleted update_post_meta($post_id, '_order_recurring_shipping_tax_total', 0); } if (isset($_POST['_order_recurring_shipping_total'])) { update_post_meta($post_id, '_order_recurring_shipping_total', stripslashes($_POST['_order_recurring_shipping_total'])); } else { // it's been deleted update_post_meta($post_id, '_order_recurring_shipping_total', 0); } } // Save tax rows $total_tax = 0; $total_shipping_tax = 0; if (isset($_POST['recurring_order_taxes_id'])) { // WC 2.0+ $tax_keys = array('recurring_order_taxes_id', 'recurring_order_taxes_rate_id', 'recurring_order_taxes_amount', 'recurring_order_taxes_shipping_amount'); foreach ($tax_keys as $tax_key) { ${$tax_key} = isset($_POST[$tax_key]) ? $_POST[$tax_key] : array(); } foreach ($recurring_order_taxes_id as $item_id => $value) { $item_id = absint($item_id); $rate_id = absint($recurring_order_taxes_rate_id[$item_id]); if ($rate_id) { $rate = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_id = %s", $rate_id)); $label = $rate->tax_rate_name ? $rate->tax_rate_name : $woocommerce->countries->tax_or_vat(); $compound = $rate->tax_rate_compound ? 1 : 0; $code = array(); $code[] = $rate->tax_rate_country; $code[] = $rate->tax_rate_state; $code[] = $rate->tax_rate_name ? $rate->tax_rate_name : 'TAX'; $code[] = absint($rate->tax_rate_priority); $code = strtoupper(implode('-', array_filter($code))); } else { $code = ''; $label = $woocommerce->countries->tax_or_vat(); } $wpdb->update($wpdb->prefix . "woocommerce_order_items", array('order_item_name' => woocommerce_clean($code)), array('order_item_id' => $item_id), array('%s'), array('%d')); woocommerce_update_order_item_meta($item_id, 'rate_id', $rate_id); woocommerce_update_order_item_meta($item_id, 'label', $label); woocommerce_update_order_item_meta($item_id, 'compound', $compound); if (isset($recurring_order_taxes_amount[$item_id])) { woocommerce_update_order_item_meta($item_id, 'tax_amount', WC_Subscriptions::format_total($recurring_order_taxes_amount[$item_id])); $total_tax += WC_Subscriptions::format_total($recurring_order_taxes_amount[$item_id]); } if (isset($recurring_order_taxes_shipping_amount[$item_id])) { woocommerce_update_order_item_meta($item_id, 'shipping_tax_amount', WC_Subscriptions::format_total($recurring_order_taxes_shipping_amount[$item_id])); $total_shipping_tax += WC_Subscriptions::format_total($recurring_order_taxes_shipping_amount[$item_id]); } } } if (!isset($_POST['_order_recurring_tax_total']) && function_exists('wc_round_tax_total')) { // WC 2.1+ update_post_meta($post_id, '_order_recurring_tax_total', wc_round_tax_total($total_tax)); } if (!isset($_POST['_order_recurring_shipping_tax_total']) && function_exists('wc_round_tax_total')) { // WC 2.1+ update_post_meta($post_id, '_order_recurring_shipping_tax_total', wc_round_tax_total($total_shipping_tax)); } // And that shipping methods are updated as required if (isset($_POST['_recurring_shipping_method']) || isset($_POST['_recurring_shipping_method_title'])) { // WC < 2.1 update_post_meta($post_id, '_recurring_shipping_method', stripslashes($_POST['_recurring_shipping_method'])); update_post_meta($post_id, '_recurring_shipping_method_title', stripslashes($_POST['_recurring_shipping_method_title'])); } // Shipping Rows $recurring_order_shipping = 0; if (isset($_POST['recurring_shipping_method_id'])) { // WC 2.1+ $get_values = array('recurring_shipping_method_id', 'recurring_shipping_method_title', 'recurring_shipping_method', 'recurring_shipping_cost'); foreach ($get_values as $value) { ${$value} = isset($_POST[$value]) ? $_POST[$value] : array(); } foreach ($recurring_shipping_method_id as $item_id => $value) { if ('new' == $item_id) { foreach ($value as $new_key => $new_value) { $method_id = woocommerce_clean($recurring_shipping_method[$item_id][$new_key]); $method_title = woocommerce_clean($recurring_shipping_method_title[$item_id][$new_key]); $cost = WC_Subscriptions::format_total($recurring_shipping_cost[$item_id][$new_key]); $new_id = woocommerce_add_order_item($post_id, array('order_item_name' => $method_title, 'order_item_type' => 'recurring_shipping')); if ($new_id) { woocommerce_add_order_item_meta($new_id, 'method_id', $method_id); woocommerce_add_order_item_meta($new_id, 'cost', $cost); } $recurring_order_shipping += $cost; } } elseif ('old' == $item_id) { // Migrate a WC 2.0.n shipping method to WC 2.1 format $method_id = woocommerce_clean($recurring_shipping_method[$item_id]); $method_title = woocommerce_clean($recurring_shipping_method_title[$item_id]); $cost = WC_Subscriptions::format_total($recurring_shipping_cost[$item_id]); $new_id = woocommerce_add_order_item($post_id, array('order_item_name' => $method_title, 'order_item_type' => 'recurring_shipping')); if ($new_id) { woocommerce_add_order_item_meta($new_id, 'method_id', $method_id); woocommerce_add_order_item_meta($new_id, 'cost', $cost); } $recurring_order_shipping += $cost; delete_post_meta($post_id, '_recurring_shipping_method'); delete_post_meta($post_id, '_recurring_shipping_method_title'); } else { $item_id = absint($item_id); $method_id = woocommerce_clean($recurring_shipping_method[$item_id]); $method_title = woocommerce_clean($recurring_shipping_method_title[$item_id]); $cost = WC_Subscriptions::format_total($recurring_shipping_cost[$item_id]); $wpdb->update($wpdb->prefix . "woocommerce_order_items", array('order_item_name' => $method_title), array('order_item_id' => $item_id), array('%s'), array('%d')); woocommerce_update_order_item_meta($item_id, 'method_id', $method_id); woocommerce_update_order_item_meta($item_id, 'cost', $cost); $recurring_order_shipping += $cost; } } } if (!isset($_POST['_order_recurring_shipping_total'])) { // WC 2.1+ update_post_meta($post_id, '_order_recurring_shipping_total', $recurring_order_shipping); } // Check if all the subscription products on the order have associated subscriptions on the user's account, and if not, add a new one foreach ($product_ids as $order_item_id => $product_id) { $is_existing_item = false; if (in_array($product_id, $existing_product_ids)) { $is_existing_item = true; } // If this is a new item and it's not a subscription product, ignore it if (!$is_existing_item && !WC_Subscriptions_Product::is_subscription($product_id)) { continue; } // If this is an existing item and it's not a subscription, ignore it if ($is_existing_item && !self::is_item_subscription($order, $product_id)) { continue; } $subscription_key = WC_Subscriptions_Manager::get_subscription_key($post_id, $product_id); $subscription = array(); if (!empty($order->customer_user) && $_POST['customer_user'] != $order->customer_user) { $customer_has_changed = true; $hook_args = array('user_id' => (int) $order->customer_user, 'subscription_key' => $subscription_key); wc_unschedule_action('scheduled_subscription_trial_end', $hook_args); wc_unschedule_action('scheduled_subscription_payment', $hook_args); wc_unschedule_action('scheduled_subscription_expiration', $hook_args); } else { $customer_has_changed = false; } // In case it's a new order or the customer has changed $order->customer_user = $order->user_id = (int) $_POST['customer_user']; $subscription = WC_Subscriptions_Manager::get_subscription($subscription_key); if (empty($subscription)) { // Add a new subscription // The order may not exist yet, so we need to set a few things ourselves if (empty($order->order_key)) { $order->order_key = uniqid('order_'); add_post_meta($post_id, '_order_key', $order->order_key, true); } if (empty($_POST['order_date'])) { $start_date = gmdate('Y-m-d H:i:s'); } else { $start_date = get_gmt_from_date($_POST['order_date'] . ' ' . (int) $_POST['order_date_hour'] . ':' . (int) $_POST['order_date_minute'] . ':00'); } WC_Subscriptions_Manager::create_pending_subscription_for_order($order, $product_id, array('start_date' => $start_date)); // Add the subscription meta for this item to the order $functions_and_meta = array('get_period' => '_order_subscription_periods', 'get_interval' => '_order_subscription_intervals', 'get_length' => '_order_subscription_lengths'); foreach ($functions_and_meta as $function_name => $meta_key) { $subscription_meta = self::get_meta($order, $meta_key, array()); $subscription_meta[$product_id] = WC_Subscriptions_Product::$function_name($product_id); update_post_meta($order->id, $meta_key, $subscription_meta); } // This works because meta is added when the item is added via Ajax self::process_shop_order_item_meta($post_id, $post); // If the order's existing status is something other than pending and the order status is not being changed, manually set the subscription's status (otherwise, it will be handled when WC transitions the order's status) if ($order->status == $_POST['order_status'] && 'pending' != $order->status) { switch ($order->status) { case 'completed': case 'processing': WC_Subscriptions_Manager::activate_subscription($order->customer_user, $subscription_key); break; case 'cancelled': WC_Subscriptions_Manager::cancel_subscription($order->customer_user, $subscription_key); break; case 'failed': WC_Subscriptions_Manager::failed_subscription_signup($order->customer_user, $subscription_key); break; } } } } // Determine whether we need to update any subscription dates for existing subscriptions (before the item meta is updated) if (!empty($product_ids)) { $start_date = $_POST['order_date'] . ' ' . (int) $_POST['order_date_hour'] . ':' . (int) $_POST['order_date_minute'] . date(':s', strtotime($order->order_date)); // Order's customer or start date changed for an existing order if ($customer_has_changed || !empty($order->order_date) && $order->order_date != $start_date) { self::$requires_update['expiration_date'] = array_values($product_ids); self::$requires_update['trial_expiration'] = array_values($product_ids); self::$requires_update['next_billing_date'] = array_values($product_ids); } elseif (isset($_POST['meta_key'])) { $item_meta_keys = isset($_POST['meta_key']) ? $_POST['meta_key'] : array(); $new_meta_values = isset($_POST['meta_value']) ? $_POST['meta_value'] : array(); foreach ($item_meta_keys as $item_meta_id => $meta_key) { $meta_data = self::get_item_meta_data($item_meta_id); $product_id = woocommerce_get_order_item_meta($meta_data->order_item_id, '_product_id'); // Set flags to update payment dates if required switch ($meta_key) { case '_subscription_period': case '_subscription_interval': if ($new_meta_values[$item_meta_id] != $meta_data->meta_value) { self::$requires_update['next_billing_date'][] = $product_id; } break; case '_subscription_start_date': case '_subscription_trial_length': case '_subscription_trial_period': if ($new_meta_values[$item_meta_id] != $meta_data->meta_value) { self::$requires_update['expiration_date'][] = $product_id; self::$requires_update['trial_expiration'][] = $product_id; self::$requires_update['next_billing_date'][] = $product_id; } break; case '_subscription_length': if ($new_meta_values[$item_meta_id] != $meta_data->meta_value) { self::$requires_update['expiration_date'][] = $product_id; self::$requires_update['next_billing_date'][] = $product_id; } break; case '_subscription_trial_expiry_date': if ($new_meta_values[$item_meta_id] != $meta_data->meta_value) { self::$requires_update['trial_expiration'][] = $product_id; } break; case '_subscription_expiry_date': if ($new_meta_values[$item_meta_id] != $meta_data->meta_value) { self::$requires_update['expiration_date'][] = $product_id; } break; } } } } }
/** * Update the recurring payment method on a subscription order. * * @param array $available_gateways The payment gateways which are currently being allowed. * @since 1.4 */ private static function update_recurring_payment_method($subscription_key, $order, $new_payment_method) { global $woocommerce; $old_payment_method = $order->recurring_payment_method; $old_payment_method_title = $order->recurring_payment_method_title; $available_gateways = $woocommerce->payment_gateways->get_available_payment_gateways(); // Also inits all payment gateways to make sure that hooks are attached correctly do_action('woocommerce_subscriptions_pre_update_recurring_payment_method', $order, $subscription_key, $new_payment_method, $old_payment_method); // Make sure the subscription is cancelled with the current gateway WC_Subscriptions_Payment_Gateways::trigger_gateway_cancelled_subscription_hook($order->customer_user, $subscription_key); // Update meta update_post_meta($order->id, '_old_recurring_payment_method', $old_payment_method); update_post_meta($order->id, '_recurring_payment_method', $new_payment_method); if (isset($available_gateways[$new_payment_method])) { $new_payment_method_title = $available_gateways[$new_payment_method]->get_title(); } else { $new_payment_method_title = ''; } update_post_meta($order->id, '_old_recurring_payment_method_title', $old_payment_method_title); update_post_meta($order->id, '_recurring_payment_method_title', $new_payment_method_title); // Log change on order $order->add_order_note(sprintf(__('Recurring payment method changed from "%s" to "%s" by the subscriber from their account page.', 'woocommerce-subscriptions'), $old_payment_method_title, $new_payment_method_title)); do_action('woocommerce_subscriptions_updated_recurring_payment_method', $order, $subscription_key, $new_payment_method, $old_payment_method); do_action('woocommerce_subscriptions_updated_recurring_payment_method_to_' . $new_payment_method, $order, $subscription_key, $old_payment_method); do_action('woocommerce_subscriptions_updated_recurring_payment_method_from_' . $old_payment_method, $order, $subscription_key, $new_payment_method); }
/** * When an order is added or updated from the admin interface, check if a subscription product * has been manually added to the order or the details of the subscription have been modified, * and create/update the subscription as required. * * Save subscription order meta items * * @param int $post_id The ID of the post which is the WC_Order object. * @param Object $post The post object of the order. * @since 1.1 */ public static function pre_process_shop_order_meta($post_id, $post) { global $woocommerce, $wpdb; $order_contains_subscription = false; $order = new WC_Order($post_id); $existing_product_ids = array(); foreach ($order->get_items() as $existing_item) { $existing_product_ids[] = self::get_items_product_id($existing_item); } $product_ids = array(); // WC <> 2.0 compatible posted product IDs if (isset($_POST['order_item_id'])) { foreach ($_POST['order_item_id'] as $order_item_id) { // WC 2.0+ has unique order item IDs and the product ID is a piece of meta $product_ids[$order_item_id] = woocommerce_get_order_item_meta($order_item_id, '_product_id'); } } elseif (isset($_POST['item_id'])) { $product_ids = $_POST['item_id']; } // WC 1.x treated order item IDs as product IDs // Check if there are new subscription products to be added, or the order already has a subscription item foreach (array_merge($product_ids, $existing_product_ids) as $order_item_id => $product_id) { $is_existing_item = false; if (in_array($product_id, $existing_product_ids)) { $is_existing_item = true; } // If this is a new item and it's a subscription product, we have a subscription if (!$is_existing_item && WC_Subscriptions_Product::is_subscription($product_id)) { $order_contains_subscription = true; } // If this is an existing item and it's a subscription item, we have a subscription if ($is_existing_item && WC_Subscriptions_Order::is_item_subscription($order, $product_id)) { $order_contains_subscription = true; } } if (!$order_contains_subscription) { return $post_id; } // If the payment method is changing, make sure we have correct manual payment flag set if (isset($_POST['_recurring_payment_method'])) { $chosen_payment_method = stripslashes($_POST['_recurring_payment_method']); $existing_payment_method = get_post_meta($post_id, '_recurring_payment_method', true); if ($chosen_payment_method != $existing_payment_method || empty($chosen_payment_method)) { $payment_gateways = $woocommerce->payment_gateways->payment_gateways(); // Make sure the subscription is cancelled with the current gateway foreach ($product_ids as $product_id) { if (isset($payment_gateways[$existing_payment_method]) && $payment_gateways[$existing_payment_method]->supports('subscriptions')) { WC_Subscriptions_Payment_Gateways::trigger_gateway_cancelled_subscription_hook(absint($_POST['customer_user']), WC_Subscriptions_Manager::get_subscription_key($post_id, $product_id)); } } if (isset($payment_gateways[$chosen_payment_method]) && $payment_gateways[$chosen_payment_method]->supports('subscriptions')) { $manual_renewal = 'false'; } else { $manual_renewal = 'true'; } update_post_meta($post_id, '_wcs_requires_manual_renewal', $manual_renewal); update_post_meta($post_id, '_recurring_payment_method', stripslashes($_POST['_recurring_payment_method'])); } } // Also allow updates to the recurring payment method's title if (isset($_POST['_recurring_payment_method_title'])) { update_post_meta($post_id, '_recurring_payment_method_title', stripslashes($_POST['_recurring_payment_method_title'])); } // Make sure the recurring order totals are correct update_post_meta($post_id, '_order_recurring_discount_cart', stripslashes($_POST['_order_recurring_discount_cart'])); update_post_meta($post_id, '_order_recurring_discount_total', stripslashes($_POST['_order_recurring_discount_total'])); update_post_meta($post_id, '_order_recurring_shipping_tax_total', stripslashes($_POST['_order_recurring_shipping_tax_total'])); update_post_meta($post_id, '_order_recurring_shipping_total', stripslashes($_POST['_order_recurring_shipping_total'])); update_post_meta($post_id, '_order_recurring_tax_total', stripslashes($_POST['_order_recurring_tax_total'])); update_post_meta($post_id, '_order_recurring_total', stripslashes($_POST['_order_recurring_total'])); // And that shipping methods are updated as required update_post_meta($post_id, '_recurring_shipping_method', stripslashes($_POST['_recurring_shipping_method'])); update_post_meta($post_id, '_recurring_shipping_method_title', stripslashes($_POST['_recurring_shipping_method_title'])); if (isset($_POST['recurring_order_taxes_id'])) { // WC 2.0+ $tax_keys = array('recurring_order_taxes_id', 'recurring_order_taxes_rate_id', 'recurring_order_taxes_amount', 'recurring_order_taxes_shipping_amount'); foreach ($tax_keys as $tax_key) { ${$tax_key} = isset($_POST[$tax_key]) ? $_POST[$tax_key] : array(); } foreach ($recurring_order_taxes_id as $item_id) { $item_id = absint($item_id); $rate_id = absint($recurring_order_taxes_rate_id[$item_id]); if ($rate_id) { $rate = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_id = %s", $rate_id)); $label = $rate->tax_rate_name ? $rate->tax_rate_name : $woocommerce->countries->tax_or_vat(); $compound = $rate->tax_rate_compound ? 1 : 0; $code = array(); $code[] = $rate->tax_rate_country; $code[] = $rate->tax_rate_state; $code[] = $rate->tax_rate_name ? $rate->tax_rate_name : 'TAX'; $code[] = absint($rate->tax_rate_priority); $code = strtoupper(implode('-', array_filter($code))); } else { $code = ''; $label = $woocommerce->countries->tax_or_vat(); } $wpdb->update($wpdb->prefix . "woocommerce_order_items", array('order_item_name' => woocommerce_clean($code)), array('order_item_id' => $item_id), array('%s'), array('%d')); woocommerce_update_order_item_meta($item_id, 'rate_id', $rate_id); woocommerce_update_order_item_meta($item_id, 'label', $label); woocommerce_update_order_item_meta($item_id, 'compound', $compound); if (isset($recurring_order_taxes_amount[$item_id])) { woocommerce_update_order_item_meta($item_id, 'tax_amount', woocommerce_clean($recurring_order_taxes_amount[$item_id])); } if (isset($recurring_order_taxes_shipping_amount[$item_id])) { woocommerce_update_order_item_meta($item_id, 'shipping_tax_amount', woocommerce_clean($recurring_order_taxes_shipping_amount[$item_id])); } } } else { // WC 1.x if (!isset($_POST['_order_recurring_taxes'])) { $_POST['_order_recurring_taxes'] = array(); } foreach ($_POST['_order_recurring_taxes'] as $index => $tax_details) { if (!isset($tax_details['compound'])) { $_POST['_order_recurring_taxes'][$index]['compound'] = 0; } } update_post_meta($post_id, '_order_recurring_taxes', $_POST['_order_recurring_taxes']); } // Check if all the subscription products on the order have associated subscriptions on the user's account, and if not, add a new one foreach ($product_ids as $order_item_id => $product_id) { $is_existing_item = false; if (in_array($product_id, $existing_product_ids)) { $is_existing_item = true; } // If this is a new item and it's not a subscription product, ignore it if (!$is_existing_item && !WC_Subscriptions_Product::is_subscription($product_id)) { continue; } // If this is an existing item and it's not a subscription, ignore it if ($is_existing_item && !WC_Subscriptions_Order::is_item_subscription($order, $product_id)) { continue; } $subscription_key = WC_Subscriptions_Manager::get_subscription_key($post_id, $product_id); $subscription = array(); // In case it's a new order or the customer has changed $order->customer_user = $order->user_id = (int) $_POST['customer_user']; $subscription = WC_Subscriptions_Manager::get_subscription($subscription_key); if (empty($subscription)) { // Add a new subscription // The order may not exist yet, so we need to set a few things ourselves if (empty($order->order_key)) { $order->order_key = uniqid('order_'); add_post_meta($post_id, '_order_key', $order->order_key, true); } if (empty($_POST['order_date'])) { $start_date = gmdate('Y-m-d H:i:s'); } else { $start_date = get_gmt_from_date($_POST['order_date'] . ' ' . (int) $_POST['order_date_hour'] . ':' . (int) $_POST['order_date_minute'] . ':00'); } WC_Subscriptions_Manager::create_pending_subscription_for_order($order, $product_id, array('start_date' => $start_date)); // Add the subscription meta for this item to the order $functions_and_meta = array('get_period' => '_order_subscription_periods', 'get_interval' => '_order_subscription_intervals', 'get_length' => '_order_subscription_lengths'); foreach ($functions_and_meta as $function_name => $meta_key) { $subscription_meta = self::get_meta($order, $meta_key, array()); $subscription_meta[$product_id] = WC_Subscriptions_Product::$function_name($product_id); update_post_meta($order->id, $meta_key, $subscription_meta); } // This works because process_shop_order_item_meta saves item meta to workaround a WC 1.x bug and in WC 2.0+ meta is added when the item is added via Ajax self::process_shop_order_item_meta($post_id, $post); // If the order's existing status is something other than pending and the order status is not being changed, manually set the subscription's status (otherwise, it will be handled when WC transitions the order's status) if ($order->status == $_POST['order_status'] && 'pending' != $order->status) { switch ($order->status) { case 'completed': case 'processing': WC_Subscriptions_Manager::activate_subscription($order->customer_user, $subscription_key); break; case 'refunded': case 'cancelled': WC_Subscriptions_Manager::cancel_subscription($order->customer_user, $subscription_key); break; case 'failed': WC_Subscriptions_Manager::failed_subscription_signup($order->customer_user, $subscription_key); break; } } } } // Determine whether we need to update any subscription dates for existing subscriptions (before the item meta is updated) if (!empty($product_ids)) { $start_date = $_POST['order_date'] . ' ' . (int) $_POST['order_date_hour'] . ':' . (int) $_POST['order_date_minute'] . ':00'; // Start date changed for an existing order if (!empty($order->order_date) && $order->order_date != $start_date) { self::$requires_update['expiration_date'] = array_values($product_ids); self::$requires_update['trial_expiration'] = array_values($product_ids); self::$requires_update['next_billing_date'] = array_values($product_ids); } elseif (isset($_POST['meta_key'])) { // WC 2.0+ $item_meta_keys = isset($_POST['meta_key']) ? $_POST['meta_key'] : array(); $new_meta_values = isset($_POST['meta_value']) ? $_POST['meta_value'] : array(); foreach ($item_meta_keys as $item_meta_id => $meta_key) { $meta_data = self::get_item_meta_data($item_meta_id); $product_id = woocommerce_get_order_item_meta($meta_data->order_item_id, '_product_id'); // Set flags to update payment dates if required switch ($meta_key) { case '_subscription_period': case '_subscription_interval': if ($new_meta_values[$item_meta_id] != $meta_data->meta_value) { self::$requires_update['next_billing_date'][] = $product_id; } break; case '_subscription_start_date': case '_subscription_trial_length': case '_subscription_trial_period': if ($new_meta_values[$item_meta_id] != $meta_data->meta_value) { self::$requires_update['expiration_date'][] = $product_id; self::$requires_update['trial_expiration'][] = $product_id; self::$requires_update['next_billing_date'][] = $product_id; } break; case '_subscription_length': if ($new_meta_values[$item_meta_id] != $meta_data->meta_value) { self::$requires_update['expiration_date'][] = $product_id; self::$requires_update['next_billing_date'][] = $product_id; } break; case '_subscription_trial_expiry_date': if ($new_meta_values[$item_meta_id] != $meta_data->meta_value) { self::$requires_update['trial_expiration'][] = $product_id; } break; case '_subscription_expiry_date': if ($new_meta_values[$item_meta_id] != $meta_data->meta_value) { self::$requires_update['expiration_date'][] = $product_id; } break; } } } elseif (isset($_POST['meta_name'])) { // WC 1.x $item_meta_names = isset($_POST['meta_name']) ? $_POST['meta_name'] : ''; $item_meta_values = isset($_POST['meta_value']) ? $_POST['meta_value'] : ''; $item_id_count = count($item_ids); for ($i = 0; $i < $item_id_count; $i++) { if (!isset($item_ids[$i]) || !$item_ids[$i]) { continue; } elseif (!in_array($item_ids[$i], $existing_product_ids)) { // New subscriptions throw a false positive continue; } // Meta $item_meta = new WC_Order_Item_Meta(); if (isset($item_meta_names[$i]) && isset($item_meta_values[$i])) { $meta_names = $item_meta_names[$i]; $meta_values = $item_meta_values[$i]; $meta_names_count = count($meta_names); for ($ii = 0; $ii < $meta_names_count; $ii++) { $meta_name = esc_attr($meta_names[$ii]); $meta_value = esc_attr($meta_values[$ii]); if (!isset($meta_name) || !isset($meta_value)) { continue; } // Set flags to update payment dates if required switch ($meta_name) { case '_subscription_period': case '_subscription_interval': if ($meta_value != self::get_item_meta($order, $meta_name, $item_ids[$i])) { self::$requires_update['next_billing_date'][] = $item_ids[$i]; } break; case '_subscription_trial_length': case '_subscription_trial_period': if ($meta_value != self::get_item_meta($order, $meta_name, $item_ids[$i])) { self::$requires_update['expiration_date'][] = $item_ids[$i]; self::$requires_update['trial_expiration'][] = $item_ids[$i]; self::$requires_update['next_billing_date'][] = $item_ids[$i]; } break; case '_subscription_length': if ($meta_value != self::get_item_meta($order, $meta_name, $item_ids[$i])) { self::$requires_update['expiration_date'][] = $item_ids[$i]; self::$requires_update['next_billing_date'][] = $item_ids[$i]; } break; } } } } } } }