/** * Save the order data meta box. * * @access public * @param mixed $post_id * @param mixed $post * @return void */ function woocommerce_process_shop_order_meta($post_id, $post) { global $wpdb, $woocommerce, $woocommerce_errors; // Add key add_post_meta($post_id, '_order_key', uniqid('order_'), true); // Update post data update_post_meta($post_id, '_billing_first_name', stripslashes($_POST['_billing_first_name'])); update_post_meta($post_id, '_billing_last_name', stripslashes($_POST['_billing_last_name'])); update_post_meta($post_id, '_billing_company', stripslashes($_POST['_billing_company'])); update_post_meta($post_id, '_billing_address_1', stripslashes($_POST['_billing_address_1'])); update_post_meta($post_id, '_billing_address_2', stripslashes($_POST['_billing_address_2'])); update_post_meta($post_id, '_billing_city', stripslashes($_POST['_billing_city'])); update_post_meta($post_id, '_billing_postcode', stripslashes($_POST['_billing_postcode'])); update_post_meta($post_id, '_billing_country', stripslashes($_POST['_billing_country'])); update_post_meta($post_id, '_billing_state', stripslashes($_POST['_billing_state'])); update_post_meta($post_id, '_billing_email', stripslashes($_POST['_billing_email'])); update_post_meta($post_id, '_billing_phone', stripslashes($_POST['_billing_phone'])); update_post_meta($post_id, '_shipping_first_name', stripslashes($_POST['_shipping_first_name'])); update_post_meta($post_id, '_shipping_last_name', stripslashes($_POST['_shipping_last_name'])); update_post_meta($post_id, '_shipping_company', stripslashes($_POST['_shipping_company'])); update_post_meta($post_id, '_shipping_address_1', stripslashes($_POST['_shipping_address_1'])); update_post_meta($post_id, '_shipping_address_2', stripslashes($_POST['_shipping_address_2'])); update_post_meta($post_id, '_shipping_city', stripslashes($_POST['_shipping_city'])); update_post_meta($post_id, '_shipping_postcode', stripslashes($_POST['_shipping_postcode'])); update_post_meta($post_id, '_shipping_country', stripslashes($_POST['_shipping_country'])); update_post_meta($post_id, '_shipping_state', stripslashes($_POST['_shipping_state'])); update_post_meta($post_id, '_order_shipping', stripslashes($_POST['_order_shipping'])); update_post_meta($post_id, '_cart_discount', stripslashes($_POST['_cart_discount'])); update_post_meta($post_id, '_order_discount', stripslashes($_POST['_order_discount'])); update_post_meta($post_id, '_order_total', stripslashes($_POST['_order_total'])); update_post_meta($post_id, '_customer_user', (int) $_POST['customer_user']); update_post_meta($post_id, '_order_tax', stripslashes($_POST['_order_tax'])); update_post_meta($post_id, '_order_shipping_tax', stripslashes($_POST['_order_shipping_tax'])); // Shipping method handling if (get_post_meta($post_id, '_shipping_method', true) !== stripslashes($_POST['_shipping_method'])) { $shipping_method = esc_attr(trim(stripslashes($_POST['_shipping_method']))); update_post_meta($post_id, '_shipping_method', $shipping_method); } if (get_post_meta($post_id, '_shipping_method_title', true) !== stripslashes($_POST['_shipping_method_title'])) { $shipping_method_title = esc_attr(trim(stripslashes($_POST['_shipping_method_title']))); if (!$shipping_method_title) { $shipping_method = esc_attr($_POST['_shipping_method']); $methods = $woocommerce->shipping->load_shipping_methods(); if (isset($methods) && isset($methods[$shipping_method])) { $shipping_method_title = $methods[$shipping_method]->get_title(); } } update_post_meta($post_id, '_shipping_method_title', $shipping_method_title); } // Payment method handling if (get_post_meta($post_id, '_payment_method', true) !== stripslashes($_POST['_payment_method'])) { $methods = $woocommerce->payment_gateways->payment_gateways(); $payment_method = esc_attr($_POST['_payment_method']); $payment_method_title = $payment_method; if (isset($methods) && isset($methods[$payment_method])) { $payment_method_title = $methods[$payment_method]->get_title(); } update_post_meta($post_id, '_payment_method', $payment_method); update_post_meta($post_id, '_payment_method_title', $payment_method_title); } // Update date if (empty($_POST['order_date'])) { $date = current_time('timestamp'); } else { $date = strtotime($_POST['order_date'] . ' ' . (int) $_POST['order_date_hour'] . ':' . (int) $_POST['order_date_minute'] . ':00'); } $wpdb->query($wpdb->prepare("UPDATE {$wpdb->posts} SET post_date = %s WHERE ID = %s", date_i18n('Y-m-d H:i:s', $date), $post_id)); // Tax rows $order_taxes = array(); if (isset($_POST['_order_taxes_label'])) { $order_taxes_label = $_POST['_order_taxes_label']; $order_taxes_compound = isset($_POST['_order_taxes_compound']) ? $_POST['_order_taxes_compound'] : array(); $order_taxes_cart = $_POST['_order_taxes_cart']; $order_taxes_shipping = $_POST['_order_taxes_shipping']; $order_taxes_label_count = sizeof($order_taxes_label); for ($i = 0; $i < $order_taxes_label_count; $i++) { // Add to array if the tax amount is set if (!$order_taxes_cart[$i] && !$order_taxes_shipping[$i]) { continue; } if (!$order_taxes_label[$i]) { $order_taxes_label[$i] = $woocommerce->countries->tax_or_vat(); } if (isset($order_taxes_compound[$i])) { $is_compound = 1; } else { $is_compound = 0; } $order_taxes[] = array('label' => esc_attr($order_taxes_label[$i]), 'compound' => $is_compound, 'cart_tax' => esc_attr($order_taxes_cart[$i]), 'shipping_tax' => esc_attr($order_taxes_shipping[$i])); } } update_post_meta($post_id, '_order_taxes', $order_taxes); // Order items $order_items = array(); if (isset($_POST['item_id'])) { $item_id = $_POST['item_id']; $item_variation = $_POST['item_variation']; $item_name = $_POST['item_name']; $item_quantity = $_POST['item_quantity']; $line_subtotal = $_POST['line_subtotal']; $line_subtotal_tax = $_POST['line_subtotal_tax']; $line_total = $_POST['line_total']; $line_tax = $_POST['line_tax']; $item_meta_names = isset($_POST['meta_name']) ? $_POST['meta_name'] : ''; $item_meta_values = isset($_POST['meta_value']) ? $_POST['meta_value'] : ''; $item_tax_class = $_POST['item_tax_class']; $item_id_count = sizeof($item_id); for ($i = 0; $i < $item_id_count; $i++) { if (!isset($item_id[$i]) || !$item_id[$i]) { continue; } if (!isset($item_name[$i])) { continue; } if (!isset($item_quantity[$i]) || $item_quantity[$i] < 1) { continue; } if (!isset($line_total[$i])) { continue; } if (!isset($line_tax[$i])) { 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 = sizeof($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 ($meta_name && $meta_value) { $item_meta->add($meta_name, $meta_value); } } } // Add to array $order_items[] = apply_filters('update_order_item', array('id' => htmlspecialchars(stripslashes($item_id[$i])), 'variation_id' => (int) $item_variation[$i], 'name' => htmlspecialchars(stripslashes($item_name[$i])), 'qty' => (int) $item_quantity[$i], 'line_total' => rtrim(rtrim(number_format(woocommerce_clean($line_total[$i]), 4, '.', ''), '0'), '.'), 'line_tax' => rtrim(rtrim(number_format(woocommerce_clean($line_tax[$i]), 4, '.', ''), '0'), '.'), 'line_subtotal' => rtrim(rtrim(number_format(woocommerce_clean($line_subtotal[$i]), 4, '.', ''), '0'), '.'), 'line_subtotal_tax' => rtrim(rtrim(number_format(woocommerce_clean($line_subtotal_tax[$i]), 4, '.', ''), '0'), '.'), 'item_meta' => $item_meta->meta, 'tax_class' => woocommerce_clean($item_tax_class[$i]))); } } update_post_meta($post_id, '_order_items', $order_items); // Order data saved, now get it so we can manipulate status $order = new WC_Order($post_id); // Order status $order->update_status($_POST['order_status']); // Handle button actions if (isset($_POST['reduce_stock']) && $_POST['reduce_stock'] && sizeof($order_items) > 0) { $order->add_order_note(__('Manually reducing stock.', 'woocommerce')); foreach ($order_items as $order_item) { $_product = $order->get_product_from_item($order_item); if ($_product->exists()) { if ($_product->managing_stock()) { $old_stock = $_product->stock; $new_quantity = $_product->reduce_stock($order_item['qty']); $order->add_order_note(sprintf(__('Item #%s stock reduced from %s to %s.', 'woocommerce'), $order_item['id'], $old_stock, $new_quantity)); $order->send_stock_notifications($_product, $new_quantity, $order_item['qty']); } } else { $order->add_order_note(sprintf(__('Item %s %s not found, skipping.', 'woocommerce'), $order_item['id'], $order_item['name'])); } } $order->add_order_note(__('Manual stock reduction complete.', 'woocommerce')); do_action('woocommerce_reduce_order_stock', $order); } elseif (isset($_POST['restore_stock']) && $_POST['restore_stock'] && sizeof($order_items) > 0) { $order->add_order_note(__('Manually restoring stock.', 'woocommerce')); foreach ($order_items as $order_item) { $_product = $order->get_product_from_item($order_item); if ($_product->exists()) { if ($_product->managing_stock()) { $old_stock = $_product->stock; $new_quantity = $_product->increase_stock($order_item['qty']); $order->add_order_note(sprintf(__('Item #%s stock increased from %s to %s.', 'woocommerce'), $order_item['id'], $old_stock, $new_quantity)); } } else { $order->add_order_note(sprintf(__('Item %s %s not found, skipping.', 'woocommerce'), $order_item['id'], $order_item['name'])); } } $order->add_order_note(__('Manual stock restore complete.', 'woocommerce')); do_action('woocommerce_restore_order_stock', $order); } elseif (isset($_POST['invoice']) && $_POST['invoice']) { do_action('woocommerce_before_send_customer_invoice', $order); $mailer = $woocommerce->mailer(); $mailer->customer_invoice($order); do_action('woocommerce_after__customer_invoice', $order); } delete_transient('woocommerce_processing_order_count'); }
/** * Add each subscription product's details to an order so that the state of the subscription persists even when a product is changed * * @since 1.2 */ public static function add_order_item_meta($order_item) { global $woocommerce; if (WC_Subscriptions_Product::is_subscription($order_item['id'])) { // Make sure existing meta persists $item_meta = new WC_Order_Item_Meta($order_item['item_meta']); // Add subscription details so order state persists even when a product is changed $item_meta->add('_subscription_period', WC_Subscriptions_Product::get_period($order_item['id'])); $item_meta->add('_subscription_interval', WC_Subscriptions_Product::get_interval($order_item['id'])); $item_meta->add('_subscription_length', WC_Subscriptions_Product::get_length($order_item['id'])); $item_meta->add('_subscription_trial_length', WC_Subscriptions_Product::get_trial_length($order_item['id'])); $item_meta->add('_subscription_trial_period', WC_Subscriptions_Product::get_trial_period($order_item['id'])); $item_meta->add('_subscription_recurring_amount', $woocommerce->cart->base_recurring_prices[$order_item['id']]); // WC_Subscriptions_Product::get_price() would return a price without filters applied $item_meta->add('_subscription_sign_up_fee', WC_Subscriptions_Product::get_sign_up_fee($order_item['id'])); // Calculated recurring amounts for the item $item_meta->add('_recurring_line_total', $woocommerce->cart->recurring_cart_contents[$order_item['id']]['recurring_line_total']); $item_meta->add('_recurring_line_tax', $woocommerce->cart->recurring_cart_contents[$order_item['id']]['recurring_line_tax']); $item_meta->add('_recurring_line_subtotal', $woocommerce->cart->recurring_cart_contents[$order_item['id']]['recurring_line_subtotal']); $item_meta->add('_recurring_line_subtotal_tax', $woocommerce->cart->recurring_cart_contents[$order_item['id']]['recurring_line_subtotal_tax']); $order_item['item_meta'] = $item_meta->meta; } return $order_item; }
/** * 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; } }
/** * Add subscription related order item meta via Ajax when a subscription product is added as an item to an order. * * This function is hooked to the 'wp_ajax_woocommerce_subscriptions_prefill_order_item_meta' hook which should only fire * on WC 1.x (because the admin.js uses a selector which was changed in WC 2.0). For WC 2.0, order item meta is pre-filled * via the 'woocommerce_new_order_item' hook in the new @see self::prefill_order_item(). * * @since 1.2.4 * @return void */ public static function prefill_order_item_meta_old() { if (function_exists('woocommerce_add_order_item_meta')) { // Meta added on the 'woocommerce_new_order_item' hook return; } check_ajax_referer(WC_Subscriptions::$text_domain, 'security'); $product_id = trim(stripslashes($_POST['item_to_add'])); $index = trim(stripslashes($_POST['index'])); $response = array('item_index' => $index, 'html' => '', 'line_totals' => array()); if (WC_Subscriptions_Product::is_subscription($product_id)) { $recurring_amount = WC_Subscriptions_Product::get_price($product_id); $item_meta = new WC_Order_Item_Meta(); // Subscription details so order state persists even when a product is changed $item_meta->add('_subscription_period', WC_Subscriptions_Product::get_period($product_id)); $item_meta->add('_subscription_interval', WC_Subscriptions_Product::get_interval($product_id)); $item_meta->add('_subscription_length', WC_Subscriptions_Product::get_length($product_id)); $item_meta->add('_subscription_trial_length', WC_Subscriptions_Product::get_trial_length($product_id)); $item_meta->add('_subscription_trial_period', WC_Subscriptions_Product::get_trial_period($product_id)); $item_meta->add('_subscription_recurring_amount', $recurring_amount); $item_meta->add('_subscription_sign_up_fee', WC_Subscriptions_Product::get_sign_up_fee($product_id)); // Recurring totals need to be calcualted $item_meta->add('_recurring_line_total', $recurring_amount); $item_meta->add('_recurring_line_tax', 0); $item_meta->add('_recurring_line_subtotal', $recurring_amount); $item_meta->add('_recurring_line_subtotal_tax', 0); $item_meta = $item_meta->meta; if (isset($item_meta) && is_array($item_meta) && sizeof($item_meta) > 0) { foreach ($item_meta as $key => $meta) { // Backwards compatibility if (is_array($meta) && isset($meta['meta_name'])) { $meta_name = $meta['meta_name']; $meta_value = $meta['meta_value']; } else { $meta_name = $key; $meta_value = $meta; } $response['html'] .= '<tr><td><input type="text" name="meta_name[' . $index . '][]" value="' . esc_attr($meta_name) . '" /></td><td><input type="text" name="meta_value[' . $index . '][]" value="' . esc_attr($meta_value) . '" /></td><td width="1%"></td></tr>'; } } // Calculate line totals for this item if ($sign_up_fee > 0) { $line_subtotal = $sign_up_fee; $line_total = $sign_up_fee; // If there is no free trial, add the recuring amounts if ($trial_length == 0) { $line_subtotal += $recurring_amount; $line_total += $recurring_amount; } $response['line_totals']['line_subtotal'] = esc_attr(number_format((double) $line_subtotal, 2, '.', '')); $response['line_totals']['line_total'] = esc_attr(number_format((double) $line_total, 2, '.', '')); } } echo json_encode($response); die; }
/** * 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() { 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($order_item['id'])) { $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); } $order_taxes = $order->get_taxes(); // Set recurring taxes to order taxes 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']; } } } update_post_meta($order_id, '_order_taxes', $order_taxes); /* 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) { $item_meta = new WC_Order_Item_Meta($order_item['item_meta']); $subscription_interval = isset($order_subscription_intervals[$order_item['id']]) ? $order_subscription_intervals[$order_item['id']] : 1; $subscription_length = isset($order_subscription_lengths[$order_item['id']]) ? $order_subscription_lengths[$order_item['id']] : 0; $subscription_trial_length = isset($order_subscription_trial_lengths[$order_item['id']]) ? $order_subscription_trial_lengths[$order_item['id']] : 0; $item_meta->add('_subscription_period', $order_subscription_periods[$order_item['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', WC_Subscriptions_Order::get_meta($order, '_cart_contents_sign_up_fee_total', 0)); // 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']); 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; } } $order_item['item_meta'] = $item_meta->meta; $order_items[$index] = $order_item; } // Save the new meta on the order items 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'); }