diff --git a/assets/css/admin.css b/assets/css/admin.css
index 1138bd7..2243186 100644
--- a/assets/css/admin.css
+++ b/assets/css/admin.css
@@ -192,6 +192,11 @@ a.close-subscriptions-search {
margin: 0;
}
+.subscription_pricing ._subscription_price_field input[type=text],
+.subscription_pricing ._subscription_trial_length_field input[type=text] {
+ padding: 4px;
+}
+
#woocommerce-product-data .wc-metaboxes-wrapper .wc-metabox table td p._subscription_trial_period_field select {
margin-left: 5px;
}
@@ -228,6 +233,14 @@ a.close-subscriptions-search {
height: 31px;
}
+.variable_subscription_trial .form-row input[type=text],
+.variable_subscription_pricing .form-row input[type=text],
+.variable_subscription_trial .form-row select,
+.variable_subscription_pricing .form-row select {
+ margin: 2px 0 0;
+ padding: 6px;
+}
+
/* Variation Pricing Fields in WooCommerce 2.3+ */
.variable_subscription_pricing_2_3 .wc_input_subscription_price,
.variable_subscription_pricing_2_3 .wc_input_subscription_period_interval {
diff --git a/assets/js/admin/admin.js b/assets/js/admin/admin.js
index e3b56b8..6727f13 100644
--- a/assets/js/admin/admin.js
+++ b/assets/js/admin/admin.js
@@ -356,6 +356,16 @@ jQuery(document).ready(function($){
$( '#_subscription_one_time_shipping' ).prop( 'disabled', is_synced_or_has_trial );
},
+ showHideSubscriptionsPanels: function() {
+ var tab = $( 'div.panel-wrap' ).find( 'ul.wc-tabs li' ).eq( 0 ).find( 'a' );
+ var panel = tab.attr( 'href' );
+ var visible = $( panel ).children( '.options_group' ).filter( function() {
+ return 'none' != $( this ).css( 'display' );
+ });
+ if ( 0 != visible.length ) {
+ tab.click().parent().show();
+ }
+ },
});
$('.options_group.pricing ._sale_price_field .description').prepend(' ');
@@ -363,7 +373,6 @@ jQuery(document).ready(function($){
// Move the subscription pricing section to the same location as the normal pricing section
$('.options_group.subscription_pricing').not('.variable_subscription_pricing .options_group.subscription_pricing').insertBefore($('.options_group.pricing:first'));
$('.show_if_subscription.clear').insertAfter($('.options_group.subscription_pricing'));
- $( '.show_if_variable' ).addClass( 'show_if_variable-subscription' );
// Move the subscription variation pricing section to a better location in the DOM on load
if($('#variable_product_options .variable_subscription_pricing').length > 0) {
@@ -385,6 +394,7 @@ jQuery(document).ready(function($){
$.setTrialPeriods();
$.showHideSyncOptions();
$.disableEnableOneTimeShipping();
+ $.showHideSubscriptionsPanels();
}
// Update subscription ranges when subscription period or interval is changed
@@ -404,6 +414,7 @@ jQuery(document).ready(function($){
$.showHideSubscriptionMeta();
$.showHideVariableSubscriptionMeta();
$.showHideSyncOptions();
+ $.showHideSubscriptionsPanels();
});
$('input#_downloadable, input#_virtual').change(function(){
diff --git a/changelog.txt b/changelog.txt
index 9e8143a..cc2629d 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -1,9 +1,63 @@
*** WooCommerce Subscriptions Changelog ***
+2017.05.26 - version 2.2.7
+* Tweak: Integrate with My Account > Payment Methods actions to make sure when a customer deletes a payment method, subscriptions using that payment method are automatically updated to use a different token. Or if there are no other payment methods on the customer's account, a customer can't delete the payment method used for automatic payments. PR#1866
+* Tweak: Do not display 'Free' on free shipping methods to improve compatibility with the approached used in WooCommerce 2.6 and newer. PR#1766
+* Tweak: Call 'wcs_get_retry_rule_raw' even if no rule is defined so that it can be used to add additional rules beyond default rule set. PR#2138
+* Tweak: Do not get products for non product post IDs to save performance overhead. PR#2034
+* Fix: WooCommerce 3.0: Repair pending cancelled subscriptions that have not got a scheduled action or been correctly transitioned to cancelled as part of the 2.2.7 upgrade process. PR#2129
+* Fix: WooCommerce 3.0: Fixes infinite loops when using a coupon for manual renewal via cart by storing coupon properties rather than full coupon objects in the session. PR#2116
+* Fix: WooCommerce 3.0: Fix updating a subscription address after paying manual renewal that uses a different address to avoid addresses being incorrectly deleted and not udpated. PR#2145
+* Fix: Register subscription report scripts on top level Reports admin screens for uses without store manage capability, but with capability to view reports. PR#2153
+* Fix: Make sure login is required when purchasing subscriptions products and "Registration on checkout" is disabled. PR#1822
+* Fix: Use more than 'ipn_track_id' for different PayPal IPN messages now that PayPal has suddently begun to use the same 'ipn_track_id' for different IPN messages. PR#2090
+* Fix: Trigger order.created webhook when creating renewal orders by Subscriptions with WC < 3.0. PR#1793
+* Fix: Lookup one time shipping setting value on the parent product on variations rather than on the variations to make sure we find the correct value. PR#2125
+* Fix: Do not cancel subscriptions that are not using PayPal as the payment method when a PayPal Billing Agreement is cancelled (including subscriptions using manual renewal). PR#2127
+* Fix: Allow single payment subscriptions to be cancelled by customer if that single payment is still in future (because there was a free trial). PR#2060
+* Fix: Avoid errors by making sure deleted subscriptions are not returned in set of switched and users subscriptions by respective functions for getting those subscriptions. PR#2075 and PR#2131
+* Fix: Change Edit Subscriptions admin screen "This order is no longer editable" text to avoid confusion. PR#2128
+* Fix: Do not ignore variations with empty prices during min/max calculations to improve compatibility with 3rd party plugins, like Name Your Price. PR#2120
+* Fix: Check status can be changed on payment failure to avoid throwing exceptions/errors when the status can't be changed, which is also almost always signifies that the status doesn't need to be changed to reflect the failure. PR#2140
+* Fix: Make it is possible to change payment method to PayPal Standard for subscriptions imported or manually added (meaning they do not have a parent order). PR#1956
+* Fix: Restore previous v3 customer/{id}/subscriptions response structure to REST API. PR#2150
+* Fix: Add support for filtering subscriptions by customer ID in v3 legacy REST API. PR#2144
+* Fix: Show General tab for variable subscription product edit page on page load. PR#2044
+* Fix: Delete the retry date after subscription status changed. PR#2143
+
+2017.05.05 - version 2.2.6
+* Fix: WooCommerce 3.0: Guard against infinite loops caused by 3rd party code calling order methods with a post object by making sure we have an order object, not just an object. PR#2100
+* Fix: WooCommerce 3.0: Improve compatibility with 3rd party code that adds custom address fields by making sure we check address method is callable before calling it, and falling back to meta data if that method doesn't exist. PR#2102
+* Fix: WooCommerce 3.0: Set correct line item meta names when paying for a renewal or resubscribe order by not relying on WooCommerce 3.0's array access implementation. PR#2105
+* Fix: WooCommerce 3.0: Assorted issues with dates not being scheduled and/or not being set at the correct time when running WooCommerce 3.0 by making sure we only save date properties after updating or deleting dates rather than also saving status transitions. PR#2091
+* Fix: WooCommerce 3.0: Match WooCommerce 3.0 approach to adding variation attributes to cart/order line item name by making sure we use the line item name rather than product title to alter renewal cart item names. PR#2109
+* Fix: WooCommerce 3.0: Match WooCommerce 3.0 filters for sign-up fee prices. Fixes compatibility with sign-up fees and Memberships. PR#2110
+* Fix: Make sure a variable product is purchasable even if the variations tab wasn't loaded before saving the product, and make sure we sync the variable product's prices even if the variation's tabe wasn't loaded. PR#2111
+* Fix: Make sure subscription is seen as having a zero total when customer completes the Change Payment Method flow to avoid charging anything at the time of changing payment method. PR#2082
+* Fix: Fix resubscribing to products for 1 payment cuased by inconsistent variable type being returned by WC_Subscription::get_billing_interval(). PR#2098
+* Fix: "Trying to get property of non-object" notice by checking order_type property exists before trying to access it. PR#2099
+* Fix: Correct subscription variation field alignmenton WooCommerce > Edit Product screen. PR#2104
+* Tweak: Improve upgrade logging by recording version at upgrade and not deleting logs after 6 weeks so we can trace time and day of upgrades in support and better diagnose issues. PR#2070
+* Tweak: Adds more flexibility to next payment date recalculation on activation of a subscription with new 'woocommerce_subscription_activation_next_payment_date_threshold' hook. PR#1860
+* Tweak: Improve when variations are saved by moving them to save on the 'woocommerce_save_product_variation' hook. PR#2111
+* Tweak: Update Action Scheduler to v1.5.3. PR#2113
+
+2017.04.21 - version 2.2.5
+* Fix: WooCommerce 3.0: Only call get_id() if product is an object and has not been deleted. PR#2071
+* Fix: WooCommerce 3.0: Do not attempt to access WC()->payment_gateways->payment_gateways() when reading the subscription object and setting the payment method, because there is no guarantee WC() will be setup yet (and we only need to do it when the method is called not when it is being instantiated. PR#2084
+* Fix: WooCommerce 3.0: Do not set payment method title in set payment method function when instantiating subscription (i.e. prior to it being read). PR#2084
+* Fix: WooCommerce 3.0: Calculate the next payment date using either last order paid date or the last order date, whichever is the later, to maintain backward compatiblity with versions prior to WooCommerce 3.0 support. PR#2087
+* Fix: WooCommerce 3.0: Replace use of deprecated WooCommerce function removed in WC 3.0. PR#2088
+* Fix: PHP 7.1 notices with products with empty signup fees and other properties after WooCommerce 3.0 changes for getting meta on products. PR#2064
+* Fix: Allow dynamically setting properties on a subscription via WC_Subscription::__set() for backwards compatibility. PR#2079
+* Fix: Do not validate admin payment method changes which will not result in a change to allow for changing meta on a payment method that is not active. PR#2085
+* Tweak: Add wc_input_price class to subscription product price inputs to make sure they use decimal separator and other store settings for validation. PR#2068
+
2017.04.12 - version 2.2.4
* Fix: WooCommerce 3.0: Correctly handle switching between grouped products in WooCommerce 3.0 where a product can have more than one parent grouped product. PR#2063
* Fix: Do not incorrectly create pending renewal orders and suspend PayPal Standard Subscriptions at the time their payment is due. PayPal controls the billing schedule for these subscriptions. PR#2069
* Tweak: Add 'woocommerce_subscriptions_order_type_dropdown' filter. PR#2065
+* Tweak: Include only Subscriptions strings in POT file.
2017.04.07 - version 2.2.3
* Fix: WooCommerce 3.0: Improve backward compatibility of get_date( 'start' ) calls for subscriptions without a date created set by WooCommerce 3.0 (which happens when manually adding a new subscriptions). PR#2047
diff --git a/includes/admin/class-wc-subscriptions-admin.php b/includes/admin/class-wc-subscriptions-admin.php
index 8a8aadb..a812e5d 100644
--- a/includes/admin/class-wc-subscriptions-admin.php
+++ b/includes/admin/class-wc-subscriptions-admin.php
@@ -84,7 +84,7 @@ class WC_Subscriptions_Admin {
// Save variable subscription meta
add_action( 'woocommerce_process_product_meta_variable-subscription', __CLASS__ . '::process_product_meta_variable_subscription' );
- add_action( 'woocommerce_ajax_save_product_variations', __CLASS__ . '::process_product_meta_variable_subscription' );
+ add_action( 'woocommerce_save_product_variation', __CLASS__ . '::save_product_variation', 20, 2 );
add_action( 'woocommerce_subscription_pre_update_status', __CLASS__ . '::check_customer_is_set', 10, 3 );
@@ -122,6 +122,11 @@ class WC_Subscriptions_Admin {
add_filter( 'woocommerce_get_formatted_order_total', __CLASS__ . '::maybe_remove_formatted_order_total_filter', 0, 2 );
add_action( 'woocommerce_payment_gateways_settings', __CLASS__ . '::add_recurring_payment_gateway_information', 10 , 1 );
+
+ // Change text for when order items cannot be edited
+ add_action( 'woocommerce_admin_order_totals_after_refunded', __CLASS__ . '::maybe_attach_gettext_callback', 10, 1 );
+ // Unhook gettext callback to prevent extra call impact
+ add_action( 'woocommerce_order_item_add_action_buttons', __CLASS__ . '::maybe_unattach_gettext_callback', 10, 1 );
}
/**
@@ -205,7 +210,7 @@ class WC_Subscriptions_Admin {
?>
-
+
$label ) { ?>
@@ -236,7 +241,7 @@ class WC_Subscriptions_Admin {
// Sign-up Fee
woocommerce_wp_text_input( array(
'id' => '_subscription_sign_up_fee',
- 'class' => 'wc_input_subscription_intial_price short',
+ 'class' => 'wc_input_subscription_intial_price wc_input_price short',
// translators: %s is a currency symbol / code
'label' => sprintf( __( 'Sign-up fee (%s)', 'woocommerce-subscriptions' ), get_woocommerce_currency_symbol() ),
'placeholder' => _x( 'e.g. 9.90', 'example price', 'woocommerce-subscriptions' ),
@@ -576,69 +581,14 @@ class WC_Subscriptions_Admin {
*/
public static function process_product_meta_variable_subscription( $post_id ) {
- if ( ! WC_Subscriptions_Product::is_subscription( $post_id ) || empty( $_POST['_wcsnonce_save_variations'] ) || ! wp_verify_nonce( $_POST['_wcsnonce_save_variations'], 'wcs_subscription_variations' ) ) {
+ if ( ! WC_Subscriptions_Product::is_subscription( $post_id ) || empty( $_POST['_wcsnonce'] ) || ! wp_verify_nonce( $_POST['_wcsnonce'], 'wcs_subscription_meta' ) ) {
return;
}
// Make sure WooCommerce calculates correct prices
$_POST['variable_regular_price'] = isset( $_POST['variable_subscription_price'] ) ? $_POST['variable_subscription_price'] : 0;
- if ( ! isset( $_REQUEST['variable_post_id'] ) ) {
- return;
- }
-
- $variable_post_ids = $_POST['variable_post_id'];
-
- $max_loop = max( array_keys( $variable_post_ids ) );
-
- // Save each variations details
- for ( $i = 0; $i <= $max_loop; $i ++ ) {
-
- if ( ! isset( $variable_post_ids[ $i ] ) ) {
- continue;
- }
-
- $variation_id = absint( $variable_post_ids[ $i ] );
-
- if ( isset( $_POST['variable_subscription_price'] ) && is_array( $_POST['variable_subscription_price'] ) ) {
- $subscription_price = wc_format_decimal( $_POST['variable_subscription_price'][ $i ] );
- update_post_meta( $variation_id, '_subscription_price', $subscription_price );
- update_post_meta( $variation_id, '_regular_price', $subscription_price );
- }
-
- // Make sure trial period is within allowable range
- $subscription_ranges = wcs_get_subscription_ranges();
-
- $max_trial_length = count( $subscription_ranges[ $_POST['variable_subscription_trial_period'][ $i ] ] ) - 1;
-
- $_POST['variable_subscription_trial_length'][ $i ] = absint( $_POST['variable_subscription_trial_length'][ $i ] );
-
- if ( $_POST['variable_subscription_trial_length'][ $i ] > $max_trial_length ) {
- $_POST['variable_subscription_trial_length'][ $i ] = $max_trial_length;
- }
-
- // Work around a WPML bug which means 'variable_subscription_trial_period' is not set when using "Edit Product" as the product translation interface
- if ( $_POST['variable_subscription_trial_length'][ $i ] < 0 ) {
- $_POST['variable_subscription_trial_length'][ $i ] = 0;
- }
-
- $subscription_fields = array(
- '_subscription_sign_up_fee',
- '_subscription_period',
- '_subscription_period_interval',
- '_subscription_length',
- '_subscription_trial_period',
- '_subscription_trial_length',
- );
-
- foreach ( $subscription_fields as $field_name ) {
- if ( isset( $_POST[ 'variable' . $field_name ][ $i ] ) ) {
- update_post_meta( $variation_id, $field_name, wc_clean( $_POST[ 'variable' . $field_name ][ $i ] ) );
- }
- }
- }
-
- // Now that all the variation's meta is saved, sync the min variation price
+ // Sync the min variation price
if ( WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
$variable_subscription = wc_get_product( $post_id );
$variable_subscription->variable_product_sync();
@@ -647,6 +597,57 @@ class WC_Subscriptions_Admin {
}
}
+ /**
+ * Save meta info for subscription variations
+ *
+ * @param int $variation_id
+ * @param int $i
+ * return void
+ * @since 2.0
+ */
+ public static function save_product_variation( $variation_id, $index ) {
+
+ if ( ! WC_Subscriptions_Product::is_subscription( $variation_id ) || empty( $_POST['_wcsnonce_save_variations'] ) || ! wp_verify_nonce( $_POST['_wcsnonce_save_variations'], 'wcs_subscription_variations' ) ) {
+ return;
+ }
+
+ if ( isset( $_POST['variable_subscription_price'][ $index ] ) ) {
+ $subscription_price = wc_format_decimal( $_POST['variable_subscription_price'][ $index ] );
+ update_post_meta( $variation_id, '_subscription_price', $subscription_price );
+ update_post_meta( $variation_id, '_regular_price', $subscription_price );
+ }
+
+ // Make sure trial period is within allowable range
+ $subscription_ranges = wcs_get_subscription_ranges();
+ $max_trial_length = count( $subscription_ranges[ $_POST['variable_subscription_trial_period'][ $index ] ] ) - 1;
+
+ $_POST['variable_subscription_trial_length'][ $index ] = absint( $_POST['variable_subscription_trial_length'][ $index ] );
+
+ if ( $_POST['variable_subscription_trial_length'][ $index ] > $max_trial_length ) {
+ $_POST['variable_subscription_trial_length'][ $index ] = $max_trial_length;
+ }
+
+ // Work around a WPML bug which means 'variable_subscription_trial_period' is not set when using "Edit Product" as the product translation interface
+ if ( $_POST['variable_subscription_trial_length'][ $index ] < 0 ) {
+ $_POST['variable_subscription_trial_length'][ $index ] = 0;
+ }
+
+ $subscription_fields = array(
+ '_subscription_sign_up_fee',
+ '_subscription_period',
+ '_subscription_period_interval',
+ '_subscription_length',
+ '_subscription_trial_period',
+ '_subscription_trial_length',
+ );
+
+ foreach ( $subscription_fields as $field_name ) {
+ if ( isset( $_POST[ 'variable' . $field_name ][ $index ] ) ) {
+ update_post_meta( $variation_id, $field_name, wc_clean( $_POST[ 'variable' . $field_name ][ $index ] ) );
+ }
+ }
+ }
+
/**
* Make sure when saving a subscription via the admin to activate it, it has a valid customer set on it.
*
@@ -1518,6 +1519,57 @@ class WC_Subscriptions_Admin {
return $formatted_total;
}
+ /**
+ * Only attach the gettext callback when on admin shop subscription screen
+ *
+ * @since 2.2.7
+ */
+ public static function maybe_attach_gettext_callback() {
+
+ $screen = get_current_screen();
+
+ if ( is_object( $screen ) && 'shop_subscription' == $screen->id ) {
+ add_filter( 'gettext', __CLASS__ . '::change_order_item_editable_text', 10, 3 );
+ }
+ }
+
+ /**
+ * Only unattach the gettext callback when it was attached
+ *
+ * @since 2.2.7
+ */
+ public static function maybe_unattach_gettext_callback() {
+
+ $screen = get_current_screen();
+
+ if ( is_object( $screen ) && 'shop_subscription' == $screen->id ) {
+ remove_filter( 'gettext', __CLASS__ . '::change_order_item_editable_text', 10, 3 );
+ }
+ }
+
+
+ /**
+ * When subscription items not editable (such as due to the payment gateway not supporting modifications),
+ * change the text to explain why
+ *
+ * @since 2.2.7
+ */
+ public static function change_order_item_editable_text( $translated_text, $text, $domain ) {
+
+ switch ( $text ) {
+
+ case 'This order is no longer editable.':
+ $translated_text = __( 'Subscription items can no longer be edited.', 'woocommerce-subscriptions' );
+ break;
+
+ case 'To edit this order change the status back to "Pending"':
+ $translated_text = __( 'This subscription is no longer editable because the payment gateway does not allow modification of recurring amounts.', 'woocommerce-subscriptions' );
+ break;
+ }
+
+ return $translated_text;
+ }
+
/**
* Add recurring payment gateway information after the Settings->Checkout->Payment Gateways table.
* This includes links to find additional gateways, information about manual renewals
diff --git a/includes/admin/class-wcs-admin-reports.php b/includes/admin/class-wcs-admin-reports.php
index 124c20d..fca8ffb 100644
--- a/includes/admin/class-wcs-admin-reports.php
+++ b/includes/admin/class-wcs-admin-reports.php
@@ -132,7 +132,7 @@ class WCS_Admin_Reports {
$wc_screen_id = sanitize_title( __( 'WooCommerce', 'woocommerce-subscriptions' ) );
// Reports Subscriptions Pages
- if ( in_array( $screen->id, apply_filters( 'woocommerce_reports_screen_ids', array( $wc_screen_id . '_page_wc-reports', 'dashboard' ) ) ) && isset( $_GET['tab'] ) && 'subscriptions' == $_GET['tab'] ) {
+ if ( in_array( $screen->id, apply_filters( 'woocommerce_reports_screen_ids', array( $wc_screen_id . '_page_wc-reports', 'toplevel_page_wc-reports', 'dashboard' ) ) ) && isset( $_GET['tab'] ) && 'subscriptions' == $_GET['tab'] ) {
wp_enqueue_script( 'wcs-reports', plugin_dir_url( WC_Subscriptions::$plugin_file ) . 'assets/js/admin/reports.js', array( 'jquery', 'jquery-ui-datepicker', 'wc-reports', 'accounting' ), WC_Subscriptions::$version );
diff --git a/includes/admin/meta-boxes/class-wcs-meta-box-subscription-data.php b/includes/admin/meta-boxes/class-wcs-meta-box-subscription-data.php
index 55f8f44..b6a49ea 100644
--- a/includes/admin/meta-boxes/class-wcs-meta-box-subscription-data.php
+++ b/includes/admin/meta-boxes/class-wcs-meta-box-subscription-data.php
@@ -121,9 +121,13 @@ class WCS_Meta_Box_Subscription_Data extends WC_Meta_Box_Order_Data {
$function_name = 'get_billing_' . $key;
- if ( $subscription->$function_name() ) {
- echo '' . esc_html( $field['label'] ) . ': ' . wp_kses_post( make_clickable( esc_html( $subscription->$function_name() ) ) ) . '
';
+ if ( is_callable( array( $subscription, $function_name ) ) ) {
+ $field_value = $subscription->$function_name( 'edit' );
+ } else {
+ $field_value = $subscription->get_meta( '_billing_' . $key );
}
+
+ echo '' . esc_html( $field['label'] ) . ': ' . wp_kses_post( make_clickable( esc_html( $field_value ) ) ) . '
';
}
echo 'get_payment_method() ) ? ' class="' . esc_attr( $subscription->get_payment_method() ) . '"' : '' ) . '>' . esc_html__( 'Payment Method', 'woocommerce-subscriptions' ) . ': ' . wp_kses_post( nl2br( $subscription->get_payment_method_to_display() ) );
@@ -187,11 +191,15 @@ class WCS_Meta_Box_Subscription_Data extends WC_Meta_Box_Order_Data {
continue;
}
- $field_name = 'shipping_' . $key;
+ $function_name = 'get_shipping_' . $key;
- if ( ! empty( $subscription->$field_name ) ) {
- echo '
' . esc_html( $field['label'] ) . ': ' . wp_kses_post( make_clickable( esc_html( $subscription->$field_name ) ) ) . '
';
+ if ( is_callable( array( $subscription, $function_name ) ) ) {
+ $field_value = $subscription->$function_name( 'edit' );
+ } else {
+ $field_value = $subscription->get_meta( '_shipping_' . $key );
}
+
+ echo '' . esc_html( $field['label'] ) . ': ' . wp_kses_post( make_clickable( esc_html( $field_value ) ) ) . '
';
}
}
diff --git a/includes/api/class-wc-rest-subscriptions-controller.php b/includes/api/class-wc-rest-subscriptions-controller.php
index a80d4f2..9aa749d 100644
--- a/includes/api/class-wc-rest-subscriptions-controller.php
+++ b/includes/api/class-wc-rest-subscriptions-controller.php
@@ -88,7 +88,7 @@ class WC_REST_Subscriptions_Controller extends WC_REST_Orders_V1_Controller {
$date_type_key = ( 'start' === $date_type ) ? 'date_created' : $date_type;
$date = $subscription->get_date( $date_type_key );
- $response->data[ $date_type . '_date'] = ( ! empty( $date ) ) ? wc_rest_prepare_date_response( $date ) : '';
+ $response->data[ $date_type . '_date' ] = ( ! empty( $date ) ) ? wc_rest_prepare_date_response( $date ) : '';
}
}
diff --git a/includes/api/legacy/class-wc-api-subscriptions-customers.php b/includes/api/legacy/class-wc-api-subscriptions-customers.php
index 67b4529..be3309d 100644
--- a/includes/api/legacy/class-wc-api-subscriptions-customers.php
+++ b/includes/api/legacy/class-wc-api-subscriptions-customers.php
@@ -66,16 +66,22 @@ class WC_API_Subscriptions_Customers extends WC_API_Customers {
if ( is_wp_error( $id ) ) {
return $id;
}
- $subscription_ids = array();
- $filter['customer_id'] = $id;
- $subscriptions = WC()->api->WC_API_Subscriptions->get_subscriptions( $fields, $filter, null, -1 );
+
+ $customer_subscriptions = $subscription_ids = array();
+ $filter['customer_id'] = $id;
+ $subscriptions = WC()->api->WC_API_Subscriptions->get_subscriptions( $fields, $filter, null, -1 );
if ( ! empty( $subscriptions['subscriptions'] ) && is_array( $subscriptions['subscriptions'] ) ) {
foreach ( $subscriptions['subscriptions'] as $subscription ) {
- $subscription_ids[] = $subscription['id'];
+ if ( isset( $subscription['billing_schedule']['interval'] ) ) { // make sure the interval is not a string to fully support backwards compat.
+ $subscription['billing_schedule']['interval'] = intval( $subscription['billing_schedule']['interval'] );
+ }
+
+ $customer_subscriptions[] = array( 'subscription' => $subscription );
+ $subscription_ids[] = $subscription['id'];
}
}
- return array( 'customer_subscriptions' => apply_filters( 'wc_subscriptions_api_customer_subscriptions', $subscriptions, $id, $fields, $subscription_ids, $this->server ) );
+ return array( 'customer_subscriptions' => apply_filters( 'wc_subscriptions_api_customer_subscriptions', $customer_subscriptions, $id, $fields, $subscription_ids, $this->server ) );
}
}
diff --git a/includes/api/legacy/class-wc-api-subscriptions.php b/includes/api/legacy/class-wc-api-subscriptions.php
index 90fa7c4..057e1a3 100644
--- a/includes/api/legacy/class-wc-api-subscriptions.php
+++ b/includes/api/legacy/class-wc-api-subscriptions.php
@@ -694,7 +694,16 @@ class WC_API_Subscriptions extends WC_API_Orders {
$query_args['post_status'] = $statuses;
unset( $args['status'] );
+ }
+ if ( ! empty( $args['customer_id'] ) ) {
+ $query_args['meta_query'] = array(
+ array(
+ 'key' => '_customer_user',
+ 'value' => absint( $args['customer_id'] ),
+ 'compare' => '=',
+ ),
+ );
}
$query_args = $this->merge_query_args( $query_args, $args );
diff --git a/includes/class-wc-subscription.php b/includes/class-wc-subscription.php
index 9cecc3b..0e1a7bf 100644
--- a/includes/class-wc-subscription.php
+++ b/includes/class-wc-subscription.php
@@ -176,6 +176,9 @@ class WC_Subscription extends WC_Order {
if ( ! WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
wcs_doing_it_wrong( $key, sprintf( 'Subscription properties should not be set directly as WooCommerce 3.0 no longer supports direct property access. Use %s instead.', $function ), '2.2.0' );
}
+ } else {
+
+ $this->$key = $value;
}
}
@@ -455,8 +458,8 @@ class WC_Subscription extends WC_Order {
// Recalculate and set next payment date
$stored_next_payment = $this->get_time( 'next_payment' );
- // Make sure the next payment date is more than 2 hours in the future
- if ( $stored_next_payment < ( gmdate( 'U' ) + 2 * HOUR_IN_SECONDS ) ) { // also accounts for a $stored_next_payment of 0, meaning it's not set
+ // Make sure the next payment date is more than 2 hours in the future by default
+ if ( $stored_next_payment < ( gmdate( 'U' ) + apply_filters( 'woocommerce_subscription_activation_next_payment_date_threshold', 2 * HOUR_IN_SECONDS, $stored_next_payment, $old_status, $this ) ) ) { // also accounts for a $stored_next_payment of 0, meaning it's not set
$calculated_next_payment = $this->calculate_date( 'next_payment' );
@@ -465,6 +468,9 @@ class WC_Subscription extends WC_Order {
} elseif ( $stored_next_payment < gmdate( 'U' ) ) { // delete the stored date if it's in the past as we're not updating it (the calculated next payment date is 0 or none)
$this->delete_date( 'next_payment' );
}
+ } else {
+ // In case plugins want to run some code when the subscription was reactivated, but the next payment date was not recalculated.
+ do_action( 'woocommerce_subscription_activation_next_payment_not_recalculated', $stored_next_payment, $old_status, $this );
}
// Trial end date and end/expiration date don't change at all - they should be set when the subscription is first created
wcs_make_user_active( $this->get_user_id() );
@@ -846,7 +852,7 @@ class WC_Subscription extends WC_Order {
* @param int $value
*/
public function set_billing_interval( $value ) {
- $this->set_prop( 'billing_interval', absint( $value ) );
+ $this->set_prop( 'billing_interval', (string) absint( $value ) );
}
/**
@@ -1251,8 +1257,8 @@ class WC_Subscription extends WC_Order {
}
if ( $is_updated && true === $this->object_read ) {
+ $this->save_dates();
do_action( 'woocommerce_subscription_date_updated', $this, $date_type, $datetime );
- $this->save();
}
}
}
@@ -1287,8 +1293,8 @@ class WC_Subscription extends WC_Order {
$this->set_date_prop( $date_type, 0 );
if ( true === $this->object_read ) {
+ $this->save_dates();
do_action( 'woocommerce_subscription_date_deleted', $this, $date_type );
- $this->save();
}
}
@@ -1392,7 +1398,7 @@ class WC_Subscription extends WC_Order {
$start_time = $this->get_time( 'date_created' );
$next_payment_time = $this->get_time( 'next_payment' );
$trial_end_time = $this->get_time( 'trial_end' );
- $last_payment_time = $this->get_time( 'last_order_date_created' );
+ $last_payment_time = max( $this->get_time( 'last_order_date_created' ), $this->get_time( 'last_order_date_paid' ) );
$end_time = $this->get_time( 'end' );
// If the subscription has a free trial period, and we're still in the free trial period, the next payment is due at the end of the free trial
@@ -1435,6 +1441,24 @@ class WC_Subscription extends WC_Order {
return $next_payment_date;
}
+ /**
+ * Complete a partial save, saving subscription date changes to the database.
+ *
+ * Sometimes it's necessary to only save changes to date properties, for example, when you
+ * don't want status transitions to be triggered by a full object @see $this->save().
+ *
+ * @since 2.2.6
+ */
+ public function save_dates() {
+ if ( $this->data_store && $this->get_id() ) {
+ $saved_dates = $this->data_store->save_dates( $this );
+
+ // Apply the saved date changes
+ $this->data = array_replace_recursive( $this->data, $saved_dates );
+ $this->changes = array_diff_key( $this->changes, $saved_dates );
+ }
+ }
+
/** Formatted Totals Methods *******************************************************/
/**
@@ -1548,7 +1572,7 @@ class WC_Subscription extends WC_Order {
}
// Remove discounts
- $subtotal = $subtotal - $this->get_cart_discount();
+ $subtotal = $subtotal - $this->get_total_discount();
$subtotal = wc_price( $subtotal, array( 'currency' => $this->get_currency() ) );
}
@@ -1693,8 +1717,10 @@ class WC_Subscription extends WC_Order {
// Allow a short circuit for plugins & payment gateways to force max failed payments exceeded
if ( 'cancelled' == $new_status || apply_filters( 'woocommerce_subscription_max_failed_payments_exceeded', false, $this ) ) {
- $this->update_status( 'cancelled', __( 'Subscription Cancelled: maximum number of failed payments reached.', 'woocommerce-subscriptions' ) );
- } else {
+ if ( $this->can_be_updated_to( 'cancelled' ) ) {
+ $this->update_status( 'cancelled', __( 'Subscription Cancelled: maximum number of failed payments reached.', 'woocommerce-subscriptions' ) );
+ }
+ } elseif ( $this->can_be_updated_to( $new_status ) ) {
$this->update_status( $new_status );
}
@@ -1937,16 +1963,17 @@ class WC_Subscription extends WC_Order {
if ( $this->get_payment_method() !== $payment_method_id ) {
- // Set the payment gateway ID depending on whether we have a string or WC_Payment_Gateway or string key
- if ( is_a( $payment_method, 'WC_Payment_Gateway' ) ) {
- $payment_gateway = $payment_method;
- } else {
- $payment_gateways = WC()->payment_gateways->payment_gateways();
- $payment_gateway = isset( $payment_gateways[ $payment_method_id ] ) ? $payment_gateways[ $payment_method_id ] : null;
- }
-
- // We shouldn't set the requires manual renewal prop while the object is being read. That prop should be set by reading it from the DB not based on settings or the payment gateway
+ // We shouldn't set the requires manual renewal prop or try to get the payment gateway while the object is being read. That prop should be set by reading it from the DB not based on settings or the payment gateway
if ( $this->object_read ) {
+
+ // Set the payment gateway ID depending on whether we have a string or WC_Payment_Gateway or string key
+ if ( is_a( $payment_method, 'WC_Payment_Gateway' ) ) {
+ $payment_gateway = $payment_method;
+ } else {
+ $payment_gateways = WC()->payment_gateways->payment_gateways();
+ $payment_gateway = isset( $payment_gateways[ $payment_method_id ] ) ? $payment_gateways[ $payment_method_id ] : null;
+ }
+
if ( 'yes' == get_option( WC_Subscriptions_Admin::$option_prefix . '_turn_off_automatic_payments', 'no' ) ) {
$this->set_requires_manual_renewal( true );
} elseif ( is_null( $payment_gateway ) || false == $payment_gateway->supports( 'subscriptions' ) ) {
@@ -1954,10 +1981,11 @@ class WC_Subscription extends WC_Order {
} else {
$this->set_requires_manual_renewal( false );
}
+
+ $this->set_prop( 'payment_method_title', is_null( $payment_gateway ) ? '' : $payment_gateway->get_title() );
}
$this->set_prop( 'payment_method', $payment_method_id );
- $this->set_prop( 'payment_method_title', is_null( $payment_gateway ) ? '' : $payment_gateway->get_title() );
}
}
}
diff --git a/includes/class-wc-subscriptions-addresses.php b/includes/class-wc-subscriptions-addresses.php
index 196571b..de508f1 100644
--- a/includes/class-wc-subscriptions-addresses.php
+++ b/includes/class-wc-subscriptions-addresses.php
@@ -154,8 +154,12 @@ class WC_Subscriptions_Addresses {
$subscription = wcs_get_subscription( absint( $_GET['subscription'] ) );
foreach ( array_keys( $address ) as $key ) {
- $function_name = 'get_' . $key;
- $address[ $key ]['value'] = $subscription->$function_name();
+
+ $function_name = 'get_' . $key;
+
+ if ( is_callable( array( $subscription, $function_name ) ) ) {
+ $address[ $key ]['value'] = $subscription->$function_name();
+ }
}
}
diff --git a/includes/class-wc-subscriptions-cart.php b/includes/class-wc-subscriptions-cart.php
index f69a513..f1ba565 100644
--- a/includes/class-wc-subscriptions-cart.php
+++ b/includes/class-wc-subscriptions-cart.php
@@ -665,7 +665,6 @@ class WC_Subscriptions_Cart {
if ( WC_Subscriptions_Product::is_subscription( $product ) && ! wcs_cart_contains_renewal() ) {
-
if ( WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
$product_price_filter = 'woocommerce_get_price';
} else {
diff --git a/includes/class-wc-subscriptions-change-payment-gateway.php b/includes/class-wc-subscriptions-change-payment-gateway.php
index 7b1c6fa..41c2e10 100644
--- a/includes/class-wc-subscriptions-change-payment-gateway.php
+++ b/includes/class-wc-subscriptions-change-payment-gateway.php
@@ -81,7 +81,7 @@ class WC_Subscriptions_Change_Payment_Gateway {
} else {
// If we're changing the payment method, we want to make sure a number of totals return $0 (to prevent payments being processed now)
- add_filter( 'woocommerce_order_get_total', __CLASS__ . '::maybe_zero_total', 11, 2 );
+ add_filter( 'woocommerce_subscription_get_total', __CLASS__ . '::maybe_zero_total', 11, 2 );
}
}
diff --git a/includes/class-wc-subscriptions-checkout.php b/includes/class-wc-subscriptions-checkout.php
index 560266c..200a5ef 100644
--- a/includes/class-wc-subscriptions-checkout.php
+++ b/includes/class-wc-subscriptions-checkout.php
@@ -11,8 +11,6 @@
*/
class WC_Subscriptions_Checkout {
- private static $signup_option_changed = false;
-
private static $guest_checkout_option_changed = false;
/**
@@ -389,23 +387,14 @@ class WC_Subscriptions_Checkout {
if ( WC_Subscriptions_Cart::cart_contains_subscription() && ! is_user_logged_in() ) {
- // Make sure users can sign up
- if ( false === $checkout->enable_signup ) {
- $checkout->enable_signup = true;
- self::$signup_option_changed = true;
- }
-
// Make sure users are required to register an account
if ( true === $checkout->enable_guest_checkout ) {
$checkout->enable_guest_checkout = false;
self::$guest_checkout_option_changed = true;
- if ( ! is_user_logged_in() ) {
- $checkout->must_create_account = true;
- }
+ $checkout->must_create_account = true;
}
}
-
}
/**
@@ -440,10 +429,6 @@ class WC_Subscriptions_Checkout {
*/
public static function restore_checkout_registration_settings( $checkout = '' ) {
- if ( self::$signup_option_changed ) {
- $checkout->enable_signup = false;
- }
-
if ( self::$guest_checkout_option_changed ) {
$checkout->enable_guest_checkout = true;
if ( ! is_user_logged_in() ) { // Also changed must_create_account
diff --git a/includes/class-wc-subscriptions-coupon.php b/includes/class-wc-subscriptions-coupon.php
index d443b14..9689995 100644
--- a/includes/class-wc-subscriptions-coupon.php
+++ b/includes/class-wc-subscriptions-coupon.php
@@ -75,7 +75,7 @@ class WC_Subscriptions_Coupon {
*/
public static function get_discount_amount( $discount, $discounting_amount, $cart_item, $single, $coupon ) {
- $coupon_type = wcs_get_coupon_property( $coupon, 'type' );
+ $coupon_type = wcs_get_coupon_property( $coupon, 'discount_type' );
// Only deal with subscriptions coupon types
if ( ! in_array( $coupon_type, array( 'recurring_fee', 'recurring_percent', 'sign_up_fee', 'sign_up_fee_percent', 'renewal_fee', 'renewal_percent', 'renewal_cart' ) ) ) {
@@ -163,12 +163,12 @@ class WC_Subscriptions_Coupon {
$discounting_amount = 0;
}
- $discount_amount = min( wcs_get_coupon_property( $coupon, 'amount' ), $discounting_amount );
+ $discount_amount = min( wcs_get_coupon_property( $coupon, 'coupon_amount' ), $discounting_amount );
$discount_amount = $single ? $discount_amount : $discount_amount * $cart_item_qty;
} elseif ( $apply_recurring_percent_coupon ) {
- $discount_amount = ( $discounting_amount / 100 ) * wcs_get_coupon_property( $coupon, 'amount' );
+ $discount_amount = ( $discounting_amount / 100 ) * wcs_get_coupon_property( $coupon, 'coupon_amount' );
} elseif ( $apply_initial_percent_coupon ) {
@@ -177,7 +177,7 @@ class WC_Subscriptions_Coupon {
$discounting_amount = 0;
}
- $discount_amount = ( $discounting_amount / 100 ) * wcs_get_coupon_property( $coupon, 'amount' );
+ $discount_amount = ( $discounting_amount / 100 ) * wcs_get_coupon_property( $coupon, 'coupon_amount' );
} elseif ( $apply_renewal_cart_coupon ) {
@@ -189,7 +189,7 @@ class WC_Subscriptions_Coupon {
*/
$discount_percent = ( $discounting_amount * $cart_item['quantity'] ) / self::get_renewal_subtotal( wcs_get_coupon_property( $coupon, 'code' ) );
- $discount_amount = ( wcs_get_coupon_property( $coupon, 'amount' ) * $discount_percent ) / $cart_item_qty;
+ $discount_amount = ( wcs_get_coupon_property( $coupon, 'coupon_amount' ) * $discount_percent ) / $cart_item_qty;
}
// Round - consistent with WC approach
@@ -222,7 +222,7 @@ class WC_Subscriptions_Coupon {
foreach ( WC()->cart->applied_coupons as $code ) {
$coupon = new WC_Coupon( $code );
- $cart_coupon_type = wcs_get_coupon_property( $coupon, 'type' );
+ $cart_coupon_type = wcs_get_coupon_property( $coupon, 'discount_type' );
if ( 'any' == $coupon_type || $coupon_type == $cart_coupon_type || ( 'core' == $coupon_type && in_array( $cart_coupon_type, $core_coupons ) ) ) {
$contains_discount = true;
@@ -246,7 +246,7 @@ class WC_Subscriptions_Coupon {
}
self::$coupon_error = '';
- $coupon_type = wcs_get_coupon_property( $coupon, 'type' );
+ $coupon_type = wcs_get_coupon_property( $coupon, 'discount_type' );
// ignore non-subscription coupons
if ( ! in_array( $coupon_type, array( 'recurring_fee', 'sign_up_fee', 'recurring_percent', 'sign_up_fee_percent', 'renewal_fee', 'renewal_percent', 'renewal_cart' ) ) ) {
@@ -379,7 +379,7 @@ class WC_Subscriptions_Coupon {
foreach ( $applied_coupons as $coupon_code ) {
$coupon = new WC_Coupon( $coupon_code );
- $coupon_type = wcs_get_coupon_property( $coupon, 'type' );
+ $coupon_type = wcs_get_coupon_property( $coupon, 'discount_type' );
if ( in_array( $coupon_type, array( 'recurring_fee', 'recurring_percent' ) ) ) { // always apply coupons to their specific calculation case
if ( 'recurring_total' == $calculation_type ) {
@@ -443,9 +443,9 @@ class WC_Subscriptions_Coupon {
foreach ( $renewal_coupons as $subscription_id => $coupons ) {
- foreach ( $coupons as $coupon ) {
+ foreach ( $coupons as $coupon_code => $coupon_properties ) {
- if ( wcs_get_coupon_property( $coupon, 'code' ) == $code ) {
+ if ( $coupon_code == $code ) {
if ( $subscription = wcs_get_subscription( $subscription_id ) ) {
$subtotal = $subscription->get_subtotal();
@@ -530,8 +530,8 @@ class WC_Subscriptions_Coupon {
foreach ( $cart->applied_coupons as $coupon_code ) {
$coupon = new WC_Coupon( $coupon_code );
- $coupon_type = wcs_get_coupon_property( $coupon, 'type' );
- $coupon_amount = wcs_get_coupon_property( $coupon, 'amount' );
+ $coupon_type = wcs_get_coupon_property( $coupon, 'discount_type' );
+ $coupon_amount = wcs_get_coupon_property( $coupon, 'coupon_amount' );
// Pre 2.5 is_valid_for_product() does not use wc_get_product_coupon_types()
if ( WC_Subscriptions::is_woocommerce_pre( '2.5' ) ) {
diff --git a/includes/class-wc-subscriptions-product.php b/includes/class-wc-subscriptions-product.php
index 86c2494..080af38 100644
--- a/includes/class-wc-subscriptions-product.php
+++ b/includes/class-wc-subscriptions-product.php
@@ -108,15 +108,20 @@ class WC_Subscriptions_Product {
*/
public static function is_subscription( $product ) {
- $is_subscription = false;
+ $is_subscription = $product_id = false;
$product = self::maybe_get_product_instance( $product );
- if ( is_object( $product ) && $product->is_type( array( 'subscription', 'subscription_variation', 'variable-subscription' ) ) ) {
- $is_subscription = true;
+ if ( is_object( $product ) ) {
+
+ $product_id = $product->get_id();
+
+ if ( $product->is_type( array( 'subscription', 'subscription_variation', 'variable-subscription' ) ) ) {
+ $is_subscription = true;
+ }
}
- return apply_filters( 'woocommerce_is_subscription', $is_subscription, $product->get_id(), $product );
+ return apply_filters( 'woocommerce_is_subscription', $is_subscription, $product_id, $product );
}
/**
@@ -438,11 +443,11 @@ class WC_Subscriptions_Product {
* Returns the subscription interval for a product, if it's a subscription.
*
* @param mixed $product A WC_Product object or product ID
- * @return string A string representation of the period, either Day, Week, Month or Year, or an empty string if product is not a subscription.
+ * @return int An integer representing the subscription interval, or 1 if the product is not a subscription or there is no interval
* @since 1.0
*/
public static function get_interval( $product ) {
- return apply_filters( 'woocommerce_subscriptions_product_period_interval', self::get_meta_data( $product, 'subscription_period_interval', 0 ), self::maybe_get_product_instance( $product ) );
+ return apply_filters( 'woocommerce_subscriptions_product_period_interval', self::get_meta_data( $product, 'subscription_period_interval', 1, 'use_default_value' ), self::maybe_get_product_instance( $product ) );
}
/**
@@ -453,7 +458,7 @@ class WC_Subscriptions_Product {
* @since 1.0
*/
public static function get_length( $product ) {
- return apply_filters( 'woocommerce_subscriptions_product_length', self::get_meta_data( $product, 'subscription_length', 0 ), self::maybe_get_product_instance( $product ) );
+ return apply_filters( 'woocommerce_subscriptions_product_length', self::get_meta_data( $product, 'subscription_length', 0, 'use_default_value' ), self::maybe_get_product_instance( $product ) );
}
/**
@@ -464,7 +469,7 @@ class WC_Subscriptions_Product {
* @since 1.0
*/
public static function get_trial_length( $product ) {
- return apply_filters( 'woocommerce_subscriptions_product_trial_length', self::get_meta_data( $product, 'subscription_trial_length', 0 ), self::maybe_get_product_instance( $product ) );
+ return apply_filters( 'woocommerce_subscriptions_product_trial_length', self::get_meta_data( $product, 'subscription_trial_length', 0, 'use_default_value' ), self::maybe_get_product_instance( $product ) );
}
/**
@@ -486,7 +491,7 @@ class WC_Subscriptions_Product {
* @since 1.0
*/
public static function get_sign_up_fee( $product ) {
- return apply_filters( 'woocommerce_subscriptions_product_sign_up_fee', self::get_meta_data( $product, 'subscription_sign_up_fee', 0 ), self::maybe_get_product_instance( $product ) );
+ return apply_filters( 'woocommerce_subscriptions_product_sign_up_fee', self::get_meta_data( $product, 'subscription_sign_up_fee', 0, 'use_default_value' ), self::maybe_get_product_instance( $product ) );
}
/**
@@ -673,7 +678,7 @@ class WC_Subscriptions_Product {
public static function user_can_not_delete_subscription( $allcaps, $caps, $args ) {
global $wpdb;
- if ( isset( $args[0] ) && in_array( $args[0], array( 'delete_post', 'delete_product' ) ) && isset( $args[2] ) && ( ! isset( $_GET['action'] ) || 'untrash' != $_GET['action'] ) ) {
+ if ( isset( $args[0] ) && in_array( $args[0], array( 'delete_post', 'delete_product' ) ) && isset( $args[2] ) && ( ! isset( $_GET['action'] ) || 'untrash' != $_GET['action'] ) && 0 === strpos( get_post_type( $args[2] ), 'product' ) ) {
$user_id = $args[2];
$post_id = $args[2];
@@ -748,7 +753,11 @@ class WC_Subscriptions_Product {
* @since 2.2.0
*/
public static function needs_one_time_shipping( $product ) {
- return apply_filters( 'woocommerce_subscriptions_product_needs_one_time_shipping', 'yes' === self::get_meta_data( $product, 'subscription_one_time_shipping', 'no' ), self::maybe_get_product_instance( $product ) );
+ $product = self::maybe_get_product_instance( $product );
+ if ( $product && $product->is_type( 'variation' ) && is_callable( array( $product, 'get_parent_id' ) ) ) {
+ $product = self::maybe_get_product_instance( $product->get_parent_id() );
+ }
+ return apply_filters( 'woocommerce_subscriptions_product_needs_one_time_shipping', 'yes' === self::get_meta_data( $product, 'subscription_one_time_shipping', 'no' ), $product );
}
/**
@@ -960,10 +969,12 @@ class WC_Subscriptions_Product {
*
* @param mixed $product A WC_Product object or product ID
* @param string $meta_key The string key for the meta data
- * @return float The value of the sign-up fee, or 0 if the product is not a subscription or the subscription has no sign-up fee
+ * @param mixed $default_value The value to return if the meta doesn't exist or isn't set
+ * @param string $empty_handling (optional) How empty values should be handled -- can be 'use_default_value' or 'allow_empty'. Defaults to 'allow_empty' returning the empty value.
+ * @return mixed
* @since 2.2.0
*/
- public static function get_meta_data( $product, $meta_key, $default_value ) {
+ public static function get_meta_data( $product, $meta_key, $default_value, $empty_handling = 'allow_empty' ) {
$product = self::maybe_get_product_instance( $product );
@@ -984,6 +995,10 @@ class WC_Subscriptions_Product {
}
}
+ if ( 'use_default_value' === $empty_handling && empty( $meta_value ) ) {
+ $meta_value = $default_value;
+ }
+
return $meta_value;
}
diff --git a/includes/class-wc-subscriptions-renewal-order.php b/includes/class-wc-subscriptions-renewal-order.php
index 5a10720..ba30c05 100644
--- a/includes/class-wc-subscriptions-renewal-order.php
+++ b/includes/class-wc-subscriptions-renewal-order.php
@@ -93,10 +93,10 @@ class WC_Subscriptions_Renewal_Order {
);
wp_update_post( $update_post_data );
- update_post_meta( $order_id, '_paid_date', current_time( 'mysql', true ) );
+ update_post_meta( $order_id, '_paid_date', current_time( 'mysql' ) );
} else {
- // In WC 3.0, only the paid date prop represents the paid date, the post date isn't used anymore, also the paid date is stored and referenced as a timestamp in site timezone, not a MySQL string
- $order->set_date_paid( current_time( 'timestamp', 0 ) );
+ // In WC 3.0, only the paid date prop represents the paid date, the post date isn't used anymore, also the paid date is stored and referenced as a MySQL date string in site timezone and a GMT timestamp
+ $order->set_date_paid( current_time( 'timestamp', 1 ) );
$order->save();
}
}
@@ -192,7 +192,7 @@ class WC_Subscriptions_Renewal_Order {
foreach ( $order_items as $order_item_id => $item ) {
if ( is_callable( array( $item, 'delete_meta_data' ) ) ) { // WC 3.0+
- foreach( $switched_order_item_keys as $switch_meta_key => $value ) {
+ foreach ( $switched_order_item_keys as $switch_meta_key => $value ) {
$item->delete_meta_data( $switch_meta_key );
}
} else { // WC 2.6
diff --git a/includes/class-wc-subscriptions-switcher.php b/includes/class-wc-subscriptions-switcher.php
index 10bf24f..00218d1 100644
--- a/includes/class-wc-subscriptions-switcher.php
+++ b/includes/class-wc-subscriptions-switcher.php
@@ -970,17 +970,16 @@ class WC_Subscriptions_Switcher {
// Select the subscriptions which had item/s switched to this subscription by its parent order
if ( ! empty( $post->post_parent ) ) {
- $switched_ids = wcs_get_objects_property( wc_get_order( $post->post_parent ), 'subscription_switch', 'multiple' );
+ $switched_subscriptions = wcs_get_subscriptions_for_switch_order( $post->post_parent );
}
// On the Edit Order screen, show any subscriptions with items switched by this order
} else {
- $switched_ids = wcs_get_objects_property( wc_get_order( $post->ID ), 'subscription_switch', 'multiple' );
+ $switched_subscriptions = wcs_get_subscriptions_for_switch_order( $post->ID );
}
- if ( is_array( $switched_ids ) ) {
- foreach ( $switched_ids as $subscription_id ) {
- $subscription = wcs_get_subscription( $subscription_id );
+ if ( is_array( $switched_subscriptions ) ) {
+ foreach ( $switched_subscriptions as $subscription_id => $subscription ) {
wcs_set_objects_property( $subscription, 'relationship', __( 'Switched Subscription', 'woocommerce-subscriptions' ), 'set_prop_only' );
$orders[ $subscription_id ] = $subscription;
}
diff --git a/includes/class-wc-subscriptions-synchroniser.php b/includes/class-wc-subscriptions-synchroniser.php
index d4b6c5b..1f1a490 100644
--- a/includes/class-wc-subscriptions-synchroniser.php
+++ b/includes/class-wc-subscriptions-synchroniser.php
@@ -64,7 +64,7 @@ class WC_Subscriptions_Synchroniser {
// Save sync options when a variable subscription product is saved
add_action( 'woocommerce_process_product_meta_variable-subscription', __CLASS__ . '::process_product_meta_variable_subscription' );
- add_action( 'woocommerce_ajax_save_product_variations', __CLASS__ . '::process_product_meta_variable_subscription' );
+ add_action( 'woocommerce_save_product_variation', __CLASS__ . '::save_product_variation', 20, 2 );
// Make sure the expiration dates are calculated from the synced start date
add_filter( 'woocommerce_subscriptions_product_trial_expiration_date', __CLASS__ . '::recalculate_product_trial_expiration_date', 10, 2 );
@@ -331,43 +331,36 @@ class WC_Subscriptions_Synchroniser {
return;
}
- $variable_post_ids = $_POST['variable_post_id'];
-
- $max_loop = max( array_keys( $variable_post_ids ) );
-
// Make sure the parent product doesn't have a sync value (in case it was once a simple subscription)
update_post_meta( $post_id, self::$post_meta_key, 0 );
+ }
+
+ /**
+ * Save sync options when a variable subscription product is saved
+ *
+ * @since 1.5
+ */
+ public static function save_product_variation( $variation_id, $index ) {
+
+ if ( empty( $_POST['_wcsnonce_save_variations'] ) || ! wp_verify_nonce( $_POST['_wcsnonce_save_variations'], 'wcs_subscription_variations' ) || ! isset( $_POST['variable_post_id'] ) || ! is_array( $_POST['variable_post_id'] ) ) {
+ return;
+ }
$day_field = 'variable' . self::$post_meta_key_day;
$month_field = 'variable' . self::$post_meta_key_month;
- // Save each variations details
- for ( $i = 0; $i <= $max_loop; $i ++ ) {
+ if ( 'year' == $_POST['variable_subscription_period'][ $index ] ) { // save the day & month for the date rather than just the day
- if ( ! isset( $variable_post_ids[ $i ] ) ) {
- continue;
- }
+ $_POST[ 'variable' . self::$post_meta_key ][ $index ] = array(
+ 'day' => isset( $_POST[ $day_field ][ $index ] ) ? $_POST[ $day_field ][ $index ] : 0,
+ 'month' => isset( $_POST[ $month_field ][ $index ] ) ? $_POST[ $month_field ][ $index ] : 0,
+ );
- $variation_id = absint( $variable_post_ids[ $i ] );
-
- if ( 'year' == $_POST['variable_subscription_period'][ $i ] ) { // save the day & month for the date rather than just the day
-
- $_POST[ 'variable' . self::$post_meta_key ][ $i ] = array(
- 'day' => isset( $_POST[ $day_field ][ $i ] ) ? $_POST[ $day_field ][ $i ] : 0,
- 'month' => isset( $_POST[ $month_field ][ $i ] ) ? $_POST[ $month_field ][ $i ] : 0,
- );
-
- } else {
-
- if ( ! isset( $_POST[ 'variable' . self::$post_meta_key ][ $i ] ) ) {
- $_POST[ 'variable' . self::$post_meta_key ][ $i ] = 0;
- }
- }
-
- if ( isset( $_POST[ 'variable' . self::$post_meta_key ][ $i ] ) ) {
- update_post_meta( $variation_id, self::$post_meta_key, $_POST[ 'variable' . self::$post_meta_key ][ $i ] );
- }
+ } elseif ( ! isset( $_POST[ 'variable' . self::$post_meta_key ][ $index ] ) ) {
+ $_POST[ 'variable' . self::$post_meta_key ][ $index ] = 0;
}
+
+ update_post_meta( $variation_id, self::$post_meta_key, $_POST[ 'variable' . self::$post_meta_key ][ $index ] );
}
/**
diff --git a/includes/class-wcs-cart-renewal.php b/includes/class-wcs-cart-renewal.php
index 6359701..6833621 100644
--- a/includes/class-wcs-cart-renewal.php
+++ b/includes/class-wcs-cart-renewal.php
@@ -35,9 +35,6 @@ class WCS_Cart_Renewal {
// Remove order action buttons from the My Account page
add_filter( 'woocommerce_my_account_my_orders_actions', array( &$this, 'filter_my_account_my_orders_actions' ), 10, 2 );
- // Update customer's address on the subscription if it is changed during renewal
- add_filter( 'woocommerce_checkout_update_customer_data', array( &$this, 'maybe_update_subscription_customer_data' ), 10, 2 );
-
// When a failed renewal order is paid for via checkout, make sure WC_Checkout::create_order() preserves its "failed" status until it is paid
add_filter( 'woocommerce_default_order_status', array( &$this, 'maybe_preserve_order_status' ) );
@@ -65,6 +62,8 @@ class WCS_Cart_Renewal {
// When a renewal order's line items are being updated, update the line item IDs stored in cart data.
add_action( 'woocommerce_add_order_item_meta', array( &$this, 'update_line_item_cart_data' ), 10, 3 );
+ add_filter( 'woocommerce_checkout_update_customer_data', array( &$this, 'maybe_update_subscription_customer_data' ), 10, 2 );
+
} else {
// For order items created as part of a renewal, keep a record of the cart item key so that we can match it later once the order item has been saved and has an ID
@@ -75,6 +74,9 @@ class WCS_Cart_Renewal {
// Don't display cart item key meta stored above on the Edit Order screen
add_action( 'woocommerce_hidden_order_itemmeta', array( &$this, 'hidden_order_itemmeta' ), 10 );
+
+ // Update customer's address on the subscription if it is changed during renewal
+ add_filter( 'woocommerce_checkout_update_user_meta', array( &$this, 'maybe_update_subscription_address_data' ), 10, 2 );
}
}
@@ -189,9 +191,10 @@ class WCS_Cart_Renewal {
}
/**
- * Set up cart item meta data for a to complete a subscription renewal via the cart.
+ * Set up cart item meta data to complete a subscription renewal via the cart.
*
- * @since 2.0
+ * @since 2.2.0
+ * @version 2.2.6
*/
protected function setup_cart( $subscription, $cart_item_data ) {
@@ -199,22 +202,43 @@ class WCS_Cart_Renewal {
$success = true;
foreach ( $subscription->get_items() as $item_id => $line_item ) {
- // Load all product info including variation data
- $product_id = (int) apply_filters( 'woocommerce_add_to_cart_product_id', $line_item['product_id'] );
- $quantity = (int) $line_item['qty'];
- $variation_id = (int) $line_item['variation_id'];
- $variations = array();
- $item_data = array();
- foreach ( $line_item['item_meta'] as $meta_name => $meta_value ) {
- if ( taxonomy_is_product_attribute( $meta_name ) ) {
- $variations[ $meta_name ] = $meta_value[0];
- } elseif ( meta_is_product_attribute( $meta_name, $meta_value[0], $product_id ) ) {
- $variations[ $meta_name ] = $meta_value[0];
+ $variations = array();
+ $item_data = array();
+
+ // Load all product info including variation data
+ if ( WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
+
+ $product_id = (int) $line_item['product_id'];
+ $quantity = (int) $line_item['qty'];
+ $variation_id = (int) $line_item['variation_id'];
+ $item_name = $line_item['name'];
+
+ foreach ( $line_item['item_meta'] as $meta_name => $meta_value ) {
+ if ( taxonomy_is_product_attribute( $meta_name ) ) {
+ $variations[ $meta_name ] = $meta_value[0];
+ } elseif ( meta_is_product_attribute( $meta_name, $meta_value[0], $product_id ) ) {
+ $variations[ $meta_name ] = $meta_value[0];
+ }
+ }
+ } else {
+
+ $product_id = $line_item->get_product_id();
+ $quantity = $line_item->get_quantity();
+ $variation_id = $line_item->get_variation_id();
+ $item_name = $line_item->get_name();
+
+ foreach ( $line_item->get_meta_data() as $meta ) {
+ if ( taxonomy_is_product_attribute( $meta->key ) ) {
+ $variations[ $meta->key ] = $meta->value;
+ } elseif ( meta_is_product_attribute( $meta->key, $meta->value, $product_id ) ) {
+ $variations[ $meta->key ] = $meta->value;
+ }
}
}
- $product = wc_get_product( $line_item['product_id'] );
+ $product_id = apply_filters( 'woocommerce_add_to_cart_product_id', $product_id );
+ $product = wc_get_product( $product_id );
// The notice displayed when a subscription product has been deleted and the custoemr attempts to manually renew or make a renewal payment for a failed recurring payment for that product/subscription
// translators: placeholder is an item name
@@ -223,16 +247,16 @@ class WCS_Cart_Renewal {
// Display error message for deleted products
if ( false === $product ) {
- wc_add_notice( sprintf( $product_deleted_error_message, $line_item['name'] ), 'error' );
+ wc_add_notice( sprintf( $product_deleted_error_message, $item_name ), 'error' );
// Make sure we don't actually need the variation ID (if the product was a variation, it will have a variation ID; however, if the product has changed from a simple subscription to a variable subscription, there will be no variation_id)
- } elseif ( $product->is_type( array( 'variable-subscription' ) ) && ! empty( $line_item['variation_id'] ) ) {
+ } elseif ( $product->is_type( array( 'variable-subscription' ) ) && ! empty( $variation_id ) ) {
$variation = wc_get_product( $variation_id );
// Display error message for deleted product variations
if ( false === $variation ) {
- wc_add_notice( sprintf( $product_deleted_error_message, $line_item['name'] ), 'error' );
+ wc_add_notice( sprintf( $product_deleted_error_message, $item_name ), 'error' );
}
}
@@ -279,7 +303,7 @@ class WCS_Cart_Renewal {
foreach ( $coupon_items as $coupon_item ) {
$coupon = new WC_Coupon( $coupon_item['name'] );
- $coupon_type = wcs_get_coupon_property( $coupon, 'type' );
+ $coupon_type = wcs_get_coupon_property( $coupon, 'discount_type' );
$coupon_code = '';
// If the coupon still exists we can use the existing/available coupon properties
@@ -290,9 +314,9 @@ class WCS_Cart_Renewal {
// Set the coupon type to be a renewal equivalent for correct validation and calculations
if ( 'recurring_percent' == $coupon_type ) {
- wcs_set_coupon_property( $coupon, 'type', 'renewal_percent' );
+ wcs_set_coupon_property( $coupon, 'discount_type', 'renewal_percent' );
} elseif ( 'recurring_fee' == $coupon_type ) {
- wcs_set_coupon_property( $coupon, 'type', 'renewal_fee' );
+ wcs_set_coupon_property( $coupon, 'discount_type', 'renewal_fee' );
}
// Adjust coupon code to reflect that it is being applied to a renewal
@@ -301,8 +325,8 @@ class WCS_Cart_Renewal {
} else {
// If the coupon doesn't exist we can only really apply the discount amount we know about - so we'll apply a cart style pseudo coupon and then set the amount
- wcs_set_coupon_property( $coupon, 'type', 'renewal_cart' );
- wcs_set_coupon_property( $coupon, 'amount', $coupon_item['item_meta']['discount_amount']['0'] );
+ wcs_set_coupon_property( $coupon, 'discount_type', 'renewal_cart' );
+ wcs_set_coupon_property( $coupon, 'coupon_amount', $coupon_item['item_meta']['discount_amount']['0'] );
// Adjust coupon code to reflect that it is being applied to a renewal
$coupon_code = wcs_get_coupon_property( $coupon, 'code' );
@@ -331,9 +355,9 @@ class WCS_Cart_Renewal {
$coupon = new WC_Coupon( 'discount_renewal' );
// Apply our cart style pseudo coupon and the set the amount
- wcs_set_coupon_property( $coupon, 'type', 'renewal_cart' );
+ wcs_set_coupon_property( $coupon, 'discount_type', 'renewal_cart' );
- wcs_set_coupon_property( $coupon, 'amount', $subscription_discount );
+ wcs_set_coupon_property( $coupon, 'coupon_amount', $subscription_discount );
// Set renewal order products as the product ids on the coupon
if ( ! WC_Subscriptions::is_woocommerce_pre( '2.5' ) ) {
@@ -432,7 +456,8 @@ class WCS_Cart_Renewal {
wcs_set_objects_property( $_product, 'subscription_sign_up_fee', 0, 'set_prop_only' );
// Allow plugins to add additional strings to the product name for renewals
- wcs_set_objects_property( $_product, 'name', apply_filters( 'woocommerce_subscriptions_renewal_product_title', $_product->get_title(), $_product ), 'set_prop_only' );
+ $line_item_name = is_callable( $item_to_renew, 'get_name' ) ? $item_to_renew->get_name() : $item_to_renew['name'];
+ wcs_set_objects_property( $_product, 'name', apply_filters( 'woocommerce_subscriptions_renewal_product_title', $line_item_name, $_product ), 'set_prop_only' );
// Make sure the same quantity is renewed
$cart_item_session_data['quantity'] = $item_to_renew['qty'];
@@ -744,58 +769,22 @@ class WCS_Cart_Renewal {
foreach ( $renewal_coupons as $subscription_id => $coupons ) {
- foreach ( $coupons as $coupon ) {
+ foreach ( $coupons as $coupon_code => $coupon_properties ) {
// Tweak the coupon data for renewal coupons
- if ( wcs_get_coupon_property( $coupon, 'code' ) == $code ) {
+ if ( $coupon_code == $code ) {
+ $expiry_date_property = WC_Subscriptions::is_woocommerce_pre( '3.0' ) ? 'expiry_date' : 'date_expires';
- $data = array(
- 'id' => true,
- 'discount_type' => wcs_get_coupon_property( $coupon, 'type' ),
- 'amount' => wcs_get_coupon_property( $coupon, 'amount' ),
- 'individual_use' => ( $individual_use = wcs_get_coupon_property( $coupon, 'individual_use' ) ) ? $individual_use : false,
- 'product_ids' => ( $product_ids = wcs_get_coupon_property( $coupon, 'product_ids' ) ) ? $product_ids : array(),
- 'excluded_product_ids' => ( $excluded_product_ids = wcs_get_coupon_property( $coupon, 'exclude_product_ids' ) ) ? $excluded_product_ids : array(),
- 'usage_limit' => '',
- 'usage_count' => '',
- 'date_expires' => '',
- 'free_shipping' => ( $free_shipping = wcs_get_coupon_property( $coupon, 'free_shipping' ) ) ? $free_shipping : false,
- 'product_categories' => ( $product_categories = wcs_get_coupon_property( $coupon, 'product_categories' ) ) ? $product_categories : array(),
- 'excluded_product_categories' => ( $excluded_product_categories = wcs_get_coupon_property( $coupon, 'exclude_product_categories' ) ) ? $excluded_product_categories : array(),
- 'exclude_sale_items' => ( $exclude_sale_items = wcs_get_coupon_property( $coupon, 'exclude_sale_items' ) ) ? $exclude_sale_items : false,
- 'minimum_amount' => ( $minimum_amount = wcs_get_coupon_property( $coupon, 'minimum_amount' ) ) ? $minimum_amount : '',
- 'maximum_amount' => ( $maximum_amount = wcs_get_coupon_property( $coupon, 'maximum_amount' ) ) ? $maximum_amount : '',
- 'customer_email' => ( $customer_email = wcs_get_coupon_property( $coupon, 'customer_email' ) ) ? $customer_email : array(),
+ // Some coupon properties are overridden specifically for renewals
+ $renewal_coupon_overrides = array(
+ 'id' => true,
+ 'usage_limit' => '',
+ 'usage_count' => '',
+ $expiry_date_property => '',
);
- if ( WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
-
- // Pre 3.0 we don't need to pass the id.
- unset( $data['id'] );
-
- // Some keys have changed between WC 2.6.x and WC 3.0. This array holds those changes in a 2.6 => 3.0 format.
- $property_changes = array(
- 'coupon_amount' => 'amount',
- 'exclude_product_ids' => 'excluded_product_ids',
- 'expiry_date' => 'date_expires',
- 'exclude_product_categories' => 'excluded_product_categories',
- 'customer_email' => 'email_restrictions',
- );
-
- foreach ( $data as $key => $value ) {
-
- // Switch the 3.0 key out for the 2.6 equivalent
- if ( in_array( $key, $property_changes ) ) {
- $data[ array_search( $key, $property_changes ) ] = $value;
- unset( $data[ $key ] );
- }
-
- // Some coupon properties have changed from accepting 'no' and 'yes' to true and false args. We need to change them into the correct format
- if ( is_bool( $value ) && in_array( $key, array( 'individual_use', 'free_shipping', 'exclude_sale_items' ) ) ) {
- $data[ $key ] = ( true == $value ) ? 'yes' : 'no';
- }
- }
- }
+ $data = array_merge( $coupon_properties, $renewal_coupon_overrides );
+ break 2;
}
}
}
@@ -835,14 +824,54 @@ class WCS_Cart_Renewal {
*/
protected function store_coupon( $subscription_id, $coupon ) {
if ( ! empty( $subscription_id ) && ! empty( $coupon ) ) {
+ $renewal_coupons = WC()->session->get( 'wcs_renewal_coupons', array() );
+ $use_bools = WC_Subscriptions::is_woocommerce_pre( '3.0' ); // Some coupon properties have changed from accepting 'no' and 'yes' to true and false args.
+ $coupon_properties = array();
+ $property_defaults = array(
+ 'discount_type' => '',
+ 'amount' => 0,
+ 'individual_use' => ( $use_bools ) ? false : 'no',
+ 'product_ids' => array(),
+ 'excluded_product_ids' => array(),
+ 'free_shipping' => ( $use_bools ) ? false : 'no',
+ 'product_categories' => array(),
+ 'excluded_product_categories' => array(),
+ 'exclude_sale_items' => ( $use_bools ) ? false : 'no',
+ 'minimum_amount' => '',
+ 'maximum_amount' => '',
+ 'email_restrictions' => array(),
+ );
- $renewal_coupons = WC()->session->get( 'wcs_renewal_coupons', array() );
+ foreach ( $property_defaults as $property => $value ) {
+ $getter = 'get_' . $property;
- // Subscriptions may have multiple coupons, store coupons in array
+ if ( is_callable( array( $coupon, $getter ) ) ) {
+ $value = $coupon->$getter();
+ } else { // WC < 3.0
+ // Map the property to its version compatible name ( 3.0+ => WC < 3.0 )
+ $getter_to_property_map = array(
+ 'amount' => 'coupon_amount',
+ 'excluded_product_ids' => 'exclude_product_ids',
+ 'date_expires' => 'expiry_date',
+ 'excluded_product_categories' => 'exclude_product_categories',
+ 'email_restrictions' => 'customer_email',
+ );
+
+ $property = array_key_exists( $property, $getter_to_property_map ) ? $getter_to_property_map[ $property ] : $property;
+
+ if ( property_exists( $coupon, $property ) ) {
+ $value = $coupon->$property;
+ }
+ }
+
+ $coupon_properties[ $property ] = $value;
+ }
+
+ // Subscriptions may have multiple coupons, store coupons in an array
if ( array_key_exists( $subscription_id, $renewal_coupons ) ) {
- $renewal_coupons[ $subscription_id ][] = $coupon;
+ $renewal_coupons[ $subscription_id ][ wcs_get_coupon_property( $coupon, 'code' ) ] = $coupon_properties;
} else {
- $renewal_coupons[ $subscription_id ] = array( $coupon );
+ $renewal_coupons[ $subscription_id ] = array( wcs_get_coupon_property( $coupon, 'code' ) => $coupon_properties );
}
WC()->session->set( 'wcs_renewal_coupons', $renewal_coupons );
@@ -861,8 +890,8 @@ class WCS_Cart_Renewal {
// Remove the coupons from the cart
if ( ! empty( $renewal_coupons ) ) {
foreach ( $renewal_coupons as $subscription_id => $coupons ) {
- foreach ( $coupons as $coupon ) {
- WC()->cart->remove_coupons( wcs_get_coupon_property( $coupon, 'code' ) );
+ foreach ( $coupons as $coupon_code => $coupon_properties ) {
+ WC()->cart->remove_coupons( $coupon_code );
}
}
}
@@ -1081,6 +1110,39 @@ class WCS_Cart_Renewal {
return $hidden_meta_keys;
}
+ /**
+ * When completing checkout for a subscription renewal, update the subscription's address to match
+ * the shipping/billing address entered on checkout.
+ *
+ * @param int $customer_id
+ * @param array $checkout_data the posted checkout data
+ * @since 2.2.7
+ */
+ public function maybe_update_subscription_address_data( $customer_id, $checkout_data ) {
+ $cart_renewal_item = $this->cart_contains();
+
+ if ( false !== $cart_renewal_item ) {
+ $subscription = wcs_get_subscription( $cart_renewal_item[ $this->cart_item_key ]['subscription_id'] );
+ $billing_address = $shipping_address = array();
+
+ foreach ( array( 'billing', 'shipping' ) as $address_type ) {
+ $checkout_fields = WC()->checkout()->get_checkout_fields( $address_type );
+
+ if ( is_array( $checkout_fields ) ) {
+ foreach ( array_keys( $checkout_fields ) as $field ) {
+ if ( isset( $checkout_data[ $field ] ) ) {
+ $field_name = str_replace( $address_type. '_', '', $field );
+ ${$address_type . '_address'}[ $field_name ] = $checkout_data[ $field ];
+ }
+ }
+ }
+ }
+
+ $subscription->set_address( $billing_address, 'billing' );
+ $subscription->set_address( $shipping_address, 'shipping' );
+ }
+ }
+
/* Deprecated */
/**
diff --git a/includes/class-wcs-cart-switch.php b/includes/class-wcs-cart-switch.php
index 2c6b56c..88ab0f4 100644
--- a/includes/class-wcs-cart-switch.php
+++ b/includes/class-wcs-cart-switch.php
@@ -27,6 +27,19 @@ class WCS_Cart_Switch extends WCS_Cart_Renewal {
add_action( 'template_redirect', array( &$this, 'maybe_setup_cart' ), 99 );
}
+ /**
+ * Attach WooCommerce version dependent hooks
+ *
+ * @since 2.2.0
+ */
+ public function attach_dependant_hooks() {
+ parent::attach_dependant_hooks();
+
+ // Remove version dependent callbacks which don't apply to switch carts
+ remove_filter( 'woocommerce_checkout_update_customer_data', array( &$this, 'maybe_update_subscription_customer_data' ), 10 );
+ remove_filter( 'woocommerce_checkout_update_user_meta', array( &$this, 'maybe_update_subscription_address_data' ), 10 );
+ }
+
/**
* Add flag to payment url for failed/ pending switch orders.
*
diff --git a/includes/class-wcs-change-payment-method-admin.php b/includes/class-wcs-change-payment-method-admin.php
index 67aabfc..22dfce8 100644
--- a/includes/class-wcs-change-payment-method-admin.php
+++ b/includes/class-wcs-change-payment-method-admin.php
@@ -109,7 +109,7 @@ class WCS_Change_Payment_Method_Admin {
$valid_payment_methods = self::get_valid_payment_methods( $subscription );
- if ( ! isset( $valid_payment_methods[ $payment_method ] ) ) {
+ if ( ! isset( $valid_payment_methods[ $payment_method ] ) && ! ( isset( $payment_gateways[ $payment_method ] ) && $subscription->get_payment_method() == $payment_gateways[ $payment_method ]->id ) ) {
throw new Exception( __( 'Please choose a valid payment gateway to change to.', 'woocommerce-subscriptions' ) );
}
diff --git a/includes/class-wcs-my-account-payment-methods.php b/includes/class-wcs-my-account-payment-methods.php
new file mode 100644
index 0000000..3b0a2d4
--- /dev/null
+++ b/includes/class-wcs-my-account-payment-methods.php
@@ -0,0 +1,239 @@
+ $payment_token->get_id(),
+ 'wcs_nonce' => wp_create_nonce( 'delete_subscription_token_' . $payment_token->get_id() ),
+ );
+
+ $payment_token_data['actions']['delete']['url'] = add_query_arg( $delete_subscription_token_args, $payment_token_data['actions']['delete']['url'] );
+ } else {
+ // Cannot delete a token used for active subscriptions where there is no alternative
+ unset( $payment_token_data['actions']['delete'] );
+ }
+ }
+ }
+
+ return $payment_token_data;
+ }
+
+ /**
+ * Update subscriptions using a deleted token to use a new token. Subscriptions with the
+ * old token value stored in post meta will be updated using the same meta key to use the
+ * new token value.
+ *
+ * @param int The deleted token id
+ * @param WC_Payment_Token The deleted token object
+ * @since 2.2.7
+ */
+ public static function maybe_update_subscriptions_payment_meta( $deleted_token_id, $deleted_token ) {
+ if ( isset( $_GET['delete_subscription_token'] ) && ! empty( $_GET['wcs_nonce'] ) && wp_verify_nonce( $_GET['wcs_nonce'], 'delete_subscription_token_' . $_GET['delete_subscription_token'] ) ) {
+ // init payment gateways
+ WC()->payment_gateways();
+
+ $new_token = self::get_customers_alternative_token( $deleted_token );
+
+ if ( empty( $new_token ) ) {
+ $notice = esc_html__( 'The deleted payment method was used for automatic subscription payments, we couldn\'t find an alternative token payment method token to change your subscriptions to.', 'woocommerce-subscriptions' );
+ wc_add_notice( $notice, 'error' );
+ return;
+ }
+
+ $subscriptions = self::get_subscriptions_by_token( $deleted_token );
+ $token_meta_key = '';
+ $notice_displayed = false;
+
+ if ( ! empty( $subscriptions ) ) {
+ // translators: $1: the token/credit card label, 2$-3$: opening and closing strong and link tags
+ $notice = sprintf( esc_html__( 'The deleted payment method was used for automatic subscription payments. To avoid failed renewal payments in future the subscriptions using this payment method have been updated to use your %1$s. To change the payment method of individual subscriptions go to your %2$sMy Account > Subscriptions%3$s page.', 'woocommerce-subscriptions' ),
+ self::get_token_label( $new_token ),
+ '',
+ ' '
+ );
+
+ wc_add_notice( $notice , 'notice' );
+ foreach ( $subscriptions as $subscription ) {
+ $subscription = wcs_get_subscription( $subscription );
+
+ if ( empty( $subscription ) ) {
+ continue;
+ }
+
+ // Attempt to find the token meta key if we haven't already found it.
+ if ( empty( $token_meta_key ) ) {
+ $payment_method_meta = apply_filters( 'woocommerce_subscription_payment_meta', array(), $subscription );
+
+ if ( is_array( $payment_method_meta ) && isset( $payment_method_meta[ $deleted_token->get_gateway_id() ] ) && is_array( $payment_method_meta[ $deleted_token->get_gateway_id() ] ) ) {
+ foreach ( $payment_method_meta[ $deleted_token->get_gateway_id() ] as $meta_table => $meta ) {
+ foreach ( $meta as $meta_key => $meta_data ) {
+ if ( $deleted_token->get_token() === $meta_data['value'] ) {
+ $token_meta_key = $meta_key;
+ break 2;
+ }
+ }
+ }
+ }
+ }
+
+ $updated = update_post_meta( $subscription->get_id(), $token_meta_key, $new_token->get_token(), $deleted_token->get_token() );
+
+ if ( $updated ) {
+ $subscription->add_order_note( sprintf( _x( 'Payment method meta updated after customer deleted a token from their My Account page. Payment meta changed from %1$s to %2$s', 'used in subscription note', 'woocommerce-subscriptions' ), $deleted_token->get_token(), $new_token->get_token() ) );
+ do_action( 'woocommerce_subscription_token_changed', $subscription, $new_token, $deleted_token );
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Get subscriptions by a WC_Payment_Token. All automatic subscriptions with the token's payment method,
+ * customer id and token value stored in post meta will be returned.
+ *
+ * @param WC_Payment_Token payment token object
+ * @return array subscription posts
+ * @since 2.2.7
+ */
+ public static function get_subscriptions_by_token( $payment_token ) {
+
+ $meta_query = array(
+ array(
+ 'key' => '_payment_method',
+ 'value' => $payment_token->get_gateway_id(),
+ ),
+ array(
+ 'key' => '_requires_manual_renewal',
+ 'value' => 'false',
+ ),
+ array(
+ 'key' => '_customer_user',
+ 'value' => $payment_token->get_user_id(),
+ 'type' => 'numeric',
+ ),
+ array(
+ 'value' => $payment_token->get_token(),
+ ),
+ );
+
+ $user_subscriptions = get_posts( array(
+ 'post_type' => 'shop_subscription',
+ 'post_status' => array( 'wc-pending', 'wc-active', 'wc-on-hold' ),
+ 'meta_query' => $meta_query,
+ 'posts_per_page' => -1,
+ ) );
+
+ return apply_filters( 'woocommerce_subscriptions_by_payment_token', $user_subscriptions, $payment_token );
+ }
+
+ /**
+ * Get a WC_Payment_Token label. eg Visa ending in 1234
+ *
+ * @param WC_Payment_Token payment token object
+ * @return string WC_Payment_Token label
+ * @since 2.2.7
+ */
+ public static function get_token_label( $token ) {
+
+ if ( method_exists( $token, 'get_last4' ) && $token->get_last4() ) {
+ $label = sprintf( __( '%s ending in %s', 'woocommerce-subscriptions' ), esc_html( wc_get_credit_card_type_label( $token->get_card_type() ) ), esc_html( $token->get_last4() ) );
+ } else {
+ $label = esc_html( wc_get_credit_card_type_label( $token->get_card_type() ) );
+ }
+
+ return $label;
+ }
+
+ /**
+ * Get a list of customer payment tokens. Caches results to avoid multiple database queries per request
+ *
+ * @param string (optional) Gateway ID for getting tokens for a specific gateway.
+ * @param int (optional) The customer id - defaults to the current user.
+ * @return array of WC_Payment_Token objects
+ * @since 2.2.7
+ */
+ public static function get_customer_tokens( $gateway_id = '', $customer_id = '' ) {
+ if ( '' === $customer_id ) {
+ $customer_id = get_current_user_id();
+ }
+
+ if ( ! isset( self::$customer_tokens[ $customer_id ][ $gateway_id ] ) ) {
+ self::$customer_tokens[ $customer_id ][ $gateway_id ] = WC_Payment_Tokens::get_customer_tokens( $customer_id, $gateway_id );
+ }
+
+ return self::$customer_tokens[ $customer_id ][ $gateway_id ];
+ }
+
+ /**
+ * Get the customer's alternative token.
+ *
+ * @param WC_Payment_Token the token to find an alternative for
+ * @return WC_Payment_Token the customer's alternative token
+ * @since 2.2.7
+ */
+ public static function get_customers_alternative_token( $token ) {
+ $payment_tokens = self::get_customer_tokens( $token->get_gateway_id(), $token->get_user_id() );
+ $alternative_token = null;
+ $has_single_alternative = count( $payment_tokens ) === 2; // if there are 2 tokens in total there is only 1 other alternative
+
+ foreach ( $payment_tokens as $payment_token ) {
+ // if there is a default token which is different we can use it as an alternative.
+ if ( $payment_token->get_id() !== $token->get_id() && ( $payment_token->is_default() || $has_single_alternative ) ) {
+ $alternative_token = $payment_token;
+ break;
+ }
+ }
+
+ return $alternative_token;
+ }
+
+ /**
+ * Determine if the customer has an alternative token.
+ *
+ * @param WC_Payment_Token payment token object
+ * @return bool
+ * @since 2.2.7
+ */
+ public static function customer_has_alternative_token( $token ) {
+ return self::get_customers_alternative_token( $token ) !== null;
+ }
+}
+WCS_My_Account_Payment_Methods::init();
diff --git a/includes/class-wcs-retry-manager.php b/includes/class-wcs-retry-manager.php
index b5b12ec..7a26d2a 100644
--- a/includes/class-wcs-retry-manager.php
+++ b/includes/class-wcs-retry-manager.php
@@ -145,6 +145,7 @@ class WCS_Retry_Manager {
// If the new status isn't the expected retry subscription status and we aren't in the process of applying a retry rule or retrying payment, cancel the retry
if ( $new_status != $retry_subscription_status && ! $applying_retry_rule && ! $retrying_payment ) {
$last_retry->update_status( 'cancelled' );
+ $subscription->delete_date( 'payment_retry' );
}
}
}
diff --git a/includes/class-wcs-webhooks.php b/includes/class-wcs-webhooks.php
index 070a7e0..7e005f1 100644
--- a/includes/class-wcs-webhooks.php
+++ b/includes/class-wcs-webhooks.php
@@ -26,7 +26,7 @@ class WCS_Webhooks {
*/
public static function init() {
- add_filter( 'woocommerce_webhook_topic_hooks', __CLASS__ . '::add_topics', 10, 2 );
+ add_filter( 'woocommerce_webhook_topic_hooks', __CLASS__ . '::add_topics', 20, 2 );
add_filter( 'woocommerce_webhook_payload', __CLASS__ . '::create_payload', 10, 4 );
@@ -42,6 +42,20 @@ class WCS_Webhooks {
add_filter( 'woocommerce_webhook_topics' , __CLASS__ . '::add_topics_admin_menu', 10, 1 );
+ add_filter( 'wcs_new_order_created', __CLASS__ . '::add_subscription_created_order_callback', 10, 1 );
+
+ }
+
+ /**
+ * Trigger `order.create` every time an order is created by Subscriptions.
+ *
+ * @param WC_Order $order WC_Order Object
+ */
+ public static function add_subscription_created_order_callback( $order ) {
+
+ do_action( 'wcs_webhook_order_created', wcs_get_objects_property( $order, 'id' ) );
+
+ return $order;
}
/**
@@ -52,28 +66,34 @@ class WCS_Webhooks {
*/
public static function add_topics( $topic_hooks, $webhook ) {
- if ( 'subscription' == $webhook->get_resource() ) {
- $topic_hooks = apply_filters( 'woocommerce_subscriptions_webhook_topics', array(
- 'subscription.created' => array(
- 'wcs_api_subscription_created',
- 'wcs_webhook_subscription_created',
- 'woocommerce_process_shop_subscription_meta',
- ),
- 'subscription.updated' => array(
- 'wc_api_subscription_updated',
- 'woocommerce_subscription_status_changed',
- 'wcs_webhook_subscription_updated',
- 'woocommerce_process_shop_subscription_meta',
- ),
- 'subscription.deleted' => array(
- 'woocommerce_subscription_trashed',
- 'woocommerce_subscription_deleted',
- 'woocommerce_api_delete_subscription',
- ),
- 'subscription.switched' => array(
- 'wcs_webhook_subscription_switched',
- ),
- ), $webhook );
+ switch ( $webhook->get_resource() ) {
+ case 'order':
+ $topic_hooks['order.created'][] = 'wcs_webhook_order_created';
+ break;
+
+ case 'subscription':
+ $topic_hooks = apply_filters( 'woocommerce_subscriptions_webhook_topics', array(
+ 'subscription.created' => array(
+ 'wcs_api_subscription_created',
+ 'wcs_webhook_subscription_created',
+ 'woocommerce_process_shop_subscription_meta',
+ ),
+ 'subscription.updated' => array(
+ 'wc_api_subscription_updated',
+ 'woocommerce_subscription_status_changed',
+ 'wcs_webhook_subscription_updated',
+ 'woocommerce_process_shop_subscription_meta',
+ ),
+ 'subscription.deleted' => array(
+ 'woocommerce_subscription_trashed',
+ 'woocommerce_subscription_deleted',
+ 'woocommerce_api_delete_subscription',
+ ),
+ 'subscription.switched' => array(
+ 'wcs_webhook_subscription_switched',
+ ),
+ ), $webhook );
+ break;
}
return $topic_hooks;
diff --git a/includes/data-stores/class-wcs-subscription-data-store-cpt.php b/includes/data-stores/class-wcs-subscription-data-store-cpt.php
index cd3c799..fb21507 100644
--- a/includes/data-stores/class-wcs-subscription-data-store-cpt.php
+++ b/includes/data-stores/class-wcs-subscription-data-store-cpt.php
@@ -259,4 +259,54 @@ class WCS_Subscription_Data_Store_CPT extends WC_Order_Data_Store_CPT implements
return $return;
}
}
+
+ /**
+ * Update subscription dates in the database.
+ *
+ * @param WC_Subscription $subscription
+ * @return array The date properties saved to the database in the format: array( $prop_name => DateTime Object )
+ * @since 2.2.6
+ */
+ public function save_dates( $subscription ) {
+ $saved_dates = array();
+ $changes = $subscription->get_changes();
+ $date_meta_keys = array(
+ '_schedule_trial_end',
+ '_schedule_next_payment',
+ '_schedule_cancelled',
+ '_schedule_end',
+ '_schedule_payment_retry',
+ );
+
+ $date_meta_keys_to_props = array_intersect_key( $this->subscription_meta_keys_to_props, array_flip( $date_meta_keys ) );
+
+ // Save the changes to scheduled dates
+ foreach ( $this->get_props_to_update( $subscription, $date_meta_keys_to_props ) as $meta_key => $prop ) {
+ update_post_meta( $subscription->get_id(), $meta_key, $subscription->get_date( $prop ) );
+ $saved_dates[ $prop ] = wcs_get_datetime_from( $subscription->get_time( $prop ) );
+ }
+
+ $post_data = array();
+
+ // Save any changes to the created date
+ if ( isset( $changes['date_created'] ) ) {
+ $post_data['post_date'] = gmdate( 'Y-m-d H:i:s', $subscription->get_date_created( 'edit' )->getOffsetTimestamp() );
+ $post_data['post_date_gmt'] = gmdate( 'Y-m-d H:i:s', $subscription->get_date_created( 'edit' )->getTimestamp() );
+ $saved_dates['date_created'] = $subscription->get_date_created();
+ }
+
+ // Save any changes to the modified date
+ if ( isset( $changes['date_modified'] ) ) {
+ $post_data['post_modified'] = gmdate( 'Y-m-d H:i:s', $subscription->get_date_modified( 'edit' )->getOffsetTimestamp() );
+ $post_data['post_modified_gmt'] = gmdate( 'Y-m-d H:i:s', $subscription->get_date_modified( 'edit' )->getTimestamp() );
+ $saved_dates['date_modified'] = $subscription->get_date_modified();
+ }
+
+ if ( ! empty( $post_data ) ) {
+ $post_data['ID'] = $subscription->get_id();
+ wp_update_post( $post_data );
+ }
+
+ return $saved_dates;
+ }
}
diff --git a/includes/gateways/paypal/includes/class-wcs-paypal-reference-transaction-ipn-handler.php b/includes/gateways/paypal/includes/class-wcs-paypal-reference-transaction-ipn-handler.php
index 62237c2..1d5f9f7 100644
--- a/includes/gateways/paypal/includes/class-wcs-paypal-reference-transaction-ipn-handler.php
+++ b/includes/gateways/paypal/includes/class-wcs-paypal-reference-transaction-ipn-handler.php
@@ -117,11 +117,14 @@ class WCS_PayPal_Reference_Transaction_IPN_Handler extends WCS_PayPal_Standard_I
$subscription = wcs_get_subscription( $subscription_id );
+ // Only cancel valid subscriptions using PayPal as the payment method that have not yet been ended
+ if ( false == $subscription || $subscription->is_manual() || 'paypal' != $subscription->get_payment_method() || $subscription->has_status( wcs_get_subscription_ended_statuses() ) ) {
+ continue;
+ }
+
try {
- if ( false !== $subscription && ! $subscription->has_status( wcs_get_subscription_ended_statuses() ) ) {
- $subscription->cancel_order( $note );
- WC_Gateway_Paypal::log( sprintf( 'Subscription %s Cancelled: %s', $subscription_id, $note ) );
- }
+ $subscription->cancel_order( $note );
+ WC_Gateway_Paypal::log( sprintf( 'Subscription %s Cancelled: %s', $subscription_id, $note ) );
} catch ( Exception $e ) {
WC_Gateway_Paypal::log( sprintf( 'Unable to cancel subscription %s: %s', $subscription_id, $e->getMessage() ) );
}
diff --git a/includes/gateways/paypal/includes/class-wcs-paypal-standard-ipn-handler.php b/includes/gateways/paypal/includes/class-wcs-paypal-standard-ipn-handler.php
index d07dce0..6b91884 100644
--- a/includes/gateways/paypal/includes/class-wcs-paypal-standard-ipn-handler.php
+++ b/includes/gateways/paypal/includes/class-wcs-paypal-standard-ipn-handler.php
@@ -103,29 +103,42 @@ class WCS_PayPal_Standard_IPN_Handler extends WC_Gateway_Paypal_IPN_Handler {
exit;
}
- if ( isset( $transaction_details['ipn_track_id'] ) ) {
+ if ( isset( $transaction_details['txn_id'] ) ) {
// Make sure the IPN request has not already been handled
- $handled_ipn_requests = get_post_meta( $subscription->get_id(), '_paypal_ipn_tracking_ids', true );
+ $handled_transactions = get_post_meta( $subscription->get_id(), '_paypal_ipn_tracking_ids', true );
- if ( empty( $handled_ipn_requests ) ) {
- $handled_ipn_requests = array();
+ if ( empty( $handled_transactions ) ) {
+ $handled_transactions = array();
}
- // The 'ipn_track_id' is not a unique ID and is shared between different transaction types, so create a unique ID by prepending the transaction type
- $ipn_id = $transaction_details['txn_type'] . '_' . $transaction_details['ipn_track_id'];
+ // $ipn_transaction_id will be 'txn_id'_'txn_type'_'payment_status'_'ipn_track_id'
+ $ipn_transaction_id = $transaction_details['txn_id'];
- if ( in_array( $ipn_id, $handled_ipn_requests ) ) {
- WC_Gateway_Paypal::log( 'Subscription IPN Error: IPN ' . $ipn_id . ' message has already been correctly handled.' );
+ if ( isset( $transaction_details['txn_type'] ) ) {
+ $ipn_transaction_id .= '_' . $transaction_details['txn_type'];
+ }
+
+ // The same transaction ID is used for different payment statuses, so make sure we handle it only once. See: http://stackoverflow.com/questions/9240235/paypal-ipn-unique-identifier
+ if ( isset( $transaction_details['payment_status'] ) ) {
+ $ipn_transaction_id .= '_' . $transaction_details['payment_status'];
+ }
+
+ if ( isset( $transaction_details['ipn_track_id'] ) ) {
+ $ipn_transaction_id .= '_' . $transaction_details['ipn_track_id'];
+ }
+
+ if ( in_array( $ipn_transaction_id, $handled_transactions ) ) {
+ WC_Gateway_Paypal::log( 'Subscription IPN Error: transaction ' . $ipn_transaction_id . ' has already been correctly handled.' );
exit;
}
// Make sure we're not in the process of handling this IPN request on a server under extreme load and therefore, taking more than a minute to process it (which is the amount of time PayPal allows before resending the IPN request)
- $ipn_lock_transient_name = 'wcs_pp_' . $ipn_id; // transient names need to be less than 45 characters and the $ipn_id will be around 30 characters, e.g. subscr_payment_5ab4c38e1f39d
+ $ipn_lock_transient_name = 'wcs_pp_' . md5( $ipn_transaction_id ); // transient names need to be less than 45 characters and the $ipn_id will be long, e.g. 34292625HU746553V_subscr_payment_completed_5ab4c38e1f39d, so md5
if ( 'in-progress' == get_transient( $ipn_lock_transient_name ) && 'recurring_payment_suspended_due_to_max_failed_payment' !== $transaction_details['txn_type'] ) {
- WC_Gateway_Paypal::log( 'Subscription IPN Error: an older IPN request with ID ' . $ipn_id . ' is still in progress.' );
+ WC_Gateway_Paypal::log( 'Subscription IPN Error: an older IPN request with ID ' . $ipn_transaction_id . ' is still in progress.' );
// We need to send an error code to make sure PayPal does retry the IPN after our lock expires, in case something is actually going wrong and the server isn't just taking a long time to process the request
status_header( 503 );
@@ -134,33 +147,6 @@ class WCS_PayPal_Standard_IPN_Handler extends WC_Gateway_Paypal_IPN_Handler {
// Set a transient to block IPNs with this transaction ID for the next 4 days (An IPN message may be present in PayPal up to 4 days after the original was sent)
set_transient( $ipn_lock_transient_name, 'in-progress', apply_filters( 'woocommerce_subscriptions_paypal_ipn_request_lock_time', 4 * DAY_IN_SECONDS ) );
-
- }
-
- if ( isset( $transaction_details['txn_id'] ) ) {
-
- // Make sure the IPN request has not already been handled
- $handled_transactions = get_post_meta( $subscription->get_id(), '_paypal_transaction_ids', true );
-
- if ( empty( $handled_transactions ) ) {
- $handled_transactions = array();
- }
-
- $transaction_id = $transaction_details['txn_id'];
-
- if ( isset( $transaction_details['txn_type'] ) ) {
- $transaction_id .= '_' . $transaction_details['txn_type'];
- }
-
- // The same transaction ID is used for different payment statuses, so make sure we handle it only once. See: http://stackoverflow.com/questions/9240235/paypal-ipn-unique-identifier
- if ( isset( $transaction_details['payment_status'] ) ) {
- $transaction_id .= '_' . $transaction_details['payment_status'];
- }
-
- if ( in_array( $transaction_id, $handled_transactions ) ) {
- WC_Gateway_Paypal::log( 'Subscription IPN Error: transaction ' . $transaction_id . ' has already been correctly handled.' );
- exit;
- }
}
$is_renewal_sign_up_after_failure = false;
@@ -491,14 +477,9 @@ class WCS_PayPal_Standard_IPN_Handler extends WC_Gateway_Paypal_IPN_Handler {
}
// Store the transaction IDs to avoid handling requests duplicated by PayPal
- if ( isset( $transaction_details['ipn_track_id'] ) ) {
- $handled_ipn_requests[] = $ipn_id;
- update_post_meta( $subscription->get_id(), '_paypal_ipn_tracking_ids', $handled_ipn_requests );
- }
-
if ( isset( $transaction_details['txn_id'] ) ) {
- $handled_transactions[] = $transaction_id;
- update_post_meta( $subscription->get_id(), '_paypal_transaction_ids', $handled_transactions );
+ $handled_transactions[] = $ipn_transaction_id;
+ update_post_meta( $subscription->get_id(), '_paypal_ipn_tracking_ids', $handled_transactions );
}
// And delete the transient that's preventing other IPN's being processed
diff --git a/includes/gateways/paypal/includes/class-wcs-paypal-standard-request.php b/includes/gateways/paypal/includes/class-wcs-paypal-standard-request.php
index db055a2..754f208 100644
--- a/includes/gateways/paypal/includes/class-wcs-paypal-standard-request.php
+++ b/includes/gateways/paypal/includes/class-wcs-paypal-standard-request.php
@@ -33,13 +33,15 @@ class WCS_PayPal_Standard_Request {
// Payment method changes act on the subscription not the original order
if ( $is_payment_change ) {
- $subscriptions = array( wcs_get_subscription( wcs_get_objects_property( $order, 'id' ) ) );
- $subscription = array_pop( $subscriptions );
- $order = $subscription->get_parent();
+ $subscription = wcs_get_subscription( wcs_get_objects_property( $order, 'id' ) );
+ $order = $subscription->get_parent();
// We need the subscription's total
- remove_filter( 'woocommerce_order_amount_total', 'WC_Subscriptions_Change_Payment_Gateway::maybe_zero_total', 11, 2 );
-
+ if ( WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
+ remove_filter( 'woocommerce_order_amount_total', 'WC_Subscriptions_Change_Payment_Gateway::maybe_zero_total', 11, 2 );
+ } else {
+ remove_filter( 'woocommerce_subscription_get_total', 'WC_Subscriptions_Change_Payment_Gateway::maybe_zero_total', 11, 2 );
+ }
} else {
// Otherwise the order is the $order
@@ -59,9 +61,6 @@ class WCS_PayPal_Standard_Request {
// 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' => wcs_get_objects_property( $order, 'id' ), 'order_key' => wcs_get_objects_property( $order, 'order_key' ), 'subscription_id' => $subscription->get_id(), 'subscription_key' => $subscription->get_order_key() ) );
-
foreach ( $subscription->get_items() as $item ) {
if ( $item['qty'] > 1 ) {
$item_names[] = $item['qty'] . ' x ' . wcs_get_paypal_item_name( $item['name'] );
@@ -70,8 +69,14 @@ class WCS_PayPal_Standard_Request {
}
}
- // 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 ) ) );
+ // Subscription imported or manually added via admin so doesn't have a parent order
+ if ( empty( $order ) ) {
+ // translators: 1$: subscription ID, 2$: names of items, comma separated
+ $paypal_args['item_name'] = wcs_get_paypal_item_name( sprintf( _x( 'Subscription %1$s - %2$s', 'item name sent to paypal', 'woocommerce-subscriptions' ), $subscription->get_order_number(), implode( ', ', $item_names ) ) );
+ } else {
+ // 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->get_billing_period(),
@@ -147,6 +152,10 @@ class WCS_PayPal_Standard_Request {
$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->get_id(), 'subscription_key' => $subscription->get_order_key() ) ) );
+ } else {
+
+ // 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' => wcs_get_objects_property( $order, 'id' ), 'order_key' => wcs_get_objects_property( $order, 'order_key' ), 'subscription_id' => $subscription->get_id(), 'subscription_key' => $subscription->get_order_key() ) );
}
if ( $order_contains_failed_renewal ) {
@@ -264,7 +273,11 @@ class WCS_PayPal_Standard_Request {
// 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 );
+ if ( WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
+ add_filter( 'woocommerce_order_amount_total', 'WC_Subscriptions_Change_Payment_Gateway::maybe_zero_total', 11, 2 );
+ } else {
+ add_filter( 'woocommerce_subscription_get_total', 'WC_Subscriptions_Change_Payment_Gateway::maybe_zero_total', 11, 2 );
+ }
}
}
diff --git a/includes/legacy/class-wc-subscription-legacy.php b/includes/legacy/class-wc-subscription-legacy.php
index 78e62c3..4166d68 100644
--- a/includes/legacy/class-wc-subscription-legacy.php
+++ b/includes/legacy/class-wc-subscription-legacy.php
@@ -539,7 +539,8 @@ class WC_Subscription_Legacy extends WC_Subscription {
public function set_status( $new_status, $note = '', $manual_update = false ) {
$old_status = $this->get_status();
- $new_status = 'wc-' === substr( $new_status, 0, 3 ) ? substr( $new_status, 3 ) : $new_status;
+ $prefix = substr( $new_status, 0, 3 );
+ $new_status = 'wc-' === $prefix ? substr( $new_status, 3 ) : $new_status;
wp_update_post( array( 'ID' => $this->get_id(), 'post_status' => wcs_maybe_prefix_key( $new_status, 'wc-' ) ) );
$this->post_status = $this->post->post_status = wcs_maybe_prefix_key( $new_status, 'wc-' );
@@ -746,4 +747,14 @@ class WC_Subscription_Legacy extends WC_Subscription {
update_post_meta( $this->get_id(), $key, $value );
}
}
+
+ /**
+ * Save subscription date changes to the database.
+ * Nothing to do here as all date properties are saved when calling @see $this->set_prop().
+ *
+ * @since 2.2.6
+ */
+ public function save_dates() {
+ // Nothing to do here.
+ }
}
diff --git a/includes/libraries/action-scheduler/action-scheduler.php b/includes/libraries/action-scheduler/action-scheduler.php
index 4536553..6b8e8d5 100644
--- a/includes/libraries/action-scheduler/action-scheduler.php
+++ b/includes/libraries/action-scheduler/action-scheduler.php
@@ -5,24 +5,24 @@ Plugin URI: https://github.com/prospress/action-scheduler
Description: A robust action scheduler for WordPress
Author: Prospress
Author URI: http://prospress.com/
-Version: 1.5.2
+Version: 1.5.3
*/
-if ( ! function_exists( 'action_scheduler_register_1_dot_5_dot_2' ) ) {
+if ( ! function_exists( 'action_scheduler_register_1_dot_5_dot_3' ) ) {
if ( ! class_exists( 'ActionScheduler_Versions' ) ) {
require_once( 'classes/ActionScheduler_Versions.php' );
add_action( 'plugins_loaded', array( 'ActionScheduler_Versions', 'initialize_latest_version' ), 1, 0 );
}
- add_action( 'plugins_loaded', 'action_scheduler_register_1_dot_5_dot_2', 0, 0 );
+ add_action( 'plugins_loaded', 'action_scheduler_register_1_dot_5_dot_3', 0, 0 );
- function action_scheduler_register_1_dot_5_dot_2() {
+ function action_scheduler_register_1_dot_5_dot_3() {
$versions = ActionScheduler_Versions::instance();
- $versions->register( '1.5.2', 'action_scheduler_initialize_1_dot_5_dot_2' );
+ $versions->register( '1.5.3', 'action_scheduler_initialize_1_dot_5_dot_3' );
}
- function action_scheduler_initialize_1_dot_5_dot_2() {
+ function action_scheduler_initialize_1_dot_5_dot_3() {
require_once( 'classes/ActionScheduler.php' );
ActionScheduler::init( __FILE__ );
}
diff --git a/includes/libraries/action-scheduler/classes/ActionScheduler_AdminView.php b/includes/libraries/action-scheduler/classes/ActionScheduler_AdminView.php
index f67f0e6..fdfec0a 100644
--- a/includes/libraries/action-scheduler/classes/ActionScheduler_AdminView.php
+++ b/includes/libraries/action-scheduler/classes/ActionScheduler_AdminView.php
@@ -29,7 +29,7 @@ class ActionScheduler_AdminView {
*/
public function init() {
- if ( defined( 'WP_DEBUG' ) && true == WP_DEBUG && is_admin() && ( ! defined( 'DOING_AJAX' ) || false == DOING_AJAX ) ) {
+ if ( is_admin() && ( ! defined( 'DOING_AJAX' ) || false == DOING_AJAX ) ) {
add_filter( 'action_scheduler_post_type_args', array( self::instance(), 'action_scheduler_post_type_args' ) );
}
@@ -60,10 +60,10 @@ class ActionScheduler_AdminView {
public function action_scheduler_post_type_args( $args ) {
return array_merge( $args, array(
- 'show_ui' => true,
- 'show_in_menu' => 'tools.php',
+ 'show_ui' => true,
+ 'show_in_menu' => 'tools.php',
'show_in_admin_bar' => false,
- ));
+ ) );
}
/**
diff --git a/includes/libraries/action-scheduler/composer.json b/includes/libraries/action-scheduler/composer.json
new file mode 100644
index 0000000..1eb77f7
--- /dev/null
+++ b/includes/libraries/action-scheduler/composer.json
@@ -0,0 +1,9 @@
+{
+ "name": "prospress/action-scheduler",
+ "version": "1.5.3",
+ "description": "Action Scheduler for WordPress and WooCommerce",
+ "type": "wordpress-plugin",
+ "license": "GPL-3.0",
+ "minimum-stability": "dev",
+ "require": {}
+}
diff --git a/includes/payment-retry/class-wcs-retry-rules.php b/includes/payment-retry/class-wcs-retry-rules.php
index 5e13093..d0fab6d 100644
--- a/includes/payment-retry/class-wcs-retry-rules.php
+++ b/includes/payment-retry/class-wcs-retry-rules.php
@@ -89,13 +89,11 @@ class WCS_Retry_Rules {
$rule = null;
- if ( isset( $this->default_retry_rules[ $retry_number ] ) ) {
+ $rule_array = ( isset( $this->default_retry_rules[ $retry_number ] ) ) ? $this->default_retry_rules[ $retry_number ] : array();
+ $rule_array = apply_filters( 'wcs_get_retry_rule_raw', $rule_array, $retry_number, $order_id );
- $rule_array = apply_filters( 'wcs_get_retry_rule_raw', $this->default_retry_rules[ $retry_number ], $retry_number, $order_id );
-
- if ( ! empty( $rule_array ) ) {
- $rule = new $this->retry_rule_class( $rule_array );
- }
+ if ( ! empty( $rule_array ) ) {
+ $rule = new $this->retry_rule_class( $rule_array );
}
return apply_filters( 'wcs_get_retry_rule', $rule, $retry_number, $order_id );
diff --git a/includes/upgrades/class-wc-subscriptions-upgrader.php b/includes/upgrades/class-wc-subscriptions-upgrader.php
index 9f6213a..09a9d2b 100644
--- a/includes/upgrades/class-wc-subscriptions-upgrader.php
+++ b/includes/upgrades/class-wc-subscriptions-upgrader.php
@@ -88,6 +88,8 @@ class WC_Subscriptions_Upgrader {
// Sometimes redirect to the Welcome/About page after an upgrade
add_action( 'woocommerce_subscriptions_upgraded', __CLASS__ . '::maybe_redirect_after_upgrade_complete', 100, 2 );
+
+ add_action( 'wcs_repair_end_of_prepaid_term_actions', __CLASS__ . '::repair_end_of_prepaid_term_actions' );
}
/**
@@ -184,6 +186,12 @@ class WC_Subscriptions_Upgrader {
do_action( 'woocommerce_subscriptions_reports_schedule_cache_updates' );
}
+ // Repair missing end_of_prepaid_term scheduled actions
+ if ( version_compare( self::$active_version, '2.2.0', '>=' ) && version_compare( self::$active_version, '2.2.7', '<' ) ) {
+ include_once( 'class-wcs-upgrade-2-2-7.php' );
+ WCS_Upgrade_2_2_7::schedule_end_of_prepaid_term_repair();
+ }
+
self::upgrade_complete();
}
@@ -730,6 +738,16 @@ class WC_Subscriptions_Upgrader {
}
}
+ /**
+ * Run the end of prepaid term repair script.
+ *
+ * @since 2.2.7
+ */
+ public static function repair_end_of_prepaid_term_actions() {
+ include_once( 'class-wcs-upgrade-2-2-7.php' );
+ WCS_Upgrade_2_2_7::repair_pending_cancelled_subscriptions();
+ }
+
/**
* Used to check if a user ID is greater than the last user upgraded to version 1.4.
*
diff --git a/includes/upgrades/class-wcs-upgrade-1-2.php b/includes/upgrades/class-wcs-upgrade-1-2.php
index 7883885..aaabb5e 100644
--- a/includes/upgrades/class-wcs-upgrade-1-2.php
+++ b/includes/upgrades/class-wcs-upgrade-1-2.php
@@ -64,8 +64,7 @@ class WCS_Upgrade_1_2 {
$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();
+ $cart_discount = $order->get_total_discount();
update_post_meta( $order_id, '_order_recurring_discount_cart', $cart_discount );
$order_discount = $order->get_order_discount();
diff --git a/includes/upgrades/class-wcs-upgrade-2-2-7.php b/includes/upgrades/class-wcs-upgrade-2-2-7.php
new file mode 100644
index 0000000..0d808e7
--- /dev/null
+++ b/includes/upgrades/class-wcs-upgrade-2-2-7.php
@@ -0,0 +1,126 @@
+get_time( 'end' );
+
+ // End date is in the past, this was likely because the end of prepaid term hook wasn't scheduled - cancel the subscription now
+ if ( gmdate( 'U' ) > $end_time ) {
+
+ self::log( sprintf( 'Subscription %d end date is in the past - cancelling now', $subscription_id ) );
+
+ $subscription->update_status( 'cancelled', __( 'Subscription end date in the past', 'woocommerce-subscriptions' ) );
+ } else {
+ $action_args = array( 'subscription_id' => $subscription_id );
+ $scheduled_action = wc_next_scheduled_action( $end_of_prepaid_term_hook, $action_args );
+
+ // If there isn't a scheduled end of prepaid term, schedule one now.
+ if ( false == $scheduled_action ) {
+ self::log( sprintf( 'Subscription %d missing scheduled end of prepaid term action - scheduled new action (end timestamp: %d)', $subscription_id, $end_time ) );
+ wc_schedule_single_action( $end_time, $end_of_prepaid_term_hook, $action_args );
+ } else {
+ self::log( sprintf( 'Subscription %d has a scheduled end of prepaid term action - there\'s nothing to do here', $subscription_id ) );
+ }
+ }
+
+ // Set a flag so we don't pull this subscription into a following batch
+ update_post_meta( $subscription_id, '_wcs_2_2_7_repaired', 'true' );
+
+ } catch ( Exception $e ) {
+ self::log( sprintf( '--- Exception caught repairing subscription %d - exception message: %s ---', $subscription_id, $e->getMessage() ) );
+ update_post_meta( $subscription_id, '_wcs_2_2_7_repaired', 'false' );
+ }
+ }
+
+ // Reattach the cancelled subscription emails
+ add_action( 'woocommerce_subscription_status_updated', 'WC_Subscriptions_Email::send_cancelled_email', 10, 2 );
+
+ // If we've processed a full batch, schedule the next batch to be repaired
+ if ( count( $subscriptions_to_repair ) == self::$batch_size ) {
+ self::schedule_end_of_prepaid_term_repair();
+ } else {
+ self::log( '2.2.7 repair missing end of prepaid terms complete' );
+ }
+ }
+
+ /**
+ * Get a batch of pending cancelled subscriptions to repair.
+ *
+ * @since 2.2.7
+ * @return array An list of subscription ids which may need to be repaired.
+ */
+ public static function get_subscriptions_to_repair() {
+ $subscriptions_to_repair = get_posts( array(
+ 'post_type' => 'shop_subscription',
+ 'post_status' => 'wc-pending-cancel',
+ 'posts_per_page' => self::$batch_size,
+ 'fields' => 'ids',
+ 'meta_query' => array(
+ array(
+ 'key' => '_wcs_2_2_7_repaired',
+ 'compare' => 'NOT EXISTS',
+ ),
+ ),
+ ) );
+
+ return $subscriptions_to_repair;
+ }
+
+ /**
+ * Add a message to the wcs-upgrade-end-of-prepaid-term-repair log
+ *
+ * @param string The message to be logged
+ * @since 2.2.7
+ */
+ protected static function log( $message ) {
+ WCS_Upgrade_Logger::add( $message, 'wcs-upgrade-end-of-prepaid-term-repair' );
+ }
+}
diff --git a/includes/upgrades/class-wcs-upgrade-logger.php b/includes/upgrades/class-wcs-upgrade-logger.php
index bc75689..f4dd7c5 100644
--- a/includes/upgrades/class-wcs-upgrade-logger.php
+++ b/includes/upgrades/class-wcs-upgrade-logger.php
@@ -26,8 +26,6 @@ class WCS_Upgrade_Logger {
public static function init() {
add_action( 'woocommerce_subscriptions_upgraded', __CLASS__ . '::schedule_cleanup' );
-
- add_action( 'woocommerce_subscriptions_clear_upgrade_log', __CLASS__ . '::clear' );
}
/**
@@ -35,11 +33,13 @@ class WCS_Upgrade_Logger {
*
* @param string $message
*/
- public static function add( $message ) {
+ public static function add( $message, $handle = '' ) {
+ $handle = ( '' === $handle ) ? self::$handle : $handle;
+
if ( empty( self::$log ) ) {
self::$log = new WC_Logger(); // can't use __get() no a static property unfortunately
}
- self::$log->add( self::$handle, $message );
+ self::$log->add( $handle, $message );
}
/**
@@ -67,9 +67,7 @@ class WCS_Upgrade_Logger {
* Schedule a hook to automatically clear the log after 8 weeks
*/
public static function schedule_cleanup() {
- $time_to_cleanup = gmdate( 'U' ) + self::$weeks_until_cleanup * WEEK_IN_SECONDS;
- self::add( sprintf( 'Upgrade complete. Scheduling log cleanup for %s GMT/UTC', gmdate( 'Y-m-d H:i:s', $time_to_cleanup ) ) );
- wc_schedule_single_action( $time_to_cleanup, 'woocommerce_subscriptions_clear_upgrade_log' );
+ self::add( sprintf( '%s upgrade complete.', WC_Subscriptions::$version ) );
}
}
WCS_Upgrade_Logger::init();
diff --git a/includes/wcs-cart-functions.php b/includes/wcs-cart-functions.php
index b52ea6f..8c782e6 100644
--- a/includes/wcs-cart-functions.php
+++ b/includes/wcs-cart-functions.php
@@ -181,7 +181,7 @@ function wcs_cart_totals_shipping_method_price_label( $method, $cart ) {
$price_label .= ' ' . WC()->countries->inc_tax_or_vat() . ' ';
}
}
- } else {
+ } elseif ( ! empty( $cart->recurring_cart_key ) ) {
$price_label .= _x( 'Free', 'shipping method price', 'woocommerce-subscriptions' );
}
diff --git a/includes/wcs-compatibility-functions.php b/includes/wcs-compatibility-functions.php
index 5af34ab..58ff539 100644
--- a/includes/wcs-compatibility-functions.php
+++ b/includes/wcs-compatibility-functions.php
@@ -195,7 +195,7 @@ function wcs_get_objects_property( $object, $property, $single = 'single', $defa
}
} elseif ( 'single' === $single && isset( $object->$property ) ) { // WC < 3.0
$value = $object->$property;
- } elseif ( metadata_exists( 'post', wcs_get_objects_property( $object, 'id' ), $prefixed_key ) ) {
+ } elseif ( strtolower( $property ) !== 'id' && metadata_exists( 'post', wcs_get_objects_property( $object, 'id' ), $prefixed_key ) ) {
// If we couldn't find a property or function, fallback to using post meta as that's what many __get() methods in WC < 3.0 did
if ( 'single' === $single ) {
$value = get_post_meta( wcs_get_objects_property( $object, 'id' ), $prefixed_key, true );
@@ -344,7 +344,7 @@ function wcs_is_order( $order ) {
if ( method_exists( $order, 'get_type' ) ) {
$is_order = ( 'shop_order' === $order->get_type() );
} else {
- $is_order = ( 'simple' === $order->order_type );
+ $is_order = ( isset( $order->order_type ) && 'simple' === $order->order_type );
}
return $is_order;
@@ -451,6 +451,7 @@ function wcs_get_coupon_property( $coupon, $property ) {
'exclude_product_categories' => 'get_excluded_product_categories',
'customer_email' => 'get_email_restrictions',
'enable_free_shipping' => 'get_free_shipping',
+ 'coupon_amount' => 'get_amount',
);
switch ( true ) {
@@ -493,6 +494,7 @@ function wcs_set_coupon_property( &$coupon, $property, $value ) {
'exclude_product_categories' => 'set_excluded_product_categories',
'customer_email' => 'set_email_restrictions',
'enable_free_shipping' => 'set_free_shipping',
+ 'coupon_amount' => 'set_amount',
);
switch ( true ) {
diff --git a/includes/wcs-helper-functions.php b/includes/wcs-helper-functions.php
index 967296e..85798b2 100644
--- a/includes/wcs-helper-functions.php
+++ b/includes/wcs-helper-functions.php
@@ -179,9 +179,9 @@ function wcs_maybe_prefix_key( $key, $prefix = '_' ) {
*/
function wcs_get_calling_function_name() {
- $backtrace = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 3 );
+ $backtrace = version_compare( phpversion(), '5.4.0', '>=' ) ? debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 3 ) : debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ); // the 2nd param for debug_backtrace() was added in PHP 5.4
$calling_function = isset( $backtrace[2]['class'] ) ? $backtrace[2]['class'] : '';
- $calling_function .= isset( $backtrace[2]['type'] ) ? $backtrace[2]['type'] : '';
+ $calling_function .= isset( $backtrace[2]['type'] ) ? ( ( '->' == $backtrace[2]['type'] ) ? '::' : $backtrace[2]['type'] ) : ''; // Ternary abuses
$calling_function .= isset( $backtrace[2]['function'] ) ? $backtrace[2]['function'] : '';
return $calling_function;
diff --git a/includes/wcs-order-functions.php b/includes/wcs-order-functions.php
index f9809d2..5df1a7d 100644
--- a/includes/wcs-order-functions.php
+++ b/includes/wcs-order-functions.php
@@ -355,7 +355,7 @@ function wcs_validate_new_order_type( $type ) {
* @return array
*/
function wcs_get_order_address( $order, $address_type = 'shipping' ) {
- if ( ! is_object( $order ) ) {
+ if ( ! is_a( $order, 'WC_Abstract_Order' ) ) {
return array();
}
@@ -405,8 +405,8 @@ function wcs_order_contains_subscription( $order, $order_type = array( 'parent',
$order_type = array( $order_type );
}
- if ( ! is_object( $order ) ) {
- $order = new WC_Order( $order );
+ if ( ! is_a( $order, 'WC_Abstract_Order' ) ) {
+ $order = wc_get_order( $order );
}
$contains_subscription = false;
diff --git a/includes/wcs-product-functions.php b/includes/wcs-product-functions.php
index 6a2f957..6be6d09 100644
--- a/includes/wcs-product-functions.php
+++ b/includes/wcs-product-functions.php
@@ -27,11 +27,13 @@ function wcs_get_price_including_tax( $product, $args = array() ) {
if ( function_exists( 'wc_get_price_including_tax' ) ) { // WC 3.0+
$price = wc_get_price_including_tax( $product, $args );
+ $filter = 'woocommerce_product_get_price';
} else { // WC < 3.0
$price = $product->get_price_including_tax( $args['qty'], $args['price'] );
+ $filter = 'woocommerce_get_price';
}
- return $price;
+ return apply_filters( $filter, $price , $product );
}
/**
@@ -51,11 +53,13 @@ function wcs_get_price_excluding_tax( $product, $args = array() ) {
if ( function_exists( 'wc_get_price_excluding_tax' ) ) { // WC 3.0+
$price = wc_get_price_excluding_tax( $product, $args );
+ $filter = 'woocommerce_product_get_price';
} else { // WC < 3.0
$price = $product->get_price_excluding_tax( $args['qty'], $args['price'] );
+ $filter = 'woocommerce_get_price';
}
- return $price;
+ return apply_filters( $filter, $price , $product );
}
/**
@@ -82,22 +86,12 @@ function wcs_get_price_html_from_text( $product = '' ) {
*/
function wcs_get_variation_prices( $variation, $variable_product ) {
- if ( WC_Subscriptions::is_woocommerce_pre( '3.0' ) ) {
- $regular_price = $variation->regular_price;
- $sale_price = $variation->sale_price;
- } else {
- $regular_price = $variation->get_regular_price( 'edit' );
- $sale_price = $variation->get_sale_price( 'edit' );
- }
-
- $prices = array(
+ return array(
'price' => apply_filters( 'woocommerce_variation_prices_price', WC_Subscriptions_Product::get_price( $variation ), $variation, $variable_product ),
'regular_price' => apply_filters( 'woocommerce_variation_prices_regular_price', WC_Subscriptions_Product::get_regular_price( $variation, 'edit' ), $variation, $variable_product ),
'sale_price' => apply_filters( 'woocommerce_variation_prices_sale_price', WC_Subscriptions_Product::get_sale_price( $variation, 'edit' ), $variation, $variable_product ),
'sign_up_fee' => apply_filters( 'woocommerce_variation_prices_sign_up_fee', WC_Subscriptions_Product::get_sign_up_fee( $variation ), $variation, $variable_product ),
);
-
- return $prices;
}
/**
@@ -171,7 +165,7 @@ function wcs_calculate_min_max_variations( $variations_data ) {
$is_max = $is_min = false;
- if ( empty( $variation_data['price'] ) && empty( $variation_data['subscription']['sign_up_fee'] ) ) {
+ if ( '' === $variation_data['price'] && '' === $variation_data['subscription']['sign_up_fee'] ) {
continue;
}
diff --git a/includes/wcs-renewal-functions.php b/includes/wcs-renewal-functions.php
index 9cf66bf..f708206 100644
--- a/includes/wcs-renewal-functions.php
+++ b/includes/wcs-renewal-functions.php
@@ -45,7 +45,7 @@ function wcs_create_renewal_order( $subscription ) {
*/
function wcs_order_contains_renewal( $order ) {
- if ( ! is_object( $order ) ) {
+ if ( ! is_a( $order, 'WC_Abstract_Order' ) ) {
$order = wc_get_order( $order );
}
@@ -111,14 +111,14 @@ function wcs_cart_contains_failed_renewal_order_payment() {
*/
function wcs_get_subscriptions_for_renewal_order( $order ) {
- if ( ! is_object( $order ) ) {
+ if ( ! is_a( $order, 'WC_Abstract_Order' ) ) {
$order = wc_get_order( $order );
}
$subscriptions = array();
// Only use the order if we actually found a valid order object
- if ( is_object( $order ) ) {
+ if ( is_a( $order, 'WC_Abstract_Order' ) ) {
$subscription_ids = wcs_get_objects_property( $order, 'subscription_renewal', 'multiple' );
foreach ( $subscription_ids as $subscription_id ) {
diff --git a/includes/wcs-resubscribe-functions.php b/includes/wcs-resubscribe-functions.php
index 2388957..5c7a302 100644
--- a/includes/wcs-resubscribe-functions.php
+++ b/includes/wcs-resubscribe-functions.php
@@ -22,8 +22,8 @@ if ( ! defined( 'ABSPATH' ) ) {
*/
function wcs_order_contains_resubscribe( $order ) {
- if ( ! is_object( $order ) ) {
- $order = new WC_Order( $order );
+ if ( ! is_a( $order, 'WC_Abstract_Order' ) ) {
+ $order = wc_get_order( $order );
}
if ( wcs_get_objects_property( $order, 'subscription_resubscribe' ) ) {
@@ -138,7 +138,7 @@ function wcs_cart_contains_resubscribe( $cart = '' ) {
*/
function wcs_get_subscriptions_for_resubscribe_order( $order ) {
- if ( ! is_object( $order ) ) {
+ if ( ! is_a( $order, 'WC_Abstract_Order' ) ) {
$order = wc_get_order( $order );
}
diff --git a/includes/wcs-switch-functions.php b/includes/wcs-switch-functions.php
index f6d3127..9ffae0d 100644
--- a/includes/wcs-switch-functions.php
+++ b/includes/wcs-switch-functions.php
@@ -20,7 +20,7 @@ if ( ! defined( 'ABSPATH' ) ) {
*/
function wcs_order_contains_switch( $order ) {
- if ( ! is_object( $order ) ) {
+ if ( ! is_a( $order, 'WC_Abstract_Order' ) ) {
$order = wc_get_order( $order );
}
@@ -30,9 +30,9 @@ function wcs_order_contains_switch( $order ) {
} else {
- $subscription_ids = wcs_get_objects_property( $order, 'subscription_switch', 'multiple' );
+ $switched_subscriptions = wcs_get_subscriptions_for_switch_order( $order );
- if ( ! empty( $subscription_ids ) ) {
+ if ( ! empty( $switched_subscriptions ) ) {
$is_switch_order = true;
} else {
$is_switch_order = false;
@@ -51,7 +51,7 @@ function wcs_order_contains_switch( $order ) {
*/
function wcs_get_subscriptions_for_switch_order( $order ) {
- if ( ! is_object( $order ) ) {
+ if ( ! is_a( $order, 'WC_Abstract_Order' ) ) {
$order = wc_get_order( $order );
}
@@ -59,7 +59,12 @@ function wcs_get_subscriptions_for_switch_order( $order ) {
$subscription_ids = wcs_get_objects_property( $order, 'subscription_switch', 'multiple' );
foreach ( $subscription_ids as $subscription_id ) {
- $subscriptions[ $subscription_id ] = wcs_get_subscription( $subscription_id );
+
+ $subscription = wcs_get_subscription( $subscription_id );
+
+ if ( $subscription ) {
+ $subscriptions[ $subscription_id ] = $subscription;
+ }
}
return $subscriptions;
diff --git a/includes/wcs-user-functions.php b/includes/wcs-user-functions.php
index e2dfb49..9add8b1 100644
--- a/includes/wcs-user-functions.php
+++ b/includes/wcs-user-functions.php
@@ -172,7 +172,11 @@ function wcs_get_users_subscriptions( $user_id = 0 ) {
) );
foreach ( $post_ids as $post_id ) {
- $subscriptions[ $post_id ] = wcs_get_subscription( $post_id );
+ $subscription = wcs_get_subscription( $post_id );
+
+ if ( $subscription ) {
+ $subscriptions[ $post_id ] = $subscription;
+ }
}
}
@@ -282,7 +286,7 @@ function wcs_get_all_user_actions_for_subscription( $subscription, $user_id ) {
// Show button for subscriptions which can be cancelled and which may actually require cancellation (i.e. has a future payment)
$next_payment = $subscription->get_time( 'next_payment' );
- if ( $subscription->can_be_updated_to( 'cancelled' ) && ! $subscription->is_one_payment() && ( $next_payment > 0 || ( $subscription->has_status( 'on-hold' ) && empty( $next_payment ) ) ) ) {
+ if ( $subscription->can_be_updated_to( 'cancelled' ) && ( ! $subscription->is_one_payment() && ( $subscription->has_status( 'on-hold' ) && empty( $next_payment ) ) || $next_payment > 0 ) ) {
$actions['cancel'] = array(
'url' => wcs_get_users_change_status_link( $subscription->get_id(), 'cancelled', $current_status ),
'name' => _x( 'Cancel', 'an action on a subscription', 'woocommerce-subscriptions' ),
diff --git a/languages/woocommerce-subscriptions.pot b/languages/woocommerce-subscriptions.pot
index 675dcdc..6615b2d 100644
--- a/languages/woocommerce-subscriptions.pot
+++ b/languages/woocommerce-subscriptions.pot
@@ -2,10 +2,10 @@
# This file is distributed under the same license as the WooCommerce Subscriptions package.
msgid ""
msgstr ""
-"Project-Id-Version: WooCommerce Subscriptions 2.2.4\n"
+"Project-Id-Version: WooCommerce Subscriptions 2.2.7\n"
"Report-Msgid-Bugs-To: "
"https://github.com/Prospress/woocommerce-subscriptions/issues\n"
-"POT-Creation-Date: 2017-04-12 19:29:43+00:00\n"
+"POT-Creation-Date: 2017-05-27 00:30:00+00:00\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
@@ -15,74 +15,74 @@ msgstr ""
"X-Generator: grunt-wp-i18n 0.5.4\n"
"Language: en_US\n"
-#: includes/admin/class-wc-subscriptions-admin.php:174
+#: includes/admin/class-wc-subscriptions-admin.php:179
msgid "Simple subscription"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:175
+#: includes/admin/class-wc-subscriptions-admin.php:180
msgid "Variable subscription"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:193
+#: includes/admin/class-wc-subscriptions-admin.php:198
msgid "Choose the subscription price, billing interval and period."
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:206
+#: includes/admin/class-wc-subscriptions-admin.php:211
#: templates/admin/html-variation-price.php:42
#. translators: placeholder is a currency symbol / code
msgid "Subscription price (%s)"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:209
+#: includes/admin/class-wc-subscriptions-admin.php:214
msgid "Subscription interval"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:215
-#: includes/admin/class-wc-subscriptions-admin.php:350
+#: includes/admin/class-wc-subscriptions-admin.php:220
+#: includes/admin/class-wc-subscriptions-admin.php:355
msgid "Subscription period"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:229
-#: includes/admin/class-wc-subscriptions-admin.php:351
+#: includes/admin/class-wc-subscriptions-admin.php:234
+#: includes/admin/class-wc-subscriptions-admin.php:356
#: templates/admin/html-variation-price.php:64
msgid "Subscription length"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:232
+#: includes/admin/class-wc-subscriptions-admin.php:237
msgid ""
"Automatically expire the subscription after this length of time. This "
"length is in addition to any free trial or amount of time provided before a "
"synchronised first renewal date."
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:241
+#: includes/admin/class-wc-subscriptions-admin.php:246
#: templates/admin/html-variation-price.php:18
#. translators: %s is a currency symbol / code
msgid "Sign-up fee (%s)"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:243
+#: includes/admin/class-wc-subscriptions-admin.php:248
msgid ""
"Optionally include an amount to be charged at the outset of the "
"subscription. The sign-up fee will be charged immediately, even if the "
"product has a free trial or the payment dates are synced."
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:254
+#: includes/admin/class-wc-subscriptions-admin.php:259
#: templates/admin/html-variation-price.php:23
msgid "Free trial"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:257
+#: includes/admin/class-wc-subscriptions-admin.php:262
#: templates/admin/deprecated/html-variation-price.php:115
msgid "Subscription Trial Period"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:289
+#: includes/admin/class-wc-subscriptions-admin.php:294
msgid "One time shipping"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:290
+#: includes/admin/class-wc-subscriptions-admin.php:295
msgid ""
"Shipping for subscription products is normally charged on the initial order "
"and all renewal orders. Enable this to only charge shipping once on the "
@@ -90,53 +90,53 @@ msgid ""
"not have a free trial or a synced renewal date."
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:347
+#: includes/admin/class-wc-subscriptions-admin.php:352
msgid "Subscription pricing"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:348
+#: includes/admin/class-wc-subscriptions-admin.php:353
msgid "Subscription sign-up fee"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:349
+#: includes/admin/class-wc-subscriptions-admin.php:354
msgid "Subscription billing interval"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:352
+#: includes/admin/class-wc-subscriptions-admin.php:357
msgid "Free trial length"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:353
+#: includes/admin/class-wc-subscriptions-admin.php:358
msgid "Free trial period"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:674
+#: includes/admin/class-wc-subscriptions-admin.php:675
msgid ""
"Unable to change subscription status to \"%s\". Please assign a customer to "
"the subscription to activate it."
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:716
+#: includes/admin/class-wc-subscriptions-admin.php:717
msgid ""
"Trashing this order will also trash the subscriptions purchased with the "
"order."
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:729
+#: includes/admin/class-wc-subscriptions-admin.php:730
msgid "Enter the new period, either day, week, month or year:"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:730
+#: includes/admin/class-wc-subscriptions-admin.php:731
msgid "Enter a new length (e.g. 5):"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:731
+#: includes/admin/class-wc-subscriptions-admin.php:732
msgid ""
"Enter a new interval as a single number (e.g. to charge every 2nd month, "
"enter 2):"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:736
+#: includes/admin/class-wc-subscriptions-admin.php:737
msgid ""
"You are about to trash one or more orders which contain a subscription.\n"
"\n"
@@ -144,7 +144,7 @@ msgid ""
"orders."
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:749
+#: includes/admin/class-wc-subscriptions-admin.php:750
msgid ""
"WARNING: Bad things are about to happen!\n"
"\n"
@@ -156,13 +156,13 @@ msgid ""
"gateway."
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:750
+#: includes/admin/class-wc-subscriptions-admin.php:751
msgid ""
"You are deleting a subscription item. You will also need to manually cancel "
"and trash the subscription on the Manage Subscriptions screen."
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:757
+#: includes/admin/class-wc-subscriptions-admin.php:758
msgid ""
"Warning: Deleting a user will also delete the user's subscriptions. The "
"user's orders will remain but be reassigned to the 'Guest' user.\n"
@@ -171,21 +171,21 @@ msgid ""
"subscriptions?"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:820
+#: includes/admin/class-wc-subscriptions-admin.php:821
msgid "Active subscriber?"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:863
+#: includes/admin/class-wc-subscriptions-admin.php:864
msgid "Manage Subscriptions"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:867
+#: includes/admin/class-wc-subscriptions-admin.php:868
#: woocommerce-subscriptions.php:233
msgid "Search Subscriptions"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:887
-#: includes/admin/class-wc-subscriptions-admin.php:983
+#: includes/admin/class-wc-subscriptions-admin.php:888
+#: includes/admin/class-wc-subscriptions-admin.php:984
#: includes/admin/class-wcs-admin-reports.php:55
#: includes/admin/reports/class-wcs-report-subscription-events-by-date.php:654
#: includes/class-wcs-query.php:95 includes/class-wcs-query.php:115
@@ -194,23 +194,23 @@ msgstr ""
msgid "Subscriptions"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1023
+#: includes/admin/class-wc-subscriptions-admin.php:1024
msgid "Button Text"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1030
+#: includes/admin/class-wc-subscriptions-admin.php:1031
msgid "Add to Cart Button Text"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1031
+#: includes/admin/class-wc-subscriptions-admin.php:1032
msgid ""
"A product displays a button with the text \"Add to Cart\". By default, a "
"subscription changes this to \"Sign Up Now\". You can customise the button "
"text for subscriptions here."
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1035
-#: includes/admin/class-wc-subscriptions-admin.php:1046
+#: includes/admin/class-wc-subscriptions-admin.php:1036
+#: includes/admin/class-wc-subscriptions-admin.php:1047
#: includes/class-wc-product-subscription-variation.php:98
#: includes/class-wc-product-subscription.php:72
#: includes/class-wc-product-variable-subscription.php:63
@@ -219,11 +219,11 @@ msgstr ""
msgid "Sign Up Now"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1041
+#: includes/admin/class-wc-subscriptions-admin.php:1042
msgid "Place Order Button Text"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1042
+#: includes/admin/class-wc-subscriptions-admin.php:1043
msgid ""
"Use this field to customise the text displayed on the checkout button when "
"an order contains a subscription. Normally the checkout submission button "
@@ -231,11 +231,11 @@ msgid ""
"changed to \"Sign Up Now\"."
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1054
+#: includes/admin/class-wc-subscriptions-admin.php:1055
msgid "Roles"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1057
+#: includes/admin/class-wc-subscriptions-admin.php:1058
#. translators: placeholders are tags
msgid ""
"Choose the default roles to assign to active and inactive subscribers. For "
@@ -244,46 +244,46 @@ msgid ""
"allocated these roles to prevent locking out administrators."
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1062
+#: includes/admin/class-wc-subscriptions-admin.php:1063
msgid "Subscriber Default Role"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1063
+#: includes/admin/class-wc-subscriptions-admin.php:1064
msgid ""
"When a subscription is activated, either manually or after a successful "
"purchase, new users will be assigned this role."
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1074
+#: includes/admin/class-wc-subscriptions-admin.php:1075
msgid "Inactive Subscriber Role"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1075
+#: includes/admin/class-wc-subscriptions-admin.php:1076
msgid ""
"If a subscriber's subscription is manually cancelled or expires, she will "
"be assigned this role."
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1095
+#: includes/admin/class-wc-subscriptions-admin.php:1096
msgid "Manual Renewal Payments"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1096
+#: includes/admin/class-wc-subscriptions-admin.php:1097
msgid "Accept Manual Renewals"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1101
+#: includes/admin/class-wc-subscriptions-admin.php:1102
#. translators: placeholders are opening and closing link tags
msgid ""
"With manual renewals, a customer's subscription is put on-hold until they "
"login and pay to renew it. %sLearn more%s."
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1107
+#: includes/admin/class-wc-subscriptions-admin.php:1108
msgid "Turn off Automatic Payments"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1112
+#: includes/admin/class-wc-subscriptions-admin.php:1113
#. translators: placeholders are opening and closing link tags
msgid ""
"If you don't want new subscription purchases to automatically charge "
@@ -292,11 +292,11 @@ msgid ""
"more%s."
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1127
+#: includes/admin/class-wc-subscriptions-admin.php:1128
msgid "Customer Suspensions"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1134
+#: includes/admin/class-wc-subscriptions-admin.php:1135
msgid ""
"Set a maximum number of times a customer can suspend their account for each "
"billing period. For example, for a value of 3 and a subscription billed "
@@ -306,28 +306,28 @@ msgid ""
"this to 0 to turn off the customer suspension feature completely."
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1138
+#: includes/admin/class-wc-subscriptions-admin.php:1139
msgid "Mixed Checkout"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1139
+#: includes/admin/class-wc-subscriptions-admin.php:1140
msgid "Allow subscriptions and products to be purchased simultaneously."
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1143
+#: includes/admin/class-wc-subscriptions-admin.php:1144
msgid "Allow subscriptions and products to be purchased in a single transaction."
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1147
+#: includes/admin/class-wc-subscriptions-admin.php:1148
#: includes/upgrades/templates/wcs-about-2-0.php:108
msgid "Drip Downloadable Content"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1148
+#: includes/admin/class-wc-subscriptions-admin.php:1149
msgid "Enable dripping for downloadable content on subscription products."
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1152
+#: includes/admin/class-wc-subscriptions-admin.php:1153
msgid ""
"Enabling this grants access to new downloadable files added to a product "
"only after the next renewal is processed.%sBy default, access to new "
@@ -335,7 +335,7 @@ msgid ""
"customer that has an active subscription with that product."
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1188
+#: includes/admin/class-wc-subscriptions-admin.php:1189
#. translators: $1-$2: opening and closing tags, $3-$4: opening and
#. closing tags
msgid ""
@@ -343,61 +343,71 @@ msgid ""
"start selling subscriptions!%4$s"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1193
+#: includes/admin/class-wc-subscriptions-admin.php:1194
msgid "Add a Subscription Product"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1194
+#: includes/admin/class-wc-subscriptions-admin.php:1195
#: includes/upgrades/templates/wcs-about-2-0.php:35
#: includes/upgrades/templates/wcs-about.php:34
#: woocommerce-subscriptions.php:991
msgid "Settings"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1280
+#: includes/admin/class-wc-subscriptions-admin.php:1281
#. translators: placeholder is a number
msgid "We can't find a subscription with ID #%d. Perhaps it was deleted?"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1313
-#: includes/admin/class-wc-subscriptions-admin.php:1318
+#: includes/admin/class-wc-subscriptions-admin.php:1314
+#: includes/admin/class-wc-subscriptions-admin.php:1319
#. translators: placeholders are opening link tag, ID of sub, and closing link
#. tag
msgid "Showing orders for %sSubscription %s%s"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1342
+#: includes/admin/class-wc-subscriptions-admin.php:1343
#. translators: number of 1$: days, 2$: weeks, 3$: months, 4$: years
msgid "The trial period can not exceed: %1s, %2s, %3s or %4s."
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1347
+#: includes/admin/class-wc-subscriptions-admin.php:1348
#. translators: placeholder is a time period (e.g. "4 weeks")
msgid "The trial period can not exceed %s."
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1400
-#: includes/admin/class-wc-subscriptions-admin.php:1428
-#: includes/admin/class-wc-subscriptions-admin.php:1481
+#: includes/admin/class-wc-subscriptions-admin.php:1401
+#: includes/admin/class-wc-subscriptions-admin.php:1429
+#: includes/admin/class-wc-subscriptions-admin.php:1482
msgid "Yes"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1400
-#: includes/admin/class-wc-subscriptions-admin.php:1428
+#: includes/admin/class-wc-subscriptions-admin.php:1401
+#: includes/admin/class-wc-subscriptions-admin.php:1429
msgid "No"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1464
+#: includes/admin/class-wc-subscriptions-admin.php:1465
msgid "Automatic Recurring Payments"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1481
+#: includes/admin/class-wc-subscriptions-admin.php:1482
msgid ""
"Supports automatic renewal payments with the WooCommerce Subscriptions "
"extension."
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1533
+#: includes/admin/class-wc-subscriptions-admin.php:1562
+msgid "Subscription items can no longer be edited."
+msgstr ""
+
+#: includes/admin/class-wc-subscriptions-admin.php:1566
+msgid ""
+"This subscription is no longer editable because the payment gateway does "
+"not allow modification of recurring amounts."
+msgstr ""
+
+#: includes/admin/class-wc-subscriptions-admin.php:1585
#. translators: $1-2: opening and closing tags of a link that takes to Woo
#. marketplace / Stripe product page
msgid ""
@@ -406,18 +416,18 @@ msgid ""
"the %1$sfree Stripe extension%2$s."
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1538
+#: includes/admin/class-wc-subscriptions-admin.php:1590
msgid "Recurring Payments"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1546
+#: includes/admin/class-wc-subscriptions-admin.php:1598
#. translators: placeholders are opening and closing link tags
msgid ""
"Payment gateways which don't support automatic recurring payments can be "
"used to process %smanual subscription renewal payments%s."
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1553
+#: includes/admin/class-wc-subscriptions-admin.php:1605
#. translators: $1-$2: opening and closing tags. Link to documents->payment
#. gateways, 3$-4$: opening and closing tags. Link to WooCommerce extensions
#. shop page
@@ -565,12 +575,12 @@ msgid "End Date"
msgstr ""
#: includes/admin/class-wcs-admin-post-types.php:457
-#: includes/wcs-user-functions.php:272
+#: includes/wcs-user-functions.php:276
msgid "Reactivate"
msgstr ""
#: includes/admin/class-wcs-admin-post-types.php:458
-#: includes/wcs-user-functions.php:267
+#: includes/wcs-user-functions.php:271
msgid "Suspend"
msgstr ""
@@ -585,12 +595,12 @@ msgid "Delete Permanently"
msgstr ""
#: includes/admin/class-wcs-admin-post-types.php:473
-#: includes/class-wc-subscriptions-product.php:717
+#: includes/class-wc-subscriptions-product.php:722
msgid "Restore this item from the Trash"
msgstr ""
#: includes/admin/class-wcs-admin-post-types.php:473
-#: includes/class-wc-subscriptions-product.php:718
+#: includes/class-wc-subscriptions-product.php:723
msgid "Restore"
msgstr ""
@@ -733,8 +743,8 @@ msgstr ""
#: includes/admin/meta-boxes/class-wcs-meta-box-subscription-data.php:111
#: includes/admin/meta-boxes/class-wcs-meta-box-subscription-data.php:113
-#: includes/admin/meta-boxes/class-wcs-meta-box-subscription-data.php:179
-#: includes/admin/meta-boxes/class-wcs-meta-box-subscription-data.php:181
+#: includes/admin/meta-boxes/class-wcs-meta-box-subscription-data.php:183
+#: includes/admin/meta-boxes/class-wcs-meta-box-subscription-data.php:185
msgid "Address"
msgstr ""
@@ -742,30 +752,30 @@ msgstr ""
msgid "No billing address set."
msgstr ""
-#: includes/admin/meta-boxes/class-wcs-meta-box-subscription-data.php:129
+#: includes/admin/meta-boxes/class-wcs-meta-box-subscription-data.php:133
#: includes/class-wcs-change-payment-method-admin.php:38
#: includes/class-wcs-change-payment-method-admin.php:51
msgid "Payment Method"
msgstr ""
-#: includes/admin/meta-boxes/class-wcs-meta-box-subscription-data.php:168
+#: includes/admin/meta-boxes/class-wcs-meta-box-subscription-data.php:172
msgid "Shipping Details"
msgstr ""
-#: includes/admin/meta-boxes/class-wcs-meta-box-subscription-data.php:181
+#: includes/admin/meta-boxes/class-wcs-meta-box-subscription-data.php:185
msgid "No shipping address set."
msgstr ""
-#: includes/admin/meta-boxes/class-wcs-meta-box-subscription-data.php:199
-#: includes/admin/meta-boxes/class-wcs-meta-box-subscription-data.php:226
+#: includes/admin/meta-boxes/class-wcs-meta-box-subscription-data.php:207
+#: includes/admin/meta-boxes/class-wcs-meta-box-subscription-data.php:234
msgid "Customer Note:"
msgstr ""
-#: includes/admin/meta-boxes/class-wcs-meta-box-subscription-data.php:227
+#: includes/admin/meta-boxes/class-wcs-meta-box-subscription-data.php:235
msgid "Customer's notes about the order"
msgstr ""
-#: includes/admin/meta-boxes/class-wcs-meta-box-subscription-data.php:295
+#: includes/admin/meta-boxes/class-wcs-meta-box-subscription-data.php:303
#. translators: placeholder is error message from the payment gateway or
#. subscriptions when updating the status
msgid "Error updating some information: %s"
@@ -1389,114 +1399,114 @@ msgstr ""
msgid "Cannot create subscription: %s."
msgstr ""
-#: includes/class-wc-subscription.php:415
+#: includes/class-wc-subscription.php:418
msgid "Unable to change subscription status to \"%s\"."
msgstr ""
-#: includes/class-wc-subscription.php:516
+#: includes/class-wc-subscription.php:522
msgid "Unable to change subscription status to \"%s\". Exception: %s"
msgstr ""
-#: includes/class-wc-subscription.php:538
+#: includes/class-wc-subscription.php:544
#. translators: 1: old subscription status 2: new subscription status
msgid "Status changed from %1$s to %2$s."
msgstr ""
-#: includes/class-wc-subscription.php:550
+#: includes/class-wc-subscription.php:556
#. translators: %s: new order status
msgid "Status set to %s."
msgstr ""
-#: includes/class-wc-subscription.php:1144
+#: includes/class-wc-subscription.php:1150
#: includes/class-wc-subscriptions-manager.php:2221
#: includes/wcs-formatting-functions.php:228
#. translators: placeholder is human time diff (e.g. "3 weeks")
msgid "In %s"
msgstr ""
-#: includes/class-wc-subscription.php:1147
+#: includes/class-wc-subscription.php:1153
#: includes/wcs-formatting-functions.php:231
#. translators: placeholder is human time diff (e.g. "3 weeks")
msgid "%s ago"
msgstr ""
-#: includes/class-wc-subscription.php:1154
+#: includes/class-wc-subscription.php:1160
msgid "Not yet ended"
msgstr ""
-#: includes/class-wc-subscription.php:1157
+#: includes/class-wc-subscription.php:1163
msgid "Not cancelled"
msgstr ""
-#: includes/class-wc-subscription.php:1272
+#: includes/class-wc-subscription.php:1278
msgid "The start date of a subscription can not be deleted, only updated."
msgstr ""
-#: includes/class-wc-subscription.php:1276
+#: includes/class-wc-subscription.php:1282
msgid "The %s date of a subscription can not be deleted. You must delete the order."
msgstr ""
-#: includes/class-wc-subscription.php:1659
+#: includes/class-wc-subscription.php:1683
msgid "Sign-up complete."
msgstr ""
-#: includes/class-wc-subscription.php:1661
+#: includes/class-wc-subscription.php:1685
msgid "Payment received."
msgstr ""
-#: includes/class-wc-subscription.php:1692
+#: includes/class-wc-subscription.php:1716
msgid "Payment failed."
msgstr ""
-#: includes/class-wc-subscription.php:1696
+#: includes/class-wc-subscription.php:1721
msgid "Subscription Cancelled: maximum number of failed payments reached."
msgstr ""
-#: includes/class-wc-subscription.php:1897
+#: includes/class-wc-subscription.php:1923
#: includes/class-wcs-change-payment-method-admin.php:156
msgid "Manual Renewal"
msgstr ""
-#: includes/class-wc-subscription.php:1974
+#: includes/class-wc-subscription.php:2002
msgid "Payment method meta must be an array."
msgstr ""
-#: includes/class-wc-subscription.php:2201
+#: includes/class-wc-subscription.php:2229
msgid "Invalid format. First parameter needs to be an array."
msgstr ""
-#: includes/class-wc-subscription.php:2205
+#: includes/class-wc-subscription.php:2233
msgid "Invalid data. First parameter was empty when passed to update_dates()."
msgstr ""
-#: includes/class-wc-subscription.php:2212
+#: includes/class-wc-subscription.php:2240
msgid ""
"Invalid data. First parameter has a date that is not in the registered date "
"types."
msgstr ""
-#: includes/class-wc-subscription.php:2276
+#: includes/class-wc-subscription.php:2304
msgid "The %s date must occur after the cancellation date."
msgstr ""
-#: includes/class-wc-subscription.php:2281
+#: includes/class-wc-subscription.php:2309
msgid "The %s date must occur after the last payment date."
msgstr ""
-#: includes/class-wc-subscription.php:2285
+#: includes/class-wc-subscription.php:2313
msgid "The %s date must occur after the next payment date."
msgstr ""
-#: includes/class-wc-subscription.php:2290
+#: includes/class-wc-subscription.php:2318
msgid "The %s date must occur after the trial end date."
msgstr ""
-#: includes/class-wc-subscription.php:2294
+#: includes/class-wc-subscription.php:2322
msgid "The %s date must occur after the start date."
msgstr ""
-#: includes/class-wc-subscription.php:2323
-#: includes/class-wc-subscriptions-checkout.php:315
+#: includes/class-wc-subscription.php:2351
+#: includes/class-wc-subscriptions-checkout.php:313
#: includes/wcs-order-functions.php:279
msgid "Backordered"
msgstr ""
@@ -1517,21 +1527,21 @@ msgstr ""
msgid "Update the %1$s used for %2$sall%3$s of my active subscriptions"
msgstr ""
-#: includes/class-wc-subscriptions-cart.php:864
+#: includes/class-wc-subscriptions-cart.php:863
msgid "Please enter a valid postcode/ZIP."
msgstr ""
-#: includes/class-wc-subscriptions-cart.php:1035
+#: includes/class-wc-subscriptions-cart.php:1034
msgid ""
"That subscription product can not be added to your cart as it already "
"contains a subscription renewal."
msgstr ""
-#: includes/class-wc-subscriptions-cart.php:1123
+#: includes/class-wc-subscriptions-cart.php:1122
msgid "Invalid recurring shipping method."
msgstr ""
-#: includes/class-wc-subscriptions-cart.php:1889
+#: includes/class-wc-subscriptions-cart.php:1888
msgid "now"
msgstr ""
@@ -1603,18 +1613,18 @@ msgstr ""
msgid "Payment method updated."
msgstr ""
-#: includes/class-wc-subscriptions-checkout.php:175
-#: includes/class-wc-subscriptions-checkout.php:346
+#: includes/class-wc-subscriptions-checkout.php:173
+#: includes/class-wc-subscriptions-checkout.php:344
#. translators: placeholder is an internal error number
msgid "Error %d: Unable to create subscription. Please try again."
msgstr ""
-#: includes/class-wc-subscriptions-checkout.php:192
+#: includes/class-wc-subscriptions-checkout.php:190
#. translators: placeholder is an internal error number
msgid "Error %d: Unable to add tax to subscription. Please try again."
msgstr ""
-#: includes/class-wc-subscriptions-checkout.php:204
+#: includes/class-wc-subscriptions-checkout.php:202
#. translators: placeholder is an internal error number
msgid "Error %d: Unable to create order. Please try again."
msgstr ""
@@ -1800,7 +1810,7 @@ msgstr ""
msgid "Subscription cancelled for refunded order %1$s#%2$s%3$s."
msgstr ""
-#: includes/class-wc-subscriptions-product.php:311
+#: includes/class-wc-subscriptions-product.php:316
#: includes/wcs-formatting-functions.php:102
#: includes/wcs-formatting-functions.php:186
#. translators: 1$: recurring amount string, 2$: day of the week (e.g. "$10
@@ -1808,34 +1818,34 @@ msgstr ""
msgid "%1$s every %2$s"
msgstr ""
-#: includes/class-wc-subscriptions-product.php:314
+#: includes/class-wc-subscriptions-product.php:319
#: includes/wcs-formatting-functions.php:111
#. translators: 1$: recurring amount string, 2$: period, 3$: day of the week
#. (e.g. "$10 every 2nd week on Wednesday")
msgid "%1$s every %2$s on %3$s"
msgstr ""
-#: includes/class-wc-subscriptions-product.php:321
+#: includes/class-wc-subscriptions-product.php:326
#: includes/wcs-formatting-functions.php:129
#. translators: placeholder is recurring amount
msgid "%s on the last day of each month"
msgstr ""
-#: includes/class-wc-subscriptions-product.php:324
+#: includes/class-wc-subscriptions-product.php:329
#: includes/wcs-formatting-functions.php:132
#. translators: 1$: recurring amount, 2$: day of the month (e.g. "23rd") (e.g.
#. "$5 every 23rd of each month")
msgid "%1$s on the %2$s of each month"
msgstr ""
-#: includes/class-wc-subscriptions-product.php:329
+#: includes/class-wc-subscriptions-product.php:334
#: includes/wcs-formatting-functions.php:148
#. translators: 1$: recurring amount, 2$: interval (e.g. "3rd") (e.g. "$10 on
#. the last day of every 3rd month")
msgid "%1$s on the last day of every %2$s month"
msgstr ""
-#: includes/class-wc-subscriptions-product.php:332
+#: includes/class-wc-subscriptions-product.php:337
#: includes/wcs-formatting-functions.php:151
#. translators: 1$: on the, 2$: day of every, 3$:
#. month (e.g. "$10 on the 23rd day of every 2nd month")
@@ -1844,7 +1854,7 @@ msgstr ""
msgid "%1$s on the %2$s day of every %3$s month"
msgstr ""
-#: includes/class-wc-subscriptions-product.php:339
+#: includes/class-wc-subscriptions-product.php:344
#: includes/wcs-formatting-functions.php:164
#. translators: 1$: on, 2$: , 3$: each year (e.g. "$15 on
#. March 15th each year")
@@ -1853,14 +1863,14 @@ msgstr ""
msgid "%1$s on %2$s %3$s each year"
msgstr ""
-#: includes/class-wc-subscriptions-product.php:342
+#: includes/class-wc-subscriptions-product.php:347
#: includes/wcs-formatting-functions.php:173
#. translators: 1$: recurring amount, 2$: month (e.g. "March"), 3$: day of the
#. month (e.g. "23rd") (e.g. "$15 on March 15th every 3rd year")
msgid "%1$s on %2$s %3$s every %4$s year"
msgstr ""
-#: includes/class-wc-subscriptions-product.php:348
+#: includes/class-wc-subscriptions-product.php:353
#: includes/wcs-formatting-functions.php:184
#. translators: 1$: recurring amount, 2$: subscription period (e.g. "month" or
#. "3 months") (e.g. "$15 / month" or "$15 every 2nd month")
@@ -1869,25 +1879,25 @@ msgid_plural " %1$s every %2$s"
msgstr[0] ""
msgstr[1] ""
-#: includes/class-wc-subscriptions-product.php:354
+#: includes/class-wc-subscriptions-product.php:359
#. translators: billing period (e.g. "every week")
msgid "every %s"
msgstr ""
-#: includes/class-wc-subscriptions-product.php:360
+#: includes/class-wc-subscriptions-product.php:365
#: includes/wcs-formatting-functions.php:194
#. translators: 1$: subscription string (e.g. "$10 up front then $5 on March
#. 23rd every 3rd year"), 2$: length (e.g. "4 years")
msgid "%1$s for %2$s"
msgstr ""
-#: includes/class-wc-subscriptions-product.php:366
+#: includes/class-wc-subscriptions-product.php:371
#. translators: 1$: subscription string (e.g. "$15 on March 15th every 3 years
#. for 6 years"), 2$: trial length (e.g.: "with 4 months free trial")
msgid "%1$s with %2$s free trial"
msgstr ""
-#: includes/class-wc-subscriptions-product.php:371
+#: includes/class-wc-subscriptions-product.php:376
#. translators: 1$: subscription string (e.g. "$15 on March 15th every 3 years
#. for 6 years with 2 months free trial"), 2$: signup fee price (e.g. "and a
#. $30 sign-up fee")
@@ -1914,7 +1924,7 @@ msgid "Choose a new subscription."
msgstr ""
#: includes/class-wc-subscriptions-switcher.php:194
-#: includes/class-wc-subscriptions-switcher.php:1017
+#: includes/class-wc-subscriptions-switcher.php:1016
msgid ""
"Your cart contained an invalid subscription switch request. It has been "
"removed."
@@ -2006,7 +2016,7 @@ msgstr ""
#: includes/class-wc-subscriptions-switcher.php:404
#: includes/class-wc-subscriptions-switcher.php:430
-#: includes/class-wc-subscriptions-switcher.php:2312
+#: includes/class-wc-subscriptions-switcher.php:2311
msgid "Upgrade or Downgrade"
msgstr ""
@@ -2018,42 +2028,42 @@ msgstr ""
msgid "Switch Order"
msgstr ""
-#: includes/class-wc-subscriptions-switcher.php:984
+#: includes/class-wc-subscriptions-switcher.php:983
msgid "Switched Subscription"
msgstr ""
-#: includes/class-wc-subscriptions-switcher.php:1089
+#: includes/class-wc-subscriptions-switcher.php:1088
msgid "You can only switch to a subscription product."
msgstr ""
-#: includes/class-wc-subscriptions-switcher.php:1095
+#: includes/class-wc-subscriptions-switcher.php:1094
msgid "We can not find your old subscription item."
msgstr ""
-#: includes/class-wc-subscriptions-switcher.php:1117
+#: includes/class-wc-subscriptions-switcher.php:1116
msgid "You can not switch to the same subscription."
msgstr ""
-#: includes/class-wc-subscriptions-switcher.php:1164
+#: includes/class-wc-subscriptions-switcher.php:1163
msgid ""
"You can not switch this subscription. It appears you do not own the "
"subscription."
msgstr ""
-#: includes/class-wc-subscriptions-switcher.php:1205
+#: includes/class-wc-subscriptions-switcher.php:1204
msgid "There was an error locating the switch details."
msgstr ""
-#: includes/class-wc-subscriptions-switcher.php:1903
-#: includes/class-wc-subscriptions-switcher.php:2218
+#: includes/class-wc-subscriptions-switcher.php:1902
+#: includes/class-wc-subscriptions-switcher.php:2217
msgid "The original subscription item being switched cannot be found."
msgstr ""
-#: includes/class-wc-subscriptions-switcher.php:1905
+#: includes/class-wc-subscriptions-switcher.php:1904
msgid "The item on the switch order cannot be found."
msgstr ""
-#: includes/class-wc-subscriptions-switcher.php:2256
+#: includes/class-wc-subscriptions-switcher.php:2255
msgid "Failed to update the subscription shipping method."
msgstr ""
@@ -2089,35 +2099,35 @@ msgstr ""
msgid "Month for Synchronisation"
msgstr ""
-#: includes/class-wc-subscriptions-synchroniser.php:603
+#: includes/class-wc-subscriptions-synchroniser.php:596
msgid "Do not synchronise"
msgstr ""
-#: includes/class-wc-subscriptions-synchroniser.php:611
+#: includes/class-wc-subscriptions-synchroniser.php:604
#. translators: placeholder is a day of the week
msgid "%s each week"
msgstr ""
-#: includes/class-wc-subscriptions-synchroniser.php:617
+#: includes/class-wc-subscriptions-synchroniser.php:610
#. translators: placeholder is a number of day with language specific suffix
#. applied (e.g. "1st", "3rd", "5th", etc...)
msgid "%s day of the month"
msgstr ""
-#: includes/class-wc-subscriptions-synchroniser.php:619
+#: includes/class-wc-subscriptions-synchroniser.php:612
msgid "Last day of the month"
msgstr ""
-#: includes/class-wc-subscriptions-synchroniser.php:667
+#: includes/class-wc-subscriptions-synchroniser.php:660
msgid "Today!"
msgstr ""
-#: includes/class-wc-subscriptions-synchroniser.php:674
+#: includes/class-wc-subscriptions-synchroniser.php:667
#. translators: placeholder is a date
msgid "First payment prorated. Next payment: %s"
msgstr ""
-#: includes/class-wc-subscriptions-synchroniser.php:677
+#: includes/class-wc-subscriptions-synchroniser.php:670
#. translators: placeholder is a date
msgid "First payment: %s"
msgstr ""
@@ -2135,27 +2145,27 @@ msgid "View and manage subscriptions"
msgstr ""
#: includes/class-wcs-cart-initial-payment.php:56
-#: includes/class-wcs-cart-renewal.php:154
+#: includes/class-wcs-cart-renewal.php:156
msgid "That doesn't appear to be your order."
msgstr ""
-#: includes/class-wcs-cart-renewal.php:182
+#: includes/class-wcs-cart-renewal.php:184
msgid "Complete checkout to renew your subscription."
msgstr ""
-#: includes/class-wcs-cart-renewal.php:221
+#: includes/class-wcs-cart-renewal.php:245
#. translators: placeholder is an item name
msgid ""
"The %s product has been deleted and can no longer be renewed. Please choose "
"a new product or contact us for assistance."
msgstr ""
-#: includes/class-wcs-cart-renewal.php:254
+#: includes/class-wcs-cart-renewal.php:278
#. translators: %s is subscription's number
msgid "Subscription #%s has not been added to the cart."
msgstr ""
-#: includes/class-wcs-cart-renewal.php:382
+#: includes/class-wcs-cart-renewal.php:406
msgid ""
"We couldn't find the original subscription for an item in your cart. The "
"item was removed."
@@ -2165,7 +2175,7 @@ msgid_plural ""
msgstr[0] ""
msgstr[1] ""
-#: includes/class-wcs-cart-renewal.php:389
+#: includes/class-wcs-cart-renewal.php:413
msgid ""
"We couldn't find the original renewal order for an item in your cart. The "
"item was removed."
@@ -2175,7 +2185,7 @@ msgid_plural ""
msgstr[0] ""
msgstr[1] ""
-#: includes/class-wcs-cart-renewal.php:663
+#: includes/class-wcs-cart-renewal.php:688
msgid "All linked subscription items have been removed from the cart."
msgstr ""
@@ -2229,6 +2239,28 @@ msgstr ""
msgid "Limit to one of any status"
msgstr ""
+#: includes/class-wcs-my-account-payment-methods.php:76
+msgid ""
+"The deleted payment method was used for automatic subscription payments, we "
+"couldn't find an alternative token payment method token to change your "
+"subscriptions to."
+msgstr ""
+
+#: includes/class-wcs-my-account-payment-methods.php:87
+#. translators: $1: the token/credit card label, 2$-3$: opening and closing
+#. strong and link tags
+msgid ""
+"The deleted payment method was used for automatic subscription payments. To "
+"avoid failed renewal payments in future the subscriptions using this "
+"payment method have been updated to use your %1$s. To change the payment "
+"method of individual subscriptions go to your %2$sMy Account > "
+"Subscriptions%3$s page."
+msgstr ""
+
+#: includes/class-wcs-my-account-payment-methods.php:177
+msgid "%s ending in %s"
+msgstr ""
+
#: includes/class-wcs-remove-item.php:107
msgid "Your request to undo your previous action was unsuccessful."
msgstr ""
@@ -2281,19 +2313,19 @@ msgid ""
"assistance."
msgstr ""
-#: includes/class-wcs-webhooks.php:90
+#: includes/class-wcs-webhooks.php:110
msgid " Subscription Created"
msgstr ""
-#: includes/class-wcs-webhooks.php:91
+#: includes/class-wcs-webhooks.php:111
msgid " Subscription Updated"
msgstr ""
-#: includes/class-wcs-webhooks.php:92
+#: includes/class-wcs-webhooks.php:112
msgid " Subscription Deleted"
msgstr ""
-#: includes/class-wcs-webhooks.php:93
+#: includes/class-wcs-webhooks.php:113
msgid " Subscription Switched"
msgstr ""
@@ -2668,28 +2700,28 @@ msgstr ""
msgid "Billing agreement cancelled at PayPal."
msgstr ""
-#: includes/gateways/paypal/includes/class-wcs-paypal-standard-ipn-handler.php:259
+#: includes/gateways/paypal/includes/class-wcs-paypal-standard-ipn-handler.php:245
msgid "IPN subscription sign up completed."
msgstr ""
-#: includes/gateways/paypal/includes/class-wcs-paypal-standard-ipn-handler.php:302
-#: includes/gateways/paypal/includes/class-wcs-paypal-standard-ipn-handler.php:379
+#: includes/gateways/paypal/includes/class-wcs-paypal-standard-ipn-handler.php:288
+#: includes/gateways/paypal/includes/class-wcs-paypal-standard-ipn-handler.php:365
msgid "IPN subscription payment completed."
msgstr ""
-#: includes/gateways/paypal/includes/class-wcs-paypal-standard-ipn-handler.php:341
+#: includes/gateways/paypal/includes/class-wcs-paypal-standard-ipn-handler.php:327
msgid "IPN subscription failing payment method changed."
msgstr ""
-#: includes/gateways/paypal/includes/class-wcs-paypal-standard-ipn-handler.php:428
+#: includes/gateways/paypal/includes/class-wcs-paypal-standard-ipn-handler.php:414
msgid "IPN subscription suspended."
msgstr ""
-#: includes/gateways/paypal/includes/class-wcs-paypal-standard-ipn-handler.php:451
+#: includes/gateways/paypal/includes/class-wcs-paypal-standard-ipn-handler.php:437
msgid "IPN subscription cancelled."
msgstr ""
-#: includes/gateways/paypal/includes/class-wcs-paypal-standard-ipn-handler.php:467
+#: includes/gateways/paypal/includes/class-wcs-paypal-standard-ipn-handler.php:453
msgid "IPN subscription payment failure."
msgstr ""
@@ -2804,13 +2836,13 @@ msgstr ""
msgid "No retries found in trash"
msgstr ""
-#: includes/upgrades/class-wc-subscriptions-upgrader.php:276
+#: includes/upgrades/class-wc-subscriptions-upgrader.php:284
#. translators: placeholder is a list of version numbers (e.g. "1.3 & 1.4 &
#. 1.5")
msgid "Database updated to version %s"
msgstr ""
-#: includes/upgrades/class-wc-subscriptions-upgrader.php:299
+#: includes/upgrades/class-wc-subscriptions-upgrader.php:307
#. translators: 1$: number of action scheduler hooks upgraded, 2$:
#. "{execution_time}", will be replaced on front end with actual time
msgid ""
@@ -2818,27 +2850,31 @@ msgid ""
"seconds)."
msgstr ""
-#: includes/upgrades/class-wc-subscriptions-upgrader.php:315
+#: includes/upgrades/class-wc-subscriptions-upgrader.php:323
#. translators: 1$: number of subscriptions upgraded, 2$: "{execution_time}",
#. will be replaced on front end with actual time it took
msgid "Migrated %1$s subscriptions to the new structure (in %2$s seconds)."
msgstr ""
-#: includes/upgrades/class-wc-subscriptions-upgrader.php:328
+#: includes/upgrades/class-wc-subscriptions-upgrader.php:336
#. translators: 1$: error message, 2$: opening link tag, 3$: closing link tag
msgid ""
"Unable to upgrade subscriptions. Error: %1$s Please refresh the "
"page and try again. If problem persists, %2$scontact support%3$s."
msgstr ""
-#: includes/upgrades/class-wc-subscriptions-upgrader.php:583
+#: includes/upgrades/class-wc-subscriptions-upgrader.php:591
msgid "Welcome to WooCommerce Subscriptions 2.1"
msgstr ""
-#: includes/upgrades/class-wc-subscriptions-upgrader.php:583
+#: includes/upgrades/class-wc-subscriptions-upgrader.php:591
msgid "About WooCommerce Subscriptions"
msgstr ""
+#: includes/upgrades/class-wcs-upgrade-2-2-7.php:60
+msgid "Subscription end date in the past"
+msgstr ""
+
#: includes/upgrades/templates/wcs-about-2-0.php:20
msgid "Welcome to Subscriptions 2.0"
msgstr ""
@@ -3634,7 +3670,7 @@ msgid_plural "a %s-year"
msgstr[0] ""
msgstr[1] ""
-#: includes/wcs-user-functions.php:279
+#: includes/wcs-user-functions.php:283
#: templates/single-product/add-to-cart/subscription.php:41
#: templates/single-product/add-to-cart/variable-subscription.php:29
msgid "Resubscribe"
@@ -4106,7 +4142,7 @@ msgstr ""
msgid "http://prospress.com/"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:195
+#: includes/admin/class-wc-subscriptions-admin.php:200
#. translators: placeholder is trial period validation message if passed an
#. invalid value (e.g. "Trial period can not exceed 4 weeks")
msgctxt "Trial period field tooltip on Edit Product administration screen"
@@ -4116,12 +4152,12 @@ msgid ""
"subscription. %s"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:208
+#: includes/admin/class-wc-subscriptions-admin.php:213
msgctxt "example price"
msgid "e.g. 5.90"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:242
+#: includes/admin/class-wc-subscriptions-admin.php:247
#: templates/admin/deprecated/html-variation-price.php:31
#: templates/admin/deprecated/html-variation-price.php:86
#: templates/admin/html-variation-price.php:19
@@ -4130,7 +4166,7 @@ msgctxt "example price"
msgid "e.g. 9.90"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:775
+#: includes/admin/class-wc-subscriptions-admin.php:776
#. translators: placeholders are for HTML tags. They are 1$: "", 2$:
#. " ", 3$: "", 4$: "", 5$: " ", 6$: "", 7$: " ", 8$:
#. "
"
@@ -4141,7 +4177,7 @@ msgid ""
"%6$sVariable subscription%7$s.%8$s"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:777
+#: includes/admin/class-wc-subscriptions-admin.php:778
#. translators: placeholders are for HTML tags. They are 1$: "", 2$:
#. " ", 3$: "", 4$: "
"
msgctxt "used in admin pointer script params in javascript as price pointer content"
@@ -4151,52 +4187,52 @@ msgid ""
"sign-up fee and free trial.%4$s"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1088
+#: includes/admin/class-wc-subscriptions-admin.php:1089
msgctxt "option section heading"
msgid "Renewals"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1120
+#: includes/admin/class-wc-subscriptions-admin.php:1121
msgctxt "options section heading"
msgid "Miscellaneous"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1128
+#: includes/admin/class-wc-subscriptions-admin.php:1129
msgctxt "there's a number immediately in front of this text"
msgid "suspensions per billing period."
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1373
+#: includes/admin/class-wc-subscriptions-admin.php:1374
msgctxt "in [subscriptions] shortcode"
msgid "No subscriptions found."
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1382
+#: includes/admin/class-wc-subscriptions-admin.php:1383
#. translators: order number
msgctxt "in [subscriptions] shortcode"
msgid "Subscription %s"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1399
-#: includes/admin/class-wc-subscriptions-admin.php:1427
+#: includes/admin/class-wc-subscriptions-admin.php:1400
+#: includes/admin/class-wc-subscriptions-admin.php:1428
msgctxt "label that indicates whether debugging is turned on for the plugin"
msgid "WCS_DEBUG"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1405
-#: includes/admin/class-wc-subscriptions-admin.php:1433
+#: includes/admin/class-wc-subscriptions-admin.php:1406
+#: includes/admin/class-wc-subscriptions-admin.php:1434
msgctxt "Live or Staging, Label on WooCommerce -> System Status page"
msgid "Subscriptions Mode"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1406
-#: includes/admin/class-wc-subscriptions-admin.php:1434
+#: includes/admin/class-wc-subscriptions-admin.php:1407
+#: includes/admin/class-wc-subscriptions-admin.php:1435
msgctxt "refers to staging site"
msgid "Staging"
msgstr ""
-#: includes/admin/class-wc-subscriptions-admin.php:1406
-#: includes/admin/class-wc-subscriptions-admin.php:1434
+#: includes/admin/class-wc-subscriptions-admin.php:1407
+#: includes/admin/class-wc-subscriptions-admin.php:1435
msgctxt "refers to live site"
msgid "Live"
msgstr ""
@@ -4224,7 +4260,7 @@ msgstr ""
#: includes/admin/class-wcs-admin-post-types.php:246
#: includes/admin/class-wcs-admin-post-types.php:459
#: includes/class-wc-subscriptions-manager.php:1771
-#: includes/wcs-user-functions.php:288
+#: includes/wcs-user-functions.php:292
#: templates/myaccount/related-orders.php:67
msgctxt "an action on a subscription"
msgid "Cancel"
@@ -4289,7 +4325,7 @@ msgctxt "edit subscription header"
msgid "Subscription #%s details"
msgstr ""
-#: includes/admin/meta-boxes/class-wcs-meta-box-subscription-data.php:133
+#: includes/admin/meta-boxes/class-wcs-meta-box-subscription-data.php:137
#: includes/class-wcs-change-payment-method-admin.php:52
msgctxt ""
"The gateway ID displayed on the Edit Subscriptions screen when editing "
@@ -4400,12 +4436,12 @@ msgctxt "API response confirming order note deleted from a subscription"
msgid "Permanently deleted subscription note"
msgstr ""
-#: includes/class-wc-subscription.php:1162
+#: includes/class-wc-subscription.php:1168
msgctxt "original denotes there is no date to display"
msgid "-"
msgstr ""
-#: includes/class-wc-subscription.php:2239
+#: includes/class-wc-subscription.php:2267
#. translators: placeholder is date type (e.g. "end", "next_payment"...)
msgctxt "appears in an error message if date is wrong format"
msgid "Invalid %s date. The date must be of the format: \"Y-m-d H:i:s\"."
@@ -4435,7 +4471,7 @@ msgctxt "used in order note as reason for why subscription status changed"
msgid "Subscription renewal payment due:"
msgstr ""
-#: includes/class-wcs-retry-manager.php:299
+#: includes/class-wcs-retry-manager.php:300
msgctxt "used in order note as reason for why subscription status changed"
msgid "Subscription renewal payment retry:"
msgstr ""
@@ -4470,7 +4506,7 @@ msgctxt "Subscription status"
msgid "On-hold"
msgstr ""
-#: includes/class-wc-subscriptions-switcher.php:2453 wcs-functions.php:212
+#: includes/class-wc-subscriptions-switcher.php:2452 wcs-functions.php:212
msgctxt "Subscription status"
msgid "Switched"
msgstr ""
@@ -4592,30 +4628,30 @@ msgctxt "when to prorate first payment / subscription length"
msgid "For All Subscription Products"
msgstr ""
-#: includes/class-wc-subscriptions-switcher.php:1789
+#: includes/class-wc-subscriptions-switcher.php:1788
msgctxt "a switch order"
msgid "Downgrade"
msgstr ""
-#: includes/class-wc-subscriptions-switcher.php:1792
+#: includes/class-wc-subscriptions-switcher.php:1791
msgctxt "a switch order"
msgid "Upgrade"
msgstr ""
-#: includes/class-wc-subscriptions-switcher.php:1795
+#: includes/class-wc-subscriptions-switcher.php:1794
msgctxt "a switch order"
msgid "Crossgrade"
msgstr ""
-#: includes/class-wc-subscriptions-switcher.php:1800
+#: includes/class-wc-subscriptions-switcher.php:1799
#. translators: %1: product subtotal, %2: HTML span tag, %3: direction
#. (upgrade, downgrade, crossgrade), %4: closing HTML span tag
msgctxt "product subtotal string"
msgid "%1$s %2$s(%3$s)%4$s"
msgstr ""
-#: includes/class-wc-subscriptions-switcher.php:1916
-#: includes/class-wc-subscriptions-switcher.php:2229
+#: includes/class-wc-subscriptions-switcher.php:1915
+#: includes/class-wc-subscriptions-switcher.php:2228
#. translators: 1$: old item, 2$: new item when switching
msgctxt "used in order notes"
msgid "Customer switched from: %1$s to %2$s."
@@ -4646,13 +4682,20 @@ msgctxt "input field placeholder for day field for annual subscriptions"
msgid "Day"
msgstr ""
-#: includes/class-wcs-cart-renewal.php:692
+#: includes/class-wcs-cart-renewal.php:717
msgctxt ""
"Used in WooCommerce by removed item notification: \"_All linked "
"subscription items were_ removed. Undo?\" Filter for item title."
msgid "All linked subscription items were"
msgstr ""
+#: includes/class-wcs-my-account-payment-methods.php:120
+msgctxt "used in subscription note"
+msgid ""
+"Payment method meta updated after customer deleted a token from their My "
+"Account page. Payment meta changed from %1$s to %2$s"
+msgstr ""
+
#: includes/class-wcs-remove-item.php:68
msgctxt "hash before subscription ID"
msgid "Subscription #%d does not exist."
@@ -4670,25 +4713,25 @@ msgctxt "used in order note"
msgid "Customer removed \"%1$s\" (Product ID: #%2$d) via the My Account page."
msgstr ""
-#: includes/gateways/paypal/includes/class-wcs-paypal-standard-ipn-handler.php:389
-#: includes/gateways/paypal/includes/class-wcs-paypal-standard-ipn-handler.php:398
+#: includes/gateways/paypal/includes/class-wcs-paypal-standard-ipn-handler.php:375
+#: includes/gateways/paypal/includes/class-wcs-paypal-standard-ipn-handler.php:384
#. translators: placeholder is payment status (e.g. "completed")
msgctxt "used in order note"
msgid "IPN subscription payment %s."
msgstr ""
-#: includes/gateways/paypal/includes/class-wcs-paypal-standard-ipn-handler.php:402
+#: includes/gateways/paypal/includes/class-wcs-paypal-standard-ipn-handler.php:388
#. translators: placeholder is payment status (e.g. "completed")
msgctxt "used in order note"
msgid "IPN subscription payment %s for reason: %s."
msgstr ""
-#: includes/class-wcs-retry-manager.php:229
+#: includes/class-wcs-retry-manager.php:230
msgctxt "used in order note as reason for why status changed"
msgid "Retry rule applied:"
msgstr ""
-#: includes/class-wcs-retry-manager.php:295
+#: includes/class-wcs-retry-manager.php:296
msgctxt "used in order note as reason for why order status changed"
msgid "Subscription renewal payment retry:"
msgstr ""
@@ -4835,8 +4878,8 @@ msgstr ""
#: includes/gateways/paypal/includes/class-wcs-paypal-reference-transaction-api-request.php:327
#: includes/gateways/paypal/includes/class-wcs-paypal-reference-transaction-api-request.php:355
#: includes/gateways/paypal/includes/class-wcs-paypal-reference-transaction-api-request.php:367
-#: includes/gateways/paypal/includes/class-wcs-paypal-standard-request.php:139
-#: includes/gateways/paypal/includes/class-wcs-paypal-standard-request.php:142
+#: includes/gateways/paypal/includes/class-wcs-paypal-standard-request.php:144
+#: includes/gateways/paypal/includes/class-wcs-paypal-standard-request.php:147
#. translators: placeholder is for blog name
msgctxt ""
"hash before the order number. Used as a character to remove from the actual "
@@ -4880,7 +4923,7 @@ msgctxt "no information about something"
msgid "N/A"
msgstr ""
-#: includes/gateways/paypal/includes/class-wcs-paypal-standard-ipn-handler.php:255
+#: includes/gateways/paypal/includes/class-wcs-paypal-standard-ipn-handler.php:241
msgctxt ""
"when it is a payment change, and there is a subscr_signup message, this "
"will be a confirmation message that PayPal accepted it being the new "
@@ -4888,7 +4931,13 @@ msgctxt ""
msgid "IPN subscription payment method changed to PayPal."
msgstr ""
-#: includes/gateways/paypal/includes/class-wcs-paypal-standard-request.php:74
+#: includes/gateways/paypal/includes/class-wcs-paypal-standard-request.php:75
+#. translators: 1$: subscription ID, 2$: names of items, comma separated
+msgctxt "item name sent to paypal"
+msgid "Subscription %1$s - %2$s"
+msgstr ""
+
+#: includes/gateways/paypal/includes/class-wcs-paypal-standard-request.php:78
#. translators: 1$: subscription ID, 2$: order ID, 3$: names of items, comma
#. separated
msgctxt "item name sent to paypal"
@@ -4905,21 +4954,21 @@ msgctxt "Admin menu name"
msgid "Renewal Payment Retries"
msgstr ""
-#: includes/upgrades/class-wc-subscriptions-upgrader.php:287
+#: includes/upgrades/class-wc-subscriptions-upgrader.php:295
#. translators: placeholder is number of upgraded subscriptions
msgctxt "used in the subscriptions upgrader"
msgid "Marked %s subscription products as \"sold individually\"."
msgstr ""
-#: includes/upgrades/class-wc-subscriptions-upgrader.php:318
-#: includes/upgrades/class-wc-subscriptions-upgrader.php:368
+#: includes/upgrades/class-wc-subscriptions-upgrader.php:326
+#: includes/upgrades/class-wc-subscriptions-upgrader.php:376
#. translators: placeholder is "{time_left}", will be replaced on front end
#. with actual time
msgctxt "Message that gets sent to front end."
msgid "Estimated time left (minutes:seconds): %s"
msgstr ""
-#: includes/upgrades/class-wc-subscriptions-upgrader.php:347
+#: includes/upgrades/class-wc-subscriptions-upgrader.php:355
#. translators: placeholder is the number of subscriptions repaired
msgctxt "Repair message that gets sent to front end."
msgid ""
@@ -4927,7 +4976,7 @@ msgid ""
"customer notes."
msgstr ""
-#: includes/upgrades/class-wc-subscriptions-upgrader.php:353
+#: includes/upgrades/class-wc-subscriptions-upgrader.php:361
#. translators: placeholder is number of subscriptions that were checked and
#. did not need repairs. There's a space at the beginning!
msgctxt "Repair message that gets sent to front end."
@@ -4936,14 +4985,14 @@ msgid_plural "%d other subscriptions were checked and did not need any repairs."
msgstr[0] ""
msgstr[1] ""
-#: includes/upgrades/class-wc-subscriptions-upgrader.php:357
+#: includes/upgrades/class-wc-subscriptions-upgrader.php:365
#. translators: placeholder is "{execution_time}", which will be replaced on
#. front end with actual time
msgctxt "Repair message that gets sent to front end."
msgid "(in %s seconds)"
msgstr ""
-#: includes/upgrades/class-wc-subscriptions-upgrader.php:360
+#: includes/upgrades/class-wc-subscriptions-upgrader.php:368
#. translators: $1: "Repaired x subs with incorrect dates...", $2: "X others
#. were checked and no repair needed", $3: "(in X seconds)". Ordering for RTL
#. languages.
@@ -4951,7 +5000,7 @@ msgctxt "The assembled repair message that gets sent to front end."
msgid "%1$s%2$s %3$s"
msgstr ""
-#: includes/upgrades/class-wc-subscriptions-upgrader.php:379
+#: includes/upgrades/class-wc-subscriptions-upgrader.php:387
#. translators: 1$: error message, 2$: opening link tag, 3$: closing link tag
msgctxt "Error message that gets sent to front end when upgrading Subscriptions"
msgid ""
diff --git a/templates/admin/deprecated/html-variation-price.php b/templates/admin/deprecated/html-variation-price.php
index ea4ae28..f9f5f96 100644
--- a/templates/admin/deprecated/html-variation-price.php
+++ b/templates/admin/deprecated/html-variation-price.php
@@ -24,7 +24,7 @@ if ( ! defined( 'ABSPATH' ) ) {
// Subscription Price
woocommerce_wp_text_input( array(
'id' => 'variable_subscription_price[' . $loop . ']',
- 'class' => 'wc_input_subscription_price',
+ 'class' => 'wc_input_subscription_price wc_input_price',
'wrapper_class' => '_subscription_price_field',
// translators: placeholder is a currency symbol / code
'label' => sprintf( __( 'Subscription Price (%s)', 'woocommerce-subscriptions' ), get_woocommerce_currency_symbol() ),
diff --git a/templates/admin/html-variation-price.php b/templates/admin/html-variation-price.php
index 5825b2a..38116b9 100644
--- a/templates/admin/html-variation-price.php
+++ b/templates/admin/html-variation-price.php
@@ -16,7 +16,7 @@ if ( ! defined( 'ABSPATH' ) ) {
-
+
@@ -42,7 +42,7 @@ if ( ! defined( 'ABSPATH' ) ) {
printf( esc_html__( 'Subscription price (%s)', 'woocommerce-subscriptions' ), esc_html( get_woocommerce_currency_symbol() ) );
?>
-
+
diff --git a/woocommerce-subscriptions.php b/woocommerce-subscriptions.php
index 5b8d45f..bff36a1 100644
--- a/woocommerce-subscriptions.php
+++ b/woocommerce-subscriptions.php
@@ -5,7 +5,7 @@
* Description: Sell products and services with recurring payments in your WooCommerce Store.
* Author: Prospress Inc.
* Author URI: http://prospress.com/
- * Version: 2.2.4
+ * Version: 2.2.7
*
* Copyright 2016 Prospress, Inc. (email : freedoms@prospress.com)
*
@@ -126,7 +126,7 @@ class WC_Subscriptions {
public static $plugin_file = __FILE__;
- public static $version = '2.2.4';
+ public static $version = '2.2.7';
private static $total_subscription_count = null;
@@ -721,6 +721,8 @@ class WC_Subscriptions {
require_once( 'includes/class-wcs-user-change-status-handler.php' );
+ require_once( 'includes/class-wcs-my-account-payment-methods.php' );
+
if ( self::is_woocommerce_pre( '3.0' ) ) {
require_once( 'includes/legacy/class-wc-subscription-legacy.php' );
@@ -737,10 +739,8 @@ class WC_Subscriptions {
if ( ! class_exists( 'WC_DateTime' ) ) {
require_once( 'includes/libraries/class-wc-datetime.php' );
}
-
} else {
-
- require_once( 'includes/class-wc-order-item-pending-switch.php');
+ require_once( 'includes/class-wc-order-item-pending-switch.php' );
require_once( 'includes/data-stores/class-wcs-subscription-data-store-cpt.php' );