PluginProbe
Subscriptions for WooCommerce with Stripe Recurring Payments / 2.4.0
Subscriptions for WooCommerce with Stripe Recurring Payments v2.4.0
2.4.0 2.3.0 2.2.0 2.1.0 2.0.0 1.11.2 1.11.1 1.11.0 1.10.9 1.10.8 1.10.7 1.10.6 1.10.5 1.10.4 1.10.3 1.10.2 1.10.1 1.10.0 1.9.6 1.9.5 trunk 1.3.0 1.3.1 1.3.2 1.4.0 All 65 releases
← All changes | includes/Frontend/MyAccount.php +184 -65 1.3.1 → 2.4.0 View file →
@@ -1,8 +1,16 @@
1 1 <?php
2 2
3 3 namespace SpringDevs\Subscription\Frontend;
4 4
5 +use SpringDevs\Subscription\Illuminate\Helper;
6 +use SpringDevs\Subscription\Illuminate\Subscription\Subscription;
7 +
8 +// HPOS: This file is compatible with WooCommerce High-Performance Order Storage (HPOS).
9 +// All WooCommerce order data is accessed via WooCommerce CRUD methods (wc_get_order, wc_get_order_item_meta, etc.).
10 +// All direct post meta access is for subscription data only, not WooCommerce order data.
11 +// If you add new order data access, use WooCommerce CRUD for HPOS compatibility.
12 +
5 13 /**
6 14 * Class MyAccount
7 15 *
8 16 * @package SpringDevs\Subscription\Frontend
@@ -7,21 +15,48 @@
7 15 *
8 16 * @package SpringDevs\Subscription\Frontend
9 17 */
10 18 class MyAccount {
19 + /**
20 + * Subscriptions endpoint slug.
21 + *
22 + * @var string
23 + */
24 + private $subscriptions_endpoint = 'subscriptions';
11 25
26 + /**
27 + * View Subscriptions endpoint slug.
28 + *
29 + * @var string
30 + */
31 + private $view_subscriptions_endpoint = 'view-subscription';
12 32
33 +
13 34 /**
14 35 * Initialize the class
15 36 */
16 37 public function __construct() {
38 + // Assign endpoint slug from settings.
39 + $this->subscriptions_endpoint = Subscription::get_user_endpoint( 'subs_list' );
40 + $this->view_subscriptions_endpoint = Subscription::get_user_endpoint( 'view_subs' );
41 +
42 + // Flush rewrite rules on init.
17 43 add_action( 'init', array( $this, 'flush_rewrite_rules' ) );
18 - add_filter( 'woocommerce_account_menu_items', array( $this, 'custom_my_account_menu_items' ) );
19 - add_filter( 'woocommerce_endpoint_view-subscription_title', array( $this, 'change_single_title' ) );
20 - add_filter( 'the_title', array( $this, 'change_lists_title' ), 10 );
44 +
45 + // Add My Subscriptions menu item.
46 + add_filter( 'woocommerce_account_menu_items', array( $this, 'custom_my_account_menu_items' ), 200 );
47 +
48 + // Add subscription url endpoints to query vars.
21 49 add_filter( 'woocommerce_get_query_vars', array( $this, 'custom_query_vars' ) );
22 - add_action( 'woocommerce_account_view-subscription_endpoint', array( $this, 'view_subscrpt_content' ) );
23 - add_action( 'woocommerce_account_subscriptions_endpoint', array( $this, 'subscrpt_endpoint_content' ) );
50 +
51 + // Subscription EndPoint Content.
52 + add_action( "woocommerce_account_{$this->subscriptions_endpoint}_endpoint", array( $this, 'subscrpt_endpoint_content' ) );
53 + add_action( "woocommerce_account_{$this->view_subscriptions_endpoint}_endpoint", array( $this, 'view_subscrpt_content' ) );
54 +
55 + // Subscription page titles
56 + add_filter( "woocommerce_endpoint_{$this->subscriptions_endpoint}_title", array( $this, 'change_subscriptions_title' ) );
57 + add_filter( "woocommerce_endpoint_{$this->view_subscriptions_endpoint}_title", array( $this, 'change_single_subscription_title' ) );
58 +
24 59 add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_styles' ) );
25 60 }
26 61
27 62 /**
@@ -31,9 +66,11 @@
31 66 *
32 67 * @return array
33 68 */
34 69 public function custom_query_vars( array $query_vars ): array {
35 - $query_vars['view-subscription'] = 'view-subscription';
70 + $query_vars[ $this->subscriptions_endpoint ] = $this->subscriptions_endpoint;
71 + $query_vars[ $this->view_subscriptions_endpoint ] = $this->view_subscriptions_endpoint;
72 +
36 73 return $query_vars;
37 74 }
38 75
39 76 /**
@@ -38,90 +75,169 @@
38 75
39 76 /**
40 77 * Display Subscription Content.
41 78 *
42 - * @param Int $id Post ID.
79 + * @param int $id Post ID.
43 80 */
44 - public function view_subscrpt_content( int $id ) {
45 - $order_id = get_post_meta( $id, '_subscrpt_order_id', true );
46 - $order_item_id = get_post_meta( $id, '_subscrpt_order_item_id', true );
47 - if ( ! $order_id || ! $order_item_id ) {
81 + public function view_subscrpt_content( $id ) {
82 + $id = absint( $id ); // typecast to int.
83 +
84 + $subs_post = get_post( $id );
85 + $author_id = $subs_post ? (int) $subs_post->post_author : 0;
86 + $current_user_id = get_current_user_id();
87 + $user_is_admin = current_user_can( 'manage_options' );
88 +
89 + if ( ! $user_is_admin && (int) $author_id !== (int) $current_user_id ) {
48 90 return wp_safe_redirect( '/404' );
49 91 }
50 - $order = wc_get_order( $order_id );
51 - $order_item = $order->get_item( $order_item_id );
52 - $status = get_post_status( $id );
53 - $user_cancel = get_post_meta( $id, '_subscrpt_user_cancel', true );
54 - $start_date = get_post_meta( $id, '_subscrpt_start_date', true );
55 - $next_date = get_post_meta( $id, '_subscrpt_next_date', true );
56 - $trial = get_post_meta( $id, '_subscrpt_trial', true );
57 - $trial_mode = get_post_meta( $id, '_subscrpt_trial_mode', true );
58 92
93 + $subscription_id = $id;
94 + $subscription_data = Helper::get_subscription_data( $subscription_id );
95 + $related_orders = Helper::get_related_orders( $subscription_id );
96 +
97 + $status = $subscription_data['status'] ?? '';
98 + $verbose_status = Helper::get_verbose_status( $status );
99 +
100 + $order_id = $subscription_data['order']['order_id'] ?? 0;
101 + $order_item_id = $subscription_data['order']['order_item_id'] ?? 0;
102 +
103 + $order = wc_get_order( $order_id );
104 + $order_item = $order ? $order->get_item( $order_item_id ) : null;
105 +
106 + if ( ! $order || ! $order_item ) {
107 + return wp_safe_redirect( '/404' );
108 + }
109 +
110 + $user_cancel = $subscription_data['can_user_cancel'] ?? false;
111 +
112 + $start_date = $subscription_data['start_date'] ?? '';
113 + $start_date = ! empty( $start_date ) ? wp_date( 'F j, Y', strtotime( $start_date ) ) : '-';
114 +
115 + $next_date = $subscription_data['next_date'] ?? '';
116 + $next_date = ! empty( $next_date ) && Helper::has_next_payment( $status ) ? wp_date( 'F j, Y', strtotime( $next_date ) ) : '-';
117 +
118 + $trial = get_post_meta( $id, '_subscrpt_trial', true );
119 + $trial_mode = get_post_meta( $id, '_subscrpt_trial_mode', true );
120 +
121 + // Undiscounted subtotal, the discount that recurs, tax on the discounted amount, and the renewal total.
122 + $display_totals = Helper::get_subscription_display_totals( $id, $order_item );
123 +
124 + $price = $display_totals['total'];
125 + $price_excl_tax = $display_totals['full_excl'];
126 + $tax_amount = $display_totals['tax'];
127 + $discount = $display_totals['discount'];
128 +
129 + $is_grace_period = isset( $subscription_data['grace_period'] );
130 + $grace_remaining = $subscription_data['grace_period']['remaining_days'] ?? 0;
131 + $grace_end_date = $subscription_data['grace_period']['end_date'] ?? '';
132 + $grace_end_date = ! empty( $grace_end_date ) ? wp_date( 'F j, Y', strtotime( $grace_end_date ) ) : '';
133 +
59 134 $subscrpt_nonce = wp_create_nonce( 'subscrpt_nonce' );
60 135 $action_buttons = array();
61 136
62 137 if ( 'cancelled' !== $status ) {
63 - if ( in_array( $status, array( 'pending', 'active', 'on_hold' ), true ) && 'yes' === $user_cancel ) {
138 + if ( in_array( $status, array( 'pending', 'active', 'on_hold' ), true ) && $user_cancel ) {
139 + $label = __( 'Cancel', 'subscription' );
140 + $label = apply_filters( 'subscrpt_split_payment_button_text', $label, 'cancel', $id, $status );
141 +
64 142 $action_buttons['cancel'] = array(
65 143 'url' => subscrpt_get_action_url( 'cancelled', $subscrpt_nonce, $id ),
66 - 'label' => __( 'Cancel', 'sdevs_subscrpt' ),
144 + 'label' => $label,
67 145 'class' => 'cancel',
68 146 );
69 147 } elseif ( trim( $status ) === trim( 'pe_cancelled' ) ) {
70 - $action_buttons['reactive'] = array(
71 - 'url' => subscrpt_get_action_url( 'reactive', $subscrpt_nonce, $id ),
72 - 'label' => __( 'Reactive', 'sdevs_subscrpt' ),
148 + $label = __( 'Reactivate', 'subscription' );
149 + $label = apply_filters( 'subscrpt_split_payment_button_text', $label, 'reactivate', $id, $status );
150 +
151 + $action_buttons['reactivate'] = array(
152 + 'url' => subscrpt_get_action_url( 'reactivate', $subscrpt_nonce, $id ),
153 + 'label' => $label,
73 154 );
74 155 } elseif ( 'expired' === $status && 'pending' !== $order->get_status() ) {
75 - $action_buttons['renew'] = array(
76 - 'url' => subscrpt_get_action_url( 'renew', $subscrpt_nonce, $id ),
77 - 'label' => __( 'Renew', 'sdevs_subscrpt' ),
78 - );
156 + // Check if maximum payments reached before showing renew button
157 + if ( ! subscrpt_is_max_payments_reached( $id ) ) {
158 + $label = __( 'Renew', 'subscription' );
159 + $label = apply_filters( 'subscrpt_split_payment_button_text', $label, 'renew', $id, $status );
160 +
161 + $action_buttons['renew'] = array(
162 + 'url' => subscrpt_get_action_url( 'renew', $subscrpt_nonce, $id ),
163 + 'label' => $label,
164 + );
165 + }
79 166 }
80 167
81 168 if ( 'pending' === $order->get_status() ) {
169 + $label = __( 'Pay now', 'subscription' );
170 + $label = apply_filters( 'subscrpt_split_payment_button_text', $label, 'pay_now', $id, $status );
171 +
82 172 $action_buttons['pay_now'] = array(
83 173 'url' => $order->get_checkout_payment_url(),
84 - 'label' => __( 'Pay now', 'sdevs_subscrpt' ),
174 + 'label' => $label,
85 175 );
86 176 }
87 177 }
88 178
89 - $is_auto_renew = get_post_meta( $id, '_subscrpt_auto_renew', true );
90 - $renewal_setting = get_option( 'subscrpt_auto_renewal_toggle', '1' );
91 - if ( '' === $is_auto_renew && '1' === $renewal_setting ) {
92 - update_post_meta( $id, '_subscrpt_auto_renew', 1 );
93 - }
179 + $is_auto_renew = $subscription_data['is_auto_renew'];
180 + $renewal_setting = in_array( get_option( 'wp_subscription_auto_renewal_toggle', '1' ), [ 1, '1', 'true', 'yes' ], true );
181 +
94 182 $saved_methods = wc_get_customer_saved_methods_list( get_current_user_id() );
95 183 $has_methods = isset( $saved_methods['cc'] );
96 - if ( $has_methods && '1' === $renewal_setting && class_exists( 'WC_Stripe' ) && $order && 'stripe' === $order->get_payment_method() ) {
97 - if ( '0' === $is_auto_renew ) {
98 - $action_buttons['auto-renew-on'] = array(
99 - 'url' => subscrpt_get_action_url( 'renew-on', $subscrpt_nonce, $id ),
100 - 'label' => __( 'Turn on Auto Renewal', 'sdevs_subscrpt' ),
101 - );
102 - } else {
103 - $action_buttons['auto-renew-off'] = array(
104 - 'url' => subscrpt_get_action_url( 'renew-off', $subscrpt_nonce, $id ),
105 - 'label' => __( 'Turn off Auto Renewal', 'sdevs_subscrpt' ),
106 - );
184 + if ( $has_methods && $renewal_setting && class_exists( 'WC_Stripe' ) && $order && 'stripe' === $order->get_payment_method() ) {
185 + // Check maximum payment limit for auto-renewal buttons too
186 + if ( ! subscrpt_is_max_payments_reached( $id ) ) {
187 + if ( ! $is_auto_renew ) {
188 + $label = __( 'Turn on Auto Renewal', 'subscription' );
189 + $label = apply_filters( 'subscrpt_split_payment_button_text', $label, 'auto-renew-on', $id, $status );
190 +
191 + $action_buttons['auto-renew-on'] = array(
192 + 'url' => subscrpt_get_action_url( 'renew-on', $subscrpt_nonce, $id ),
193 + 'label' => $label,
194 + );
195 + } else {
196 + $label = __( 'Turn off Auto Renewal', 'subscription' );
197 + $label = apply_filters( 'subscrpt_split_payment_button_text', $label, 'auto-renew-off', $id, $status );
198 +
199 + $action_buttons['auto-renew-off'] = array(
200 + 'url' => subscrpt_get_action_url( 'renew-off', $subscrpt_nonce, $id ),
201 + 'label' => $label,
202 + );
203 + }
107 204 }
108 205 }
109 206
110 - $post_status_object = get_post_status_object( $status );
111 - $action_buttons = apply_filters( 'subscrpt_single_action_buttons', $action_buttons, $id, $subscrpt_nonce, $status );
207 + // Allow programmatically disabling cancel button
208 + $disable_cancel = apply_filters( 'subscrpt_split_payment_disable_cancel', false, $id, $status );
209 + if ( $disable_cancel && isset( $action_buttons['cancel'] ) ) {
210 + unset( $action_buttons['cancel'] );
211 + }
112 212
213 + $action_buttons = apply_filters( 'subscrpt_single_action_buttons', $action_buttons, $id, $subscrpt_nonce, $status );
214 +
215 + // Nothing renews once a subscription has ended, so drop the auto-renewal toggle — Pro adds it through the filter too.
216 + if ( ! Helper::has_next_payment( $status ) ) {
217 + unset( $action_buttons['auto-renew-on'], $action_buttons['auto-renew-off'] );
218 + }
219 +
113 220 wc_get_template(
114 221 'myaccount/single.php',
115 222 array(
116 223 'id' => $id,
224 + 'status' => $status,
225 + 'verbose_status' => $verbose_status,
117 226 'start_date' => $start_date,
118 227 'next_date' => $next_date,
228 + 'is_grace_period' => $is_grace_period,
229 + 'grace_remaining' => $grace_remaining,
230 + 'grace_end_date' => $grace_end_date,
119 231 'trial' => $trial,
120 232 'trial_mode' => empty( $trial_mode ) ? 'off' : $trial_mode,
121 233 'order' => $order,
122 234 'order_item' => $order_item,
123 - 'status' => $post_status_object,
235 + 'related_orders' => $related_orders,
236 + 'price' => $price,
237 + 'price_excl_tax' => $price_excl_tax,
238 + 'tax' => $tax_amount,
239 + 'discount' => $discount,
124 240 'user_cancel' => $user_cancel,
125 241 'action_buttons' => $action_buttons,
126 242 'wp_button_class' => wc_wp_theme_get_element_class_name( 'button' ) ? ' ' . wc_wp_theme_get_element_class_name( 'button' ) : '',
127 243 ),
@@ -133,37 +249,36 @@
133 249 /**
134 250 * Re-write flush
135 251 */
136 252 public function flush_rewrite_rules() {
137 - add_rewrite_endpoint( 'subscriptions', EP_ROOT | EP_PAGES );
253 + add_rewrite_endpoint( $this->subscriptions_endpoint, EP_ROOT | EP_PAGES );
138 254 flush_rewrite_rules();
139 255 }
140 256
141 257 /**
142 - * Change View Subscription Title
258 + * Change All Subscriptions Title
143 259 *
144 260 * @param string $title Title.
145 261 *
146 262 * @return string
147 263 */
148 - public function change_single_title( string $title ): string {
149 - /* translators: %s: Subscription ID */
150 - return sprintf( __( 'Subscription #%s', 'sdevs_subscrpt' ), get_query_var( 'view-subscription' ) );
264 + public function change_subscriptions_title( string $title ): string {
265 + $title = __( 'My Subscriptions', 'subscription' );
266 + return $title;
151 267 }
152 268
153 269 /**
154 - * Change Subscription Lists Title
270 + * Change View Subscription Title
155 271 *
156 272 * @param string $title Title.
157 273 *
158 274 * @return string
159 275 */
160 - public function change_lists_title( string $title ): string {
161 - global $wp_query;
162 - $is_endpoint = isset( $wp_query->query_vars['subscriptions'] );
163 - if ( $is_endpoint && ! is_admin() && is_account_page() ) {
164 - $title = __( 'My Subscriptions', 'sdevs_subscrpt' );
165 - }
276 + public function change_single_subscription_title( string $title ): string {
277 + $subs_id = get_query_var( $this->view_subscriptions_endpoint, 0 );
278 +
279 + /* translators: %s: Subscription ID */
280 + $title = sprintf( __( 'Subscription #%s', 'subscription' ), $subs_id );
166 281 return $title;
167 282 }
168 283
169 284 /**
@@ -172,12 +287,16 @@
172 287 * @param array $items MyAccount menu items.
173 288 * @return array
174 289 */
175 290 public function custom_my_account_menu_items( array $items ): array {
176 - $logout = $items['customer-logout'];
177 - unset( $items['customer-logout'] );
178 - $items['subscriptions'] = __( 'Subscriptions', 'sdevs_subscrpt' );
179 - $items['customer-logout'] = $logout;
291 + // Check if subscriptions menu item already exists to prevent duplicates
292 + if ( ! isset( $items[ $this->subscriptions_endpoint ] ) ) {
293 + $logout = $items['customer-logout'];
294 + unset( $items['customer-logout'] );
295 +
296 + $items[ $this->subscriptions_endpoint ] = __( 'Subscriptions', 'subscription' );
297 + $items['customer-logout'] = $logout;
298 + }
180 299 return $items;
181 300 }
182 301
183 302 /**
@@ -191,9 +310,9 @@
191 310 'author' => get_current_user_id(),
192 311 'posts_per_page' => 10,
193 312 'paged' => $current_page,
194 313 'post_type' => 'subscrpt_order',
195 - 'post_status' => array( 'pending', 'active', 'on_hold', 'cancelled', 'expired', 'pe_cancelled' ),
314 + 'post_status' => array( 'pending', 'active', 'on_hold', 'cancelled', 'expired', 'pe_cancelled', 'completed' ),
196 315 );
197 316
198 317 $postslist = new \WP_Query( $args );
199 318 wc_get_template(