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/Cart.php +71 -8 1.11.0 → 2.4.0 View file →
@@ -1,5 +1,10 @@
1 1 <?php
2 +/**
3 + * Cart handling for subscription products.
4 + *
5 + * @package SpringDevs\Subscription
6 + */
2 7
3 8 namespace SpringDevs\Subscription\Frontend;
4 9
5 10 use SpringDevs\Subscription\Illuminate\Helper;
@@ -44,10 +49,10 @@
44 49 * @param int $variation_id Variation Id.
45 50 *
46 51 * @return bool
47 52 */
48 - public function add_to_cart_validation( bool $passed, int $product_id, int $quantity, int $variation_id = 0 ): bool {
49 - $product_id = $variation_id > 0 ? $variation_id : $product_id;
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;
50 55 $validation = $this->validate_cart_items( $product_id );
51 56
52 57 if ( $validation['failed'] ) {
53 58 $error_notice = empty( $validation['error_notice'] ) ? __( 'This product cannot be added to the cart.', 'subscription' ) : $validation['error_notice'];
@@ -81,8 +86,22 @@
81 86 * @param int $product_id Product Id.
82 87 * @return array
83 88 */
84 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 +
85 104 $cart_items = WC()->cart->cart_contents;
86 105
87 106 $product = Subscription::get_subs_product( $product_id );
88 107
@@ -87,9 +106,10 @@
87 106 $product = Subscription::get_subs_product( $product_id );
88 107
89 108 $error_notice = null;
90 109 $failed = false;
91 - $enabled = $product->is_enabled();
110 + // A tied plan counts as a subscription even without classic `_subscrpt_enabled`.
111 + $enabled = $product->is_enabled() || subscrpt_plan_offered( $product_id );
92 112
93 113 foreach ( $cart_items as $key => $cart_item ) {
94 114 if ( isset( $cart_item['subscription'] ) ) {
95 115 if ( $enabled ) {
@@ -182,8 +202,16 @@
182 202 }
183 203 $cart_items = WC()->cart->cart_contents;
184 204 if ( is_array( $cart_items ) ) {
185 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 +
186 214 /**
187 215 * Product Object.
188 216 *
189 217 * @var \WC_Product $product
@@ -201,9 +229,9 @@
201 229 // remove the item.
202 230 wc_add_notice( __( 'An item which is no longer available was removed from your cart.', 'subscription' ), 'error' );
203 231 WC()->cart->remove_cart_item( $key );
204 232 }
205 - } elseif ( $product->get_meta( '_subscrpt_enabled' ) ) {
233 + } elseif ( $product->is_enabled() ) {
206 234 // remove the item.
207 235 wc_add_notice( __( 'An item which is no longer available was removed from your cart.', 'subscription' ), 'error' );
208 236 WC()->cart->remove_cart_item( $key );
209 237 }
@@ -303,8 +331,13 @@
303 331 'description' => __( 'Maximum Total Payments', 'subscription' ),
304 332 'type' => array( 'number' ),
305 333 'readonly' => true,
306 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 + ),
307 340 ),
308 341 ),
309 342 );
310 343 }
@@ -350,10 +383,13 @@
350 383 'recurring_limit' => $price_data['recurring_limit'],
351 384 'time' => $time,
352 385 'type' => $type,
353 386 'description' => $description,
354 - 'can_user_cancel' => $cart_item['data']->get_meta( '_subscrpt_user_cancel' ),
355 - 'max_no_payment' => $cart_item['data']->get_meta( '_subscrpt_max_no_payment' ),
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,
356 392 ),
357 393 $cart_item
358 394 );
359 395 }
@@ -423,8 +459,25 @@
423 459 if ( isset( $cart_item['subscription'] ) ) {
424 460 $item_data = $cart_item['subscription'];
425 461 unset( $item_data['per_cost'] );
426 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 + }
427 480 }
428 481 if ( ! subscrpt_pro_activated() ) {
429 482 $item_data['time'] = null;
430 483 $item_data['signup_fee'] = null;
@@ -445,8 +498,15 @@
445 498 $product = Subscription::get_subs_product( $product_id );
446 499 if ( ! $product->is_type( 'simple' ) ) {
447 500 return $cart_item_data;
448 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 + }
449 509 if ( $product->is_enabled() ) :
450 510 $subscription_data = array();
451 511 $subscription_data['time'] = null;
452 512 $subscription_data['type'] = $product->get_timing_option();
@@ -489,9 +549,12 @@
489 549 if ( ! $product->is_type( 'simple' ) ) {
490 550 return $price;
491 551 }
492 552
493 - if ( $product->is_enabled() ) {
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() ) ) {
494 557 return $product->get_price_html();
495 558 }
496 559
497 560 return $price;
@@ -577,9 +640,9 @@
577 640 sprintf(
578 641 // translators: 1: number of installments, 2: total amount.
579 642 __( 'This subscription will be billed in %1$s installments, for a total of %2$s.', 'subscription' ),
580 643 esc_html( $recurr['max_no_payment'] ),
581 - wc_price( $recurr['price'] * (int) $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'] )
582 645 )
583 646 );
584 647 ?>
585 648 </small>