コード例 #1
0
 /**
  * Get PayPal Args for passing to PP
  *
  * Based on the HTML Variables documented here: https://developer.paypal.com/webapps/developer/docs/classic/paypal-payments-standard/integration-guide/Appx_websitestandard_htmlvariables/#id08A6HI00JQU
  *
  * @param WC_Order $order
  * @return array
  */
 public static function get_paypal_args($paypal_args, $order)
 {
     $is_payment_change = WC_Subscriptions_Change_Payment_Gateway::$is_request_to_change_payment;
     $order_contains_failed_renewal = false;
     // Payment method changes act on the subscription not the original order
     if ($is_payment_change) {
         $subscriptions = array(wcs_get_subscription($order->id));
         $subscription = array_pop($subscriptions);
         $order = $subscription->order;
         // We need the subscription's total
         remove_filter('woocommerce_order_amount_total', 'WC_Subscriptions_Change_Payment_Gateway::maybe_zero_total', 11, 2);
     } else {
         // Otherwise the order is the $order
         if ($cart_item = wcs_cart_contains_failed_renewal_order_payment() || false !== WC_Subscriptions_Renewal_Order::get_failed_order_replaced_by($order->id)) {
             $subscriptions = wcs_get_subscriptions_for_renewal_order($order);
             $order_contains_failed_renewal = true;
         } else {
             $subscriptions = wcs_get_subscriptions_for_order($order);
         }
         // Only one subscription allowed per order with PayPal
         $subscription = array_pop($subscriptions);
     }
     if ($order_contains_failed_renewal || !empty($subscription) && $subscription->get_total() > 0 && 'yes' !== get_option(WC_Subscriptions_Admin::$option_prefix . '_turn_off_automatic_payments', 'no')) {
         // It's a subscription
         $paypal_args['cmd'] = '_xclick-subscriptions';
         // Store the subscription ID in the args sent to PayPal so we can access them later
         $paypal_args['custom'] = wcs_json_encode(array('order_id' => $order->id, 'order_key' => $order->order_key, 'subscription_id' => $subscription->id, 'subscription_key' => $subscription->order_key));
         foreach ($subscription->get_items() as $item) {
             if ($item['qty'] > 1) {
                 $item_names[] = $item['qty'] . ' x ' . wcs_get_paypal_item_name($item['name']);
             } elseif ($item['qty'] > 0) {
                 $item_names[] = wcs_get_paypal_item_name($item['name']);
             }
         }
         // translators: 1$: subscription ID, 2$: order ID, 3$: names of items, comma separated
         $paypal_args['item_name'] = wcs_get_paypal_item_name(sprintf(_x('Subscription %1$s (Order %2$s) - %3$s', 'item name sent to paypal', 'woocommerce-subscriptions'), $subscription->get_order_number(), $order->get_order_number(), implode(', ', $item_names)));
         $unconverted_periods = array('billing_period' => $subscription->billing_period, 'trial_period' => $subscription->trial_period);
         $converted_periods = array();
         // Convert period strings into PayPay's format
         foreach ($unconverted_periods as $key => $period) {
             switch (strtolower($period)) {
                 case 'day':
                     $converted_periods[$key] = 'D';
                     break;
                 case 'week':
                     $converted_periods[$key] = 'W';
                     break;
                 case 'year':
                     $converted_periods[$key] = 'Y';
                     break;
                 case 'month':
                 default:
                     $converted_periods[$key] = 'M';
                     break;
             }
         }
         $price_per_period = $subscription->get_total();
         $subscription_interval = $subscription->billing_interval;
         $start_timestamp = $subscription->get_time('start');
         $trial_end_timestamp = $subscription->get_time('trial_end');
         $next_payment_timestamp = $subscription->get_time('next_payment');
         $is_synced_subscription = WC_Subscriptions_Synchroniser::subscription_contains_synced_product($subscription->id);
         if ($is_synced_subscription) {
             $length_from_timestamp = $next_payment_timestamp;
         } elseif ($trial_end_timestamp > 0) {
             $length_from_timestamp = $trial_end_timestamp;
         } else {
             $length_from_timestamp = $start_timestamp;
         }
         $subscription_length = wcs_estimate_periods_between($length_from_timestamp, $subscription->get_time('end'), $subscription->billing_period);
         $subscription_installments = $subscription_length / $subscription_interval;
         $initial_payment = $is_payment_change ? 0 : $order->get_total();
         if ($order_contains_failed_renewal || $is_payment_change) {
             if ($is_payment_change) {
                 // Add a nonce to the order ID to avoid "This invoice has already been paid" error when changing payment method to PayPal when it was previously PayPal
                 $suffix = '-wcscpm-' . wp_create_nonce();
             } else {
                 // Failed renewal order, append a descriptor and renewal order's ID
                 $suffix = '-wcsfrp-' . $order->id;
             }
             // Change the 'invoice' and the 'custom' values to be for the original order (if there is one)
             if (false === $subscription->order) {
                 // No original order so we need to use the subscriptions values instead
                 $order_number = ltrim($subscription->get_order_number(), _x('#', 'hash before the order number. Used as a character to remove from the actual order number', 'woocommerce-subscriptions')) . '-subscription';
                 $order_id_key = array('order_id' => $subscription->id, 'order_key' => $subscription->order_key);
             } else {
                 $order_number = ltrim($subscription->order->get_order_number(), _x('#', 'hash before the order number. Used as a character to remove from the actual order number', 'woocommerce-subscriptions'));
                 $order_id_key = array('order_id' => $subscription->order->id, 'order_key' => $subscription->order->order_key);
             }
             $order_details = false !== $subscription->order ? $subscription->order : $subscription;
             // Set the invoice details to the original order's invoice but also append a special string and this renewal orders ID so that we can match it up as a failed renewal order payment later
             $paypal_args['invoice'] = WCS_PayPal::get_option('invoice_prefix') . $order_number . $suffix;
             $paypal_args['custom'] = wcs_json_encode(array_merge($order_id_key, array('subscription_id' => $subscription->id, 'subscription_key' => $subscription->order_key)));
         }
         if ($order_contains_failed_renewal) {
             $subscription_trial_length = 0;
             $subscription_installments = max($subscription_installments - $subscription->get_completed_payment_count(), 0);
             // If we're changing the payment date or switching subs, we need to set the trial period to the next payment date & installments to be the number of installments left
         } elseif ($is_payment_change || $is_synced_subscription) {
             $next_payment_timestamp = $subscription->get_time('next_payment');
             // When the subscription is on hold
             if (false != $next_payment_timestamp && !empty($next_payment_timestamp)) {
                 $trial_until = wcs_calculate_paypal_trial_periods_until($next_payment_timestamp);
                 $subscription_trial_length = $trial_until['first_trial_length'];
                 $converted_periods['trial_period'] = $trial_until['first_trial_period'];
                 $second_trial_length = $trial_until['second_trial_length'];
                 $second_trial_period = $trial_until['second_trial_period'];
             } else {
                 $subscription_trial_length = 0;
             }
             // If this is a payment change, we need to account for completed payments on the number of installments owing
             if ($is_payment_change && $subscription_length > 0) {
                 $subscription_installments = max($subscription_installments - $subscription->get_completed_payment_count(), 0);
             }
         } else {
             $subscription_trial_length = wcs_estimate_periods_between($start_timestamp, $trial_end_timestamp, $subscription->trial_period);
         }
         if ($subscription_trial_length > 0) {
             // Specify a free trial period
             $paypal_args['a1'] = $initial_payment > 0 ? $initial_payment : 0;
             // Trial period length
             $paypal_args['p1'] = $subscription_trial_length;
             // Trial period
             $paypal_args['t1'] = $converted_periods['trial_period'];
             // We need to use a second trial period before we have more than 90 days until the next payment
             if (isset($second_trial_length) && $second_trial_length > 0) {
                 $paypal_args['a2'] = 0.01;
                 // Alas, although it's undocumented, PayPal appears to require a non-zero value in order to allow a second trial period
                 $paypal_args['p2'] = $second_trial_length;
                 $paypal_args['t2'] = $second_trial_period;
             }
         } elseif ($initial_payment != $price_per_period) {
             // No trial period, but initial amount includes a sign-up fee and/or other items, so charge it as a separate period
             if (1 == $subscription_installments) {
                 $param_number = 3;
             } else {
                 $param_number = 1;
             }
             $paypal_args['a' . $param_number] = $initial_payment;
             // Sign Up interval
             $paypal_args['p' . $param_number] = $subscription_interval;
             // Sign Up unit of duration
             $paypal_args['t' . $param_number] = $converted_periods['billing_period'];
         }
         // We have a recurring payment
         if (!isset($param_number) || 1 == $param_number) {
             // Subscription price
             $paypal_args['a3'] = $price_per_period;
             // Subscription duration
             $paypal_args['p3'] = $subscription_interval;
             // Subscription period
             $paypal_args['t3'] = $converted_periods['billing_period'];
         }
         // Recurring payments
         if (1 == $subscription_installments || $initial_payment != $price_per_period && 0 == $subscription_trial_length && 2 == $subscription_installments) {
             // Non-recurring payments
             $paypal_args['src'] = 0;
         } else {
             $paypal_args['src'] = 1;
             if ($subscription_installments > 0) {
                 // An initial period is being used to charge a sign-up fee
                 if ($initial_payment != $price_per_period && 0 == $subscription_trial_length) {
                     $subscription_installments--;
                 }
                 $paypal_args['srt'] = $subscription_installments;
             }
         }
         // Don't reattempt failed payments, instead let Subscriptions handle the failed payment
         $paypal_args['sra'] = 0;
         // Force return URL so that order description & instructions display
         $paypal_args['rm'] = 2;
         // Reattach the filter we removed earlier
         if ($is_payment_change) {
             add_filter('woocommerce_order_amount_total', 'WC_Subscriptions_Change_Payment_Gateway::maybe_zero_total', 11, 2);
         }
     }
     return $paypal_args;
 }
