PluginProbe
SellKit – Funnel builder and checkout optimizer for WooCommerce to sell more, faster / 1.7.4
SellKit – Funnel builder and checkout optimizer for WooCommerce to sell more, faster v1.7.4
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.4, at includes/funnel/steps/checkout.php

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