mirror of
https://github.com/pronamic/woocommerce-subscriptions.git
synced 2025-10-07 10:04:03 +00:00
2.2.7
This commit is contained in:

committed by
Remco Tolsma

parent
5e536ec717
commit
b99eba2f79
@@ -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 {
|
||||
|
@@ -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('<span id="sale-price-period" style="display: none;"></span>');
|
||||
@@ -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(){
|
||||
|
@@ -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
|
||||
|
@@ -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 {
|
||||
?><p class="form-field _subscription_price_fields _subscription_price_field">
|
||||
<label for="_subscription_price"><?php printf( esc_html__( 'Subscription price (%s)', 'woocommerce-subscriptions' ), esc_html( get_woocommerce_currency_symbol() ) ); ?></label>
|
||||
<span class="wrap">
|
||||
<input type="text" id="_subscription_price" name="_subscription_price" class="wc_input_subscription_price" placeholder="<?php echo esc_attr_x( 'e.g. 5.90', 'example price', 'woocommerce-subscriptions' ); ?>" step="any" min="0" value="<?php echo esc_attr( $chosen_price ); ?>" />
|
||||
<input type="text" id="_subscription_price" name="_subscription_price" class="wc_input_price wc_input_subscription_price" placeholder="<?php echo esc_attr_x( 'e.g. 5.90', 'example price', 'woocommerce-subscriptions' ); ?>" step="any" min="0" value="<?php echo esc_attr( $chosen_price ); ?>" />
|
||||
<label for="_subscription_period_interval" class="wcs_hidden_label"><?php esc_html_e( 'Subscription interval', 'woocommerce-subscriptions' ); ?></label>
|
||||
<select id="_subscription_period_interval" name="_subscription_period_interval" class="wc_input_subscription_period_interval">
|
||||
<?php foreach ( wcs_get_subscription_period_interval_strings() as $value => $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
|
||||
|
@@ -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 );
|
||||
|
||||
|
@@ -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 '<p><strong>' . esc_html( $field['label'] ) . ':</strong> ' . wp_kses_post( make_clickable( esc_html( $subscription->$function_name() ) ) ) . '</p>';
|
||||
if ( is_callable( array( $subscription, $function_name ) ) ) {
|
||||
$field_value = $subscription->$function_name( 'edit' );
|
||||
} else {
|
||||
$field_value = $subscription->get_meta( '_billing_' . $key );
|
||||
}
|
||||
|
||||
echo '<p><strong>' . esc_html( $field['label'] ) . ':</strong> ' . wp_kses_post( make_clickable( esc_html( $field_value ) ) ) . '</p>';
|
||||
}
|
||||
|
||||
echo '<p' . ( ( '' != $subscription->get_payment_method() ) ? ' class="' . esc_attr( $subscription->get_payment_method() ) . '"' : '' ) . '><strong>' . esc_html__( 'Payment Method', 'woocommerce-subscriptions' ) . ':</strong>' . 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 '<p><strong>' . esc_html( $field['label'] ) . ':</strong> ' . wp_kses_post( make_clickable( esc_html( $subscription->$field_name ) ) ) . '</p>';
|
||||
if ( is_callable( array( $subscription, $function_name ) ) ) {
|
||||
$field_value = $subscription->$function_name( 'edit' );
|
||||
} else {
|
||||
$field_value = $subscription->get_meta( '_shipping_' . $key );
|
||||
}
|
||||
|
||||
echo '<p><strong>' . esc_html( $field['label'] ) . ':</strong> ' . wp_kses_post( make_clickable( esc_html( $field_value ) ) ) . '</p>';
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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 ) : '';
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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 ) );
|
||||
}
|
||||
}
|
||||
|
@@ -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 );
|
||||
|
@@ -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() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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 {
|
||||
|
@@ -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 );
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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' ) ) {
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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 ] );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -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 */
|
||||
|
||||
/**
|
||||
|
@@ -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.
|
||||
*
|
||||
|
@@ -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' ) );
|
||||
}
|
||||
|
||||
|
239
includes/class-wcs-my-account-payment-methods.php
Normal file
239
includes/class-wcs-my-account-payment-methods.php
Normal file
@@ -0,0 +1,239 @@
|
||||
<?php
|
||||
/**
|
||||
* Manage the process of deleting, adding, assigning default payment tokens associated with automatic subscriptions
|
||||
*
|
||||
* @package WooCommerce Subscriptions
|
||||
* @category Class
|
||||
* @author Prospress
|
||||
* @since 2.2.7
|
||||
*/
|
||||
class WCS_My_Account_Payment_Methods {
|
||||
|
||||
/* A cache of a customer's payment tokens to avoid running multiple queries in the same request */
|
||||
protected static $customer_tokens = array();
|
||||
|
||||
/**
|
||||
* Initialize filters and hooks for class.
|
||||
*
|
||||
* @since 2.2.7
|
||||
*/
|
||||
public static function init() {
|
||||
|
||||
// Only hook class functions if the payment token object exists
|
||||
if ( class_exists( 'WC_Payment_Token' ) ) {
|
||||
add_filter( 'woocommerce_payment_methods_list_item', __CLASS__ . '::flag_subscription_payment_token_deletions', 10, 2 );
|
||||
add_action( 'woocommerce_payment_token_deleted', __CLASS__ . '::maybe_update_subscriptions_payment_meta', 10, 2 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add additional query args to delete token URLs which are being used for subscription automatic payments.
|
||||
*
|
||||
* @param array data about the token including a list of actions which can be triggered by the customer from their my account page
|
||||
* @param WC_Payment_Token payment token object
|
||||
* @return array payment token data
|
||||
* @since 2.2.7
|
||||
*/
|
||||
public static function flag_subscription_payment_token_deletions( $payment_token_data, $payment_token ) {
|
||||
|
||||
if ( $payment_token instanceof WC_Payment_Token && isset( $payment_token_data['actions']['delete']['url'] ) ) {
|
||||
|
||||
if ( 0 < count( self::get_subscriptions_by_token( $payment_token ) ) ) {
|
||||
if ( self::customer_has_alternative_token( $payment_token ) ) {
|
||||
$delete_subscription_token_args = array(
|
||||
'delete_subscription_token' => $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 ),
|
||||
'<a href="' . esc_url( wc_get_account_endpoint_url( get_option( 'woocommerce_myaccount_subscriptions_endpoint', 'subscriptions' ) ) ) . '"><strong>',
|
||||
'</strong></a>'
|
||||
);
|
||||
|
||||
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();
|
@@ -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' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -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() ) );
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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.
|
||||
}
|
||||
}
|
||||
|
@@ -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__ );
|
||||
}
|
||||
|
@@ -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,
|
||||
));
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
|
9
includes/libraries/action-scheduler/composer.json
Normal file
9
includes/libraries/action-scheduler/composer.json
Normal file
@@ -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": {}
|
||||
}
|
@@ -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 );
|
||||
|
@@ -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.
|
||||
*
|
||||
|
@@ -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();
|
||||
|
126
includes/upgrades/class-wcs-upgrade-2-2-7.php
Normal file
126
includes/upgrades/class-wcs-upgrade-2-2-7.php
Normal file
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
/**
|
||||
* Repair missing end_of_prepaid_term scheduled actions caused by race conditions when saving subscription date properties post WCS 2.2.0
|
||||
*
|
||||
* @author Prospress
|
||||
* @category Admin
|
||||
* @package WooCommerce Subscriptions/Admin/Upgrades
|
||||
* @version 2.2.7
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class WCS_Upgrade_2_2_7 {
|
||||
|
||||
private static $cron_hook = 'wcs_repair_end_of_prepaid_term_actions';
|
||||
private static $batch_size = 30;
|
||||
|
||||
/**
|
||||
* Schedule an WP-Cron event to run in 5 minutes to repair pending cancelled subscriptions.
|
||||
*
|
||||
* @since 2.2.7
|
||||
*/
|
||||
public static function schedule_end_of_prepaid_term_repair() {
|
||||
wp_schedule_single_event( gmdate( 'U' ) + ( MINUTE_IN_SECONDS * 3 ), self::$cron_hook );
|
||||
}
|
||||
|
||||
/**
|
||||
* Repair a batch of pending cancelled subscriptions.
|
||||
*
|
||||
* Subscriptions 2.2.0 included a race condition which causes cancelled subscriptions to not schedule
|
||||
* end of prepaid term actions. This results in pending cancelled subscriptions not transitioning to
|
||||
* cancelled automatically.
|
||||
*
|
||||
* @since 2.2.7
|
||||
*/
|
||||
public static function repair_pending_cancelled_subscriptions() {
|
||||
$subscriptions_to_repair = self::get_subscriptions_to_repair();
|
||||
$end_of_prepaid_term_hook = apply_filters( 'woocommerce_subscriptions_scheduled_action_hook', 'woocommerce_scheduled_subscription_end_of_prepaid_term', 'end' );
|
||||
|
||||
// Unhook emails to prevent a bunch of Cancelled Subscription emails being sent to Admin
|
||||
remove_action( 'woocommerce_subscription_status_updated', 'WC_Subscriptions_Email::send_cancelled_email', 10 );
|
||||
|
||||
foreach ( $subscriptions_to_repair as $subscription_id ) {
|
||||
try {
|
||||
$subscription = wcs_get_subscription( $subscription_id );
|
||||
|
||||
if ( false === $subscription ) {
|
||||
throw new Exception( 'Failed to instantiate subscription object' );
|
||||
}
|
||||
|
||||
$end_time = $subscription->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' );
|
||||
}
|
||||
}
|
@@ -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();
|
||||
|
@@ -181,7 +181,7 @@ function wcs_cart_totals_shipping_method_price_label( $method, $cart ) {
|
||||
$price_label .= ' <small>' . WC()->countries->inc_tax_or_vat() . '</small>';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
} elseif ( ! empty( $cart->recurring_cart_key ) ) {
|
||||
$price_label .= _x( 'Free', 'shipping method price', 'woocommerce-subscriptions' );
|
||||
}
|
||||
|
||||
|
@@ -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 ) {
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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 ) {
|
||||
|
@@ -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 );
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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' ),
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -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() ),
|
||||
|
@@ -16,7 +16,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||
<div class="variable_subscription_trial variable_subscription_pricing_2_3 show_if_variable-subscription variable_subscription_trial_sign_up">
|
||||
<p class="form-row form-row-first form-field show_if_variable-subscription sign-up-fee-cell">
|
||||
<label for="variable_subscription_sign_up_fee[<?php echo esc_attr( $loop ); ?>]"><?php printf( esc_html__( 'Sign-up fee (%s)', 'woocommerce-subscriptions' ), esc_html( get_woocommerce_currency_symbol() ) ); ?></label>
|
||||
<input type="text" class="wc_input_subscription_intial_price" name="variable_subscription_sign_up_fee[<?php echo esc_attr( $loop ); ?>]" value="<?php echo esc_attr( WC_Subscriptions_Product::get_sign_up_fee( $variation_product ) ); ?>" placeholder="<?php echo esc_attr_x( 'e.g. 9.90', 'example price', 'woocommerce-subscriptions' ); ?>">
|
||||
<input type="text" class="wc_input_price wc_input_subscription_intial_price" name="variable_subscription_sign_up_fee[<?php echo esc_attr( $loop ); ?>]" value="<?php echo esc_attr( WC_Subscriptions_Product::get_sign_up_fee( $variation_product ) ); ?>" placeholder="<?php echo esc_attr_x( 'e.g. 9.90', 'example price', 'woocommerce-subscriptions' ); ?>">
|
||||
</p>
|
||||
<p class="form-row form-row-last show_if_variable-subscription">
|
||||
<label for="variable_subscription_trial_length[<?php echo esc_attr( $loop ); ?>]">
|
||||
@@ -42,7 +42,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||
printf( esc_html__( 'Subscription price (%s)', 'woocommerce-subscriptions' ), esc_html( get_woocommerce_currency_symbol() ) );
|
||||
?>
|
||||
</label>
|
||||
<input type="text" class="wc_input_subscription_price" name="variable_subscription_price[<?php echo esc_attr( $loop ); ?>]" value="<?php echo esc_attr( WC_Subscriptions_Product::get_price( $variation_product ) ); ?>" placeholder="<?php echo esc_attr_x( 'e.g. 9.90', 'example price', 'woocommerce-subscriptions' ); ?>">
|
||||
<input type="text" class="wc_input_price wc_input_subscription_price" name="variable_subscription_price[<?php echo esc_attr( $loop ); ?>]" value="<?php echo esc_attr( WC_Subscriptions_Product::get_price( $variation_product ) ); ?>" placeholder="<?php echo esc_attr_x( 'e.g. 9.90', 'example price', 'woocommerce-subscriptions' ); ?>">
|
||||
|
||||
<label for="variable_subscription_period_interval[<?php echo esc_attr( $loop ); ?>]" class="wcs_hidden_label"><?php esc_html_e( 'Billing interval:', 'woocommerce-subscriptions' ); ?></label>
|
||||
<select name="variable_subscription_period_interval[<?php echo esc_attr( $loop ); ?>]" class="wc_input_subscription_period_interval">
|
||||
|
@@ -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' );
|
||||
|
||||
|
Reference in New Issue
Block a user