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 / Product.php

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

368 lines 11.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Storefront product handling for subscriptions.
4 *
5 * @package SpringDevs\Subscription
6 */
7
8 namespace SpringDevs\Subscription\Frontend;
9
10 use SpringDevs\Subscription\Illuminate\Helper;
11 use SpringDevs\Subscription\Illuminate\Subscription\Subscription;
12
13 // HPOS: This file is compatible with WooCommerce High-Performance Order Storage (HPOS).
14 // All WooCommerce order data is accessed via WooCommerce CRUD methods (wc_get_order, wc_get_order_item_meta, etc.).
15 // All direct post meta access is for subscription data only, not WooCommerce order data.
16 // If you add new order data access, use WooCommerce CRUD for HPOS compatibility.
17
18 /**
19 * Product class
20 * control single product page
21 */
22 class Product {
23
24 /**
25 * Initialize the class
26 */
27 public function __construct() {
28 add_filter(
29 'woocommerce_product_single_add_to_cart_text',
30 array(
31 $this,
32 'change_single_add_to_cart_text',
33 ),
34 10,
35 2
36 );
37 add_filter( 'woocommerce_product_add_to_cart_text', array( $this, 'change_loop_add_to_cart_text' ), 10, 2 );
38 // Plan-tied simple products behave like a variable product on the shop /
39 // Product Collection loops: a "Select options" link to the product page (so
40 // the customer picks a plan), never a direct/ajax add-to-cart. These native
41 // product-method filters drive both the classic loop and the block button.
42 add_filter( 'woocommerce_product_add_to_cart_url', array( $this, 'plan_loop_add_to_cart_url' ), 10, 2 );
43 add_filter( 'woocommerce_product_supports', array( $this, 'plan_loop_disable_ajax' ), 10, 3 );
44 add_filter( 'woocommerce_get_price_html', array( $this, 'change_price_html' ), 10, 2 );
45 add_filter( 'woocommerce_quantity_input_args', array( $this, 'update_input_args' ), 10, 2 );
46 add_filter( 'woocommerce_add_to_cart', array( $this, 'update_cart_quantity' ), 10, 3 );
47 add_filter(
48 'woocommerce_store_api_product_quantity_minimum',
49 array(
50 $this,
51 'update_quantity_min_max',
52 ),
53 10,
54 2
55 );
56 add_filter(
57 'woocommerce_store_api_product_quantity_maximum',
58 array(
59 $this,
60 'update_quantity_min_max',
61 ),
62 10,
63 2
64 );
65 add_action(
66 'woocommerce_after_cart_item_quantity_update',
67 array(
68 $this,
69 'validate_quantity_on_manual_renewal',
70 ),
71 10,
72 2
73 );
74 add_filter( 'woocommerce_is_purchasable', array( $this, 'check_if_purchasable' ), 20, 2 );
75 add_action( 'woocommerce_single_product_summary', array( $this, 'text_if_active' ) );
76 add_filter( 'woocommerce_loop_add_to_cart_link', array( $this, 'remove_button_active_products' ), 10, 2 );
77 }
78
79 /**
80 * Remove button if product already subscribed.
81 *
82 * @param mixed $button Button.
83 * @param \WC_Product $product Product.
84 *
85 * @return mixed
86 */
87 public function remove_button_active_products( $button, $product ) {
88 $product = Subscription::get_subs_product( $product );
89 if ( ! $product->is_type( 'simple' ) ) {
90 return $button;
91 }
92
93 if ( $product->is_enabled() ) {
94 $limit = $product->get_limit();
95 if ( 'one' === $limit ) {
96 $unexpired = Helper::subscription_exists( $product->get_id(), array( 'active', 'pending' ) );
97 if ( $unexpired ) {
98 return;
99 }
100 } elseif ( 'only_one' === $limit && ! Helper::check_trial( $product->get_id() ) ) {
101 return;
102 }
103 }
104
105 return $button;
106 }
107
108 /**
109 * Display notice if already purchased.
110 */
111 public function text_if_active() {
112 global $product;
113 $sdevs_product = Subscription::get_subs_product( $product );
114 if ( ! $sdevs_product->is_type( 'simple' ) ) {
115 return;
116 }
117
118 if ( $sdevs_product->is_enabled() ) {
119 $limit = $sdevs_product->get_limit();
120 if ( 'unlimited' === $limit ) {
121 return;
122 }
123 if ( 'one' === $limit ) {
124 $unexpired = Helper::subscription_exists( $sdevs_product->get_id(), array( 'active', 'pending' ) );
125 if ( ! $unexpired ) {
126 return false;
127 } else {
128 echo '<strong>' . esc_html_e( 'You Already Subscribed These Product!', 'subscription' ) . '</strong>';
129 }
130 }
131 if ( 'only_one' === $limit ) {
132 if ( ! Helper::check_trial( $sdevs_product->get_id() ) ) {
133 echo '<strong>' . esc_html_e( 'You Already Subscribed These Product!', 'subscription' ) . '</strong>';
134 }
135 }
136 }
137 }
138
139 /**
140 * Check if product purchasable.
141 *
142 * @param boolean $is_purchasable True\False.
143 * @param \WC_Product $product Product.
144 *
145 * @return boolean
146 */
147 public function check_if_purchasable( $is_purchasable, $product ) {
148 $product = Subscription::get_subs_product( $product );
149 if ( $product->is_enabled() ) {
150 $limit = $product->get_limit();
151 if ( 'unlimited' === $limit ) {
152 return true;
153 } elseif ( 'only_one' === $limit ) {
154 return Helper::check_trial( $product->get_id() );
155 } elseif ( 'one' === $limit ) {
156 return ! Helper::subscription_exists( $product->get_id(), array( 'active', 'pending' ) );
157 }
158 }
159
160 return $is_purchasable;
161 }
162
163 /**
164 * Validate quantity after add to cart cart on manual renewal process.
165 *
166 * @param string $cart_item_key Cart Item Key.
167 * @param int $quantity Quantity.
168 *
169 * @return void
170 */
171 public function validate_quantity_on_manual_renewal( $cart_item_key, $quantity ) {
172 $cart_item = WC()->cart->get_cart_item( $cart_item_key );
173 $expired = Helper::subscription_exists( $cart_item['data']->get_id(), 'expired' );
174 if ( $expired ) {
175 $order_item_id = get_post_meta( $expired, '_subscrpt_order_item_id', true );
176 $item_quantity = (int) wc_get_order_item_meta( $order_item_id, '_qty', true );
177 if ( $item_quantity !== $quantity ) {
178 WC()->cart->set_quantity( $cart_item_key, $item_quantity );
179 /* translators: %d: the number of items allowed in the cart. */
180 wc_add_notice( sprintf( _n( 'You can only add %d item to the cart.', 'You can only add %d items to the cart.', $item_quantity, 'subscription' ), $item_quantity ), 'error' );
181 }
182 }
183 }
184
185 /**
186 * Update quantity min max for renewal process.
187 *
188 * @param int $value Value.
189 * @param \WC_Product $product Product Object.
190 *
191 * @return int
192 */
193 public function update_quantity_min_max( $value, $product ) {
194 $expired = Helper::subscription_exists( $product->get_id(), 'expired' );
195 if ( $expired ) {
196 $order_item_id = get_post_meta( $expired, '_subscrpt_order_item_id', true );
197 $item_quantity = (int) wc_get_order_item_meta( $order_item_id, '_qty', true );
198
199 return $item_quantity;
200 }
201
202 return $value;
203 }
204
205 /**
206 * Update cart quantity on manual renewal process.
207 *
208 * @param string $cart_item_key Cart item key.
209 * @param int $product_id Product Id.
210 * @param int $quantity Quantity.
211 *
212 * @return void
213 */
214 public function update_cart_quantity( $cart_item_key, $product_id, $quantity ) {
215 $expired = Helper::subscription_exists( $product_id, 'expired' );
216 if ( $expired ) {
217 $order_item_id = get_post_meta( $expired, '_subscrpt_order_item_id', true );
218 $item_quantity = (int) wc_get_order_item_meta( $order_item_id, '_qty', true );
219 if ( $item_quantity !== $quantity ) {
220 WC()->cart->set_quantity( $cart_item_key, $item_quantity );
221 /* translators: %d: the number of items allowed in the cart. */
222 wc_add_notice( sprintf( _n( 'You can only add %d item to the cart.', 'You can only add %d items to the cart.', $item_quantity, 'subscription' ), $item_quantity ), 'error' );
223 }
224 }
225 }
226
227 /**
228 * Update quantity input args.
229 *
230 * @param array $args args.
231 * @param \WC_Product $product Product object.
232 *
233 * @return array
234 */
235 public function update_input_args( $args, $product ) {
236 $expired = Helper::subscription_exists( $product->get_id(), 'expired' );
237 if ( $expired ) {
238 $order_item_id = get_post_meta( $expired, '_subscrpt_order_item_id', true );
239 $item_quantity = (int) wc_get_order_item_meta( $order_item_id, '_qty', true );
240 $args['input_value'] = $item_quantity;
241 $args['min_value'] = $item_quantity;
242 $args['max_value'] = $item_quantity;
243 $args['step'] = 1;
244 }
245
246 return $args;
247 }
248
249 /**
250 * Change single product add-to-cart button text.
251 *
252 * @param string $text Add-to-cart button Text.
253 * @param \WC_Product $product Product Object.
254 */
255 public function change_single_add_to_cart_text( $text, $product ) {
256 $product = Subscription::get_subs_product( $product );
257 if ( $product->is_type( 'variable' ) || '' === $product->get_price() ) {
258 return $text;
259 }
260 $cart_btn_label = $product->get_button_label();
261 $expired = Helper::subscription_exists( $product->get_id(), 'expired' );
262 if ( $expired ) {
263 $text = __( 'Renew', 'subscription' );
264 } elseif ( $product->is_enabled() && $cart_btn_label ) {
265 $text = $cart_btn_label;
266 }
267
268 return $text;
269 }
270
271 /**
272 * Whether this is a plan-tied simple product that should behave like a
273 * variable product on the shop / Product Collection loops.
274 *
275 * @param mixed $product Product object.
276 *
277 * @return bool
278 */
279 private function is_plan_loop_product( $product ) {
280 return $product instanceof \WC_Product
281 && $product->is_type( 'simple' )
282 && subscrpt_plan_offered( $product->get_id() );
283 }
284
285 /**
286 * Loop / block add-to-cart button text: "Select options" for plan products,
287 * otherwise the classic label logic.
288 *
289 * @param string $text Add-to-cart button text.
290 * @param \WC_Product $product Product object.
291 *
292 * @return string
293 */
294 public function change_loop_add_to_cart_text( $text, $product ) {
295 if ( $this->is_plan_loop_product( $product ) ) {
296 return __( 'Select options', 'subscription' );
297 }
298
299 return $this->change_single_add_to_cart_text( $text, $product );
300 }
301
302 /**
303 * Loop / block add-to-cart URL: the product page for plan products, so the
304 * customer lands on the plan selector instead of a bare add-to-cart.
305 *
306 * @param string $url Add-to-cart URL.
307 * @param \WC_Product $product Product object.
308 *
309 * @return string
310 */
311 public function plan_loop_add_to_cart_url( $url, $product ) {
312 return $this->is_plan_loop_product( $product ) ? $product->get_permalink() : $url;
313 }
314
315 /**
316 * Disable ajax add-to-cart for plan products so their loop / block button
317 * renders as a "Select options" link (navigates to the product page) rather
318 * than an ajax add.
319 *
320 * @param bool $supports Whether the feature is supported.
321 * @param string $feature Feature name.
322 * @param \WC_Product $product Product object.
323 *
324 * @return bool
325 */
326 public function plan_loop_disable_ajax( $supports, $feature, $product ) {
327 if ( 'ajax_add_to_cart' === $feature && $this->is_plan_loop_product( $product ) ) {
328 return false;
329 }
330
331 return $supports;
332 }
333
334 /**
335 * Add trial, signup fee etc. with product price.
336 *
337 * @param mixed $price Price.
338 * @param \WC_Product $product Product.
339 *
340 * @return mixed
341 */
342 public function change_price_html( $price, $product ) {
343 $product = Subscription::get_subs_product( $product );
344 if ( ! $product->is_type( 'simple' ) || '' === $price ) {
345 return $price;
346 }
347
348 if ( $product->is_enabled() ) :
349 $timing_option = $product->get_timing_option();
350 $type = ucfirst( Helper::get_typos( 1, $timing_option, true ) );
351
352 $trial = null;
353 if ( $product->has_trial() ) {
354 $meta_trial_time = $product->get_trial_timing_per();
355 $trial_type = Helper::get_typos( $meta_trial_time, $product->get_trial_timing_option(), true );
356 $trial = '<br/><small> + Get ' . $meta_trial_time . ' ' . ucfirst( $trial_type ) . ' free trial!</small>';
357 }
358
359 $timing_html = "<span class='wpsubs-subscription-timing'>&nbsp;/&nbsp;{$type}</span>";
360 $price_html = $price . $timing_html . $trial;
361
362 return apply_filters( 'subscrpt_simple_price_html', $price_html, $product, $price, $trial );
363 else :
364 return $price;
365 endif;
366 }
367 }
368