コード例 #2
0
 /**
  * Records the initial payment against a subscription.
  *
  * This function is called when an orders status is changed to completed or processing
  * for those gateways which never call @see WC_Order::payment_complete(), like the core
  * WooCommerce Cheque and Bank Transfer gateways.
  *
  * It will also set the start date on the subscription to the time the payment is completed.
  *
  * @param $order_id int|WC_Order
  * @param $old_order_status
  * @param $new_order_status
  * @since 2.0
  */
 public static function maybe_record_subscription_payment($order_id, $old_order_status, $new_order_status)
 {
     if (wcs_order_contains_subscription($order_id)) {
         $subscriptions = wcs_get_subscriptions_for_order($order_id);
         $was_activated = false;
         $order_completed = in_array($new_order_status, array(apply_filters('woocommerce_payment_complete_order_status', 'processing', $order_id), 'processing', 'completed')) && in_array($old_order_status, apply_filters('woocommerce_valid_order_statuses_for_payment', array('pending', 'on-hold', 'failed')));
         foreach ($subscriptions as $subscription) {
             // Do we need to activate a subscription?
             if ($order_completed && !$subscription->has_status(wcs_get_subscription_ended_statuses()) && !$subscription->has_status('active')) {
                 $new_start_date_offset = current_time('timestamp', true) - $subscription->get_time('start');
                 $dates = array('start' => current_time('mysql', true));
                 if (0 != $subscription->get_time('trial_end')) {
                     $dates['trial_end'] = gmdate('Y-m-d H:i:s', $subscription->get_time('trial_end') + $new_start_date_offset);
                 }
                 if (0 != $subscription->get_time('next_payment')) {
                     if (WC_Subscriptions_Synchroniser::subscription_contains_synced_product($subscription)) {
                         $prior_date = isset($dates['trial_end']) ? $dates['trial_end'] : $dates['start'];
                         if ($subscription->get_time('next_payment') < strtotime($prior_date)) {
                             foreach ($subscription->get_items() as $item) {
                                 $product_id = wcs_get_canonical_product_id($item);
                                 if (WC_Subscriptions_Synchroniser::is_product_synced($product_id)) {
                                     $dates['next_payment'] = WC_Subscriptions_Synchroniser::calculate_first_payment_date($product_id);
                                     break;
                                 }
                             }
                         }
                     } else {
                         $dates['next_payment'] = gmdate('Y-m-d H:i:s', $subscription->get_time('next_payment') + $new_start_date_offset);
                     }
                 }
                 if (0 != $subscription->get_time('end')) {
                     $dates['end'] = gmdate('Y-m-d H:i:s', $subscription->get_time('end') + $new_start_date_offset);
                 }
                 $subscription->update_dates($dates);
                 $subscription->payment_complete();
                 $was_activated = true;
             } elseif ('failed' == $new_order_status) {
                 $subscription->payment_failed();
             }
         }
         if ($was_activated) {
             do_action('subscriptions_activated_for_order', $order_id);
         }
     }
 }
