PluginProbe
WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell / 3.13.1
WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell v3.13.1
3.13.1 3.13.0 3.12.13 3.12.12 3.12.11 3.12.10 3.12.9 3.12.8 3.12.7 3.12.6 3.12.5 3.12.4 3.12.3 3.12.1 3.12.2 3.12.0 3.11.1 3.11.0 3.10.9 3.10.8 3.10.7 3.10.6 2.8.16 2.8.17 2.8.18 All 259 releases
wpfunnels / includes / core / rest-api / Controllers / OfferController.php

OfferController.php in WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell 3.13.1, at includes/core/rest-api/Controllers/OfferController.php

622 lines 25.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace WPFunnels\Rest\Controllers;
4
5 use WP_Error;
6 use WP_REST_Request;
7 use WPFunnels\Wpfnl_functions;
8 use \WC_Subscriptions_Product;
9 // use Wpfnl_Pro_OfferProduct_Factory; // Removed - checking class_exists() instead to support free plugin
10
11 class OfferController extends Wpfnl_REST_Controller
12 {
13
14 /**
15 * Endpoint namespace.
16 *
17 * @var string
18 */
19 protected $namespace = 'wpfunnels/v1';
20
21 /**
22 * Route base.
23 *
24 * @var string
25 */
26 protected $rest_base = 'offer';
27
28 /**
29 * check if user has valid permission
30 *
31 * @param $request
32 * @return bool|WP_Error
33 * @since 1.0.0
34 */
35 public function update_items_permissions_check($request)
36 {
37 if (!Wpfnl_functions::wpfnl_rest_check_manager_permissions( 'steps', 'edit' )) {
38 return new WP_Error('wpfunnels_rest_cannot_edit', __('Sorry, you cannot edit this resource.', 'wpfnl'), ['status' => rest_authorization_required_code()]);
39 }
40 return true;
41 }
42
43 /**
44 * Makes sure the current user has access to READ the settings APIs.
45 *
46 * @param WP_REST_Request $request Full data about the request.
47 * @return WP_Error|boolean
48 * @since 3.0.0
49 */
50 public function get_items_permissions_check($request)
51 {
52 if (!Wpfnl_functions::wpfnl_rest_check_manager_permissions('settings')) {
53 return new WP_Error('wpfunnels_rest_cannot_view', __('Sorry, you cannot list resources.', 'wpfnl'), ['status' => rest_authorization_required_code()]);
54 }
55 return true;
56 }
57
58
59 /**
60 * register rest routes
61 *
62 * @since 1.0.0
63 */
64 public function register_routes()
65 {
66
67 register_rest_route($this->namespace, '/' . $this->rest_base . '/getUpsellData/', [
68 [
69 'methods' => \WP_REST_Server::READABLE,
70 'callback' => [
71 $this,
72 'get_upsell_data'
73 ],
74 'permission_callback' => [
75 $this,
76 'update_items_permissions_check'
77 ] ,
78 ],
79 ]);
80
81 register_rest_route($this->namespace, '/' . $this->rest_base . '/saveUpsellData/', [
82 [
83 'methods' => \WP_REST_Server::EDITABLE,
84 'callback' => [
85 $this,
86 'save_upsell_data'
87 ],
88 'permission_callback' => [
89 $this,
90 'update_items_permissions_check'
91 ] ,
92 ],
93 ]);
94
95 register_rest_route($this->namespace, '/' . $this->rest_base . '/add-offer-product/', [
96 [
97 'methods' => \WP_REST_Server::EDITABLE,
98 'callback' => [
99 $this,
100 'add_offer_product'
101 ],
102 'permission_callback' => [
103 $this,
104 'update_items_permissions_check'
105 ] ,
106 ],
107 ]);
108
109 register_rest_route($this->namespace, '/' . $this->rest_base . '/getDownsellData/', [
110 [
111 'methods' => \WP_REST_Server::READABLE,
112 'callback' => [
113 $this,
114 'get_downsell_data'
115 ],
116 'permission_callback' => [
117 $this,
118 'update_items_permissions_check'
119 ] ,
120 ],
121 ]);
122
123 register_rest_route($this->namespace, '/' . $this->rest_base . '/saveDownsellData/', [
124 [
125 'methods' => \WP_REST_Server::EDITABLE,
126 'callback' => [
127 $this,
128 'save_downsell_data'
129 ],
130 'permission_callback' => [
131 $this,
132 'update_items_permissions_check'
133 ] ,
134 ],
135 ]);
136 }
137
138
139 /**
140 * Save upsell data
141 */
142 public function save_upsell_data($request)
143 {
144 $step_id = $request['step_id'];
145 $products = array();
146 $data = json_decode($request['product'], true);
147 $products[] = $data;
148 update_post_meta($step_id, '_wpfnl_upsell_products', $products);
149
150 $funnel_id = absint( get_post_meta( absint( $step_id ), '_funnel_id', true ) );
151 do_action( 'wpfunnels_upsell_configured', $funnel_id, absint( $step_id ), 'upsell' );
152
153 return 'success';
154 }
155
156
157 /**
158 * Save upsell data
159 */
160 public function add_offer_product($request)
161 {
162 $step_id = isset($request['step_id']) ? sanitize_text_field($request['step_id']) : '';
163 $id = isset($request['product_id']) ? sanitize_text_field( $request['product_id'] ) : '';
164 $quantity = isset($request['quantity']) ? sanitize_text_field( $request['quantity'] ) : '';
165 $offer_type = isset($request['type']) ? sanitize_text_field( $request['type'] ) : 'upsell';
166 if(!$step_id) {
167 return [
168 'success' => false,
169 ];
170 }
171 $data = array(
172 array(
173 'id' => $id,
174 'quantity' => $quantity
175 )
176 );
177 $type = '';
178 if( $request['isLms'] == 'true' ){
179 $type = 'lms';
180 }else{
181 $type = 'wc';
182 }
183
184 // Check if pro plugin offer factory is available
185 if ( class_exists( '\Wpfnl_Pro_OfferProduct_Factory' ) ) {
186 $class_object = \Wpfnl_Pro_OfferProduct_Factory::build( $type );
187 if( $class_object ){
188 $function = 'add_'.$offer_type.'_items';
189 $response = $class_object->$function( $id, $data, $step_id );
190
191 if( $response ){
192 return $response;
193 }
194 }
195 } else {
196 // Fallback for free plugin - basic offer product addition
197 $response = $this->add_basic_offer_product( $id, $data, $step_id, $offer_type );
198 if( $response ){
199 return $response;
200 }
201 }
202
203 return array(
204 'success' => false,
205 'message' => __('Product Not Found', 'wpfnl')
206 );
207 }
208
209
210 /**
211 * Get upsell product data
212 *
213 * @param $request
214 * @return WP_Error|\WP_REST_Response
215 *
216 * @since 1.0.0
217 */
218 public function get_upsell_data( $request )
219 {
220 $response = [];
221 $step_id = $request['step_id'];
222 $_products = get_post_meta( $step_id, '_wpfnl_upsell_products', true );
223 $products = apply_filters( 'wpfunnels/upsell_product', $_products, $step_id );
224 $funnel_id = Wpfnl_functions::get_funnel_id_from_step($step_id);
225 $type = get_post_meta($funnel_id, '_wpfnl_funnel_type', true);
226 if( 'lms' === $type ){
227 $_class = 'lms';
228 }else{
229 $_class = 'wc';
230 }
231
232 // Check if pro plugin offer factory is available
233 if ( class_exists( '\Wpfnl_Pro_OfferProduct_Factory' ) ) {
234 $class_object = \Wpfnl_Pro_OfferProduct_Factory::build( $_class );
235 if( $class_object ){
236 $response = $class_object->get_upsell_items( $products, $step_id );
237 }
238 } else {
239 // Fallback for free plugin - basic offer product data structure
240 $response = $this->get_basic_upsell_items( $products, $step_id );
241 }
242
243 $response['priceConfig'] = Wpfnl_functions::get_wc_price_config();
244 return $this->prepare_item_for_response( $response, $request );
245 }
246
247 /**
248 * Save downsell data
249 */
250 public function save_downsell_data($request)
251 {
252 $step_id = $request['step_id'];
253 $products = array();
254 $data = json_decode($request['product'], true);
255 $products[] = $data;
256 update_post_meta($step_id, '_wpfnl_downsell_product', $products);
257
258 $funnel_id = absint( get_post_meta( absint( $step_id ), '_funnel_id', true ) );
259 do_action( 'wpfunnels_upsell_configured', $funnel_id, absint( $step_id ), 'downsell' );
260
261 return 'success';
262 }
263
264
265 /**
266 * get downsell product data
267 *
268 * @param $request
269 * @return WP_Error|\WP_REST_Response
270 *
271 * @since 1.0.0
272 */
273 public function get_downsell_data($request)
274 {
275 $response = [];
276 $step_id = $request['step_id'];
277 $_products = get_post_meta( $step_id, '_wpfnl_downsell_products', true );
278 $products = apply_filters( 'wpfunnels/downsell_product', $_products, $step_id );
279 $discount = get_post_meta( $step_id, '_wpfnl_downsell_discount', true );
280 $offer_settings = \WPFunnels\Wpfnl_functions::get_offer_settings();
281 $funnel_id = Wpfnl_functions::get_funnel_id_from_step($step_id);
282 $type = get_post_meta($funnel_id, '_wpfnl_funnel_type', true);
283 if( 'lms' === $type ){
284 $_class = 'lms';
285 }else{
286 $_class = 'wc';
287 }
288
289 // Check if pro plugin offer factory is available
290 if ( class_exists( '\Wpfnl_Pro_OfferProduct_Factory' ) ) {
291 $class_object = \Wpfnl_Pro_OfferProduct_Factory::build( $_class );
292
293 if( $class_object ){
294 $response = $class_object->get_downsell_items( $products, $step_id );
295 }
296 } else {
297 // Fallback for free plugin - basic offer product data structure
298 $response = $this->get_basic_downsell_items( $products, $step_id );
299 }
300 $response['priceConfig'] = Wpfnl_functions::get_wc_price_config();
301 return $this->prepare_item_for_response( $response, $request );
302 }
303
304
305 /**
306 * Prepare a single setting object for response.
307 *
308 * @param object $item Setting object.
309 * @param WP_REST_Request $request Request object.
310 * @return \WP_REST_Response $response Response data.
311 * @since 1.0.0
312 */
313 public function prepare_item_for_response($item, $request)
314 {
315 $data = $this->add_additional_fields_to_object($item, $request);
316 return rest_ensure_response($data);
317 }
318
319
320 /**
321 * Get basic upsell items for free plugin
322 *
323 * @param array $products
324 * @param int $step_id
325 * @return array
326 */
327 private function get_basic_upsell_items( $products, $step_id ) {
328 $response = array();
329
330 if( $products && count($products)) {
331 $product_obj = $products[0];
332 $product = wc_get_product($product_obj['id']);
333
334 if( $product instanceof \WC_Product ) {
335 $title = $product->get_type() == 'variation' ? Wpfnl_functions::get_formated_product_name( $product ) : $product->get_name();
336 $image = wp_get_attachment_image_src( $product->get_image_id(), 'single-post-thumbnail' );
337 $price = $product->get_price();
338
339 if($product->get_type() == 'variable' || $product->get_type() == 'variable-subscription') {
340 $regular_price = $product->get_variation_regular_price( 'min' ) ? $product->get_variation_regular_price( 'min' ) : $product->get_price();
341 } else {
342 $regular_price = $product->get_regular_price() ? $product->get_regular_price() : $product->get_price();
343 }
344
345 $sale_price = $product->get_type() == 'variable' ? $price : $product->get_sale_price();
346
347 $response['products'][] = array(
348 'id' => $product_obj['id'],
349 'title' => $title,
350 'price' => wc_price( $price ),
351 'numeric_price' => $price,
352 'currency' => get_woocommerce_currency_symbol(),
353 'quantity' => $product_obj['quantity'],
354 'image' => $image ? $image[0] : '',
355 'regular_price' => $regular_price,
356 'sale_price' => $sale_price ? $sale_price : $price,
357 'html_price' => $product->get_price_html(),
358 'product_edit_link' => in_array( $product->get_type(), array( 'variation', 'subscription_variation' ) ) ? get_edit_post_link( $product->get_parent_id() ) : get_edit_post_link( $product->get_id() ),
359 'product_view_link' => in_array( $product->get_type(), array( 'variation', 'subscription_variation' ) ) ? get_permalink( $product->get_parent_id() ) : get_permalink( $product->get_id() ),
360 'is_trash' => 'trash' === $product->get_status() ? 'yes' : 'no',
361 'is_deleted' => 'no',
362 );
363
364 $discount = get_post_meta( $step_id, '_wpfnl_upsell_discount', true );
365 $response['discount'] = $discount ? $discount : array(
366 'discountType' => 'original',
367 'discountApplyTo' => 'regular',
368 'discountValue' => '0',
369 'discountPrice' => floatval($price) * intval($product_obj['quantity']),
370 'discountPriceHtml' => wc_format_sale_price( floatval($price) * floatval($product_obj['quantity']), ( $sale_price ? $sale_price : floatval($product->get_price()) ) * floatval($product_obj['quantity']) ),
371 );
372 } else {
373 $response['products'][] = array(
374 'is_trash' => 'no',
375 'is_deleted' => 'yes',
376 );
377 $response['discount'] = array(
378 'discountType' => 'original',
379 'discountApplyTo' => 'regular',
380 'discountValue' => '0',
381 'discountPrice' => 0,
382 'discountPriceHtml' => wc_price(0),
383 );
384 }
385 } else {
386 $response['products'] = array();
387 $response['discount'] = array(
388 'discountType' => 'original',
389 'discountApplyTo' => 'regular',
390 'discountValue' => '0',
391 'discountPrice' => 0,
392 'discountPriceHtml' => wc_price(0),
393 );
394 }
395
396 // Add additional fields required by Vue component
397 $replaceSettings = get_post_meta( $step_id, '_wpfnl_upsell_replacement_settings', true );
398 if( $replaceSettings == 'true' ) {
399 $response['replaceSettings'] = $replaceSettings;
400 $isOfferReplace = get_post_meta( $step_id, '_wpfnl_upsell_replacement', true );
401 $response['isOfferReplace'] = array(
402 'replacement_type' => isset($isOfferReplace['replacement_type']) ? $isOfferReplace['replacement_type'] : '',
403 'value' => isset($isOfferReplace['value']) ? $isOfferReplace['value'] : '',
404 );
405 } else {
406 $response['replaceSettings'] = $replaceSettings;
407 $response['isOfferReplace'] = array(
408 'replacement_type' => '',
409 'value' => '',
410 );
411 }
412
413 $offer_settings = Wpfnl_functions::get_offer_settings();
414 $funnel_id = Wpfnl_functions::get_funnel_id_from_step( $step_id );
415 $prev_step = Wpfnl_functions::get_prev_step( $funnel_id, $step_id );
416 $response['prevStep'] = isset( $prev_step['step_type'] ) && $prev_step['step_type'] ? $prev_step['step_type'] : '';
417 $response['success'] = true;
418 $response['isChildOrder'] = $offer_settings['offer_orders'] == 'child-order' ? true : false;
419 $response['columns'] = Wpfnl_functions::get_checkout_columns( $step_id );
420
421 $time_bound_discount_settings = get_post_meta($step_id, '_wpfnl_time_bound_discount_settings', true);
422 if( !$time_bound_discount_settings ){
423 $dateTime = new \DateTime();
424 $time_bound_discount_settings = array(
425 'isEnabled' => 'no',
426 'fromDate' => $dateTime->format('M d, Y'),
427 'toDate' => $dateTime->add(new \DateInterval('P1D'))->format('M d, Y')
428 );
429 }
430 $response['time_bound_discount_settings'] = $time_bound_discount_settings;
431
432 return $response;
433 }
434
435
436 /**
437 * Get basic downsell items for free plugin
438 *
439 * @param array $products
440 * @param int $step_id
441 * @return array
442 */
443 private function get_basic_downsell_items( $products, $step_id ) {
444 $response = array();
445
446 if( $products && count($products)) {
447 $product_obj = $products[0];
448 $product = wc_get_product($product_obj['id']);
449
450 if( $product instanceof \WC_Product ) {
451 $title = $product->get_type() == 'variation' ? Wpfnl_functions::get_formated_product_name( $product ) : $product->get_name();
452 $image = wp_get_attachment_image_src( $product->get_image_id(), 'single-post-thumbnail' );
453 $price = $product->get_price();
454
455 if($product->get_type() == 'variable' || $product->get_type() == 'variable-subscription') {
456 $regular_price = $product->get_variation_regular_price( 'min' ) ? $product->get_variation_regular_price( 'min' ) : $product->get_price();
457 } else {
458 $regular_price = $product->get_regular_price() ? $product->get_regular_price() : $product->get_price();
459 }
460
461 $sale_price = $product->get_type() == 'variable' ? $price : $product->get_sale_price();
462
463 $response['products'][] = array(
464 'id' => $product_obj['id'],
465 'title' => $title,
466 'price' => wc_price( $price ),
467 'numeric_price' => $price,
468 'currency' => get_woocommerce_currency_symbol(),
469 'quantity' => $product_obj['quantity'],
470 'image' => $image ? $image[0] : '',
471 'regular_price' => $regular_price,
472 'sale_price' => $sale_price ? $sale_price : $price,
473 'html_price' => $product->get_price_html(),
474 'product_edit_link' => in_array( $product->get_type(), array( 'variation', 'subscription_variation' ) ) ? get_edit_post_link( $product->get_parent_id() ) : get_edit_post_link( $product->get_id() ),
475 'product_view_link' => in_array( $product->get_type(), array( 'variation', 'subscription_variation' ) ) ? get_permalink( $product->get_parent_id() ) : get_permalink( $product->get_id() ),
476 'is_trash' => 'trash' === $product->get_status() ? 'yes' : 'no',
477 'is_deleted' => 'no',
478 );
479
480 $discount = get_post_meta( $step_id, '_wpfnl_downsell_discount', true );
481 $response['discount'] = $discount ? $discount : array(
482 'discountType' => 'original',
483 'discountApplyTo' => 'regular',
484 'discountValue' => '0',
485 'discountPrice' => floatval($price) * intval($product_obj['quantity']),
486 'discountPriceHtml' => wc_format_sale_price( floatval($price) * floatval($product_obj['quantity']), ( $sale_price ? $sale_price : floatval($product->get_price()) ) * floatval($product_obj['quantity']) ),
487 );
488 } else {
489 $response['products'][] = array(
490 'is_trash' => 'no',
491 'is_deleted' => 'yes',
492 );
493 $response['discount'] = array(
494 'discountType' => 'original',
495 'discountApplyTo' => 'regular',
496 'discountValue' => '0',
497 'discountPrice' => 0,
498 'discountPriceHtml' => wc_price(0),
499 );
500 }
501 } else {
502 $response['products'] = array();
503 $response['discount'] = array(
504 'discountType' => 'original',
505 'discountApplyTo' => 'regular',
506 'discountValue' => '0',
507 'discountPrice' => 0,
508 'discountPriceHtml' => wc_price(0),
509 );
510 }
511
512 // Add additional fields required by Vue component
513 $replaceSettings = get_post_meta( $step_id, '_wpfnl_downsell_replacement_settings', true );
514 if( $replaceSettings == 'true' ) {
515 $response['replaceSettings'] = $replaceSettings;
516 $isOfferReplace = get_post_meta( $step_id, '_wpfnl_downsell_replacement', true );
517 $response['isOfferReplace'] = array(
518 'replacement_type' => isset($isOfferReplace['replacement_type']) ? $isOfferReplace['replacement_type'] : '',
519 'value' => isset($isOfferReplace['value']) ? $isOfferReplace['value'] : '',
520 );
521 } else {
522 $response['replaceSettings'] = $replaceSettings;
523 $response['isOfferReplace'] = array(
524 'replacement_type' => '',
525 'value' => '',
526 );
527 }
528
529 $offer_settings = Wpfnl_functions::get_offer_settings();
530 $funnel_id = Wpfnl_functions::get_funnel_id_from_step( $step_id );
531 $prev_step = Wpfnl_functions::get_prev_step( $funnel_id, $step_id );
532 $response['prevStep'] = isset( $prev_step['step_type'] ) && $prev_step['step_type'] ? $prev_step['step_type'] : '';
533 $response['success'] = true;
534 $response['isChildOrder'] = $offer_settings['offer_orders'] == 'child-order' ? true : false;
535 $response['columns'] = Wpfnl_functions::get_checkout_columns( $step_id );
536
537 $time_bound_discount_settings = get_post_meta($step_id, '_wpfnl_time_bound_discount_settings', true);
538 if( !$time_bound_discount_settings ){
539 $dateTime = new \DateTime();
540 $time_bound_discount_settings = array(
541 'isEnabled' => 'no',
542 'fromDate' => $dateTime->format('M d, Y'),
543 'toDate' => $dateTime->add(new \DateInterval('P1D'))->format('M d, Y')
544 );
545 }
546 $response['time_bound_discount_settings'] = $time_bound_discount_settings;
547
548 return $response;
549 }
550
551
552 /**
553 * Add basic offer product for free plugin
554 *
555 * @param int $product_id
556 * @param array $data
557 * @param int $step_id
558 * @param string $offer_type
559 * @return array
560 */
561 private function add_basic_offer_product( $product_id, $data, $step_id, $offer_type ) {
562 $product = wc_get_product($product_id);
563
564 if ( !$product instanceof \WC_Product ) {
565 return array(
566 'success' => false,
567 'message' => __('Product Not Found', 'wpfnl')
568 );
569 }
570
571 // Save product data based on offer type
572 $meta_key = $offer_type === 'upsell' ? '_wpfnl_upsell_products' : '_wpfnl_downsell_products';
573 update_post_meta($step_id, $meta_key, $data);
574
575 $title = $product->get_type() == 'variation' ? Wpfnl_functions::get_formated_product_name( $product ) : $product->get_name();
576 $image = wp_get_attachment_image_src( $product->get_image_id(), 'single-post-thumbnail' );
577 $price = $product->get_price();
578
579 if($product->get_type() == 'variable' || $product->get_type() == 'variable-subscription') {
580 $regular_price = $product->get_variation_regular_price( 'min' ) ? $product->get_variation_regular_price( 'min' ) : $product->get_price();
581 } else {
582 $regular_price = $product->get_regular_price() ? $product->get_regular_price() : $product->get_price();
583 }
584
585 $sale_price = $product->get_type() == 'variable' ? $price : $product->get_sale_price();
586 $quantity = isset($data[0]['quantity']) ? intval($data[0]['quantity']) : 1;
587
588 return array(
589 'success' => true,
590 'message' => __('Product Added Successfully', 'wpfnl'),
591 'products' => array(
592 array(
593 'id' => $product_id,
594 'title' => $title,
595 'price' => wc_price( $price ),
596 'numeric_price' => $price,
597 'currency' => get_woocommerce_currency_symbol(),
598 'quantity' => $quantity,
599 'image' => $image ? $image[0] : '',
600 'regular_price' => $regular_price,
601 'sale_price' => $sale_price ? $sale_price : $price,
602 'html_price' => $product->get_price_html(),
603 'product_edit_link' => in_array( $product->get_type(), array( 'variation', 'subscription_variation' ) ) ? get_edit_post_link( $product->get_parent_id() ) : get_edit_post_link( $product->get_id() ),
604 'product_view_link' => in_array( $product->get_type(), array( 'variation', 'subscription_variation' ) ) ? get_permalink( $product->get_parent_id() ) : get_permalink( $product->get_id() ),
605 'is_trash' => 'trash' === $product->get_status() ? 'yes' : 'no',
606 'is_deleted' => 'no',
607 )
608 ),
609 'discount' => array(
610 'discountType' => 'original',
611 'discountApplyTo' => 'regular',
612 'discountValue' => '0',
613 'discountPrice' => floatval($price) * intval($quantity),
614 'discountPriceHtml' => wc_format_sale_price( floatval($price) * floatval($quantity), ( $sale_price ? $sale_price : floatval($product->get_price()) ) * floatval($quantity) ),
615 ),
616 'columns' => Wpfnl_functions::get_checkout_columns( $step_id )
617 );
618 }
619
620
621 }
622