PluginProbe
SellKit – Funnel builder and checkout optimizer for WooCommerce to sell more, faster / trunk
SellKit – Funnel builder and checkout optimizer for WooCommerce to sell more, faster vtrunk
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 / thankyou.php

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

335 lines 9.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 defined( 'ABSPATH' ) || die();
6
7 use Sellkit\Global_Checkout\Checkout as Global_Checkout;
8 use Elementor\Plugin as Elementor;
9
10 /**
11 * Class Sellkit_Thankyou.
12 *
13 * @SuppressWarnings(PHPMD.NPathComplexity)
14 * @since 1.1.0
15 */
16 class Thankyou extends Base_Step {
17
18 /**
19 * Post ID.
20 *
21 * @var int
22 * @since 2.3.0
23 */
24 private $post_id;
25
26 /**
27 * Thankyou constructor.
28 *
29 * @since 1.1.0
30 */
31 public function __construct() {
32 parent::__construct();
33
34 add_action( 'template_redirect', [ $this, 'redirect_after_purchase' ], 10 );
35 }
36
37 /**
38 * Redirects after purchasing.
39 *
40 * @since 1.1.0
41 */
42 public function redirect_after_purchase() {
43 if ( ! class_exists( 'woocommerce' ) ) {
44 return;
45 }
46
47 global $wp;
48
49 $funnel = sellkit_funnel();
50
51 if ( ! empty( $funnel->funnel_id ) && 'thankyou' === $funnel->current_step_data['type']['key'] ) {
52 return;
53 }
54
55 if ( ! function_exists( 'is_checkout' ) ) {
56 return;
57 }
58
59 $order_key = filter_input( INPUT_GET, 'key', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
60
61 if ( empty( $order_key ) ) {
62 return;
63 }
64
65 $order_id = wc_get_order_id_by_order_key( $order_key );
66 $order = wc_get_order( $order_id );
67
68 if ( ! is_a( $order, 'WC_Order' ) ) {
69 return;
70 }
71
72 // Always run WooCommerce's native cart-clearing logic on a valid order-received
73 // request, regardless of whether SellKit has its own next-step data to follow.
74 // This covers express-checkout flows that bypass SellKit's hidden fields and
75 // protects against SellKit's global-checkout template_redirect exit preventing
76 // WooCommerce's own priority-20 cart-clearing hook from running.
77 if ( function_exists( 'wc_clear_cart_after_payment' ) ) {
78 wc_clear_cart_after_payment();
79 }
80
81 $next_step = $order->get_meta( 'sellkit_funnel_next_step_data' );
82 $funnel_id = (int) $order->get_meta( 'sellkit_funnel_id' );
83
84 if ( empty( $next_step ) ) {
85 return;
86 }
87
88 if ( empty( $funnel_id ) ) {
89 $step_id = intval( $next_step['page_id'] );
90 $step_data = get_post_meta( $step_id, 'step_data', true );
91 $funnel_id = (int) $step_data['funnel_id'];
92 }
93
94 // For the new method( upsell popup ) we show thakyou page right away after checkout step.
95 $thankyou_id = $this->find_funnel_thankyou_page( $funnel_id );
96 $next_step_link = add_query_arg( [ 'order-key' => $order_key ], get_permalink( $thankyou_id ) );
97
98 if ( class_exists( 'SitePress' ) ) {
99 $thankyou_data = $this->wpml_compatibility_get_thank_you_page_data( $thankyou_id );
100 $thankyou_id = isset( $thankyou_data['id'] ) ? $thankyou_data['id'] : $thankyou_id;
101
102 if ( isset( $thankyou_data['lang'] ) ) {
103 $next_step_link = $this->wpml_compatibility_get_next_step_link( $thankyou_id, $order_key, $thankyou_data['lang'] );
104 }
105 }
106
107 $last_price = $order->get_total() - $order->get_total_discount() - $order->get_total_tax();
108
109 $this->contacts->add_total_spent( $last_price, $funnel_id );
110
111 $global_funnel_id = (int) get_option( Global_Checkout::SELLKIT_GLOBAL_CHECKOUT_OPTION );
112 if ( $global_funnel_id === $funnel_id ) {
113 $this->global_thankyou( $thankyou_id );
114 exit();
115 }
116
117 wp_safe_redirect( $next_step_link );
118 exit();
119 }
120
121 /**
122 * Retrieves the translated thank you page data for WPML compatibility.
123 *
124 * This function checks the referring page to determine the current language
125 * and retrieves the translated thank you page ID for that language.
126 *
127 * @since 2.3.2
128 *
129 * @param int $thankyou_id The ID of the default thank you page.
130 *
131 * @return array {
132 * An associative array containing the translated thank you page ID and language code.
133 *
134 * @type int $id The translated thank you page ID or the original if no translation is found.
135 * @type string $lang The language code for the thank you page.
136 * }
137 */
138 private function wpml_compatibility_get_thank_you_page_data( $thankyou_id ) {
139 $referrer_url = wp_get_referer();
140 $previous_page_id = 0;
141
142 $data = [
143 'id' => $thankyou_id,
144 'lang' => '',
145 ];
146
147 if ( $referrer_url ) {
148 $previous_page_id = url_to_postid( $referrer_url );
149 }
150
151 if ( $previous_page_id ) {
152 $order_language_details = apply_filters( 'wpml_post_language_details', null, $previous_page_id ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WPML API.
153 $current_language = $order_language_details['language_code'];
154
155 if ( ! empty( $current_language ) ) {
156 $thankyou_id_translated = apply_filters( 'wpml_object_id', $thankyou_id, 'sellkit_step', true, $current_language ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WPML API.
157
158 if ( ! empty( $thankyou_id_translated ) ) {
159 $data['id'] = $thankyou_id_translated;
160 $data['lang'] = $current_language;
161
162 return $data;
163 }
164 }
165
166 return $data;
167 }
168
169 return $data;
170 }
171
172 /**
173 * Generates the next step link with WPML compatibility.
174 *
175 * This function generates the next step link by retrieving the translated permalink
176 * for the thank you page in the specified language and appending the order key as a query argument.
177 *
178 * @since 2.3.2
179 *
180 * @param int $thankyou_id The ID of the thank you page.
181 * @param string $order_key The order key used for generating the next step link.
182 * @param string $lang The language code for the thank you page.
183 *
184 * @return string The next step link URL, including the translated permalink and order key.
185 */
186 private function wpml_compatibility_get_next_step_link( $thankyou_id, $order_key, $lang ) {
187 $permalink = apply_filters( 'wpml_permalink', get_permalink( $thankyou_id ), $lang ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WPML API.
188 $next_step_link = add_query_arg( [ 'order-key' => $order_key ], $permalink );
189
190 return $next_step_link;
191 }
192
193 /**
194 * Find funnel thankyou page using one of steps data.
195 *
196 * @param int $funnel_id funnel id.
197 * @since 1.6.2
198 */
199 private function find_funnel_thankyou_page( $funnel_id ) {
200 $funnel_data = get_post_meta( $funnel_id, 'nodes', true );
201 $id = 0;
202
203 foreach ( $funnel_data as $step ) {
204 $step['type'] = (array) $step['type'];
205
206 if ( 'thankyou' === $step['type']['key'] ) {
207 $id = $step['page_id'];
208 }
209 }
210
211 return $id;
212 }
213
214 /**
215 * Show global thankyou page.
216 *
217 * @param int $thankyou_id thankyou page id.
218 * @since 1.8.6
219 */
220 private function global_thankyou( $thankyou_id ) {
221 // Remove previous content.
222 remove_all_filters( 'the_content' );
223
224 add_filter( 'sellkit_global_thankyou', '__return_true' );
225
226 if ( 'gutenberg' === sellkit()->page_builder() ) {
227 $thankyou_id = get_post( $thankyou_id );
228
229 $this->load_order_cart_details_block_frontend();
230
231 $thankyou_post = get_post( $thankyou_id );
232
233 global $post;
234 $post = $thankyou_post; // phpcs:ignore:WordPress.WP.GlobalVariablesOverride.OverrideProhibited
235 setup_postdata( $post );
236
237 $content = do_blocks( $post->post_content );
238
239 $content = apply_filters( 'the_content', $content ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WordPress core filter.
240
241 add_filter( 'the_content', function() use ( $content ) {
242 ob_Start();
243
244 echo $content; // phpcs:ignore:WordPress.Security.EscapeOutput.OutputNotEscaped
245
246 return ob_get_clean();
247 }, 5 );
248
249 sellkit()->load_files( [
250 'templates/default-canvas'
251 ] );
252 }
253
254 if ( defined( 'ELEMENTOR_VERSION' ) && 'elementor' === sellkit()->page_builder() ) {
255 // Add new content.
256 add_filter( 'the_content', function() use ( $thankyou_id ) {
257 ob_Start();
258 $content = Elementor::instance()->frontend->get_builder_content_for_display( (int) $thankyou_id, true );
259 echo do_shortcode( $content );
260 return ob_get_clean();
261 }, 5 );
262
263 sellkit()->load_files( [
264 'templates/canvas'
265 ] );
266 }
267 }
268
269 /**
270 * Load order cart details block on frontend.
271 *
272 * @since 2.3.0
273 */
274 public function load_order_cart_details_block_frontend() {
275 global $post;
276
277 if ( empty( $post->post_content ) ) {
278 return;
279 }
280
281 $this->post_id = $post->ID;
282
283 $block = 'blocks/order-cart-details';
284
285 $block_data = explode( '/', $block );
286 $block_name = $block_data[1];
287
288 $class_name = str_replace( '-', ' ', $block_name );
289 $class_name = str_replace( ' ', '_', ucwords( $class_name ) );
290 $class_name = "Sellkit\blocks\Render\\{$class_name}";
291 $class_path = 'block-editor/' . $block . '/index';
292
293 sellkit()->load_files( [
294 $class_path,
295 ] );
296
297 $new_class = new $class_name( $this->post_id );
298 $this->register_inner_blocks_by_parent( $new_class );
299 $new_class->register_block_meta();
300 }
301
302 /**
303 * Register inner blocks by parent.
304 *
305 * @param object $parent_class Parent class name.
306 * @since 2.3.0
307 * @return void
308 */
309 private function register_inner_blocks_by_parent( $parent_class ) {
310 if ( ! method_exists( $parent_class, 'has_inner_blocks' ) ) {
311 return;
312 }
313
314 $inner_blocks = $parent_class->get_inner_block();
315
316 sellkit()->load_files( $inner_blocks );
317
318 foreach ( $inner_blocks as $key => $value ) {
319 if ( isset( $this->inner_blocks[ "blocks/{$key}" ] ) ) {
320 continue;
321 }
322
323 $inner_block_class = 'Sellkit\Blocks\Inner_Block\\' . str_replace( '-', '_', ucwords( $key ) );
324
325 if ( ! class_exists( $inner_block_class ) ) {
326 continue;
327 }
328
329 $inner_block_instance = new $inner_block_class( $this->post_id );
330
331 $inner_block_instance->register_block_meta();
332 }
333 }
334 }
335