コード例 #3
0
 /**
  *  Determine if the subscription is for one payment only.
  *
  * @return bool whether the subscription is for only one payment
  * @since 2.0.17
  */
 public function is_one_payment()
 {
     $is_one_payment = false;
     if (0 != ($end_time = $this->get_time('end'))) {
         $from_timestamp = $this->get_time('start');
         if (0 != $this->get_time('trial_end') || WC_Subscriptions_Synchroniser::subscription_contains_synced_product($this)) {
             $subscription_order_count = count($this->get_related_orders());
             // when we have a sync'd subscription before its 1st payment, we need to base the calculations for the next payment on the first/next payment timestamp.
             if ($subscription_order_count < 2 && 0 != ($next_payment_timestamp = $this->get_time('next_payment'))) {
                 $from_timestamp = $next_payment_timestamp;
                 // when we have a sync'd subscription after its 1st payment, we need to base the calculations for the next payment on the last payment timestamp.
             } else {
                 if (!($subscription_order_count > 2) && 0 != ($last_payment_timestamp = $this->get_time('last_payment'))) {
                     $from_timestamp = $last_payment_timestamp;
                 }
             }
         }
         $next_payment_timestamp = wcs_add_time($this->billing_interval, $this->billing_period, $from_timestamp);
         if ($next_payment_timestamp + DAY_IN_SECONDS - 1 > $end_time) {
             $is_one_payment = true;
         }
     }
     return apply_filters('woocommerce_subscription_is_one_payment', $is_one_payment, $this);
 }
