diff --git a/changelog.txt b/changelog.txt index 218dea7..edc6d51 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,18 @@ *** Woo Subscriptions Changelog *** +2024-04-11 - version 6.2.0 +* Add: Declare WooCommerce as a plugin dependency in the plugin header. +* Fix: Ensure next payment dates are only extended when early renewal orders paid via the modal are fully paid. Prevents extending dates on authorized but not captured payments. +* Fix: Updated the switching calculator to handle situations where an upgrade has a new price per day less than the old price per day. Previously this would result in a negative upgrade cost. +* Fix: Update the failing payment method on a subscription when customers successfully pay for a failed renewal order via the block checkout. +* Fix: Resolved an issue that would cause subscriptions to be directly cancelled by the WooCommerce process of automatically cancelling unpaid orders in-line with the hold stock setting. +* Fix: Prevent duplicate status transition notes on subscriptions and potential infinite loops when processing subscription status transitions. +* Fix: Resolved an issue that could lead to "Undefined array key 'order-received'" errors. +* Fix: Resolved errors that could occur when paying for renewal orders via the checkout when the store has custom checkout fields. +* Fix: Resolved database errors that would occur when ordering the subscriptions list table by the 'Last order date' on sites with HPOS enabled. +* Dev: Introduced a new filter ('wcs_setup_cart_for_subscription_initial_payment') to enable third-party plugins to use the pay-for-order flow to complete a subscription's initial payment. +* Dev: Updated subscriptions-core to 7.0.0. + 2024-03-28 - version 6.1.0 * Fix: Ensure the subscription renewal payment process doesn't attempt to reprocess previously paid renewal orders. * Fix: Resolved an issue where discounts, when reapplied to failed or manual subscription order payments, would incorrectly account for inclusive tax. diff --git a/includes/early-renewal/class-wcs-early-renewal-modal-handler.php b/includes/early-renewal/class-wcs-early-renewal-modal-handler.php index 5f79cde..04eb8e8 100644 --- a/includes/early-renewal/class-wcs-early-renewal-modal-handler.php +++ b/includes/early-renewal/class-wcs-early-renewal-modal-handler.php @@ -146,7 +146,11 @@ class WCS_Early_Renewal_Modal_Handler { wc_add_notice( __( 'Payment for the renewal order was unsuccessful with your payment method on file, please try again.', 'woocommerce-subscriptions' ), 'error' ); wp_redirect( wcs_get_early_renewal_url( $subscription ) ); exit(); - } else { + } + + // Paid early renewals trigger the subscription payment complete hooks, extend next payment dates and reset suspension counts and user roles. + // Orders which are on-hold (manual payment or auth/capture gateways) will be handled when the order eventually is marked as payment complete (process/completed). + if ( $renewal_order->is_paid() ) { // Trigger the subscription payment complete hooks and reset suspension counts and user roles. $subscription->payment_complete(); diff --git a/includes/switching/class-wcs-switch-totals-calculator.php b/includes/switching/class-wcs-switch-totals-calculator.php index dd948b7..1b15b74 100755 --- a/includes/switching/class-wcs-switch-totals-calculator.php +++ b/includes/switching/class-wcs-switch-totals-calculator.php @@ -107,6 +107,12 @@ class WCS_Switch_Totals_Calculator { $this->reset_prorated_price( $switch_item ); $upgrade_cost = $this->calculate_upgrade_cost( $switch_item ); + + // If a negative upgrade cost has been calculated. Have the customer pay a full price minus what they are owed and set the next payment to be the new products first payment date. + if ( $upgrade_cost < 0 ) { + $upgrade_cost = $this->calculate_fully_reduced_upgrade_cost( $cart_item_key, $switch_item ); + } + $this->set_upgrade_cost( $switch_item, $upgrade_cost ); } } @@ -514,6 +520,32 @@ class WCS_Switch_Totals_Calculator { return $this->cart->cart_contents[ $cart_item_key ]['subscription_switch']['first_payment_timestamp']; } + /** + * Calculates the cost of the upgrade when the customer pays the new product's full price minus the amount paid and still owing. + * + * This function is used when a switch results in a negative upgrade cost which typically occurs when stores use the `wcs_switch_proration_switch_type` filter to change the default switch type. + * For example, if a customer is switching from a monthly subscription to a yearly subscription, they will pay the yearly product's full price minus whatever is still owed on the monthly product's price. + * + * eg $20/month switched to a $200 yearly product. The upgrade cost would be 200 - ((20/30) * days-left-in-the-current-billing-term). + * Switching on the first day of the month would result in the following calculation: 200 - ((20/30) * 30) = 200 - 20 = 180. The full $20 is owed. + * Switching halfway through the month would result in the following calculation: 200 - ((20/30) * 15) = 200 - 10 = 190. The customer is owed $10 or half what they paid. + * + * @param string $cart_item_key The switch item's cart item key. + * @param WCS_Switch_Cart_Item $switch_item The switch item. + * + * @return float The upgrade cost. + */ + protected function calculate_fully_reduced_upgrade_cost( $cart_item_key, $switch_item ) { + // When a customer pays the full new product price minus the amount already paid, we need to reduce the prepaid term and the subscription's next payment is 1 billing cycle away. + $this->cart->cart_contents[ $cart_item_key ]['subscription_switch']['first_payment_timestamp'] = WC_Subscriptions_Product::get_first_renewal_payment_time( $switch_item->product ); + + // The customer is owed whatever they didn't use. If they paid $100 for a monthly subscription and are switching half way through the month, they are owed $50. + $remaining_amount_not_consumed = ( $switch_item->get_total_paid_for_current_period() / $switch_item->get_days_in_old_cycle() ) * $switch_item->get_days_until_next_payment(); + + // The customer pays the full price of the new product minus the amount they didn't use. + return ( WC_Subscriptions_Product::get_price( $switch_item->product ) * $switch_item->cart_item['quantity'] ) - ( $remaining_amount_not_consumed ); + } + /** Helpers */ /** diff --git a/languages/woocommerce-subscriptions.pot b/languages/woocommerce-subscriptions.pot index a82d1bc..975821a 100644 --- a/languages/woocommerce-subscriptions.pot +++ b/languages/woocommerce-subscriptions.pot @@ -2,16 +2,16 @@ # This file is distributed under the same license as the Woo Subscriptions plugin. msgid "" msgstr "" -"Project-Id-Version: Woo Subscriptions 6.1.0\n" +"Project-Id-Version: Woo Subscriptions 6.2.0\n" "Report-Msgid-Bugs-To: https://woocommerce.com/contact-us\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"POT-Creation-Date: 2024-03-28T06:13:35+00:00\n" +"POT-Creation-Date: 2024-04-11T02:40:29+00:00\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"X-Generator: WP-CLI 2.10.0\n" +"X-Generator: WP-CLI 2.9.0\n" "X-Domain: woocommerce-subscriptions\n" #. Plugin Name of the plugin @@ -1368,7 +1368,7 @@ msgstr "" msgid "Payment for the renewal order was unsuccessful with your payment method on file, please try again." msgstr "" -#: includes/early-renewal/class-wcs-early-renewal-modal-handler.php:154 +#: includes/early-renewal/class-wcs-early-renewal-modal-handler.php:158 msgid "Your early renewal order was successful." msgstr "" @@ -1385,7 +1385,7 @@ msgstr "" #. translators: %d: subscription ID. #. translators: placeholder is a subscription ID. #: includes/gateways/class-wc-subscriptions-payment-gateways.php:77 -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:192 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:194 msgid "Subscription doesn't exist in scheduled action: %d" msgstr "" @@ -1871,7 +1871,7 @@ msgstr "" msgid "Invalid switch type \"%s\". Switch must be one of: \"upgrade\", \"downgrade\" or \"crossgrade\"." msgstr "" -#: includes/switching/class-wcs-switch-totals-calculator.php:196 +#: includes/switching/class-wcs-switch-totals-calculator.php:202 msgid "Your cart contained an invalid subscription switch request. It has been removed from your cart." msgstr "" @@ -1887,46 +1887,6 @@ msgstr "" msgid "Want to renew early via the checkout? Click %shere.%s" msgstr "" -#: tests/unit/scheduler/scheduler.php:65 -#: vendor/woocommerce/subscriptions-core/wcs-functions.php:291 -msgctxt "table heading" -msgid "Start Date" -msgstr "" - -#: tests/unit/scheduler/scheduler.php:66 -#: vendor/woocommerce/subscriptions-core/wcs-functions.php:292 -msgctxt "table heading" -msgid "Trial End" -msgstr "" - -#: tests/unit/scheduler/scheduler.php:67 -#: vendor/woocommerce/subscriptions-core/templates/myaccount/my-subscriptions.php:40 -#: vendor/woocommerce/subscriptions-core/wcs-functions.php:293 -msgctxt "table heading" -msgid "Next Payment" -msgstr "" - -#: tests/unit/scheduler/scheduler.php:68 -#: vendor/woocommerce/subscriptions-core/templates/emails/cancelled-subscription.php:23 -#: vendor/woocommerce/subscriptions-core/templates/emails/expired-subscription.php:23 -#: vendor/woocommerce/subscriptions-core/templates/emails/on-hold-subscription.php:23 -#: vendor/woocommerce/subscriptions-core/wcs-functions.php:294 -msgctxt "table heading" -msgid "Last Order Date" -msgstr "" - -#: tests/unit/scheduler/scheduler.php:69 -#: vendor/woocommerce/subscriptions-core/wcs-functions.php:295 -msgctxt "table heading" -msgid "Cancelled Date" -msgstr "" - -#: tests/unit/scheduler/scheduler.php:70 -#: vendor/woocommerce/subscriptions-core/wcs-functions.php:296 -msgctxt "table heading" -msgid "End Date" -msgstr "" - #. translators: 1: relation type, 2: list of valid relation types. #: vendor/woocommerce/subscriptions-core/includes/abstracts/abstract-wcs-related-order-store.php:148 msgid "Invalid relation type: %1$s. Order relationship type must be one of: %2$s." @@ -2408,7 +2368,7 @@ msgstr "" #: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:327 #: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1365 #: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1754 -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:1937 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:1955 #: vendor/woocommerce/subscriptions-core/includes/wcs-user-functions.php:329 #: vendor/woocommerce/subscriptions-core/templates/myaccount/related-orders.php:78 msgctxt "an action on a subscription" @@ -2532,7 +2492,7 @@ msgstr[1] "" #. translators: placeholder is the display name of a payment gateway a subscription was paid by #. translators: %s: payment method. #: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:645 -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:2126 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:2147 msgid "Via %s" msgstr "" @@ -2600,7 +2560,7 @@ msgid "None" msgstr "" #: vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php:1159 -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:2108 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:2129 #: vendor/woocommerce/subscriptions-core/includes/class-wcs-change-payment-method-admin.php:170 msgid "Manual Renewal" msgstr "" @@ -2955,139 +2915,139 @@ msgid "Read more" msgstr "" #. translators: %s: subscription status. -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:425 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:437 msgid "Unable to change subscription status to \"%s\"." msgstr "" #. translators: 1: subscription status, 2: error message. -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:548 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:560 msgid "Unable to change subscription status to \"%1$s\". Exception: %2$s" msgstr "" #. translators: 1: old subscription status 2: new subscription status -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:578 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:593 msgid "Status changed from %1$s to %2$s." msgstr "" #. translators: %s: new order status -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:592 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:607 msgid "Status set to %s." msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:606 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:621 msgid "Error during subscription status transition." msgstr "" #. translators: placeholder is human time diff (e.g. "3 weeks") -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:1296 -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:2387 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:1308 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:2405 msgid "In %s" msgstr "" #. translators: placeholder is human time diff (e.g. "3 weeks") -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:1299 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:1311 #: vendor/woocommerce/subscriptions-core/includes/wcs-formatting-functions.php:246 msgid "%s ago" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:1306 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:1318 msgid "Not yet ended" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:1309 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:1321 msgid "Not cancelled" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:1314 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:1326 msgctxt "original denotes there is no date to display" msgid "-" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:1422 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:1434 msgid "The creation date of a subscription can not be deleted, only updated." msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:1425 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:1437 msgid "The start date of a subscription can not be deleted, only updated." msgstr "" #. translators: %s: date type (e.g. "trial_end"). -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:1430 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:1442 msgid "The %s date of a subscription can not be deleted. You must delete the order." msgstr "" #. translators: %d: subscription ID. #. translators: %d: order ID. -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:1439 -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:2549 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:1451 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:2570 msgid "Subscription #%d: " msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:1853 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:1865 msgid "Payment status marked complete." msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:1886 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:1907 msgid "Payment failed." msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:1891 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:1912 msgid "Subscription Cancelled: maximum number of failed payments reached." msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:2001 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:2022 msgid "The \"all\" value for $order_type parameter is deprecated. It was a misnomer, as it did not return resubscribe orders. It was also inconsistent with order type values accepted by wcs_get_subscription_orders(). Use array( \"parent\", \"renewal\", \"switch\" ) to maintain previous behaviour, or \"any\" to receive all order types, including switch and resubscribe." msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:2205 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:2226 #: vendor/woocommerce/subscriptions-core/wcs-functions.php:835 msgid "Payment method meta must be an array." msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:2441 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:2462 msgid "Invalid format. First parameter needs to be an array." msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:2445 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:2466 msgid "Invalid data. First parameter was empty when passed to update_dates()." msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:2452 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:2473 msgid "Invalid data. First parameter has a date that is not in the registered date types." msgstr "" #. translators: placeholder is date type (e.g. "end", "next_payment"...) -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:2479 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:2500 msgctxt "appears in an error message if date is wrong format" msgid "Invalid %s date. The date must be of the format: \"Y-m-d H:i:s\"." msgstr "" #. translators: %s: date type (e.g. "end"). -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:2517 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:2538 msgid "The %s date must occur after the cancellation date." msgstr "" #. translators: %s: date type (e.g. "end"). -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:2523 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:2544 msgid "The %s date must occur after the last payment date." msgstr "" #. translators: %s: date type (e.g. "end"). -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:2528 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:2549 msgid "The %s date must occur after the next payment date." msgstr "" #. translators: %s: date type (e.g. "end"). -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:2534 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:2555 msgid "The %s date must occur after the trial end date." msgstr "" #. translators: %s: date type (e.g. "next_payment"). -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:2539 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:2560 msgid "The %s date must occur after the start date." msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:2569 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php:2590 #: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-checkout.php:348 msgid "Backordered" msgstr "" @@ -3699,145 +3659,145 @@ msgstr "" msgid "Would you like to add a payment method now?" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:112 -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:2001 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:114 #: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:2019 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:2037 msgctxt "used in order note as reason for why subscription status changed" msgid "Subscription renewal payment due:" msgstr "" #. translators: placeholder %1 is an order note. %2 is the error message that was thrown. -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:149 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:151 msgid "Error: Unable to create renewal order with note \"%1$s\". Message: %2$s" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:159 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:161 msgid "Manual renewal order awaiting customer payment." msgstr "" #. translators: $1: order number, $2: error message -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:342 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:344 msgid "Failed to activate subscription status for order #%1$s: %2$s" msgstr "" #. translators: $1: order number, $2: error message -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:370 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:372 msgid "Failed to update subscription status after order #%1$s was put on-hold: %2$s" msgstr "" #. translators: $1: order number, $2: error message -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:398 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:400 msgid "Failed to cancel subscription after order #%1$s was cancelled: %2$s" msgstr "" #. translators: $1: order number, $2: error message -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:426 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:428 msgid "Failed to set subscription as expired for order #%1$s: %2$s" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:452 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:454 msgid "Subscription sign up failed." msgstr "" #. translators: $1: order number, $2: error message -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:462 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:464 msgid "Failed to process failed payment on subscription for order #%1$s: %2$s" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:536 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:538 msgid "Error: Unable to create subscription. Please try again." msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:558 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:560 msgid "Error: Unable to add product to created subscription. Please try again." msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:603 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:605 msgid "Pending subscription created." msgstr "" #. Translators: 1: The subscription ID number. 2: The current user's username. -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:997 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:1015 msgid "The related subscription #%1$s has been deleted after the customer was deleted by %2$s." msgstr "" #. Translators: Placeholder is the subscription ID number. -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:1000 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:1018 msgid "The related subscription #%s has been deleted after the customer was deleted." msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:1146 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:1164 #: vendor/woocommerce/subscriptions-core/wcs-functions.php:221 msgctxt "Subscription status" msgid "Active" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:1149 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:1167 #: vendor/woocommerce/subscriptions-core/wcs-functions.php:223 msgctxt "Subscription status" msgid "Cancelled" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:1152 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:1170 #: vendor/woocommerce/subscriptions-core/wcs-functions.php:225 msgctxt "Subscription status" msgid "Expired" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:1155 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:1173 #: vendor/woocommerce/subscriptions-core/wcs-functions.php:220 msgctxt "Subscription status" msgid "Pending" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:1158 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:1176 msgctxt "Subscription status" msgid "Failed" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:1162 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:1180 msgctxt "Subscription status" msgid "On-hold" msgstr "" #. translators: 1$: month number (e.g. "01"), 2$: month abbreviation (e.g. "Jan") -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:1914 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:1932 msgctxt "used in a select box" msgid "%1$s-%2$s" msgstr "" #. translators: all fields are full html nodes: 1$: month input, 2$: day input, 3$: year input, 4$: hour input, 5$: minute input. Change the order if you'd like -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:1927 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:1945 msgid "%1$s%2$s, %3$s @ %4$s : %5$s" msgstr "" #. translators: all fields are full html nodes: 1$: month input, 2$: day input, 3$: year input. Change the order if you'd like -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:1931 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:1949 msgid "%1$s%2$s, %3$s" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:1936 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:1954 msgid "Change" msgstr "" #. translators: placeholder is subscription ID -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:2269 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:2287 msgid "Failed sign-up for subscription %s." msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:2360 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:2378 msgid "Invalid security token, please reload the page and try again." msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:2364 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:2382 msgid "Only store managers can edit payment dates." msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:2368 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:2386 msgid "Please enter all date fields." msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:2393 +#: vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php:2411 msgid "Date Changed" msgstr "" @@ -4137,7 +4097,7 @@ msgstr "" msgid "Weekly" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/class-wcs-cart-initial-payment.php:65 +#: vendor/woocommerce/subscriptions-core/includes/class-wcs-cart-initial-payment.php:85 #: vendor/woocommerce/subscriptions-core/includes/class-wcs-cart-renewal.php:199 msgid "That doesn't appear to be your order." msgstr "" @@ -4177,16 +4137,16 @@ msgid_plural "We couldn't find the original renewal orders for items in your car msgstr[0] "" msgstr[1] "" -#: vendor/woocommerce/subscriptions-core/includes/class-wcs-cart-renewal.php:687 +#: vendor/woocommerce/subscriptions-core/includes/class-wcs-cart-renewal.php:698 msgid "All linked subscription items have been removed from the cart." msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/class-wcs-cart-renewal.php:716 +#: vendor/woocommerce/subscriptions-core/includes/class-wcs-cart-renewal.php:727 msgctxt "Used in WooCommerce by removed item notification: \"_All linked subscription items were_ removed. Undo?\" Filter for item title." msgid "All linked subscription items were" msgstr "" -#: vendor/woocommerce/subscriptions-core/includes/class-wcs-cart-renewal.php:1532 +#: vendor/woocommerce/subscriptions-core/includes/class-wcs-cart-renewal.php:1543 msgctxt "The place order button text while renewing a subscription" msgid "Renew subscription" msgstr "" @@ -6407,6 +6367,14 @@ msgctxt "table headings in notification email" msgid "Price" msgstr "" +#: vendor/woocommerce/subscriptions-core/templates/emails/cancelled-subscription.php:23 +#: vendor/woocommerce/subscriptions-core/templates/emails/expired-subscription.php:23 +#: vendor/woocommerce/subscriptions-core/templates/emails/on-hold-subscription.php:23 +#: vendor/woocommerce/subscriptions-core/wcs-functions.php:294 +msgctxt "table heading" +msgid "Last Order Date" +msgstr "" + #: vendor/woocommerce/subscriptions-core/templates/emails/cancelled-subscription.php:24 msgctxt "table headings in notification email" msgid "End of Prepaid Term" @@ -6689,6 +6657,12 @@ msgstr "" msgid "ID" msgstr "" +#: vendor/woocommerce/subscriptions-core/templates/myaccount/my-subscriptions.php:40 +#: vendor/woocommerce/subscriptions-core/wcs-functions.php:293 +msgctxt "table heading" +msgid "Next Payment" +msgstr "" + #: vendor/woocommerce/subscriptions-core/templates/myaccount/my-subscriptions.php:46 #: vendor/woocommerce/subscriptions-core/templates/myaccount/related-orders.php:53 #: vendor/woocommerce/subscriptions-core/templates/myaccount/related-subscriptions.php:42 @@ -6868,6 +6842,26 @@ msgstr "" msgid "Can not get address type display name. Address type is not a string." msgstr "" +#: vendor/woocommerce/subscriptions-core/wcs-functions.php:291 +msgctxt "table heading" +msgid "Start Date" +msgstr "" + +#: vendor/woocommerce/subscriptions-core/wcs-functions.php:292 +msgctxt "table heading" +msgid "Trial End" +msgstr "" + +#: vendor/woocommerce/subscriptions-core/wcs-functions.php:295 +msgctxt "table heading" +msgid "Cancelled Date" +msgstr "" + +#: vendor/woocommerce/subscriptions-core/wcs-functions.php:296 +msgctxt "table heading" +msgid "End Date" +msgstr "" + #: vendor/woocommerce/subscriptions-core/wcs-functions.php:331 msgid "Date type is not a string." msgstr "" diff --git a/vendor/autoload.php b/vendor/autoload.php index e5e0b2d..aaaf88e 100644 --- a/vendor/autoload.php +++ b/vendor/autoload.php @@ -22,4 +22,4 @@ if (PHP_VERSION_ID < 50600) { require_once __DIR__ . '/composer/autoload_real.php'; -return ComposerAutoloaderInite5f25043b4323431d48e7e46b04aa2c9::getLoader(); +return ComposerAutoloaderInit270a670e4e35541a06419730c5cc18f6::getLoader(); diff --git a/vendor/composer/ClassLoader.php b/vendor/composer/ClassLoader.php index 7824d8f..a72151c 100644 --- a/vendor/composer/ClassLoader.php +++ b/vendor/composer/ClassLoader.php @@ -45,34 +45,35 @@ class ClassLoader /** @var \Closure(string):void */ private static $includeFile; - /** @var string|null */ + /** @var ?string */ private $vendorDir; // PSR-4 /** - * @var array> + * @var array[] + * @psalm-var array> */ private $prefixLengthsPsr4 = array(); /** - * @var array> + * @var array[] + * @psalm-var array> */ private $prefixDirsPsr4 = array(); /** - * @var list + * @var array[] + * @psalm-var array */ private $fallbackDirsPsr4 = array(); // PSR-0 /** - * List of PSR-0 prefixes - * - * Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2'))) - * - * @var array>> + * @var array[] + * @psalm-var array> */ private $prefixesPsr0 = array(); /** - * @var list + * @var array[] + * @psalm-var array */ private $fallbackDirsPsr0 = array(); @@ -80,7 +81,8 @@ class ClassLoader private $useIncludePath = false; /** - * @var array + * @var string[] + * @psalm-var array */ private $classMap = array(); @@ -88,20 +90,21 @@ class ClassLoader private $classMapAuthoritative = false; /** - * @var array + * @var bool[] + * @psalm-var array */ private $missingClasses = array(); - /** @var string|null */ + /** @var ?string */ private $apcuPrefix; /** - * @var array + * @var self[] */ private static $registeredLoaders = array(); /** - * @param string|null $vendorDir + * @param ?string $vendorDir */ public function __construct($vendorDir = null) { @@ -110,7 +113,7 @@ class ClassLoader } /** - * @return array> + * @return string[] */ public function getPrefixes() { @@ -122,7 +125,8 @@ class ClassLoader } /** - * @return array> + * @return array[] + * @psalm-return array> */ public function getPrefixesPsr4() { @@ -130,7 +134,8 @@ class ClassLoader } /** - * @return list + * @return array[] + * @psalm-return array */ public function getFallbackDirs() { @@ -138,7 +143,8 @@ class ClassLoader } /** - * @return list + * @return array[] + * @psalm-return array */ public function getFallbackDirsPsr4() { @@ -146,7 +152,8 @@ class ClassLoader } /** - * @return array Array of classname => path + * @return string[] Array of classname => path + * @psalm-return array */ public function getClassMap() { @@ -154,7 +161,8 @@ class ClassLoader } /** - * @param array $classMap Class to filename map + * @param string[] $classMap Class to filename map + * @psalm-param array $classMap * * @return void */ @@ -171,25 +179,24 @@ class ClassLoader * Registers a set of PSR-0 directories for a given prefix, either * appending or prepending to the ones previously set for this prefix. * - * @param string $prefix The prefix - * @param list|string $paths The PSR-0 root directories - * @param bool $prepend Whether to prepend the directories + * @param string $prefix The prefix + * @param string[]|string $paths The PSR-0 root directories + * @param bool $prepend Whether to prepend the directories * * @return void */ public function add($prefix, $paths, $prepend = false) { - $paths = (array) $paths; if (!$prefix) { if ($prepend) { $this->fallbackDirsPsr0 = array_merge( - $paths, + (array) $paths, $this->fallbackDirsPsr0 ); } else { $this->fallbackDirsPsr0 = array_merge( $this->fallbackDirsPsr0, - $paths + (array) $paths ); } @@ -198,19 +205,19 @@ class ClassLoader $first = $prefix[0]; if (!isset($this->prefixesPsr0[$first][$prefix])) { - $this->prefixesPsr0[$first][$prefix] = $paths; + $this->prefixesPsr0[$first][$prefix] = (array) $paths; return; } if ($prepend) { $this->prefixesPsr0[$first][$prefix] = array_merge( - $paths, + (array) $paths, $this->prefixesPsr0[$first][$prefix] ); } else { $this->prefixesPsr0[$first][$prefix] = array_merge( $this->prefixesPsr0[$first][$prefix], - $paths + (array) $paths ); } } @@ -219,9 +226,9 @@ class ClassLoader * Registers a set of PSR-4 directories for a given namespace, either * appending or prepending to the ones previously set for this namespace. * - * @param string $prefix The prefix/namespace, with trailing '\\' - * @param list|string $paths The PSR-4 base directories - * @param bool $prepend Whether to prepend the directories + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param string[]|string $paths The PSR-4 base directories + * @param bool $prepend Whether to prepend the directories * * @throws \InvalidArgumentException * @@ -229,18 +236,17 @@ class ClassLoader */ public function addPsr4($prefix, $paths, $prepend = false) { - $paths = (array) $paths; if (!$prefix) { // Register directories for the root namespace. if ($prepend) { $this->fallbackDirsPsr4 = array_merge( - $paths, + (array) $paths, $this->fallbackDirsPsr4 ); } else { $this->fallbackDirsPsr4 = array_merge( $this->fallbackDirsPsr4, - $paths + (array) $paths ); } } elseif (!isset($this->prefixDirsPsr4[$prefix])) { @@ -250,18 +256,18 @@ class ClassLoader throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); } $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; - $this->prefixDirsPsr4[$prefix] = $paths; + $this->prefixDirsPsr4[$prefix] = (array) $paths; } elseif ($prepend) { // Prepend directories for an already registered namespace. $this->prefixDirsPsr4[$prefix] = array_merge( - $paths, + (array) $paths, $this->prefixDirsPsr4[$prefix] ); } else { // Append directories for an already registered namespace. $this->prefixDirsPsr4[$prefix] = array_merge( $this->prefixDirsPsr4[$prefix], - $paths + (array) $paths ); } } @@ -270,8 +276,8 @@ class ClassLoader * Registers a set of PSR-0 directories for a given prefix, * replacing any others previously set for this prefix. * - * @param string $prefix The prefix - * @param list|string $paths The PSR-0 base directories + * @param string $prefix The prefix + * @param string[]|string $paths The PSR-0 base directories * * @return void */ @@ -288,8 +294,8 @@ class ClassLoader * Registers a set of PSR-4 directories for a given namespace, * replacing any others previously set for this namespace. * - * @param string $prefix The prefix/namespace, with trailing '\\' - * @param list|string $paths The PSR-4 base directories + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param string[]|string $paths The PSR-4 base directories * * @throws \InvalidArgumentException * @@ -475,9 +481,9 @@ class ClassLoader } /** - * Returns the currently registered loaders keyed by their corresponding vendor directories. + * Returns the currently registered loaders indexed by their corresponding vendor directories. * - * @return array + * @return self[] */ public static function getRegisteredLoaders() { diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php index f9ecb29..b8cba3a 100644 --- a/vendor/composer/autoload_real.php +++ b/vendor/composer/autoload_real.php @@ -2,7 +2,7 @@ // autoload_real.php @generated by Composer -class ComposerAutoloaderInite5f25043b4323431d48e7e46b04aa2c9 +class ComposerAutoloaderInit270a670e4e35541a06419730c5cc18f6 { private static $loader; @@ -24,12 +24,12 @@ class ComposerAutoloaderInite5f25043b4323431d48e7e46b04aa2c9 require __DIR__ . '/platform_check.php'; - spl_autoload_register(array('ComposerAutoloaderInite5f25043b4323431d48e7e46b04aa2c9', 'loadClassLoader'), true, true); + spl_autoload_register(array('ComposerAutoloaderInit270a670e4e35541a06419730c5cc18f6', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__)); - spl_autoload_unregister(array('ComposerAutoloaderInite5f25043b4323431d48e7e46b04aa2c9', 'loadClassLoader')); + spl_autoload_unregister(array('ComposerAutoloaderInit270a670e4e35541a06419730c5cc18f6', 'loadClassLoader')); require __DIR__ . '/autoload_static.php'; - call_user_func(\Composer\Autoload\ComposerStaticInite5f25043b4323431d48e7e46b04aa2c9::getInitializer($loader)); + call_user_func(\Composer\Autoload\ComposerStaticInit270a670e4e35541a06419730c5cc18f6::getInitializer($loader)); $loader->register(true); diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index 3aae124..f3eec5c 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -4,7 +4,7 @@ namespace Composer\Autoload; -class ComposerStaticInite5f25043b4323431d48e7e46b04aa2c9 +class ComposerStaticInit270a670e4e35541a06419730c5cc18f6 { public static $prefixLengthsPsr4 = array ( 'C' => @@ -129,9 +129,9 @@ class ComposerStaticInite5f25043b4323431d48e7e46b04aa2c9 public static function getInitializer(ClassLoader $loader) { return \Closure::bind(function () use ($loader) { - $loader->prefixLengthsPsr4 = ComposerStaticInite5f25043b4323431d48e7e46b04aa2c9::$prefixLengthsPsr4; - $loader->prefixDirsPsr4 = ComposerStaticInite5f25043b4323431d48e7e46b04aa2c9::$prefixDirsPsr4; - $loader->classMap = ComposerStaticInite5f25043b4323431d48e7e46b04aa2c9::$classMap; + $loader->prefixLengthsPsr4 = ComposerStaticInit270a670e4e35541a06419730c5cc18f6::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInit270a670e4e35541a06419730c5cc18f6::$prefixDirsPsr4; + $loader->classMap = ComposerStaticInit270a670e4e35541a06419730c5cc18f6::$classMap; }, null, ClassLoader::class); } diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index 752ab41..8612194 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -156,17 +156,17 @@ }, { "name": "woocommerce/subscriptions-core", - "version": "6.9.0", - "version_normalized": "6.9.0.0", + "version": "7.0.0", + "version_normalized": "7.0.0.0", "source": { "type": "git", "url": "https://github.com/Automattic/woocommerce-subscriptions-core.git", - "reference": "baf73669ba923b544aec87421282b753e277171f" + "reference": "80a6cff950b4d43932382ebda2bad801b35af229" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Automattic/woocommerce-subscriptions-core/zipball/baf73669ba923b544aec87421282b753e277171f", - "reference": "baf73669ba923b544aec87421282b753e277171f", + "url": "https://api.github.com/repos/Automattic/woocommerce-subscriptions-core/zipball/80a6cff950b4d43932382ebda2bad801b35af229", + "reference": "80a6cff950b4d43932382ebda2bad801b35af229", "shasum": "" }, "require": { @@ -179,7 +179,7 @@ "woocommerce/woocommerce-sniffs": "0.1.0", "yoast/phpunit-polyfills": "1.1.0" }, - "time": "2024-03-28T05:12:56+00:00", + "time": "2024-04-11T01:44:20+00:00", "type": "wordpress-plugin", "extra": { "phpcodesniffer-search-depth": 2 @@ -209,7 +209,7 @@ "description": "Sell products and services with recurring payments in your WooCommerce Store.", "homepage": "https://github.com/Automattic/woocommerce-subscriptions-core", "support": { - "source": "https://github.com/Automattic/woocommerce-subscriptions-core/tree/6.9.0", + "source": "https://github.com/Automattic/woocommerce-subscriptions-core/tree/7.0.0", "issues": "https://github.com/Automattic/woocommerce-subscriptions-core/issues" }, "install-path": "../woocommerce/subscriptions-core" diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php index cef6f2e..d13a40c 100644 --- a/vendor/composer/installed.php +++ b/vendor/composer/installed.php @@ -1,9 +1,9 @@ array( 'name' => 'woocommerce/woocommerce-subscriptions', - 'pretty_version' => 'dev-release/6.1.0', - 'version' => 'dev-release/6.1.0', - 'reference' => 'a609ac6f082f5fff09d0059eebcc48aa11229f58', + 'pretty_version' => 'dev-release/6.2.0', + 'version' => 'dev-release/6.2.0', + 'reference' => '6c5d3d921f40709889b3f583726869f2fa95877f', 'type' => 'wordpress-plugin', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -32,18 +32,18 @@ ), ), 'woocommerce/subscriptions-core' => array( - 'pretty_version' => '6.9.0', - 'version' => '6.9.0.0', - 'reference' => 'baf73669ba923b544aec87421282b753e277171f', + 'pretty_version' => '7.0.0', + 'version' => '7.0.0.0', + 'reference' => '80a6cff950b4d43932382ebda2bad801b35af229', 'type' => 'wordpress-plugin', 'install_path' => __DIR__ . '/../woocommerce/subscriptions-core', 'aliases' => array(), 'dev_requirement' => false, ), 'woocommerce/woocommerce-subscriptions' => array( - 'pretty_version' => 'dev-release/6.1.0', - 'version' => 'dev-release/6.1.0', - 'reference' => 'a609ac6f082f5fff09d0059eebcc48aa11229f58', + 'pretty_version' => 'dev-release/6.2.0', + 'version' => 'dev-release/6.2.0', + 'reference' => '6c5d3d921f40709889b3f583726869f2fa95877f', 'type' => 'wordpress-plugin', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), diff --git a/vendor/woocommerce/subscriptions-core/changelog.txt b/vendor/woocommerce/subscriptions-core/changelog.txt index 6de6e82..fda7ac6 100644 --- a/vendor/woocommerce/subscriptions-core/changelog.txt +++ b/vendor/woocommerce/subscriptions-core/changelog.txt @@ -1,5 +1,14 @@ *** WooCommerce Subscriptions Core Changelog *** += 7.0.0 - 2024-04-11 = +* Fix - Update the failing payment method on a subscription when customers successfully pay for a failed renewal order via the block checkout. +* Fix - Resolved an issue that would cause subscriptions to be directly cancelled by the WooCommerce process of automatically cancelling unpaid orders in-line with the hold stock setting. +* Fix - Prevent duplicate status transition notes on subscriptions and potential infinite loops when processing subscription status transitions. +* Fix - Resolved an issue that could lead to "Undefined array key 'order-received'" errors. +* Fix - Resolved errors that could occur when paying for renewal orders via the checkout when the store has custom checkout fields. +* Fix - Resolved database errors that would occur when ordering the subscriptions list table by the 'Last order date' on sites with HPOS enabled. +* Dev - Introduced a new filter ('wcs_setup_cart_for_subscription_initial_payment') to enable third-party plugins to use the pay-for-order flow to complete a subscription's initial payment. + = 6.9.0 - 2024-03-28 = * Fix - Resolved an issue where discounts, when reapplied to failed or manual subscription order payments, would incorrectly account for inclusive tax. * Fix - Resolved an issue that could cause an empty error notice to appear on the My Account > Payment methods page when a customer attempted to delete a token used by subscriptions. diff --git a/vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php b/vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php index 463adf6..d98a8af 100644 --- a/vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php +++ b/vendor/woocommerce/subscriptions-core/includes/admin/class-wcs-admin-post-types.php @@ -1811,9 +1811,7 @@ class WCS_Admin_Post_Types { $query_order = strtoupper( $args['order'] ); - // fields and order are identical in both cases - $pieces['fields'] .= ', COALESCE(lp.last_payment, orders.date_created_gmt, 0) as lp'; - $pieces['orderby'] = "CAST(lp AS DATETIME) {$query_order}"; + $pieces['orderby'] = "COALESCE(lp.last_payment, parent_order.date_created_gmt, 0) {$query_order}"; return $pieces; } @@ -1841,7 +1839,7 @@ class WCS_Admin_Post_Types { WHERE order_meta.meta_key = '_subscription_renewal' GROUP BY order_meta.meta_value) lp ON {$order_table}.id = lp.meta_value - LEFT JOIN {$order_table} orders on {$order_table}.parent_order_id = orders.ID"; + LEFT JOIN {$order_table} as parent_order on {$order_table}.parent_order_id = parent_order.ID"; return $pieces; } @@ -1866,24 +1864,22 @@ class WCS_Admin_Post_Types { $table_name = substr( "{$wpdb->prefix}tmp_{$session}_lastpayment", 0, 64 ); // Create a temporary table, drop the previous one. - $wpdb->query( $wpdb->prepare( 'DROP TEMPORARY TABLE IF EXISTS %s', $table_name ) ); + //phpcs:disable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared + $wpdb->query( "DROP TEMPORARY TABLE IF EXISTS {$table_name}" ); $wpdb->query( - $wpdb->prepare( - "CREATE TEMPORARY TABLE %s (id INT PRIMARY KEY, last_payment DATETIME) AS - SELECT order_meta.meta_value as id, MAX( orders.date_created_gmt ) as last_payment FROM %s order_meta - LEFT JOIN %s as orders ON orders.id = order_meta.order_id - WHERE order_meta.meta_key = '_subscription_renewal' - GROUP BY order_meta.meta_value", - $table_name, - $meta_table, - $order_table - ) + "CREATE TEMPORARY TABLE {$table_name} (id INT PRIMARY KEY, last_payment DATETIME) AS + SELECT order_meta.meta_value as id, MAX( orders.date_created_gmt ) as last_payment + FROM {$meta_table} as order_meta + LEFT JOIN {$order_table} as orders ON orders.id = order_meta.order_id + WHERE order_meta.meta_key = '_subscription_renewal' + GROUP BY order_meta.meta_value" ); + //phpcs:enable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared - $pieces['join'] .= "LEFT JOIN {$table_name} lp + $pieces['join'] .= "LEFT JOIN {$table_name} as lp ON {$order_table}.id = lp.id - LEFT JOIN {$order_table} orders on {$order_table}.parent_order_id = orders.id"; + LEFT JOIN {$order_table} as parent_order on {$order_table}.parent_order_id = parent_order.id"; return $pieces; } diff --git a/vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php b/vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php index 4c3604d..673a3f2 100644 --- a/vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php +++ b/vendor/woocommerce/subscriptions-core/includes/class-wc-subscription.php @@ -92,6 +92,18 @@ class WC_Subscription extends WC_Order { 'suspension_count', ); + /** + * The meta key used to flag that the subscription's payment failed. + * + * Stored on the renewal order itself. + * + * Payments via the Block checkout transition the order status from failed to pending and then to processing. + * This makes it impossible for us to know if the order was initially failed. This meta key flags that the order was initially failed. + * + * @var string + */ + const RENEWAL_FAILED_META_KEY = '_failed_renewal_order'; + /** * Initializes a specific subscription if the ID is passed, otherwise a new and empty instance of a subscription. * @@ -564,6 +576,9 @@ class WC_Subscription extends WC_Order { // Use local copy of status transition value. $status_transition = $this->status_transition; + // Reset status transition variable. + $this->status_transition = false; + // If we're not currently in the midst of a status transition, bail early. if ( ! $status_transition ) { return; @@ -605,9 +620,6 @@ class WC_Subscription extends WC_Order { ); $this->add_order_note( __( 'Error during subscription status transition.', 'woocommerce-subscriptions' ) . ' ' . $e->getMessage() ); } - - // This has run, so reset status transition variable - $this->status_transition = false; } /** @@ -1862,6 +1874,8 @@ class WC_Subscription extends WC_Order { do_action( 'woocommerce_subscription_payment_complete', $this ); if ( false !== $last_order && wcs_order_contains_renewal( $last_order ) ) { + $last_order->delete_meta_data( self::RENEWAL_FAILED_META_KEY ); + $last_order->save(); do_action( 'woocommerce_subscription_renewal_payment_complete', $this, $last_order ); } } @@ -1876,10 +1890,17 @@ class WC_Subscription extends WC_Order { // Make sure the last order's status is set to failed $last_order = $this->get_last_order( 'all', 'any' ); - if ( false !== $last_order && false === $last_order->has_status( 'failed' ) ) { - remove_filter( 'woocommerce_order_status_changed', 'WC_Subscriptions_Renewal_Order::maybe_record_subscription_payment' ); - $last_order->update_status( 'failed' ); - add_filter( 'woocommerce_order_status_changed', 'WC_Subscriptions_Renewal_Order::maybe_record_subscription_payment', 10, 3 ); + if ( false !== $last_order ) { + $last_order->update_meta_data( self::RENEWAL_FAILED_META_KEY, wc_bool_to_string( true ) ); + + if ( false === $last_order->has_status( 'failed' ) ) { + remove_filter( 'woocommerce_order_status_changed', 'WC_Subscriptions_Renewal_Order::maybe_record_subscription_payment' ); + $last_order->update_status( 'failed' ); + add_filter( 'woocommerce_order_status_changed', 'WC_Subscriptions_Renewal_Order::maybe_record_subscription_payment', 10, 3 ); + } else { + // If we didn't update the status, save the order to make sure our self::RENEWAL_FAILED_META_KEY meta data is saved. + $last_order->save(); + } } // Log payment failure on order diff --git a/vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-core-plugin.php b/vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-core-plugin.php index 30228a4..1e14df6 100644 --- a/vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-core-plugin.php +++ b/vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-core-plugin.php @@ -16,7 +16,7 @@ class WC_Subscriptions_Core_Plugin { * The version of subscriptions-core library. * @var string */ - protected $library_version = '6.9.0'; // WRCS: DEFINED_VERSION. + protected $library_version = '7.0.0'; // WRCS: DEFINED_VERSION. /** * The subscription scheduler instance. diff --git a/vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php b/vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php index 85a3f3d..7667b34 100644 --- a/vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php +++ b/vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php @@ -57,6 +57,8 @@ class WC_Subscriptions_Manager { // Do the same thing for WordPress networks add_action( 'wpmu_delete_user', __CLASS__ . '::trash_users_subscriptions_for_network' ); + + add_filter( 'woocommerce_cancel_unpaid_order', [ __CLASS__, 'exclude_subscription_from_order_cleanup' ], 10, 2 ); } /** @@ -605,6 +607,22 @@ class WC_Subscriptions_Manager { do_action( 'pending_subscription_created_for_order', $order, $product_id ); } + /** + * Excludes subscriptions from the order cleanup process. + * + * @param bool $should_cancel Whether the order should be cancelled. + * @param WC_Order $order The order object. + * + * @return bool Whether the order should be cancelled. + */ + public static function exclude_subscription_from_order_cleanup( $should_cancel, $order ) { + if ( $should_cancel && 'shop_subscription' === $order->get_type() ) { + $should_cancel = false; + } + + return $should_cancel; + } + /** * Creates subscriptions against a users account with a status of pending when a user creates * an order containing subscriptions. diff --git a/vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-renewal-order.php b/vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-renewal-order.php index 0b2fe14..c34b079 100644 --- a/vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-renewal-order.php +++ b/vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-renewal-order.php @@ -127,7 +127,7 @@ class WC_Subscriptions_Renewal_Order { if ( $order_completed && ! $subscription->has_status( wcs_get_subscription_ended_statuses() ) && ! $subscription->has_status( 'active' ) ) { // Included here because calling payment_complete sets the retry status to 'cancelled' - $is_failed_renewal_order = 'failed' === $orders_old_status; + $is_failed_renewal_order = 'failed' === $orders_old_status || wc_string_to_bool( $order->get_meta( WC_Subscription::RENEWAL_FAILED_META_KEY, true ) ); $is_failed_renewal_order = apply_filters( 'woocommerce_subscriptions_is_failed_renewal_order', $is_failed_renewal_order, $order_id, $orders_old_status ); if ( $order_needed_payment ) { diff --git a/vendor/woocommerce/subscriptions-core/includes/class-wcs-cart-initial-payment.php b/vendor/woocommerce/subscriptions-core/includes/class-wcs-cart-initial-payment.php index d9e7215..eb97e53 100644 --- a/vendor/woocommerce/subscriptions-core/includes/class-wcs-cart-initial-payment.php +++ b/vendor/woocommerce/subscriptions-core/includes/class-wcs-cart-initial-payment.php @@ -52,6 +52,26 @@ class WCS_Cart_Initial_Payment extends WCS_Cart_Renewal { return; } + /** + * Filter whether to set up the cart during the pay-for-order payment flow. + * + * Allows developers to bypass cart setup for the pay-for-order payment flow. + * This is intended for situations in which re-creating the cart will result in + * the loss of order data. + * + * @since 6.2.0 + * + * @param bool $recreate_cart Whether to recreate the initial payment order. Default true. + * @param WC_Order $order The order object. + * @param string $order_key The order key. + * @param int $order_id The order ID. + */ + $recreate_cart = apply_filters( "wcs_setup_cart_for_{$this->cart_item_key}", true, $order, $order_key, $order_id ); + + if ( ! $recreate_cart ) { + return; + } + if ( ! is_user_logged_in() ) { // Allow the customer to login first and then redirect them back. $redirect = add_query_arg( diff --git a/vendor/woocommerce/subscriptions-core/includes/class-wcs-cart-renewal.php b/vendor/woocommerce/subscriptions-core/includes/class-wcs-cart-renewal.php index ab37f01..4cd2b91 100644 --- a/vendor/woocommerce/subscriptions-core/includes/class-wcs-cart-renewal.php +++ b/vendor/woocommerce/subscriptions-core/includes/class-wcs-cart-renewal.php @@ -494,33 +494,44 @@ class WCS_Cart_Renewal { * Returns address details from the renewal order if the checkout is for a renewal. * * @param string $value Default checkout field value. - * @param string $key The checkout form field name/key + * @param string $key The checkout form field name/key. + * * @return string $value Checkout field value. */ public function checkout_get_value( $value, $key ) { - // Only hook in after WC()->checkout() has been initialised - if ( $this->cart_contains() && did_action( 'woocommerce_checkout_init' ) > 0 ) { + // Only hook in after WC()->checkout() has been initialised. + if ( ! $this->cart_contains() || did_action( 'woocommerce_checkout_init' ) <= 0 ) { + return $value; + } - // Guard against the fake WC_Checkout singleton, see https://github.com/woocommerce/woocommerce-subscriptions/issues/427#issuecomment-260763250 - remove_filter( 'woocommerce_checkout_get_value', array( &$this, 'checkout_get_value' ), 10 ); + // Get the most specific order object, which will be the renewal order for renewals, initial order for initial payments, or a subscription for switches/resubscribes. + $order = $this->get_order(); - if ( is_callable( array( WC()->checkout(), 'get_checkout_fields' ) ) ) { // WC 3.0+ - $address_fields = array_merge( WC()->checkout()->get_checkout_fields( 'billing' ), WC()->checkout()->get_checkout_fields( 'shipping' ) ); - } else { - $address_fields = array_merge( WC()->checkout()->checkout_fields['billing'], WC()->checkout()->checkout_fields['shipping'] ); - } + if ( ! $order ) { + return $value; + } - add_filter( 'woocommerce_checkout_get_value', array( &$this, 'checkout_get_value' ), 10, 2 ); + $address_fields = array_merge( + WC()->countries->get_address_fields( + $order->get_billing_country(), + 'billing_' + ), + WC()->countries->get_address_fields( + $order->get_shipping_country(), + 'shipping_' + ) + ); - if ( array_key_exists( $key, $address_fields ) && false !== ( $item = $this->cart_contains() ) ) { + // Generate the address getter method for the key. + $getter = "get_{$key}"; - // Get the most specific order object, which will be the renewal order for renewals, initial order for initial payments, or a subscription for switches/resubscribes - $order = $this->get_order( $item ); + if ( array_key_exists( $key, $address_fields ) && is_callable( [ $order, $getter ] ) ) { + $order_value = call_user_func( [ $order, $getter ] ); - if ( ( $order_value = wcs_get_objects_property( $order, $key ) ) ) { - $value = $order_value; - } + // Given this is fetching the value for a checkout field, we need to ensure the value is a scalar. + if ( is_scalar( $order_value ) ) { + $value = $order_value; } } diff --git a/vendor/woocommerce/subscriptions-core/includes/gateways/paypal/class-wcs-paypal.php b/vendor/woocommerce/subscriptions-core/includes/gateways/paypal/class-wcs-paypal.php index aef1d02..23b1e9d 100644 --- a/vendor/woocommerce/subscriptions-core/includes/gateways/paypal/class-wcs-paypal.php +++ b/vendor/woocommerce/subscriptions-core/includes/gateways/paypal/class-wcs-paypal.php @@ -511,11 +511,11 @@ class WCS_PayPal { * @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.5.3 */ public static function maybe_add_payment_lock() { - if ( ! wcs_is_order_received_page() ) { + global $wp; + if ( ! isset( $wp->query_vars['order-received'] ) ) { return; } - global $wp; $order = wc_get_order( absint( $wp->query_vars['order-received'] ) ); if ( $order && self::instance()->get_id() === $order->get_payment_method() && $order->needs_payment() && ! self::are_reference_transactions_enabled() && wcs_order_contains_subscription( $order, array( 'parent' ) ) ) { diff --git a/vendor/woocommerce/subscriptions-core/woocommerce-subscriptions-core.php b/vendor/woocommerce/subscriptions-core/woocommerce-subscriptions-core.php index 3e9a3b6..e63d0f9 100644 --- a/vendor/woocommerce/subscriptions-core/woocommerce-subscriptions-core.php +++ b/vendor/woocommerce/subscriptions-core/woocommerce-subscriptions-core.php @@ -6,5 +6,5 @@ * Author: Automattic * Author URI: https://woocommerce.com/ * Requires WP: 5.6 - * Version: 6.9.0 + * Version: 7.0.0 */ diff --git a/woocommerce-subscriptions.php b/woocommerce-subscriptions.php index 93b1c65..bc24fbe 100644 --- a/woocommerce-subscriptions.php +++ b/woocommerce-subscriptions.php @@ -5,7 +5,8 @@ * Description: Sell products and services with recurring payments in your WooCommerce Store. * Author: WooCommerce * Author URI: https://woocommerce.com/ - * Version: 6.1.0 + * Version: 6.2.0 + * Requires Plugins: woocommerce * * WC requires at least: 7.7.0 * WC tested up to: 8.7.0 @@ -77,7 +78,7 @@ class WC_Subscriptions { public static $plugin_file = __FILE__; /** @var string */ - public static $version = '6.1.0'; // WRCS: DEFINED_VERSION. + public static $version = '6.2.0'; // WRCS: DEFINED_VERSION. /** @var string */ public static $wc_minimum_supported_version = '7.7';