PluginProbe
Subscriptions for WooCommerce with Stripe Recurring Payments / 2.0.0
Subscriptions for WooCommerce with Stripe Recurring Payments v2.0.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 / Product.php

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

366 lines 11.2 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 wc_add_notice( 'You can only add ' . $item_quantity . ' ' . ( $item_quantity > 1 ? 'items' : 'item' ) . ' on cart!', 'error' );
180 }
181 }
182 }
183
184 /**
185 * Update quantity min max for renewal process.
186 *
187 * @param int $value Value.
188 * @param \WC_Product $product Product Object.
189 *
190 * @return int
191 */
192 public function update_quantity_min_max( $value, $product ) {
193 $expired = Helper::subscription_exists( $product->get_id(), 'expired' );
194 if ( $expired ) {
195 $order_item_id = get_post_meta( $expired, '_subscrpt_order_item_id', true );
196 $item_quantity = (int) wc_get_order_item_meta( $order_item_id, '_qty', true );
197
198 return $item_quantity;
199 }
200
201 return $value;
202 }
203
204 /**
205 * Update cart quantity on manual renewal process.
206 *
207 * @param string $cart_item_key Cart item key.
208 * @param int $product_id Product Id.
209 * @param int $quantity Quantity.
210 *
211 * @return void
212 */
213 public function update_cart_quantity( $cart_item_key, $product_id, $quantity ) {
214 $expired = Helper::subscription_exists( $product_id, 'expired' );
215 if ( $expired ) {
216 $order_item_id = get_post_meta( $expired, '_subscrpt_order_item_id', true );
217 $item_quantity = (int) wc_get_order_item_meta( $order_item_id, '_qty', true );
218 if ( $item_quantity !== $quantity ) {
219 WC()->cart->set_quantity( $cart_item_key, $item_quantity );
220 wc_add_notice( 'You can only add ' . $item_quantity . ' ' . ( $item_quantity > 1 ? 'items' : 'item' ) . ' on cart!', 'error' );
221 }
222 }
223 }
224
225 /**
226 * Update quantity input args.
227 *
228 * @param array $args args.
229 * @param \WC_Product $product Product object.
230 *
231 * @return array
232 */
233 public function update_input_args( $args, $product ) {
234 $expired = Helper::subscription_exists( $product->get_id(), 'expired' );
235 if ( $expired ) {
236 $order_item_id = get_post_meta( $expired, '_subscrpt_order_item_id', true );
237 $item_quantity = (int) wc_get_order_item_meta( $order_item_id, '_qty', true );
238 $args['input_value'] = $item_quantity;
239 $args['min_value'] = $item_quantity;
240 $args['max_value'] = $item_quantity;
241 $args['step'] = 1;
242 }
243
244 return $args;
245 }
246
247 /**
248 * Change single product add-to-cart button text.
249 *
250 * @param string $text Add-to-cart button Text.
251 * @param \WC_Product $product Product Object.
252 */
253 public function change_single_add_to_cart_text( $text, $product ) {
254 $product = Subscription::get_subs_product( $product );
255 if ( $product->is_type( 'variable' ) || '' === $product->get_price() ) {
256 return $text;
257 }
258 $cart_btn_label = $product->get_button_label();
259 $expired = Helper::subscription_exists( $product->get_id(), 'expired' );
260 if ( $expired ) {
261 $text = __( 'Renew', 'subscription' );
262 } elseif ( $product->is_enabled() && $cart_btn_label ) {
263 $text = $cart_btn_label;
264 }
265
266 return $text;
267 }
268
269 /**
270 * Whether this is a plan-tied simple product that should behave like a
271 * variable product on the shop / Product Collection loops.
272 *
273 * @param mixed $product Product object.
274 *
275 * @return bool
276 */
277 private function is_plan_loop_product( $product ) {
278 return $product instanceof \WC_Product
279 && $product->is_type( 'simple' )
280 && subscrpt_plan_offered( $product->get_id() );
281 }
282
283 /**
284 * Loop / block add-to-cart button text: "Select options" for plan products,
285 * otherwise the classic label logic.
286 *
287 * @param string $text Add-to-cart button text.
288 * @param \WC_Product $product Product object.
289 *
290 * @return string
291 */
292 public function change_loop_add_to_cart_text( $text, $product ) {
293 if ( $this->is_plan_loop_product( $product ) ) {
294 return __( 'Select options', 'subscription' );
295 }
296
297 return $this->change_single_add_to_cart_text( $text, $product );
298 }
299
300 /**
301 * Loop / block add-to-cart URL: the product page for plan products, so the
302 * customer lands on the plan selector instead of a bare add-to-cart.
303 *
304 * @param string $url Add-to-cart URL.
305 * @param \WC_Product $product Product object.
306 *
307 * @return string
308 */
309 public function plan_loop_add_to_cart_url( $url, $product ) {
310 return $this->is_plan_loop_product( $product ) ? $product->get_permalink() : $url;
311 }
312
313 /**
314 * Disable ajax add-to-cart for plan products so their loop / block button
315 * renders as a "Select options" link (navigates to the product page) rather
316 * than an ajax add.
317 *
318 * @param bool $supports Whether the feature is supported.
319 * @param string $feature Feature name.
320 * @param \WC_Product $product Product object.
321 *
322 * @return bool
323 */
324 public function plan_loop_disable_ajax( $supports, $feature, $product ) {
325 if ( 'ajax_add_to_cart' === $feature && $this->is_plan_loop_product( $product ) ) {
326 return false;
327 }
328
329 return $supports;
330 }
331
332 /**
333 * Add trial, signup fee etc. with product price.
334 *
335 * @param mixed $price Price.
336 * @param \WC_Product $product Product.
337 *
338 * @return mixed
339 */
340 public function change_price_html( $price, $product ) {
341 $product = Subscription::get_subs_product( $product );
342 if ( ! $product->is_type( 'simple' ) || '' === $price ) {
343 return $price;
344 }
345
346 if ( $product->is_enabled() ) :
347 $timing_option = $product->get_timing_option();
348 $type = ucfirst( Helper::get_typos( 1, $timing_option, true ) );
349
350 $trial = null;
351 if ( $product->has_trial() ) {
352 $meta_trial_time = $product->get_trial_timing_per();
353 $trial_type = Helper::get_typos( $meta_trial_time, $product->get_trial_timing_option(), true );
354 $trial = '<br/><small> + Get ' . $meta_trial_time . ' ' . ucfirst( $trial_type ) . ' free trial!</small>';
355 }
356
357 $timing_html = "<span class='wpsubs-subscription-timing'>&nbsp;/&nbsp;{$type}</span>";
358 $price_html = $price . $timing_html . $trial;
359
360 return apply_filters( 'subscrpt_simple_price_html', $price_html, $product, $price, $trial );
361 else :
362 return $price;
363 endif;
364 }
365 }
366