コード例 #4
0
 /**
  * Set the subscription prices to be used in calculating totals by @see WC_Subscriptions_Cart::calculate_subscription_totals()
  *
  * @since 2.0
  */
 public static function calculate_prorated_totals($cart)
 {
     if (false === self::cart_contains_switches()) {
         return;
     }
     // Maybe charge an initial amount to account for upgrading from a cheaper subscription
     $apportion_recurring_price = get_option(WC_Subscriptions_Admin::$option_prefix . '_apportion_recurring_price', 'no');
     $apportion_sign_up_fee = get_option(WC_Subscriptions_Admin::$option_prefix . '_apportion_sign_up_fee', 'no');
     $apportion_length = get_option(WC_Subscriptions_Admin::$option_prefix . '_apportion_length', 'no');
     foreach (WC()->cart->get_cart() as $cart_item_key => $cart_item) {
         if (!isset($cart_item['subscription_switch']['subscription_id'])) {
             continue;
         }
         $subscription = wcs_get_subscription($cart_item['subscription_switch']['subscription_id']);
         $existing_item = wcs_get_order_item($cart_item['subscription_switch']['item_id'], $subscription);
         if (empty($existing_item)) {
             WC()->cart->remove_cart_item($cart_item_key);
             continue;
         }
         $item_data = $cart_item['data'];
         $product_id = wcs_get_canonical_product_id($cart_item);
         $product = wc_get_product($product_id);
         $is_virtual_product = $product->is_virtual();
         // Set when the first payment and end date for the new subscription should occur
         WC()->cart->cart_contents[$cart_item_key]['subscription_switch']['first_payment_timestamp'] = $cart_item['subscription_switch']['next_payment_timestamp'];
         WC()->cart->cart_contents[$cart_item_key]['subscription_switch']['end_timestamp'] = $end_timestamp = strtotime(WC_Subscriptions_Product::get_expiration_date($product_id, $subscription->get_date('last_payment')));
         // Add any extra sign up fees required to switch to the new subscription
         if ('yes' == $apportion_sign_up_fee) {
             // Because product add-ons etc. don't apply to sign-up fees, it's safe to use the product's sign-up fee value rather than the cart item's
             $sign_up_fee_due = $product->subscription_sign_up_fee;
             $sign_up_fee_paid = $subscription->get_items_sign_up_fee($existing_item, 'inclusive_of_tax');
             // Make sure total prorated sign-up fee is prorated across total amount of sign-up fee so that customer doesn't get extra discounts
             if ($cart_item['quantity'] > $existing_item['qty']) {
                 $sign_up_fee_paid = $sign_up_fee_paid * $existing_item['qty'] / $cart_item['quantity'];
             }
             WC()->cart->cart_contents[$cart_item_key]['data']->subscription_sign_up_fee = max($sign_up_fee_due - $sign_up_fee_paid, 0);
             WC()->cart->cart_contents[$cart_item_key]['data']->subscription_sign_up_fee_prorated = WC()->cart->cart_contents[$cart_item_key]['data']->subscription_sign_up_fee;
         } elseif ('no' == $apportion_sign_up_fee) {
             // $0 the initial sign-up fee
             WC()->cart->cart_contents[$cart_item_key]['data']->subscription_sign_up_fee = 0;
         }
         // Get the current subscription's last payment date
         $last_payment_timestamp = $subscription->get_time('last_payment');
         $days_since_last_payment = floor((gmdate('U') - $last_payment_timestamp) / (60 * 60 * 24));
         // Get the current subscription's next payment date
         $next_payment_timestamp = $cart_item['subscription_switch']['next_payment_timestamp'];
         $days_until_next_payment = ceil(($next_payment_timestamp - gmdate('U')) / (60 * 60 * 24));
         // If the subscription contains a synced product and the next payment is actually the first payment, determine the days in the "old" cycle from the subscription object
         if (WC_Subscriptions_Synchroniser::subscription_contains_synced_product($subscription->id) && WC_Subscriptions_Synchroniser::calculate_first_payment_date($product, 'timestamp', $subscription->get_date('start')) == $next_payment_timestamp) {
             $days_in_old_cycle = wcs_get_days_in_cycle($subscription->billing_period, $subscription->billing_interval);
         } else {
             // Find the number of days between the two
             $days_in_old_cycle = $days_until_next_payment + $days_since_last_payment;
         }
         // Find the actual recurring amount charged for the old subscription (we need to use the '_recurring_line_total' meta here rather than '_subscription_recurring_amount' because we want the recurring amount to include extra from extensions, like Product Add-ons etc.)
         $old_recurring_total = $existing_item['line_total'];
         if ('yes' == $subscription->prices_include_tax || true === $subscription->prices_include_tax) {
             // WC_Abstract_Order::$prices_include_tax can be set to true in __construct() or to 'yes' in populate()
             $old_recurring_total += $existing_item['line_tax'];
         }
         // Find the $price per day for the old subscription's recurring total
         $old_price_per_day = $old_recurring_total / $days_in_old_cycle;
         // Find the price per day for the new subscription's recurring total
         // If the subscription uses the same billing interval & cycle as the old subscription,
         if ($item_data->subscription_period == $subscription->billing_period && $item_data->subscription_period_interval == $subscription->billing_interval) {
             $days_in_new_cycle = $days_in_old_cycle;
             // Use $days_in_old_cycle to make sure they're consistent
         } else {
             // We need to figure out the price per day for the new subscription based on its billing schedule
             $days_in_new_cycle = wcs_get_days_in_cycle($item_data->subscription_period, $item_data->subscription_period_interval);
         }
         // We need to use the cart items price to ensure we include extras added by extensions like Product Add-ons
         $new_price_per_day = $item_data->price * $cart_item['quantity'] / $days_in_new_cycle;
         if ($old_price_per_day < $new_price_per_day) {
             WC()->cart->cart_contents[$cart_item_key]['subscription_switch']['upgraded_or_downgraded'] = 'upgraded';
         } elseif ($old_price_per_day > $new_price_per_day && $new_price_per_day >= 0) {
             WC()->cart->cart_contents[$cart_item_key]['subscription_switch']['upgraded_or_downgraded'] = 'downgraded';
         }
         // Now lets see if we should add a prorated amount to the sign-up fee (for upgrades) or extend the next payment date (for downgrades)
         if (in_array($apportion_recurring_price, array('yes', 'yes-upgrade')) || in_array($apportion_recurring_price, array('virtual', 'virtual-upgrade')) && $is_virtual_product) {
             // If the customer is upgrading, we may need to add a gap payment to the sign-up fee or to reduce the pre-paid period (or both)
             if ($old_price_per_day < $new_price_per_day) {
                 // The new subscription may be more expensive, but it's also on a shorter billing cycle, so reduce the next pre-paid term
                 if ($days_in_old_cycle > $days_in_new_cycle) {
                     // Find out how many days at the new price per day the customer would receive for the total amount already paid
                     // (e.g. if the customer paid $10 / month previously, and was switching to a $5 / week subscription, she has pre-paid 14 days at the new price)
                     $pre_paid_days = 0;
                     do {
                         $pre_paid_days++;
                         $new_total_paid = $pre_paid_days * $new_price_per_day;
                     } while ($new_total_paid < $old_recurring_total);
                     // If the total amount the customer has paid entitles her to more days at the new price than she has received, there is no gap payment, just shorten the pre-paid term the appropriate number of days
                     if ($days_since_last_payment < $pre_paid_days) {
                         WC()->cart->cart_contents[$cart_item_key]['subscription_switch']['first_payment_timestamp'] = $last_payment_timestamp + $pre_paid_days * 60 * 60 * 24;
                         // If the total amount the customer has paid entitles her to the same or less days at the new price then start the new subscription from today
                     } else {
                         WC()->cart->cart_contents[$cart_item_key]['subscription_switch']['first_payment_timestamp'] = 0;
                     }
                 } else {
                     $extra_to_pay = $days_until_next_payment * ($new_price_per_day - $old_price_per_day);
                     // when calculating a subscription with one length (no more next payment date and the end date may have been pushed back) we need to pay for those extra days at the new price per day between the old next payment date and new end date
                     if (1 == $item_data->subscription_length) {
                         $days_to_new_end = floor(($end_timestamp - $next_payment_timestamp) / (60 * 60 * 24));
                         if ($days_to_new_end > 0) {
                             $extra_to_pay += $days_to_new_end * $new_price_per_day;
                         }
                     }
                     // We need to find the per item extra to pay so we can set it as the sign-up fee (WC will then multiply it by the quantity)
                     $extra_to_pay = $extra_to_pay / $cart_item['quantity'];
                     // Keep a record of the two separate amounts so we store these and calculate future switch amounts correctly
                     WC()->cart->cart_contents[$cart_item_key]['data']->subscription_sign_up_fee_prorated = WC()->cart->cart_contents[$cart_item_key]['data']->subscription_sign_up_fee;
                     WC()->cart->cart_contents[$cart_item_key]['data']->subscription_price_prorated = round($extra_to_pay, 2);
                     WC()->cart->cart_contents[$cart_item_key]['data']->subscription_sign_up_fee += round($extra_to_pay, 2);
                 }
                 // If the customer is downgrading, set the next payment date and maybe extend it if downgrades are prorated
             } elseif ($old_price_per_day > $new_price_per_day && $new_price_per_day > 0) {
                 $old_total_paid = $old_price_per_day * $days_until_next_payment;
                 $new_total_paid = $new_price_per_day;
                 // if downgrades are apportioned, extend the next payment date for n more days
                 if (in_array($apportion_recurring_price, array('virtual', 'yes'))) {
                     // Find how many more days at the new lower price it takes to exceed the amount already paid
                     for ($days_to_add = 0; $new_total_paid <= $old_total_paid; $days_to_add++) {
                         $new_total_paid = $days_to_add * $new_price_per_day;
                     }
                     $days_to_add -= $days_until_next_payment;
                 } else {
                     $days_to_add = 0;
                 }
                 WC()->cart->cart_contents[$cart_item_key]['subscription_switch']['first_payment_timestamp'] = $next_payment_timestamp + $days_to_add * 60 * 60 * 24;
             }
             // The old price per day == the new price per day, no need to change anything
             if (WC()->cart->cart_contents[$cart_item_key]['subscription_switch']['first_payment_timestamp'] != $cart_item['subscription_switch']['next_payment_timestamp']) {
                 WC()->cart->cart_contents[$cart_item_key]['subscription_switch']['recurring_payment_prorated'] = true;
             }
         }
         // Finally, if we need to make sure the initial total doesn't include any recurring amount, we can by spoofing a free trial
         if (0 != WC()->cart->cart_contents[$cart_item_key]['subscription_switch']['first_payment_timestamp']) {
             WC()->cart->cart_contents[$cart_item_key]['data']->subscription_trial_length = 1;
         }
         if ('yes' == $apportion_length || 'virtual' == $apportion_length && $is_virtual_product) {
             $base_length = WC_Subscriptions_Product::get_length($product_id);
             $completed_payments = $subscription->get_completed_payment_count();
             $length_remaining = $base_length - $completed_payments;
             // Default to the base length if more payments have already been made than this subscription requires
             if ($length_remaining <= 0) {
                 $length_remaining = $base_length;
             }
             WC()->cart->cart_contents[$cart_item_key]['data']->subscription_length = $length_remaining;
         }
     }
 }
