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
subscription / includes / Frontend / Cart.php

Cart.php in Subscriptions for WooCommerce with Stripe Recurring Payments 2.4.0, at includes/Frontend/Cart.php

680 lines 23.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Cart handling for subscription products.
4 *
5 * @package SpringDevs\Subscription
6 */
7
8 namespace SpringDevs\Subscription\Frontend;
9
10 use SpringDevs\Subscription\Illuminate\Helper;
11 use Automattic\WooCommerce\Blocks\Package;
12 use Automattic\WooCommerce\Blocks\Domain\Services\ExtendRestApi;
13 use Automattic\WooCommerce\StoreApi\Schemas\V1\CartItemSchema;
14 use Automattic\WooCommerce\StoreApi\Schemas\V1\CartSchema;
15 use SpringDevs\Subscription\Illuminate\Subscription\Subscription;
16
17 /**
18 * Cart class
19 */
20 class Cart {
21
22 /**
23 * Initialize the class
24 */
25 public function __construct() {
26 add_filter( 'woocommerce_add_cart_item_data', array( $this, 'add_to_cart_item_data' ), 10, 2 );
27 add_action( 'woocommerce_blocks_loaded', array( $this, 'define_custom_schema' ) );
28 add_filter( 'woocommerce_cart_item_price', array( $this, 'change_price_cart_html' ), 10, 2 );
29 add_filter( 'woocommerce_cart_item_subtotal', array( $this, 'change_price_cart_html' ), 10, 2 );
30 add_action( 'woocommerce_cart_totals_after_order_total', array( $this, 'add_rows_order_total' ) );
31 add_action( 'woocommerce_review_order_after_order_total', array( $this, 'add_rows_order_total' ) );
32 add_filter( 'woocommerce_add_cart_item_data', array( $this, 'set_renew_status' ), 10, 2 );
33 add_action( 'woocommerce_check_cart_items', array( $this, 'check_cart_items' ) );
34 add_filter( 'woocommerce_get_item_data', array( $this, 'set_line_item_meta' ), 10, 2 );
35 add_action( 'woocommerce_before_calculate_totals', array( $this, 'add_calculation_price_filter' ) );
36 add_action( 'woocommerce_calculate_totals', array( $this, 'remove_calculation_price_filter' ) );
37 add_action( 'woocommerce_after_calculate_totals', array( $this, 'remove_calculation_price_filter' ) );
38
39 add_filter( 'woocommerce_add_to_cart_validation', array( $this, 'add_to_cart_validation' ), 10, 4 );
40 add_action( 'woocommerce_store_api_validate_add_to_cart', array( $this, 'add_to_cart_validation_store_api' ), 10, 1 );
41 }
42
43 /**
44 * Add to cart validation.
45 *
46 * @param bool $passed Passed ?.
47 * @param int $product_id Product Id.
48 * @param int $quantity Quantity.
49 * @param int $variation_id Variation Id.
50 *
51 * @return bool
52 */
53 public function add_to_cart_validation( $passed, $product_id, $quantity, $variation_id = 0 ) {
54 $product_id = (int) $variation_id > 0 ? (int) $variation_id : (int) $product_id;
55 $validation = $this->validate_cart_items( $product_id );
56
57 if ( $validation['failed'] ) {
58 $error_notice = empty( $validation['error_notice'] ) ? __( 'This product cannot be added to the cart.', 'subscription' ) : $validation['error_notice'];
59 wc_add_notice( $error_notice, 'error' );
60 return false;
61 }
62
63 // Validation passed.
64 return $passed;
65 }
66
67 /**
68 * Add to cart validation.
69 *
70 * @param \WC_Product $product Product.
71 * @throws \Exception If validation fails.
72 */
73 public function add_to_cart_validation_store_api( $product ) {
74 $product_id = $product->get_id();
75 $validation = $this->validate_cart_items( $product_id );
76
77 if ( $validation['failed'] ) {
78 $error_notice = empty( $validation['error_notice'] ) ? __( 'This product cannot be added to the cart.', 'subscription' ) : $validation['error_notice'];
79 throw new \Exception( esc_html( $error_notice ) );
80 }
81 }
82
83 /**
84 * Validate cart items.
85 *
86 * @param int $product_id Product Id.
87 * @return array
88 */
89 public function validate_cart_items( $product_id ) {
90 /**
91 * Filters whether the cart may hold more than one subscription, or a
92 * subscription alongside a normal product, bypassing the restriction below.
93 *
94 * @param bool $allow Whether to allow it. Default false.
95 * @param int $product_id Product being added to the cart.
96 */
97 if ( apply_filters( 'subscrpt_allow_multiple_subscriptions_in_cart', false, $product_id ) ) {
98 return [
99 'failed' => false,
100 'error_notice' => null,
101 ];
102 }
103
104 $cart_items = WC()->cart->cart_contents;
105
106 $product = Subscription::get_subs_product( $product_id );
107
108 $error_notice = null;
109 $failed = false;
110 // A tied plan counts as a subscription even without classic `_subscrpt_enabled`.
111 $enabled = $product->is_enabled() || subscrpt_plan_offered( $product_id );
112
113 foreach ( $cart_items as $key => $cart_item ) {
114 if ( isset( $cart_item['subscription'] ) ) {
115 if ( $enabled ) {
116 $error_notice = __( 'You cannot purchase multiple subscriptions at the same time.', 'subscription' );
117 } else {
118 $error_notice = __( 'You cannot purchase a subscription and a non-subscription product at the same time.', 'subscription' );
119 }
120 $failed = true;
121 } elseif ( $enabled ) {
122 $error_notice = __( 'You cannot purchase a subscription along with other products. Please remove other products from your cart first.', 'subscription' );
123 $failed = true;
124 }
125 }
126
127 return [
128 'failed' => (bool) $failed,
129 'error_notice' => $error_notice,
130 ];
131 }
132
133 /**
134 * Add filter before cart calculation.
135 *
136 * @return void
137 */
138 public function add_calculation_price_filter() {
139 add_filter( 'woocommerce_product_get_price', array( $this, 'set_prices_for_calculation' ), 100, 2 );
140 }
141
142 /**
143 * Return 0 if product has trial.
144 *
145 * @param float $price Price.
146 * @param \WC_Product $product Product object.
147 *
148 * @return float
149 */
150 public function set_prices_for_calculation( $price, $product ) {
151 $product = Subscription::get_subs_product( $product );
152 if ( $product->is_enabled() && $product->is_type( 'simple' ) ) {
153 $trial_time_per = $product->get_meta( '_subscrpt_trial_timing_per' );
154 if ( ! empty( $trial_time_per ) && $trial_time_per > 0 && Helper::check_trial( $product->get_id() ) ) {
155 return 0;
156 }
157 }
158
159 return $price;
160 }
161
162 /**
163 * Remove filter after calculate calculation.
164 *
165 * @return void
166 */
167 public function remove_calculation_price_filter() {
168 remove_filter( 'woocommerce_product_get_price', array( $this, 'set_prices_for_calculation' ), 100 );
169 }
170
171 /**
172 * Set line item for display meta details.
173 *
174 * @param array $cart_item_data Cart Item Data.
175 * @param array $cart_item Cart Item.
176 *
177 * @return array
178 */
179 public function set_line_item_meta( $cart_item_data, $cart_item ) {
180 if ( isset( $cart_item['subscription'] ) ) {
181 if ( $cart_item['subscription']['trial'] ) {
182 $cart_item_data[] = array(
183 'key' => __( 'Free Trial', 'subscription' ),
184 'value' => $cart_item['subscription']['trial'],
185 'hidden' => true,
186 '__experimental_woocommerce_blocks_hidden' => false,
187 );
188 }
189 }
190
191 return $cart_item_data;
192 }
193
194 /**
195 * Check cart items if it's valid or not?
196 *
197 * @return void
198 */
199 public function check_cart_items() {
200 if ( subscrpt_pro_activated() ) {
201 return;
202 }
203 $cart_items = WC()->cart->cart_contents;
204 if ( is_array( $cart_items ) ) {
205 foreach ( $cart_items as $key => $value ) {
206 // Plan items were validated against the plan by the resolver at
207 // add-to-cart; their `subscription` snapshot intentionally differs
208 // from the product's classic meta, so skip the classic re-check
209 // (which would otherwise drop them from the cart).
210 if ( ! empty( $value['subscrpt_plan_id'] ) ) {
211 continue;
212 }
213
214 /**
215 * Product Object.
216 *
217 * @var \WC_Product $product
218 */
219 $product = $value['data'];
220 $product = Subscription::get_subs_product( $product );
221 if ( isset( $value['subscription'] ) ) {
222 if ( $product->is_type( 'simple' ) ) {
223 if ( Helper::get_typos( 1, $product->get_meta( '_subscrpt_timing_option' ) ) !== $value['subscription']['type'] || $product->get_trial() !== $value['subscription']['trial'] ) {
224 // remove the item.
225 wc_add_notice( __( 'An item which is no longer available was removed from your cart.', 'subscription' ), 'error' );
226 WC()->cart->remove_cart_item( $key );
227 }
228 } else {
229 // remove the item.
230 wc_add_notice( __( 'An item which is no longer available was removed from your cart.', 'subscription' ), 'error' );
231 WC()->cart->remove_cart_item( $key );
232 }
233 } elseif ( $product->is_enabled() ) {
234 // remove the item.
235 wc_add_notice( __( 'An item which is no longer available was removed from your cart.', 'subscription' ), 'error' );
236 WC()->cart->remove_cart_item( $key );
237 }
238 }
239 }
240 }
241
242 /**
243 * Define custom schema.
244 *
245 * @return void
246 */
247 public function define_custom_schema() {
248 $this->register_endpoint_data(
249 array(
250 'endpoint' => CartItemSchema::IDENTIFIER,
251 'namespace' => 'sdevs_subscription',
252 'data_callback' => array( $this, 'extend_cart_item_data' ),
253 'schema_callback' => array( $this, 'extend_cart_item_schema' ),
254 'schema_type' => ARRAY_A,
255 )
256 );
257 $this->register_endpoint_data(
258 array(
259 'endpoint' => CartSchema::IDENTIFIER,
260 'namespace' => 'sdevs_subscription',
261 'data_callback' => array( $this, 'extend_cart_data' ),
262 'schema_callback' => array( $this, 'extend_cart_schema' ),
263 'schema_type' => ARRAY_A,
264 )
265 );
266 }
267
268 /**
269 * Register subscription product schema into cart/items endpoint.
270 *
271 * @return array Registered schema.
272 */
273 public function extend_cart_schema() {
274 return array(
275 'recurring_totals' => array(
276 'description' => __( 'List of recurring totals in cart.', 'subscription' ),
277 'type' => 'array',
278 'readonly' => true,
279 'recurring_totals' => array(
280 'price' => array(
281 'description' => __( 'price of the subscription, after any discount that applies to renewals.', 'subscription' ),
282 'type' => array( 'string' ),
283 'readonly' => true,
284 ),
285 'full_price' => array(
286 'description' => __( 'price of the subscription before any discount.', 'subscription' ),
287 'type' => array( 'string' ),
288 'readonly' => true,
289 ),
290 'first_price' => array(
291 'description' => __( 'amount charged today, after all discounts.', 'subscription' ),
292 'type' => array( 'string' ),
293 'readonly' => true,
294 ),
295 'has_recurring_discount' => array(
296 'description' => __( 'Whether a discount applies to renewals.', 'subscription' ),
297 'type' => array( 'boolean' ),
298 'readonly' => true,
299 ),
300 'has_one_time_discount' => array(
301 'description' => __( 'Whether a discount applies to the first payment only.', 'subscription' ),
302 'type' => array( 'boolean' ),
303 'readonly' => true,
304 ),
305 'recurring_limit' => array(
306 'description' => __( 'Number of payments the recurring discount covers. 0 means unlimited.', 'subscription' ),
307 'type' => array( 'number' ),
308 'readonly' => true,
309 ),
310 'time' => array(
311 'description' => __( 'time of the subscription.', 'subscription' ),
312 'type' => array( 'number' ),
313 'readonly' => true,
314 ),
315 'type' => array(
316 'description' => __( 'type of the subscription.', 'subscription' ),
317 'type' => array( 'string' ),
318 'readonly' => true,
319 ),
320 'description' => array(
321 'description' => __( 'price of the subscription description.', 'subscription' ),
322 'type' => array( 'string' ),
323 'readonly' => true,
324 ),
325 'can_user_cancel' => array(
326 'description' => __( 'Allow User Cancellation?', 'subscription' ),
327 'type' => array( 'string' ),
328 'readonly' => true,
329 ),
330 'max_no_payment' => array(
331 'description' => __( 'Maximum Total Payments', 'subscription' ),
332 'type' => array( 'number' ),
333 'readonly' => true,
334 ),
335 'split_total' => array(
336 'description' => __( 'Total price for a split-payment plan (entered plan price).', 'subscription' ),
337 'type' => array( 'number', 'null' ),
338 'readonly' => true,
339 ),
340 ),
341 ),
342 );
343 }
344
345 /**
346 * Register subscription product data into cart/items endpoint.
347 *
348 * @return array $item_data Registered data or empty array if condition is not satisfied.
349 */
350 public function extend_cart_data() {
351 $cart_items = WC()->cart->cart_contents;
352 $recurrings = array();
353 if ( $cart_items ) {
354 foreach ( $cart_items as $cart_item_key => $cart_item ) {
355 if ( isset( $cart_item['subscription'] ) && $cart_item['subscription']['type'] ) {
356 $cart_subscription = $cart_item['subscription'];
357 $start_date = Helper::start_date( $cart_subscription['trial'] );
358 $next_date = Helper::next_date(
359 ( $cart_subscription['time'] ?? 1 ) . ' ' . $cart_subscription['type'],
360 $cart_subscription['trial']
361 );
362
363 // Subscription timing & type
364 $time = $cart_subscription['time'];
365 $type = Helper::get_typos( $time, $cart_subscription['type'], true );
366
367 // Discount-aware totals, shared with the classic cart.
368 $price_data = Helper::build_cart_recurring_price_data( $cart_item, $cart_item_key, $type );
369
370 // Description
371 $description = empty( $cart_subscription['trial'] )
372 ? __( 'Next billing on', 'subscription' ) . ': ' . $next_date
373 : __( 'First billing on', 'subscription' ) . ': ' . $start_date;
374
375 $recurrings[] = apply_filters(
376 'subscrpt_cart_recurring_data',
377 array(
378 'price' => $price_data['total'],
379 'full_price' => $price_data['full_total'],
380 'first_price' => $price_data['first_total'],
381 'has_recurring_discount' => $price_data['has_recurring_discount'],
382 'has_one_time_discount' => $price_data['has_one_time_discount'],
383 'recurring_limit' => $price_data['recurring_limit'],
384 'time' => $time,
385 'type' => $type,
386 'description' => $description,
387 'can_user_cancel' => Helper::can_user_cancel( $cart_item['data']->get_meta( '_subscrpt_user_cancel' ) ) ? 'yes' : 'no',
388 'max_no_payment' => ! empty( $cart_item['subscrpt_max_no_payment'] )
389 ? (int) $cart_item['subscrpt_max_no_payment']
390 : $cart_item['data']->get_meta( '_subscrpt_max_no_payment' ),
391 'split_total' => isset( $cart_item['subscrpt_split_total'] ) ? (float) $cart_item['subscrpt_split_total'] : null,
392 ),
393 $cart_item
394 );
395 }
396 }
397 }
398
399 return $recurrings;
400 }
401
402 /**
403 * Register subscription product schema into cart/items endpoint.
404 *
405 * @return array Registered schema.
406 */
407 public function extend_cart_item_schema() {
408 return array(
409 'time' => array(
410 'description' => __( 'time of the subscription type.', 'subscription' ),
411 'type' => array( 'number', 'null' ),
412 'readonly' => true,
413 ),
414 'type' => array(
415 'description' => __( 'the subscription type.', 'subscription' ),
416 'type' => array( 'string', 'null' ),
417 'readonly' => true,
418 ),
419 'trial' => array(
420 'description' => __( 'the subscription trial.', 'subscription' ),
421 'type' => array( 'string', 'null' ),
422 'readonly' => true,
423 ),
424 'signup_fee' => array(
425 'description' => __( 'Signup Fee amount.', 'subscription' ),
426 'type' => array( 'string', 'null' ),
427 'readonly' => true,
428 ),
429 'cost' => array(
430 'description' => __( 'Recurring amount.', 'subscription' ),
431 'type' => array( 'string', 'null' ),
432 'readonly' => true,
433 ),
434 'max_no_payment' => array(
435 'description' => __( 'Maximum Total Payments', 'subscription' ),
436 'type' => array( 'number' ),
437 'readonly' => true,
438 ),
439 );
440 }
441
442 /**
443 * Register subscription product data into cart/items endpoint.
444 *
445 * @param array $cart_item Current cart item data.
446 *
447 * @return array $item_data Registered data or empty array if condition is not satisfied.
448 */
449 public function extend_cart_item_data( $cart_item ) {
450 $item_data = array(
451 'time' => null,
452 'type' => null,
453 'trial' => null,
454 'signup_fee' => null,
455 'cost' => null,
456 'max_no_payment' => null,
457 );
458
459 if ( isset( $cart_item['subscription'] ) ) {
460 $item_data = $cart_item['subscription'];
461 unset( $item_data['per_cost'] );
462 $item_data['cost'] = (float) $cart_item['subscription']['per_cost'] * $cart_item['quantity'];
463
464 // Plan items don't stamp the installment count into the subscription
465 // array (it rides the cart item as subscrpt_max_no_payment); classic
466 // products carry it on the product meta.
467 if ( ! isset( $item_data['max_no_payment'] ) ) {
468 $item_data['max_no_payment'] = ! empty( $cart_item['subscrpt_max_no_payment'] )
469 ? (int) $cart_item['subscrpt_max_no_payment']
470 : $cart_item['data']->get_meta( '_subscrpt_max_no_payment' );
471 }
472
473 // Normalise the cadence word to singular/plural by frequency for the
474 // blocks (Store API) cart — plan items store the raw plural interval
475 // (e.g. "months"), which the block would otherwise render as-is.
476 if ( ! empty( $item_data['type'] ) ) {
477 $sub_time = max( 1, (int) ( $item_data['time'] ?? 1 ) );
478 $item_data['type'] = Helper::get_typos( $sub_time, $item_data['type'] );
479 }
480 }
481 if ( ! subscrpt_pro_activated() ) {
482 $item_data['time'] = null;
483 $item_data['signup_fee'] = null;
484 }
485
486 return $item_data;
487 }
488
489 /**
490 * Add product meta on cart item.
491 *
492 * @param array $cart_item_data cart_item_data.
493 * @param int $product_id Product ID.
494 *
495 * @return array
496 */
497 public function add_to_cart_item_data( array $cart_item_data, int $product_id ): array {
498 $product = Subscription::get_subs_product( $product_id );
499 if ( ! $product->is_type( 'simple' ) ) {
500 return $cart_item_data;
501 }
502 // Plan products stamp their subscription snapshot in the plan checkout
503 // (Frontend\PlanCheckout / Pro), gated on the chosen plan id — so a One-Time
504 // purchase of a plan product is not wrongly tagged as a subscription here
505 // (which would show a cadence + list it under "Recurring totals").
506 if ( subscrpt_product_has_plan( $product_id ) ) {
507 return $cart_item_data;
508 }
509 if ( $product->is_enabled() ) :
510 $subscription_data = array();
511 $subscription_data['time'] = null;
512 $subscription_data['type'] = $product->get_timing_option();
513 $subscription_data['trial'] = null;
514 if ( $product->has_trial() ) {
515 $subscription_data['trial'] = $product->get_trial();
516 }
517 $subscription_data['signup_fee'] = null;
518 $subscription_data['per_cost'] = $product->get_price();
519 $cart_item_data['subscription'] = apply_filters( 'subscrpt_block_simple_cart_item_data', $subscription_data, $product, $cart_item_data );
520 $cart_item_data['subscription']['max_no_payment'] = $product->get_meta( '_subscrpt_max_no_payment' );
521 endif;
522
523 return $cart_item_data;
524 }
525
526 /**
527 * Register endpoint data with the API.
528 *
529 * @param array $args Endpoint data to register.
530 */
531 protected function register_endpoint_data( $args ) {
532 if ( function_exists( 'woocommerce_store_api_register_endpoint_data' ) ) {
533 woocommerce_store_api_register_endpoint_data( $args );
534 } else {
535 Package::container()->get( ExtendRestApi::class )->register_endpoint_data( $args );
536 }
537 }
538
539 /**
540 * Display formatted price on cart.
541 *
542 * @param string $price price.
543 * @param array $cart_item cart item.
544 *
545 * @return string
546 */
547 public function change_price_cart_html( $price, $cart_item ) {
548 $product = Subscription::get_subs_product( $cart_item['product_id'] );
549 if ( ! $product->is_type( 'simple' ) ) {
550 return $price;
551 }
552
553 // A tied plan makes it a subscription even without classic `_subscrpt_enabled`;
554 // get_price_html() already resolves to the plan line (Frontend\Plans), so this
555 // shows the plan cadence on the cart line without doubling.
556 if ( $product->is_enabled() || subscrpt_plan_offered( $product->get_id() ) ) {
557 return $product->get_price_html();
558 }
559
560 return $price;
561 }
562
563 /**
564 * Display "Recurring totals" on cart
565 *
566 * @return void
567 */
568 public function add_rows_order_total() {
569 $cart_items = WC()->cart->get_cart_contents();
570 $recurrs = Helper::get_recurrs_from_cart( $cart_items );
571 if ( 0 === count( $recurrs ) ) {
572 return;
573 }
574 ?>
575 <tr class="recurring-total">
576 <th><?php esc_html_e( 'Recurring totals', 'subscription' ); ?></th>
577 <td data-title="<?php esc_attr_e( 'Recurring totals', 'subscription' ); ?>">
578 <?php foreach ( $recurrs as $recurr ) : ?>
579 <p>
580 <span><?php echo wp_kses_post( $recurr['price_html'] ); ?></span>
581 <?php if ( $recurr['max_no_payment'] > 0 ) : ?>
582 <span>x <?php echo esc_html( $recurr['max_no_payment'] ); ?></span>
583 <?php endif; ?>
584 <br />
585
586 <small>
587 <?php
588 $billing_text = $recurr['trial_status']
589 ? __( 'First billing on', 'subscription' )
590 : __( 'Next billing on', 'subscription' );
591
592 echo esc_html( $billing_text . ': ' );
593 echo esc_html( $recurr['trial_status'] ? $recurr['start_date'] : $recurr['next_date'] );
594 ?>
595 </small>
596
597 <?php if ( ! empty( $recurr['has_one_time_discount'] ) ) : ?>
598 <br />
599 <small>
600 <?php
601 echo wp_kses_post(
602 sprintf(
603 // translators: 1: amount paid today, 2: amount charged on each renewal.
604 __( 'You pay %1$s today. %2$s will be charged from the next renewal.', 'subscription' ),
605 wc_price( $recurr['first_total'] ?? 0 ),
606 wc_price( $recurr['total'] ?? 0 )
607 )
608 );
609 ?>
610 </small>
611 <?php endif; ?>
612
613 <?php if ( ! empty( $recurr['has_recurring_discount'] ) && (int) ( $recurr['recurring_limit'] ?? 0 ) > 1 ) : ?>
614 <br />
615 <small>
616 <?php
617 echo wp_kses_post(
618 sprintf(
619 // translators: 1: number of discounted payments, 2: full price charged afterwards.
620 __( 'Discount applies to your first %1$s payments. After that, %2$s.', 'subscription' ),
621 number_format_i18n( (int) $recurr['recurring_limit'] ),
622 $recurr['full_price_html'] ?? ''
623 )
624 );
625 ?>
626 </small>
627 <?php endif; ?>
628
629 <?php if ( 'yes' === $recurr['can_user_cancel'] && 0 === (int) $recurr['max_no_payment'] ) : ?>
630 <br />
631 <small><?php esc_html_e( 'You can cancel subscription at any time!', 'subscription' ); ?></small>
632 <?php endif; ?>
633
634 <!-- add how many times will be build if _subscrpt_renewal_limit is not 0 -->
635 <?php if ( (int) $recurr['max_no_payment'] > 0 ) : ?>
636 <br>
637 <small>
638 <?php
639 echo wp_kses_post(
640 sprintf(
641 // translators: 1: number of installments, 2: total amount.
642 __( 'This subscription will be billed in %1$s installments, for a total of %2$s.', 'subscription' ),
643 esc_html( $recurr['max_no_payment'] ),
644 wc_price( isset( $recurr['split_total'] ) && null !== $recurr['split_total'] ? $recurr['split_total'] : $recurr['price'] * (int) $recurr['max_no_payment'] )
645 )
646 );
647 ?>
648 </small>
649 <?php endif; ?>
650 </p>
651 <?php endforeach; ?>
652 </td>
653 </tr>
654 <?php
655 }
656
657 /**
658 * Add renew status.
659 *
660 * @param array $cart_item_data cart_item_data.
661 * @param int $product_id Product ID.
662 *
663 * @return array
664 */
665 public function set_renew_status( $cart_item_data, $product_id ) {
666 $expired = Helper::subscription_exists( $product_id, 'expired' );
667 if ( $expired ) {
668 // Check if maximum payment limit has been reached
669 if ( subscrpt_is_max_payments_reached( $expired ) ) {
670 wc_add_notice( __( 'This subscription has reached its maximum payment limit and cannot be renewed further.', 'subscription' ), 'error' );
671 return $cart_item_data; // Don't add renew status
672 }
673
674 $cart_item_data['renew_subscrpt'] = true;
675 }
676
677 return $cart_item_data;
678 }
679 }
680