/** * Create a new subscription from a cart item on checkout. * * The function doesn't validate whether the cart item is a subscription product, meaning it can be used for any cart item, * but the item will need a `subscription_period` and `subscription_period_interval` value set on it, at a minimum. * * @param WC_Order $order * @param WC_Cart $cart * @since 2.0 */ public static function create_subscription($order, $cart) { global $wpdb; try { // Start transaction if available $wpdb->query('START TRANSACTION'); // Set the recurring line totals on the subscription $variation_id = wcs_cart_pluck($cart, 'variation_id'); $product_id = empty($variation_id) ? wcs_cart_pluck($cart, 'product_id') : $variation_id; // We need to use the $order->order_date value because the post_date_gmt isn't always set $order_date_gmt = get_gmt_from_date($order->order_date); $subscription = wcs_create_subscription(array('start_date' => $cart->start_date, 'order_id' => $order->id, 'customer_id' => $order->get_user_id(), 'billing_period' => wcs_cart_pluck($cart, 'subscription_period'), 'billing_interval' => wcs_cart_pluck($cart, 'subscription_period_interval'), 'customer_note' => $order->customer_note)); if (is_wp_error($subscription)) { throw new Exception($subscription->get_error_message()); } // Set the subscription's billing and shipping address $subscription = wcs_copy_order_address($order, $subscription); $subscription->update_dates(array('trial_end' => $cart->trial_end_date, 'next_payment' => $cart->next_payment_date, 'end' => $cart->end_date)); // Store trial period for PayPal if (wcs_cart_pluck($cart, 'subscription_trial_length') > 0) { update_post_meta($subscription->id, '_trial_period', wcs_cart_pluck($cart, 'subscription_trial_period')); } // Set the payment method on the subscription $available_gateways = WC()->payment_gateways->get_available_payment_gateways(); if ($cart->needs_payment() && isset($available_gateways[$order->payment_method])) { $subscription->set_payment_method($available_gateways[$order->payment_method]); } if (!$cart->needs_payment() || 'yes' == get_option(WC_Subscriptions_Admin::$option_prefix . '_turn_off_automatic_payments', 'no')) { $subscription->update_manual('true'); } elseif (!isset($available_gateways[$order->payment_method]) || !$available_gateways[$order->payment_method]->supports('subscriptions')) { $subscription->update_manual('true'); } wcs_copy_order_meta($order, $subscription, 'subscription'); // Store the line items foreach ($cart->get_cart() as $cart_item_key => $cart_item) { $item_id = self::add_cart_item($subscription, $cart_item, $cart_item_key); } // Store fees (although no fees recur by default, extensions may add them) foreach ($cart->get_fees() as $fee_key => $fee) { $item_id = $subscription->add_fee($fee); if (!$item_id) { // translators: placeholder is an internal error number throw new Exception(sprintf(__('Error %d: Unable to create subscription. Please try again.', 'woocommerce-subscriptions'), 403)); } // Allow plugins to add order item meta to fees do_action('woocommerce_add_order_fee_meta', $order->id, $item_id, $fee, $fee_key); } self::add_shipping($subscription, $cart); // Store tax rows foreach (array_keys($cart->taxes + $cart->shipping_taxes) as $tax_rate_id) { if ($tax_rate_id && !$subscription->add_tax($tax_rate_id, $cart->get_tax_amount($tax_rate_id), $cart->get_shipping_tax_amount($tax_rate_id)) && apply_filters('woocommerce_cart_remove_taxes_zero_rate_id', 'zero-rated') !== $tax_rate_id) { // translators: placeholder is an internal error number throw new Exception(sprintf(__('Error %d: Unable to add tax to subscription. Please try again.', 'woocommerce-subscriptions'), 405)); } } // Store coupons foreach ($cart->get_coupons() as $code => $coupon) { if (!$subscription->add_coupon($code, $cart->get_coupon_discount_amount($code), $cart->get_coupon_discount_tax_amount($code))) { // translators: placeholder is an internal error number throw new Exception(sprintf(__('Error %d: Unable to create order. Please try again.', 'woocommerce-subscriptions'), 406)); } } // Set the recurring totals on the subscription $subscription->set_total($cart->shipping_total, 'shipping'); $subscription->set_total($cart->get_cart_discount_total(), 'cart_discount'); $subscription->set_total($cart->get_cart_discount_tax_total(), 'cart_discount_tax'); $subscription->set_total($cart->tax_total, 'tax'); $subscription->set_total($cart->shipping_tax_total, 'shipping_tax'); $subscription->set_total($cart->total); // If we got here, the subscription was created without problems $wpdb->query('COMMIT'); } catch (Exception $e) { // There was an error adding the subscription $wpdb->query('ROLLBACK'); return new WP_Error('checkout-error', $e->getMessage()); } return $subscription; }
/** * Function to create an order from a subscription. It can be used for a renewal or for a resubscribe * order creation. It is the common in both of those instances. * * @param WC_Subscription|int $subscription Subscription we're basing the order off of * @param string $type Type of new order. Default values are 'renewal_order'|'resubscribe_order' * @return WC_Order New order */ function wcs_create_order_from_subscription($subscription, $type) { $type = wcs_validate_new_order_type($type); if (is_wp_error($type)) { return $type; } global $wpdb; try { $wpdb->query('START TRANSACTION'); if (!is_object($subscription)) { $subscription = wcs_get_subscription($subscription); } $new_order = wc_create_order(array('customer_id' => $subscription->get_user_id(), 'customer_note' => $subscription->customer_note)); $new_order->post->post_title = wcs_get_new_order_title($type); wcs_copy_order_meta($subscription, $new_order, $type); // Copy over line items and allow extensions to add/remove items or item meta $items = apply_filters('wcs_new_order_items', $subscription->get_items(array('line_item', 'fee', 'shipping', 'tax')), $new_order, $subscription); $items = apply_filters('wcs_' . $type . '_items', $items, $new_order, $subscription); foreach ($items as $item_index => $item) { $item_name = apply_filters('wcs_new_order_item_name', $item['name'], $item, $subscription); $item_name = apply_filters('wcs_' . $type . '_item_name', $item_name, $item, $subscription); // Create order line item on the renewal order $recurring_item_id = wc_add_order_item($new_order->id, array('order_item_name' => $item_name, 'order_item_type' => $item['type'])); // Remove recurring line items and set item totals based on recurring line totals foreach ($item['item_meta'] as $meta_key => $meta_values) { foreach ($meta_values as $meta_value) { wc_add_order_item_meta($recurring_item_id, $meta_key, maybe_unserialize($meta_value)); } } } // If we got here, the subscription was created without problems $wpdb->query('COMMIT'); return apply_filters('wcs_new_order_created', $new_order, $subscription); } catch (Exception $e) { // There was an error adding the subscription $wpdb->query('ROLLBACK'); return new WP_Error('new-order-error', $e->getMessage()); } }