/**
 * Create a new subscription
 *
 * Returns a new WC_Subscription object on success which can then be used to add additional data.
 *
 * @return WC_Subscription | WP_Error A WC_Subscription on success or WP_Error object on failure
 * @since  2.0
 */
function wcs_create_subscription($args = array())
{
    $order = isset($args['order_id']) ? wc_get_order($args['order_id']) : null;
    if (!empty($order) && isset($order->post->post_date)) {
        $default_start_date = '0000-00-00 00:00:00' != $order->post->post_date_gmt ? $order->post->post_date_gmt : get_gmt_from_date($order->post->post_date);
    } else {
        $default_start_date = current_time('mysql', true);
    }
    $default_args = array('status' => '', 'order_id' => 0, 'customer_note' => null, 'customer_id' => !empty($order) ? $order->get_user_id() : null, 'start_date' => $default_start_date, 'created_via' => !empty($order) ? $order->created_via : '', 'order_version' => !empty($order) ? $order->order_version : WC_VERSION, 'currency' => !empty($order) ? $order->order_currency : get_woocommerce_currency(), 'prices_include_tax' => !empty($order) ? $order->prices_include_tax ? 'yes' : 'no' : get_option('woocommerce_prices_include_tax'));
    $args = wp_parse_args($args, $default_args);
    $subscription_data = array();
    // validate the start_date field
    if (!is_string($args['start_date']) || false === wcs_is_datetime_mysql_format($args['start_date'])) {
        return new WP_Error('woocommerce_subscription_invalid_start_date_format', _x('Invalid date. The date must be a string and of the format: "Y-m-d H:i:s".', 'Error message while creating a subscription', 'woocommerce-subscriptions'));
    } else {
        if (strtotime($args['start_date']) > current_time('timestamp', true)) {
            return new WP_Error('woocommerce_subscription_invalid_start_date', _x('Subscription start date must be before current day.', 'Error message while creating a subscription', 'woocommerce-subscriptions'));
        }
    }
    // check customer id is set
    if (empty($args['customer_id']) || !is_numeric($args['customer_id']) || $args['customer_id'] <= 0) {
        return new WP_Error('woocommerce_subscription_invalid_customer_id', _x('Invalid subscription customer_id.', 'Error message while creating a subscription', 'woocommerce-subscriptions'));
    }
    // check the billing period
    if (empty($args['billing_period']) || !in_array(strtolower($args['billing_period']), array_keys(wcs_get_subscription_period_strings()))) {
        return new WP_Error('woocommerce_subscription_invalid_billing_period', __('Invalid subscription billing period given.', 'woocommerce-subscriptions'));
    }
    // check the billing interval
    if (empty($args['billing_interval']) || !is_numeric($args['billing_interval']) || absint($args['billing_interval']) <= 0) {
        return new WP_Error('woocommerce_subscription_invalid_billing_interval', __('Invalid subscription billing interval given. Must be an integer greater than 0.', 'woocommerce-subscriptions'));
    }
    $subscription_data['post_type'] = 'shop_subscription';
    $subscription_data['post_status'] = 'wc-' . apply_filters('woocommerce_default_subscription_status', 'pending');
    $subscription_data['ping_status'] = 'closed';
    $subscription_data['post_author'] = 1;
    $subscription_data['post_password'] = uniqid('order_');
    // translators: Order date parsed by strftime
    $post_title_date = strftime(_x('%b %d, %Y @ %I:%M %p', 'Used in subscription post title. "Subscription renewal order - <this>"', 'woocommerce-subscriptions'));
    // translators: placeholder is order date parsed by strftime
    $subscription_data['post_title'] = sprintf(_x('Subscription &ndash; %s', 'The post title for the new subscription', 'woocommerce-subscriptions'), $post_title_date);
    $subscription_data['post_date_gmt'] = $args['start_date'];
    $subscription_data['post_date'] = get_date_from_gmt($args['start_date']);
    if ($args['order_id'] > 0) {
        $subscription_data['post_parent'] = absint($args['order_id']);
    }
    if (!is_null($args['customer_note']) && !empty($args['customer_note'])) {
        $subscription_data['post_excerpt'] = $args['customer_note'];
    }
    // Only set the status if creating a new subscription, use wcs_update_subscription to update the status
    if ($args['status']) {
        if (!in_array('wc-' . $args['status'], array_keys(wcs_get_subscription_statuses()))) {
            return new WP_Error('woocommerce_invalid_subscription_status', __('Invalid subscription status given.', 'woocommerce-subscriptions'));
        }
        $subscription_data['post_status'] = 'wc-' . $args['status'];
    }
    $subscription_id = wp_insert_post(apply_filters('woocommerce_new_subscription_data', $subscription_data, $args), true);
    if (is_wp_error($subscription_id)) {
        return $subscription_id;
    }
    // Default order meta data.
    update_post_meta($subscription_id, '_order_key', 'wc_' . apply_filters('woocommerce_generate_order_key', uniqid('order_')));
    update_post_meta($subscription_id, '_order_currency', $args['currency']);
    update_post_meta($subscription_id, '_prices_include_tax', $args['prices_include_tax']);
    update_post_meta($subscription_id, '_created_via', sanitize_text_field($args['created_via']));
    // add/update the billing
    update_post_meta($subscription_id, '_billing_period', $args['billing_period']);
    update_post_meta($subscription_id, '_billing_interval', absint($args['billing_interval']));
    update_post_meta($subscription_id, '_customer_user', $args['customer_id']);
    update_post_meta($subscription_id, '_order_version', $args['order_version']);
    return new WC_Subscription($subscription_id);
}
 /**
  * Set the dates on the subscription.
  *
  * Because dates are interdependent on each other, this function will take an array of dates, make sure that all
  * dates are in the right order in the right format, that there is at least something to update.
  *
  * @param array 		$dates 			array containing dates with keys: 'start', 'trial_end', 'next_payment',
  *                           			'last_payment' or 'end'. Values are time
  * @param string 		$timezone 		The timezone of the $datetime param. Default 'gmt'.
  */
 public function update_dates($dates, $timezone = 'gmt')
 {
     global $wpdb;
     if (!is_array($dates)) {
         throw new InvalidArgumentException(__('Invalid format. First parameter needs to be an array.', 'woocommerce-subscriptions'));
     }
     if (empty($dates)) {
         throw new InvalidArgumentException(__('Invalid data. First parameter was empty when passed to update_dates().', 'woocommerce-subscriptions'));
     }
     $allowed_date_keys = array_keys(wcs_get_subscription_date_types());
     $passed_date_keys = array_keys($dates);
     $extra_keys = array_diff(str_replace('_date', '', $passed_date_keys), $allowed_date_keys);
     if (!empty($extra_keys)) {
         throw new InvalidArgumentException(__('Invalid data. First parameter has a date that is not in the registered date types.', 'woocommerce-subscriptions'));
     }
     $timestamps = array();
     foreach ($dates as $date_type => $datetime) {
         if (!empty($datetime) && false === wcs_is_datetime_mysql_format($datetime)) {
             // translators: placeholder is date type (e.g. "end", "next_payment"...)
             throw new InvalidArgumentException(sprintf(_x('Invalid %s date. The date must be of the format: "Y-m-d H:i:s".', 'appears in an error message if date is wrong format', 'woocommerce-subscriptions'), $date_type));
         }
         $date_type = str_replace('_date', '', $date_type);
         if (empty($datetime)) {
             $timestamps[$date_type] = 0;
         } else {
             if ('gmt' !== strtolower($timezone)) {
                 $datetime = get_gmt_from_date($datetime);
             }
             $timestamps[$date_type] = strtotime($datetime);
         }
     }
     foreach ($allowed_date_keys as $date_type) {
         if (!array_key_exists($date_type, $timestamps)) {
             $timestamps[$date_type] = $this->get_time($date_type);
         }
         if (0 == $timestamps[$date_type]) {
             // Last payment is not in the UI, and it should NOT be deleted as that would mess with scheduling
             if ('last_payment' != $date_type && 'start' != $date_type) {
                 $this->delete_date($date_type);
             }
             unset($timestamps[$date_type]);
             continue;
         }
     }
     $messages = array();
     // And then iterate over them. We need the two separate loops as we need a full array before we start checking
     // the relationships between them.
     foreach ($timestamps as $date_type => $datetime) {
         switch ($date_type) {
             case 'end':
                 if (array_key_exists('last_payment', $timestamps) && $datetime <= $timestamps['last_payment']) {
                     $messages[] = sprintf(__('The %s date must occur after the last payment date.', 'woocommerce-subscriptions'), $date_type);
                 }
                 if (array_key_exists('next_payment', $timestamps) && $datetime <= $timestamps['next_payment']) {
                     $messages[] = sprintf(__('The %s date must occur after the next payment date.', 'woocommerce-subscriptions'), $date_type);
                 }
             case 'next_payment':
                 // Guarantees that end is strictly after trial_end, because if next_payment and end can't be at same
                 // time
                 if (array_key_exists('trial_end', $timestamps) && $datetime < $timestamps['trial_end']) {
                     $messages[] = sprintf(__('The %s date must occur after the trial end date.', 'woocommerce-subscriptions'), $date_type);
                 }
             case 'trial_end':
                 if ($datetime <= $timestamps['start']) {
                     $messages[] = sprintf(__('The %s date must occur after the start date.', 'woocommerce-subscriptions'), $date_type);
                 }
         }
     }
     if (!empty($messages)) {
         throw new Exception(join(' ', $messages));
     }
     $is_updated = false;
     foreach ($timestamps as $date_type => $timestamp) {
         $datetime = date('Y-m-d H:i:s', $timestamp);
         if ($datetime == $this->get_date($date_type)) {
             continue;
         }
         switch ($date_type) {
             case 'next_payment':
             case 'trial_end':
             case 'end':
                 $is_updated = update_post_meta($this->id, wcs_get_date_meta_key($date_type), $datetime);
                 break;
             case 'start':
                 $wpdb->query($wpdb->prepare("UPDATE {$wpdb->posts} SET post_date = %s, post_date_gmt = %s WHERE ID = %s", get_date_from_gmt($datetime), $datetime, $this->id));
                 // Don't use wp_update_post() to avoid infinite loops here
                 $is_updated = true;
                 break;
             case 'last_payment':
                 $this->update_last_payment_date($datetime);
                 $is_updated = true;
                 break;
         }
         if ($is_updated) {
             $this->schedule->{$date_type} = $datetime;
             do_action('woocommerce_subscription_date_updated', $this, $date_type, $datetime);
         }
     }
 }