__( 'Pay now', 'woocommerce-subscriptions' ), 'attributes' => array( 'id' => 'early_renewal_modal_submit', 'class' => 'button alt', 'href' => add_query_arg( array( 'subscription_id' => $subscription->get_id(), 'process_early_renewal' => true, 'wcs_nonce' => wp_create_nonce( 'wcs-renew-early-modal-' . $subscription->get_id() ), ) ), 'data-payment-method' => $subscription->get_payment_method(), ), ); if ( wc_wp_theme_get_element_class_name( 'button' ) ) { $place_order_action['attributes']['class'] .= ' ' . wc_wp_theme_get_element_class_name( 'button' ); $place_order_action['attributes']['role'] = 'button'; } $callback_args = array( 'callback' => array( __CLASS__, 'output_early_renewal_modal' ), 'parameters' => array( 'subscription' => $subscription ), ); $modal = new WCS_Modal( $callback_args, '.subscription_renewal_early', 'callback', __( 'Renew early', 'woocommerce-subscriptions' ) ); // Set the modal ID to match the predictable value used for aria-controls in subscription-details.php $modal->set_id( 'wcs-early-renewal-modal-' . $subscription->get_id() ); $modal->add_action( $place_order_action ); $modal->print_html(); } /** * Prints the early renewal modal HTML. * * @since 2.6.0 * @param WC_Subscription $subscription The subscription to print the modal for. */ public static function output_early_renewal_modal( $subscription ) { $totals = $subscription->get_order_item_totals(); $date_changes = WCS_Early_Renewal_Manager::get_dates_to_update( $subscription ); if ( isset( $totals['payment_method'] ) ) { $totals['payment_method']['label'] = __( 'Payment:', 'woocommerce-subscriptions' ); } // Convert the new next payment date into the site's timezone. if ( ! empty( $date_changes['next_payment'] ) ) { $new_next_payment_date = new WC_DateTime( $date_changes['next_payment'], new DateTimeZone( 'UTC' ) ); $new_next_payment_date->setTimezone( new DateTimeZone( wc_timezone_string() ) ); } else { $new_next_payment_date = null; } wc_get_template( 'html-early-renewal-modal-content.php', array( 'subscription' => $subscription, 'totals' => $totals, 'new_next_payment_date' => $new_next_payment_date, ), '', WC_Subscriptions_Plugin::instance()->get_plugin_directory( 'templates/' ) ); } /** * Processes the request to renew early via the modal. * * @since 2.6.0 */ public static function process_early_renewal_request() { if ( ! isset( $_GET['process_early_renewal'], $_GET['subscription_id'], $_GET['wcs_nonce'] ) ) { return; } if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['wcs_nonce'] ) ), 'wcs-renew-early-modal-' . absint( $_GET['subscription_id'] ) ) ) { wc_add_notice( __( 'There was an error with your request. Please try again.', 'woocommerce-subscriptions' ), 'error' ); self::redirect(); } $subscription = wcs_get_subscription( absint( $_GET['subscription_id'] ) ); if ( ! $subscription ) { wc_add_notice( __( 'We were unable to locate that subscription, please try again.', 'woocommerce-subscriptions' ), 'error' ); self::redirect(); } if ( ! self::can_user_renew_early_via_modal( $subscription ) ) { wc_add_notice( __( "You can't renew the subscription at this time. Please try again.", 'woocommerce-subscriptions' ), 'error' ); self::redirect(); } // Before processing the request, detach the functions which handle standard renewal orders. Note we don't need to reattach them as this request will terminate soon. self::detach_renewal_callbacks(); $renewal_order = wcs_create_renewal_order( $subscription ); if ( ! wcs_is_order( $renewal_order ) ) { wc_add_notice( __( "We couldn't create a renewal order for your subscription, please try again.", 'woocommerce-subscriptions' ), 'error' ); self::redirect(); } $renewal_order->set_payment_method( wc_get_payment_gateway_by_order( $subscription ) ); $renewal_order->update_meta_data( '_subscription_renewal_early', $subscription->get_id() ); $renewal_order->save(); // Attempt to collect payment with the subscription's current payment method. WC_Subscriptions_Payment_Gateways::trigger_gateway_renewal_payment_hook( $renewal_order ); // Now that we've attempted to process the payment, refresh the order. $renewal_order = wc_get_order( $renewal_order->get_id() ); // Failed early renewals won't place the subscription on-hold so delete unsuccessful early renewal orders and redirect the user to complete the payment via checkout. if ( $renewal_order->needs_payment() ) { $renewal_order->delete( true ); wc_add_notice( __( 'Payment for the renewal order was unsuccessful with your payment method on file, please try again.', 'woocommerce-subscriptions' ), 'error' ); wp_safe_redirect( wcs_get_early_renewal_url( $subscription ) ); exit(); } // 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(); wcs_update_dates_after_early_renewal( $subscription, $renewal_order ); wc_add_notice( __( 'Your early renewal order was successful.', 'woocommerce-subscriptions' ), 'success' ); } self::redirect(); } /** * Checks if a user can renew a subscription early via the modal window. * * @param int|WC_Subscription $subscription Post ID of a 'shop_subscription' post, or instance of a WC_Subscription object. * @param int $user_id The ID of a user. Defaults to the current user. * @return boolean * * @since 3.0.5 */ public static function can_user_renew_early_via_modal( $subscription, $user_id = 0 ) { $user_id = ! empty( $user_id ) ? absint( $user_id ) : get_current_user_id(); $subscription = wcs_get_subscription( $subscription ); if ( ! $subscription ) { return false; } if ( ! WCS_Early_Renewal_Manager::is_early_renewal_via_modal_enabled() || ! wcs_can_user_renew_early( $subscription, $user_id ) ) { return false; } return apply_filters( 'woocommerce_subscriptions_can_user_renew_early_via_modal', $subscription->get_user_id() === $user_id, $subscription, $user_id ); } /** * Redirect the user after processing their early renewal request. * * @since 2.6.0 */ private static function redirect() { wp_safe_redirect( remove_query_arg( array( 'process_early_renewal', 'subscription_id', 'wcs_nonce' ) ) ); exit(); } /** * Removes filters which shouldn't run while processing early renewals via the modal. * * @since 2.6.0 */ private static function detach_renewal_callbacks() { remove_filter( 'wcs_renewal_order_created', 'WC_Subscriptions_Renewal_Order::add_order_note' ); remove_filter( 'woocommerce_order_status_changed', 'WC_Subscriptions_Renewal_Order::maybe_record_subscription_payment' ); } }