コード例 #5
0
 /**
  * Records the initial payment against a subscription.
  *
  * This function is called when an orders status is changed to completed or processing
  * for those gateways which never call @see WC_Order::payment_complete(), like the core
  * WooCommerce Cheque and Bank Transfer gateways.
  *
  * It will also set the start date on the subscription to the time the payment is completed.
  *
  * @param $order_id int|WC_Order
  * @param $old_order_status
  * @param $new_order_status
  * @since 2.0
  */
 public static function maybe_record_subscription_payment($order_id, $old_order_status, $new_order_status)
 {
     if (wcs_order_contains_subscription($order_id)) {
         $subscriptions = wcs_get_subscriptions_for_order($order_id);
         $was_activated = false;
         $order_completed = in_array($new_order_status, array(apply_filters('woocommerce_payment_complete_order_status', 'processing', $order_id), 'processing', 'completed')) && in_array($old_order_status, apply_filters('woocommerce_valid_order_statuses_for_payment', array('pending', 'on-hold', 'failed')));
         foreach ($subscriptions as $subscription) {
             // Do we need to activate a subscription?
             if ($order_completed && !$subscription->has_status(wcs_get_subscription_ended_statuses()) && !$subscription->has_status('active')) {
                 $new_start_date_offset = current_time('timestamp', true) - $subscription->get_time('start');
                 // if the payment has been processed more than an hour after the order was first created, let's update the dates on the subscription to account for that, because it may have even been processed days after it was first placed
                 if ($new_start_date_offset > HOUR_IN_SECONDS) {
                     $dates = array('start' => current_time('mysql', true));
                     if (WC_Subscriptions_Synchroniser::subscription_contains_synced_product($subscription)) {
                         $trial_end = $subscription->get_time('trial_end');
                         $next_payment = $subscription->get_time('next_payment');
                         // if either there is a free trial date or a next payment date that falls before now, we need to recalculate all the sync'd dates
                         if ($trial_end > 0 && $trial_end < strtotime($dates['start']) || $next_payment > 0 && $next_payment < strtotime($dates['start'])) {
                             foreach ($subscription->get_items() as $item) {
                                 $product_id = wcs_get_canonical_product_id($item);
                                 if (WC_Subscriptions_Synchroniser::is_product_synced($product_id)) {
                                     $dates['trial_end'] = WC_Subscriptions_Product::get_trial_expiration_date($product_id, $dates['start']);
                                     $dates['next_payment'] = WC_Subscriptions_Synchroniser::calculate_first_payment_date($product_id, 'mysql', $dates['start']);
                                     $dates['end'] = WC_Subscriptions_Product::get_expiration_date($product_id, $dates['start']);
                                     break;
                                 }
                             }
                         }
                     } else {
                         // No sync'ing to mess about with, just add the offset to the existing dates
                         foreach (array('trial_end', 'next_payment', 'end') as $date_type) {
                             if (0 != $subscription->get_time($date_type)) {
                                 $dates[$date_type] = gmdate('Y-m-d H:i:s', $subscription->get_time($date_type) + $new_start_date_offset);
                             }
                         }
                     }
                     $subscription->update_dates($dates);
                 }
                 $subscription->payment_complete();
                 $was_activated = true;
             } elseif ('failed' == $new_order_status) {
                 $subscription->payment_failed();
             }
         }
         if ($was_activated) {
             do_action('subscriptions_activated_for_order', $order_id);
         }
     }
 }