PluginProbe
Subscriptions for WooCommerce with Stripe Recurring Payments / 1.10.0
Subscriptions for WooCommerce with Stripe Recurring Payments v1.10.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 1.4.1 1.4.2 1.5.0 1.5.1 All 61 releases
subscription / includes / Frontend / Cart.php

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

517 lines 16.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace SpringDevs\Subscription\Frontend;
4
5 use SpringDevs\Subscription\Illuminate\Helper;
6 use Automattic\WooCommerce\Blocks\Package;
7 use Automattic\WooCommerce\Blocks\Domain\Services\ExtendRestApi;
8 use Automattic\WooCommerce\StoreApi\Schemas\V1\CartItemSchema;
9 use Automattic\WooCommerce\StoreApi\Schemas\V1\CartSchema;
10 use SpringDevs\Subscription\Illuminate\Subscription\Subscription;
11
12 /**
13 * Cart class
14 */
15 class Cart {
16
17 /**
18 * Initialize the class
19 */
20 public function __construct() {
21 add_filter( 'woocommerce_add_cart_item_data', array( $this, 'add_to_cart_item_data' ), 10, 2 );
22 add_action( 'woocommerce_blocks_loaded', array( $this, 'define_custom_schema' ) );
23 add_filter( 'woocommerce_cart_item_price', array( $this, 'change_price_cart_html' ), 10, 2 );
24 add_filter( 'woocommerce_cart_item_subtotal', array( $this, 'change_price_cart_html' ), 10, 2 );
25 add_action( 'woocommerce_cart_totals_after_order_total', array( $this, 'add_rows_order_total' ) );
26 add_action( 'woocommerce_review_order_after_order_total', array( $this, 'add_rows_order_total' ) );
27 add_filter( 'woocommerce_add_cart_item_data', array( $this, 'set_renew_status' ), 10, 2 );
28 add_action( 'woocommerce_check_cart_items', array( $this, 'check_cart_items' ) );
29 add_filter( 'woocommerce_get_item_data', array( $this, 'set_line_item_meta' ), 10, 2 );
30 add_action( 'woocommerce_before_calculate_totals', array( $this, 'add_calculation_price_filter' ) );
31 add_action( 'woocommerce_calculate_totals', array( $this, 'remove_calculation_price_filter' ) );
32 add_action( 'woocommerce_after_calculate_totals', array( $this, 'remove_calculation_price_filter' ) );
33 add_filter( 'woocommerce_add_to_cart_validation', array( $this, 'add_to_cart_validation' ), 10, 2 );
34 }
35
36 /**
37 * Add to cart validation.
38 *
39 * @param bool $passed Passed ?.
40 * @param int $product_id Product Id.
41 *
42 * @return bool
43 */
44 public function add_to_cart_validation( bool $passed, int $product_id ): bool {
45 $cart_items = WC()->cart->cart_contents;
46 $error_notice = null;
47 $failed = false;
48 $product = Subscription::get_subs_product( $product_id );
49 $enabled = $product->is_enabled();
50
51 foreach ( $cart_items as $key => $cart_item ) {
52 if ( isset( $cart_item['subscription'] ) ) {
53 if ( $enabled ) {
54 $error_notice = __( 'You cannot purchase multiple subscriptions at the same time.', 'subscription' );
55 } else {
56 $error_notice = __( 'You cannot purchase a subscription and a non-subscription product at the same time.', 'subscription' );
57 }
58 $failed = true;
59 } elseif ( $enabled ) {
60 $error_notice = __( 'You cannot purchase a subscription along with other products. Please remove other products from your cart first.', 'subscription' );
61 $failed = true;
62 }
63 }
64
65 if ( $failed ) {
66 wc_add_notice( $error_notice, 'error' );
67 return false;
68 }
69
70 return $passed;
71 }
72
73 /**
74 * Add filter before cart calculation.
75 *
76 * @return void
77 */
78 public function add_calculation_price_filter() {
79 add_filter( 'woocommerce_product_get_price', array( $this, 'set_prices_for_calculation' ), 100, 2 );
80 }
81
82 /**
83 * Return 0 if product has trial.
84 *
85 * @param float $price Price.
86 * @param \WC_Product $product Product object.
87 *
88 * @return float
89 */
90 public function set_prices_for_calculation( $price, $product ) {
91 $product = Subscription::get_subs_product( $product );
92 if ( $product->is_enabled() && $product->is_type( 'simple' ) ) {
93 $trial_time_per = $product->get_meta( '_subscrpt_trial_timing_per' );
94 if ( ! empty( $trial_time_per ) && $trial_time_per > 0 && Helper::check_trial( $product->get_id() ) ) {
95 return 0;
96 }
97 }
98
99 return $price;
100 }
101
102 /**
103 * Remove filter after calculate calculation.
104 *
105 * @return void
106 */
107 public function remove_calculation_price_filter() {
108 remove_filter( 'woocommerce_product_get_price', array( $this, 'set_prices_for_calculation' ), 100 );
109 }
110
111 /**
112 * Set line item for display meta details.
113 *
114 * @param array $cart_item_data Cart Item Data.
115 * @param array $cart_item Cart Item.
116 *
117 * @return array
118 */
119 public function set_line_item_meta( $cart_item_data, $cart_item ) {
120 if ( isset( $cart_item['subscription'] ) ) {
121 if ( $cart_item['subscription']['trial'] ) {
122 $cart_item_data[] = array(
123 'key' => __( 'Free Trial', 'subscription' ),
124 'value' => $cart_item['subscription']['trial'],
125 'hidden' => true,
126 '__experimental_woocommerce_blocks_hidden' => false,
127 );
128 }
129 }
130
131 return $cart_item_data;
132 }
133
134 /**
135 * Check cart items if it's valid or not?
136 *
137 * @return void
138 */
139 public function check_cart_items() {
140 if ( subscrpt_pro_activated() ) {
141 return;
142 }
143 $cart_items = WC()->cart->cart_contents;
144 if ( is_array( $cart_items ) ) {
145 foreach ( $cart_items as $key => $value ) {
146 /**
147 * Product Object.
148 *
149 * @var \WC_Product $product
150 */
151 $product = $value['data'];
152 $product = Subscription::get_subs_product( $product );
153 if ( isset( $value['subscription'] ) ) {
154 if ( $product->is_type( 'simple' ) ) {
155 if ( Helper::get_typos( 1, $product->get_meta( '_subscrpt_timing_option' ) ) !== $value['subscription']['type'] || $product->get_trial() !== $value['subscription']['trial'] ) {
156 // remove the item.
157 wc_add_notice( __( 'An item which is no longer available was removed from your cart.', 'subscription' ), 'error' );
158 WC()->cart->remove_cart_item( $key );
159 }
160 } else {
161 // remove the item.
162 wc_add_notice( __( 'An item which is no longer available was removed from your cart.', 'subscription' ), 'error' );
163 WC()->cart->remove_cart_item( $key );
164 }
165 } elseif ( $product->get_meta( '_subscrpt_enabled' ) ) {
166 // remove the item.
167 wc_add_notice( __( 'An item which is no longer available was removed from your cart.', 'subscription' ), 'error' );
168 WC()->cart->remove_cart_item( $key );
169 }
170 }
171 }
172 }
173
174 /**
175 * Define custom schema.
176 *
177 * @return void
178 */
179 public function define_custom_schema() {
180 $this->register_endpoint_data(
181 array(
182 'endpoint' => CartItemSchema::IDENTIFIER,
183 'namespace' => 'sdevs_subscription',
184 'data_callback' => array( $this, 'extend_cart_item_data' ),
185 'schema_callback' => array( $this, 'extend_cart_item_schema' ),
186 'schema_type' => ARRAY_A,
187 )
188 );
189 $this->register_endpoint_data(
190 array(
191 'endpoint' => CartSchema::IDENTIFIER,
192 'namespace' => 'sdevs_subscription',
193 'data_callback' => array( $this, 'extend_cart_data' ),
194 'schema_callback' => array( $this, 'extend_cart_schema' ),
195 'schema_type' => ARRAY_A,
196 )
197 );
198 }
199
200 /**
201 * Register subscription product schema into cart/items endpoint.
202 *
203 * @return array Registered schema.
204 */
205 public function extend_cart_schema() {
206 return array(
207 'recurring_totals' => array(
208 'description' => __( 'List of recurring totals in cart.', 'subscription' ),
209 'type' => 'array',
210 'readonly' => true,
211 'recurring_totals' => array(
212 'price' => array(
213 'description' => __( 'price of the subscription.', 'subscription' ),
214 'type' => array( 'string' ),
215 'readonly' => true,
216 ),
217 'time' => array(
218 'description' => __( 'time of the subscription.', 'subscription' ),
219 'type' => array( 'number' ),
220 'readonly' => true,
221 ),
222 'type' => array(
223 'description' => __( 'type of the subscription.', 'subscription' ),
224 'type' => array( 'string' ),
225 'readonly' => true,
226 ),
227 'description' => array(
228 'description' => __( 'price of the subscription description.', 'subscription' ),
229 'type' => array( 'string' ),
230 'readonly' => true,
231 ),
232 'can_user_cancel' => array(
233 'description' => __( 'Allow User Cancellation?', 'subscription' ),
234 'type' => array( 'string' ),
235 'readonly' => true,
236 ),
237 'max_no_payment' => array(
238 'description' => __( 'Maximum Total Payments', 'subscription' ),
239 'type' => array( 'number' ),
240 'readonly' => true,
241 ),
242 ),
243 ),
244 );
245 }
246
247 /**
248 * Register subscription product data into cart/items endpoint.
249 *
250 * @return array $item_data Registered data or empty array if condition is not satisfied.
251 */
252 public function extend_cart_data() {
253 $cart_items = WC()->cart->cart_contents;
254 $recurrings = array();
255 if ( $cart_items ) {
256 foreach ( $cart_items as $cart_item ) {
257 if ( isset( $cart_item['subscription'] ) && $cart_item['subscription']['type'] ) {
258 $cart_subscription = $cart_item['subscription'];
259 $start_date = Helper::start_date( $cart_subscription['trial'] );
260 $next_date = Helper::next_date(
261 ( $cart_subscription['time'] ?? 1 ) . ' ' . $cart_subscription['type'],
262 $cart_subscription['trial']
263 );
264
265 // Total amount with tax
266 $product = $cart_item['data'];
267 $quantity = (int) $cart_item['quantity'];
268 $total_amount = wc_get_price_including_tax( $product, [ 'qty' => $quantity ] );
269
270 // Subscription timing & type
271 $time = $cart_subscription['time'];
272 $type = Helper::get_typos( $time, $cart_subscription['type'], true );
273
274 // Description
275 $description = empty( $cart_subscription['trial'] )
276 ? __( 'Next billing on', 'subscription' ) . ': ' . $next_date
277 : __( 'First billing on', 'subscription' ) . ': ' . $start_date;
278
279 $recurrings[] = apply_filters(
280 'subscrpt_cart_recurring_data',
281 array(
282 'price' => $total_amount,
283 'time' => $time,
284 'type' => $type,
285 'description' => $description,
286 'can_user_cancel' => $cart_item['data']->get_meta( '_subscrpt_user_cancel' ),
287 'max_no_payment' => $cart_item['data']->get_meta( '_subscrpt_max_no_payment' ),
288 ),
289 $cart_item
290 );
291 }
292 }
293 }
294
295 return $recurrings;
296 }
297
298 /**
299 * Register subscription product schema into cart/items endpoint.
300 *
301 * @return array Registered schema.
302 */
303 public function extend_cart_item_schema() {
304 return array(
305 'time' => array(
306 'description' => __( 'time of the subscription type.', 'subscription' ),
307 'type' => array( 'number', 'null' ),
308 'readonly' => true,
309 ),
310 'type' => array(
311 'description' => __( 'the subscription type.', 'subscription' ),
312 'type' => array( 'string', 'null' ),
313 'readonly' => true,
314 ),
315 'trial' => array(
316 'description' => __( 'the subscription trial.', 'subscription' ),
317 'type' => array( 'string', 'null' ),
318 'readonly' => true,
319 ),
320 'signup_fee' => array(
321 'description' => __( 'Signup Fee amount.', 'subscription' ),
322 'type' => array( 'string', 'null' ),
323 'readonly' => true,
324 ),
325 'cost' => array(
326 'description' => __( 'Recurring amount.', 'subscription' ),
327 'type' => array( 'string', 'null' ),
328 'readonly' => true,
329 ),
330 'max_no_payment' => array(
331 'description' => __( 'Maximum Total Payments', 'subscription' ),
332 'type' => array( 'number' ),
333 'readonly' => true,
334 ),
335 );
336 }
337
338 /**
339 * Register subscription product data into cart/items endpoint.
340 *
341 * @param array $cart_item Current cart item data.
342 *
343 * @return array $item_data Registered data or empty array if condition is not satisfied.
344 */
345 public function extend_cart_item_data( $cart_item ) {
346 $item_data = array(
347 'time' => null,
348 'type' => null,
349 'trial' => null,
350 'signup_fee' => null,
351 'cost' => null,
352 'max_no_payment' => null,
353 );
354
355 if ( isset( $cart_item['subscription'] ) ) {
356 $item_data = $cart_item['subscription'];
357 unset( $item_data['per_cost'] );
358 $item_data['cost'] = (float) $cart_item['subscription']['per_cost'] * $cart_item['quantity'];
359 }
360 if ( ! subscrpt_pro_activated() ) {
361 $item_data['time'] = null;
362 $item_data['signup_fee'] = null;
363 }
364
365 return $item_data;
366 }
367
368 /**
369 * Add product meta on cart item.
370 *
371 * @param array $cart_item_data cart_item_data.
372 * @param int $product_id Product ID.
373 *
374 * @return array
375 */
376 public function add_to_cart_item_data( array $cart_item_data, int $product_id ): array {
377 $product = Subscription::get_subs_product( $product_id );
378 if ( ! $product->is_type( 'simple' ) ) {
379 return $cart_item_data;
380 }
381 if ( $product->is_enabled() ) :
382 $subscription_data = array();
383 $subscription_data['time'] = null;
384 $subscription_data['type'] = $product->get_timing_option();
385 $subscription_data['trial'] = null;
386 if ( $product->has_trial() ) {
387 $subscription_data['trial'] = $product->get_trial();
388 }
389 $subscription_data['signup_fee'] = null;
390 $subscription_data['per_cost'] = $product->get_price();
391 $cart_item_data['subscription'] = apply_filters( 'subscrpt_block_simple_cart_item_data', $subscription_data, $product, $cart_item_data );
392 $cart_item_data['subscription']['max_no_payment'] = $product->get_meta( '_subscrpt_max_no_payment' );
393 endif;
394
395 return $cart_item_data;
396 }
397
398 /**
399 * Register endpoint data with the API.
400 *
401 * @param array $args Endpoint data to register.
402 */
403 protected function register_endpoint_data( $args ) {
404 if ( function_exists( 'woocommerce_store_api_register_endpoint_data' ) ) {
405 woocommerce_store_api_register_endpoint_data( $args );
406 } else {
407 Package::container()->get( ExtendRestApi::class )->register_endpoint_data( $args );
408 }
409 }
410
411 /**
412 * Display formatted price on cart.
413 *
414 * @param string $price price.
415 * @param array $cart_item cart item.
416 *
417 * @return string
418 */
419 public function change_price_cart_html( $price, $cart_item ) {
420 $product = Subscription::get_subs_product( $cart_item['product_id'] );
421 if ( ! $product->is_type( 'simple' ) ) {
422 return $price;
423 }
424
425 if ( $product->is_enabled() ) {
426 return $product->get_price_html();
427 }
428
429 return $price;
430 }
431
432 /**
433 * Display "Recurring totals" on cart
434 *
435 * @return void
436 */
437 public function add_rows_order_total() {
438 $cart_items = WC()->cart->get_cart_contents();
439 $recurrs = Helper::get_recurrs_from_cart( $cart_items );
440 if ( 0 === count( $recurrs ) ) {
441 return;
442 }
443 ?>
444 <tr class="recurring-total">
445 <th><?php esc_html_e( 'Recurring totals', 'subscription' ); ?></th>
446 <td data-title="<?php esc_attr_e( 'Recurring totals', 'subscription' ); ?>">
447 <?php foreach ( $recurrs as $recurr ) : ?>
448 <p>
449 <span><?php echo wp_kses_post( $recurr['price_html'] ); ?></span>
450 <?php if ( $recurr['max_no_payment'] > 0 ) : ?>
451 <span>x <?php echo esc_html( $recurr['max_no_payment'] ); ?></span>
452 <?php endif; ?>
453 <br />
454
455 <small>
456 <?php
457 $billing_text = $recurr['trial_status']
458 ? __( 'First billing on', 'subscription' )
459 : __( 'Next billing on', 'subscription' );
460
461 echo esc_html( $billing_text . ': ' );
462 echo esc_html( $recurr['trial_status'] ? $recurr['start_date'] : $recurr['next_date'] );
463 ?>
464 </small>
465
466 <?php if ( 'yes' === $recurr['can_user_cancel'] && 0 === (int) $recurr['max_no_payment'] ) : ?>
467 <br />
468 <small><?php esc_html_e( 'You can cancel subscription at any time!', 'subscription' ); ?></small>
469 <?php endif; ?>
470
471 <!-- add how many times will be build if _subscrpt_renewal_limit is not 0 -->
472 <?php if ( (int) $recurr['max_no_payment'] > 0 ) : ?>
473 <br>
474 <small>
475 <?php
476 echo wp_kses_post(
477 sprintf(
478 // translators: 1: number of installments, 2: total amount.
479 __( 'This subscription will be billed in %1$s installments, for a total of %2$s.', 'subscription' ),
480 esc_html( $recurr['max_no_payment'] ),
481 wc_price( $recurr['price'] * (int) $recurr['max_no_payment'] )
482 )
483 );
484 ?>
485 </small>
486 <?php endif; ?>
487 </p>
488 <?php endforeach; ?>
489 </td>
490 </tr>
491 <?php
492 }
493
494 /**
495 * Add renew status.
496 *
497 * @param array $cart_item_data cart_item_data.
498 * @param int $product_id Product ID.
499 *
500 * @return array
501 */
502 public function set_renew_status( $cart_item_data, $product_id ) {
503 $expired = Helper::subscription_exists( $product_id, 'expired' );
504 if ( $expired ) {
505 // Check if maximum payment limit has been reached
506 if ( subscrpt_is_max_payments_reached( $expired ) ) {
507 wc_add_notice( __( 'This subscription has reached its maximum payment limit and cannot be renewed further.', 'subscription' ), 'error' );
508 return $cart_item_data; // Don't add renew status
509 }
510
511 $cart_item_data['renew_subscrpt'] = true;
512 }
513
514 return $cart_item_data;
515 }
516 }
517