PluginProbe
SellKit – Funnel builder and checkout optimizer for WooCommerce to sell more, faster / 1.7.9
SellKit – Funnel builder and checkout optimizer for WooCommerce to sell more, faster v1.7.9
2.6.0 trunk 1.1.0 1.1.4 1.2.1 1.2.2 1.2.3 1.2.5 1.2.9 1.3.1 1.3.2 1.5.0 1.5.1 1.5.4 1.5.7 1.5.8 1.5.9 1.6.2 1.6.5 1.6.8 1.7.2 1.7.4 1.7.5 1.7.9 1.8.1 All 42 releases
sellkit / includes / funnel / steps / checkout.php

checkout.php in SellKit – Funnel builder and checkout optimizer for WooCommerce to sell more, faster 1.7.9, at includes/funnel/steps/checkout.php

448 lines 11.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Sellkit\Funnel\Steps;
4
5 use Sellkit\Funnel\Analytics\Data_Updater;
6 use Sellkit\Funnel\Contacts\Base_Contacts;
7 use Sellkit\Database;
8 use Sellkit_Funnel;
9 use Sellkit\Global_Checkout\Checkout as Global_Checkout;
10
11 defined( 'ABSPATH' ) || die();
12
13 /**
14 * Class Sellkit_Checkout.
15 *
16 * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
17 * @since 1.1.0
18 */
19 class Checkout {
20
21 /**
22 * Funnel instance.
23 *
24 * @var Sellkit_Funnel Funnel object.
25 * @since 1.1.0
26 */
27 public $funnel;
28
29 /**
30 * Checkout constructor.
31 *
32 * @since 1.1.0
33 */
34 public function __construct() {
35 $this->update_funnel_data();
36
37 if ( empty( $this->funnel->funnel_id ) ) {
38 return;
39 }
40
41 add_action( 'woocommerce_checkout_create_order', [ $this, 'save_order_custom_meta_data' ], 10 );
42 add_action( 'woocommerce_after_order_notes', [ $this, 'custom_checkout_hidden_fields' ], 10 );
43 add_action( 'wc_ajax_update_order_review', [ $this, 'do_actions' ] );
44 }
45
46 /**
47 * Adding items to the checkout page.
48 *
49 * @since 1.1.0
50 * @SuppressWarnings(PHPMD.NPathComplexity)
51 * @SuppressWarnings(PHPMD.CyclomaticComplexity)
52 */
53 public function do_actions() {
54 // These variables should be used in checkout builder widget.
55 $funnel_data = $this->funnel->current_step_data;
56 $optimization_data = ! empty( $funnel_data['data']['optimization'] ) ? $funnel_data['data']['optimization'] : '';
57 $redirect_url = ! empty( $optimization_data['closed_checkout_redirect_url'] ) ? $optimization_data['closed_checkout_redirect_url'] : site_url();
58 $reset_cart = isset( $funnel_data['data']['products']['reset_cart'] ) ? $funnel_data['data']['products']['reset_cart'] : 'true';
59
60 add_action( 'template_redirect', function () use ( $funnel_data, $reset_cart ) {
61 $product_settings = ! empty( $funnel_data['data']['products']['settings'] ) ? $funnel_data['data']['products']['settings'] : '';
62 $bump_data = ! empty( $funnel_data['bump'] ) ? $funnel_data['bump'] : '';
63
64 if ( ! empty( $product_settings ) ) {
65 set_query_var( 'funnel_product_settings', $product_settings );
66 set_query_var( 'funnel_reset_cart', $reset_cart );
67 }
68
69 if ( ! empty( $bump_data ) ) {
70 $bump_data = $this->get_valid_bumps( $bump_data );
71
72 set_query_var( 'bump_data', $bump_data );
73 }
74 } );
75
76 if (
77 ! empty( $optimization_data['expiration_date'] ) &&
78 ! empty( $optimization_data['expire_checkout_coupons_switch'] ) &&
79 time() > $optimization_data['expiration_date'] &&
80 'on' === $optimization_data['expire_checkout_coupons_switch']
81 ) {
82 wp_redirect( $this->add_http_to_url( $redirect_url ) ); // phpcs:ignore
83 exit;
84 }
85
86 if ( empty( $funnel_data ) ) {
87 return;
88 }
89
90 if (
91 empty( $funnel_data['data']['products']['list'] ) ||
92 ! is_array( $funnel_data['data']['products']['list'] )
93 ) {
94 return;
95 }
96
97 $funnel_products = $funnel_data['data']['products']['list'];
98
99 if ( ! sellkit()->has_valid_dependencies() ) {
100 return;
101 }
102
103 $action = sellkit_htmlspecialchars( INPUT_GET, 'wc-ajax' );
104
105 if ( 'update_order_review' !== $action && 'checkout' !== $action ) {
106 if ( 'true' !== $reset_cart ) {
107 foreach ( WC()->cart->get_cart() as $item ) {
108 if ( empty( $item['product_id'] ) ) {
109 continue;
110 }
111
112 if ( array_key_exists( $item['product_id'], $funnel_products ) ) {
113 $funnel_products[ $item['product_id'] ]['quantity'] = $item['quantity'];
114
115 continue;
116 }
117
118 $key = $item['product_id'];
119
120 if ( ! empty( $item['variation_id'] ) ) {
121 $key = $item['variation_id'];
122 }
123
124 $funnel_products[ $key ] = [
125 'quantity' => $item['quantity'],
126 'discount' => '',
127 'discountType' => 'fixed',
128 ];
129 }
130 }
131
132 WC()->cart->empty_cart();
133
134 foreach ( $funnel_products as $product_id => $product ) {
135 if ( empty( $product ) ) {
136 continue;
137 }
138
139 $quantity = ! empty( $product['quantity'] ) ? $product['quantity'] : 1;
140
141 wc()->cart->add_to_cart( $product_id, $quantity );
142 }
143 }
144
145 if ( empty( $optimization_data ) ) {
146 return;
147 }
148
149 if ( self::apply_coupon_validation( $optimization_data ) ) {
150 foreach ( $optimization_data['auto_apply_coupons'] as $auto_apply_coupon ) {
151 wc()->cart->add_discount( get_the_title( $auto_apply_coupon['value'] ) );
152 }
153
154 wc_clear_notices();
155 }
156 }
157
158 /**
159 * Adds http if it is needed.
160 *
161 * @since 1.5.0
162 * @param string $url Entered url.
163 * @return string|boolean
164 */
165 private function add_http_to_url( $url ) {
166 if ( ! preg_match( '~^(?:f|ht)tps?://~i', $url ) ) {
167 return 'http://' . $url;
168 }
169
170 return $url;
171 }
172
173 /**
174 * Saving the funnel data to the order.
175 *
176 * @since 1.1.0
177 * @param object $order Order object.
178 */
179 public function save_order_custom_meta_data( $order ) {
180 if ( empty( $this->funnel->next_step_data ) ) {
181 return;
182 }
183
184 Base_Contacts::step_is_passed();
185 $this->check_bump_acceptance( $order );
186
187 $analytics_updater = new Data_Updater();
188
189 $analytics_updater->set_funnel_id( $this->funnel->funnel_id );
190 $analytics_updater->add_new_order_log( $order );
191
192 $order->update_meta_data( 'sellkit_funnel_next_step_data', $this->funnel->next_step_data );
193 $order->update_meta_data( 'sellkit_funnel_id', $this->funnel->funnel_id );
194 }
195
196 /**
197 * Adding page id field to the inputs.
198 *
199 * @since 1.1.0
200 */
201 public function custom_checkout_hidden_fields() {
202 $id = get_the_ID();
203 $global_checkout_id = get_option( Global_Checkout::SELLKIT_GLOBAL_CHECKOUT_OPTION, 0 );
204 $default_checkout_id = wc_get_page_id( 'checkout' );
205
206 if (
207 (int) $global_checkout_id > 0 &&
208 'publish' === get_post_status( $global_checkout_id ) &&
209 $id === $default_checkout_id
210 ) {
211 $steps = get_post_meta( $global_checkout_id, 'nodes', true );
212 $checkout_id = 0;
213
214 foreach ( $steps as $step ) {
215 $step['type'] = (array) $step['type'];
216
217 if ( 'checkout' === $step['type']['key'] ) {
218 $checkout_id = $step['page_id'];
219 }
220 }
221
222 if ( (int) $checkout_id > 0 ) {
223 $id = $checkout_id;
224 }
225 }
226
227 woocommerce_form_field( 'sellkit_current_page_id', [
228 'type' => 'hidden',
229 'class' => [ 'hidden form-row-wide' ],
230 ], $id );
231 }
232
233 /**
234 * Check If a coupon should be applied.
235 *
236 * @since 1.1.0
237 * @param array $optimization_data Optimization data.
238 */
239 public static function apply_coupon_validation( $optimization_data ) {
240 if (
241 empty( $optimization_data['auto_apply_coupons_switch'] ) ||
242 'on' !== $optimization_data['auto_apply_coupons_switch']
243 ) {
244 return false;
245 }
246
247 if (
248 empty( $optimization_data['auto_apply_coupons'] ) ||
249 ! is_array( $optimization_data['auto_apply_coupons'] )
250 ) {
251 return false;
252 }
253
254 return true;
255 }
256
257 /**
258 * Calculate total discount.
259 *
260 * @since 1.1.0
261 * @param string $total Total.
262 * @param string $value Value.
263 * @param string $type Type.
264 * @return false|float|int|mixed
265 */
266 public function calculate_discount( $total, $value, $type ) {
267 if ( empty( $value ) || 1 > $value ) {
268 return 0;
269 }
270
271 if ( strpos( $type, 'fixed' ) !== false ) {
272 return $value;
273 }
274
275 if ( strpos( $type, 'percentage' ) !== false ) {
276 return ( floatval( $total ) * floatval( $value ) ) / 100;
277 }
278
279 return 0;
280 }
281
282 /**
283 * Gets extracted post data.
284 *
285 * @since 1.1.0
286 * @param string $post_data Post data.
287 */
288 private function get_current_page_id_by_post_data( $post_data ) {
289 $vars = explode( '&', $post_data );
290 $extracted_data = [];
291
292 foreach ( $vars as $value ) {
293 $result = explode( '=', urldecode( $value ) );
294 $extracted_data[ $result[0] ] = $result[1];
295 }
296
297 if ( empty( $extracted_data['sellkit_current_page_id'] ) ) {
298 return null;
299 }
300
301 return $extracted_data['sellkit_current_page_id'];
302 }
303
304 /**
305 * Checks all bumps and return data.
306 *
307 * @since 1.1.0
308 * @param array $bump_data Bump data.
309 */
310 public function get_valid_bumps( $bump_data ) {
311 $valid_bumps = [];
312
313 foreach ( $bump_data as $bump ) {
314 $conditions = ! empty( $bump['data']['conditions'] ) ? $bump['data']['conditions'] : '';
315
316 if ( ! empty( $conditions ) && empty( sellkit_conditions_validation( $conditions ) ) ) {
317 continue;
318 }
319
320 $valid_bumps[] = $bump;
321 }
322
323 return $valid_bumps;
324 }
325
326 /**
327 * Checks if bump is accepted during checkout or not and updates table accordingly.
328 *
329 * @param object $order WooCommerce order object.
330 * @return void
331 *
332 * @since 1.5.0
333 * @access private
334 */
335 private function check_bump_acceptance( $order ) {
336 if (
337 empty( $this->funnel->funnel_id ) ||
338 empty( $this->funnel->current_step_data['bump'] )
339 ) {
340 return;
341 }
342
343 $bump_product_ids = [];
344 foreach ( $this->funnel->current_step_data['bump'] as $bump ) {
345 if ( empty( $bump['data']['products'] ) ) {
346 continue;
347 }
348
349 $bump_product_ids = array_merge( $bump_product_ids, array_keys( $bump['data']['products']['list'] ) );
350 }
351
352 $bump_exists_in_order = false;
353
354 foreach ( $order->get_items() as $item ) {
355 $product_id = $item->get_changes()['product_id'];
356
357 if ( in_array( $product_id, $bump_product_ids, true ) ) {
358 $bump_exists_in_order = true;
359 break;
360 }
361 }
362
363 if ( ! $bump_exists_in_order ) {
364 return;
365 }
366
367 $database = new Database();
368 $old_values = [];
369
370 // Getting old data.
371 $result = $database->get( 'funnel_contact', [ 'id' => $_SESSION['entered_funnel_id'] ] );
372
373 if ( ! empty( $result[0]['order_bump'] ) ) {
374 $old_values = unserialize( $result[0]['order_bump'] ); // phpcs:ignore
375 }
376
377 $new_values = array_merge( $old_values, [ $this->funnel->current_step_data['page_id'] ] );
378
379 $database->update(
380 'funnel_contact',
381 [ 'order_bump' => $new_values ],
382 [ 'id' => $_SESSION['entered_funnel_id'] ]
383 );
384 }
385
386 /**
387 * Updates funnel data.
388 *
389 * @since 1.1.0
390 */
391 public function update_funnel_data() {
392 if ( is_admin() ) {
393 return;
394 }
395
396 $action = sellkit_htmlspecialchars( INPUT_GET, 'wc-ajax' );
397
398 if ( 'update_order_review' === $action ) {
399 $post_data = sellkit_htmlspecialchars( INPUT_POST, 'post_data' );
400
401 $this->funnel = new Sellkit_Funnel( $this->get_current_page_id_by_post_data( $post_data ) );
402
403 if ( empty( $this->funnel->funnel_id ) ) {
404 $this->global_checkout_funnel_integration();
405 }
406
407 return;
408 }
409
410 $this->funnel = Sellkit_Funnel::get_instance();
411
412 if ( empty( $this->funnel->funnel_id ) ) {
413 $this->global_checkout_funnel_integration();
414 }
415 }
416
417 /**
418 * Setup global checkout as a native sellkit funnel, so every functions works with no issue.
419 * detect funnel id in the global checkout.
420 *
421 * @since 1.7.4
422 */
423 public function global_checkout_funnel_integration() {
424 $global_checkout_id = get_option( Global_Checkout::SELLKIT_GLOBAL_CHECKOUT_OPTION, 0 );
425
426 if ( $global_checkout_id > 0 && 'publish' === get_post_status( $global_checkout_id ) ) {
427 $steps = get_post_meta( $global_checkout_id, 'nodes', true );
428 $checkout_id = 0;
429
430 foreach ( $steps as $step ) {
431 $step['type'] = (array) $step['type'];
432
433 if ( 'checkout' === $step['type']['key'] ) {
434 $checkout_id = $step['page_id'];
435 }
436 }
437
438 if ( (int) $checkout_id > 0 ) {
439 $this->funnel = new Sellkit_Funnel( $checkout_id );
440
441 add_filter( 'sellkit_global_checkout_activated', function() {
442 return true;
443 } );
444 }
445 }
446 }
447 }
448