PluginProbe
SellKit – Funnel builder and checkout optimizer for WooCommerce to sell more, faster / 2.7.0
SellKit – Funnel builder and checkout optimizer for WooCommerce to sell more, faster v2.7.0
2.7.0 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 All 43 releases
← All changes | includes/funnel/steps/checkout.php +99 -8 1.7.52.7.0 View file →
@@ -40,11 +40,52 @@
40 40
41 41 add_action( 'woocommerce_checkout_create_order', [ $this, 'save_order_custom_meta_data' ], 10 );
42 42 add_action( 'woocommerce_after_order_notes', [ $this, 'custom_checkout_hidden_fields' ], 10 );
43 43 add_action( 'wc_ajax_update_order_review', [ $this, 'do_actions' ] );
44 +
45 + // Persist the current checkout step id in the WC session so express-checkout
46 + // AJAX requests (which do not submit the sellkit_current_page_id hidden field)
47 + // can still resolve the funnel context and save sellkit_funnel_next_step_data
48 + // on the created order.
49 + if ( ! wp_doing_ajax() && ! is_admin() ) {
50 + add_action( 'wp', [ $this, 'store_checkout_step_in_session' ], 20 );
51 + }
44 52 }
45 53
46 54 /**
55 + * Store the current checkout step id in the WC session when rendering an actual
56 + * SellKit funnel checkout step page. The value is later consumed by
57 + * Sellkit_Funnel::get_page_id() during AJAX requests (e.g. Stripe express
58 + * checkout) that lack the sellkit_current_page_id POST field.
59 + *
60 + * A timestamp is stored alongside the id so the fallback can be ignored if it
61 + * is stale, avoiding cross-contamination between unrelated checkouts.
62 + *
63 + * @since 2.3.3
64 + * @return void
65 + */
66 + public function store_checkout_step_in_session() {
67 + if ( ! function_exists( 'WC' ) || ! is_object( WC()->session ) ) {
68 + return;
69 + }
70 +
71 + $post_id = get_the_ID();
72 +
73 + if ( empty( $post_id ) || 'sellkit_step' !== get_post_type( $post_id ) ) {
74 + return;
75 + }
76 +
77 + $step_data = get_post_meta( $post_id, 'step_data', true );
78 +
79 + if ( empty( $step_data['type']['key'] ) || 'checkout' !== $step_data['type']['key'] ) {
80 + return;
81 + }
82 +
83 + WC()->session->set( 'sellkit_current_page_id', (int) $post_id );
84 + WC()->session->set( 'sellkit_current_page_id_time', time() );
85 + }
86 +
87 + /**
47 88 * Adding items to the checkout page.
48 89 *
49 90 * @since 1.1.0
50 91 * @SuppressWarnings(PHPMD.NPathComplexity)
@@ -54,15 +95,29 @@
54 95 // These variables should be used in checkout builder widget.
55 96 $funnel_data = $this->funnel->current_step_data;
56 97 $optimization_data = ! empty( $funnel_data['data']['optimization'] ) ? $funnel_data['data']['optimization'] : '';
57 98 $redirect_url = ! empty( $optimization_data['closed_checkout_redirect_url'] ) ? $optimization_data['closed_checkout_redirect_url'] : site_url();
99 + $reset_cart = isset( $funnel_data['data']['products']['reset_cart'] ) ? $funnel_data['data']['products']['reset_cart'] : 'true';
58 100
59 - add_action( 'template_redirect', function () use ( $funnel_data ) {
101 + $auto_apply_coupon_cookie_value = null;
102 +
103 + if ( function_exists( 'sellkit_pro' ) && ! sellkit_pro()->is_active_sellkit_pro ) {
104 + $optimization_data = null;
105 + }
106 +
107 + if ( isset( $_COOKIE['sellkit_rejected_coupon'] ) ) {
108 + // Get the value of the cookie.
109 + $auto_apply_coupon_cookie_value = sanitize_text_field( wp_unslash( $_COOKIE['sellkit_rejected_coupon'] ) );
110 + $auto_apply_coupon_cookie_value = explode( ',', $auto_apply_coupon_cookie_value );
111 + }
112 +
113 + add_action( 'template_redirect', function () use ( $funnel_data, $reset_cart ) {
60 114 $product_settings = ! empty( $funnel_data['data']['products']['settings'] ) ? $funnel_data['data']['products']['settings'] : '';
61 115 $bump_data = ! empty( $funnel_data['bump'] ) ? $funnel_data['bump'] : '';
62 116
63 117 if ( ! empty( $product_settings ) ) {
64 118 set_query_var( 'funnel_product_settings', $product_settings );
119 + set_query_var( 'funnel_reset_cart', $reset_cart );
65 120 }
66 121
67 122 if ( ! empty( $bump_data ) ) {
68 123 $bump_data = $this->get_valid_bumps( $bump_data );
@@ -67,9 +122,13 @@
67 122 if ( ! empty( $bump_data ) ) {
68 123 $bump_data = $this->get_valid_bumps( $bump_data );
69 124
70 125 set_query_var( 'bump_data', $bump_data );
126 + } else {
127 + set_query_var( 'bump_data', [] );
71 128 }
129 +
130 + set_query_var( 'funnel_reset_cart', $reset_cart );
72 131 } );
73 132
74 133 if (
75 134 ! empty( $optimization_data['expiration_date'] ) &&
@@ -100,9 +159,11 @@
100 159
101 160 $action = sellkit_htmlspecialchars( INPUT_GET, 'wc-ajax' );
102 161
103 162 if ( 'update_order_review' !== $action && 'checkout' !== $action ) {
104 - wc()->cart->empty_cart();
163 + if ( 'true' === $reset_cart || true === $reset_cart ) {
164 + wc()->cart->empty_cart();
165 + }
105 166
106 167 foreach ( $funnel_products as $product_id => $product ) {
107 168 if ( empty( $product ) ) {
108 169 continue;
@@ -107,11 +168,14 @@
107 168 if ( empty( $product ) ) {
108 169 continue;
109 170 }
110 171
111 - $quantity = ! empty( $product['quantity'] ) ? $product['quantity'] : 1;
172 + $quantity = ! empty( $product['quantity'] ) ? $product['quantity'] : 1;
173 + $product_cart_id = WC()->cart->generate_cart_id( $product_id );
112 174
113 - wc()->cart->add_to_cart( $product_id, $quantity );
175 + if ( ! WC()->cart->find_product_in_cart( $product_cart_id ) ) {
176 + WC()->cart->add_to_cart( $product_id, $quantity );
177 + }
114 178 }
115 179 }
116 180
117 181 if ( empty( $optimization_data ) ) {
@@ -119,8 +183,12 @@
119 183 }
120 184
121 185 if ( self::apply_coupon_validation( $optimization_data ) ) {
122 186 foreach ( $optimization_data['auto_apply_coupons'] as $auto_apply_coupon ) {
187 +
188 + if ( ! empty( $auto_apply_coupon_cookie_value ) && in_array( $auto_apply_coupon['label'], $auto_apply_coupon_cookie_value, true ) ) {
189 + continue;
190 + }
123 191 wc()->cart->add_discount( get_the_title( $auto_apply_coupon['value'] ) );
124 192 }
125 193
126 194 wc_clear_notices();
@@ -174,8 +242,12 @@
174 242 $id = get_the_ID();
175 243 $global_checkout_id = get_option( Global_Checkout::SELLKIT_GLOBAL_CHECKOUT_OPTION, 0 );
176 244 $default_checkout_id = wc_get_page_id( 'checkout' );
177 245
246 + if ( ! empty( $default_checkout_id ) ) {
247 + $default_checkout_id = apply_filters( 'wpml_object_id', $default_checkout_id, 'page', true ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WPML API.
248 + }
249 +
178 250 if (
179 251 (int) $global_checkout_id > 0 &&
180 252 'publish' === get_post_status( $global_checkout_id ) &&
181 253 $id === $default_checkout_id
@@ -186,9 +258,9 @@
186 258 foreach ( $steps as $step ) {
187 259 $step['type'] = (array) $step['type'];
188 260
189 261 if ( 'checkout' === $step['type']['key'] ) {
190 - $checkout_id = $step['page_id'];
262 + $checkout_id = apply_filters( 'wpml_object_id', $step['page_id'], 'sellkit_step', true ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WPML API.
191 263 }
192 264 }
193 265
194 266 if ( (int) $checkout_id > 0 ) {
@@ -199,8 +271,17 @@
199 271 woocommerce_form_field( 'sellkit_current_page_id', [
200 272 'type' => 'hidden',
201 273 'class' => [ 'hidden form-row-wide' ],
202 274 ], $id );
275 +
276 + // This field added for passing upsell discount price to backend.
277 + woocommerce_form_field( 'sellkit_product_prices', [
278 + 'type' => 'hidden',
279 + 'class' => [ 'hidden form-row-wide' ],
280 + 'autocomplete' => 'off',
281 + 'autocorrect' => 'off',
282 + 'autocapitalize' => 'off',
283 + ], '0' );
203 284 }
204 285
205 286 /**
206 287 * Check If a coupon should be applied.
@@ -302,8 +383,10 @@
302 383 * @return void
303 384 *
304 385 * @since 1.5.0
305 386 * @access private
387 + *
388 + * @SuppressWarnings(PHPMD.NPathComplexity)
306 389 */
307 390 private function check_bump_acceptance( $order ) {
308 391 if (
309 392 empty( $this->funnel->funnel_id ) ||
@@ -317,9 +400,11 @@
317 400 if ( empty( $bump['data']['products'] ) ) {
318 401 continue;
319 402 }
320 403
321 - $bump_product_ids = array_merge( $bump_product_ids, array_keys( $bump['data']['products']['list'] ) );
404 + $product_list = ! empty( $bump['data']['products']['list'] ) ? $bump['data']['products']['list'] : [];
405 +
406 + $bump_product_ids = array_merge( $bump_product_ids, array_keys( $product_list ) );
322 407 }
323 408
324 409 $bump_exists_in_order = false;
325 410
@@ -339,10 +424,12 @@
339 424 $database = new Database();
340 425 $old_values = [];
341 426
342 427 // Getting old data.
343 - $result = $database->get( 'funnel_contact', [ 'id' => $_SESSION['entered_funnel_id'] ] );
428 + $contact_id = isset( $_SESSION['entered_funnel_id'] ) ? absint( $_SESSION['entered_funnel_id'] ) : 0;
344 429
430 + $result = $database->get( 'funnel_contact', [ 'id' => $contact_id ] );
431 +
345 432 if ( ! empty( $result[0]['order_bump'] ) ) {
346 433 $old_values = unserialize( $result[0]['order_bump'] ); // phpcs:ignore
347 434 }
348 435
@@ -350,9 +437,9 @@
350 437
351 438 $database->update(
352 439 'funnel_contact',
353 440 [ 'order_bump' => $new_values ],
354 - [ 'id' => $_SESSION['entered_funnel_id'] ]
441 + [ 'id' => $contact_id ]
355 442 );
356 443 }
357 444
358 445 /**
@@ -397,8 +484,12 @@
397 484
398 485 if ( $global_checkout_id > 0 && 'publish' === get_post_status( $global_checkout_id ) ) {
399 486 $steps = get_post_meta( $global_checkout_id, 'nodes', true );
400 487 $checkout_id = 0;
488 +
489 + if ( ! is_array( $steps ) ) {
490 + return;
491 + }
401 492
402 493 foreach ( $steps as $step ) {
403 494 $step['type'] = (array) $step['type'];
404 495