PluginProbe
SellKit – Funnel builder and checkout optimizer for WooCommerce to sell more, faster / 1.2.2
SellKit – Funnel builder and checkout optimizer for WooCommerce to sell more, faster v1.2.2
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
sellkit / includes / contact-segmentation / contact-data-updater.php

contact-data-updater.php in SellKit – Funnel builder and checkout optimizer for WooCommerce to sell more, faster 1.2.2, at includes/contact-segmentation/contact-data-updater.php

423 lines 10.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Sellkit\Contact_Segmentation;
4
5 use Sellkit\Contact_Segmentation\libraries\Mobile_Detect;
6 use Sellkit\Database;
7
8 defined( 'ABSPATH' ) || die();
9
10 /**
11 * Class Contact Data Updater.
12 *
13 * @package Sellkit\Contact_Segmentation
14 * @SuppressWarnings(ExcessiveClassComplexity)
15 * @since 1.1.0
16 */
17 class Contact_Data_Updater {
18
19 const SELLKIT_CONTACT_SEGMENTATION_LOCATION_API = 'sjYudEYJDjBMDDoKyxHd'; // TODO we should get it dynamically.
20 const SELLKIT_CONTACT_SEGMENTATION_FORCE_UPDATE = 'sellkit_contact_segmentation_force_update';
21
22 /**
23 * Class instance.
24 *
25 * @since 1.1.0
26 * @var Contact_Data_Updater
27 */
28 private static $instance = null;
29
30 /**
31 * Contact data.
32 *
33 * @since 1.1.0
34 * @var array Contact data.
35 */
36 public $data;
37
38 /**
39 * New contact data.
40 *
41 * @since 1.1.0
42 * @var array Contact data.
43 */
44 public static $new_data = [];
45
46 /**
47 * Check if data is expired or not.
48 *
49 * @since 1.1.0
50 * @var boolean Is data expired.
51 */
52 public $is_expired = false;
53
54 /**
55 * Contact_Data constructor.
56 *
57 * @since 1.1.0
58 */
59 public function __construct() {
60 if ( wp_doing_ajax() ) {
61 return;
62 }
63
64 $this->data = Contact_Data::$historical_data;
65
66 if ( ! is_user_logged_in() ) {
67 $this->update_browser_language();
68 $this->maybe_update_device();
69 $this->update_utm_data();
70 $this->maybe_update_ip();
71 $this->maybe_update_locations();
72
73 add_action( 'wp', [ $this, 'update_viewed_details' ], 10 );
74 add_action( 'wp', [ $this, 'maybe_update_data' ], 9999 );
75 return;
76 }
77
78 $this->update_utm_data();
79
80 add_action( 'wp', [ $this, 'update_viewed_details' ], 10 );
81
82 $force_update = get_user_meta( get_current_user_id(), self::SELLKIT_CONTACT_SEGMENTATION_FORCE_UPDATE, true );
83
84 if ( empty( $this->data ) ) {
85 $force_update = true;
86 }
87
88 $this->maybe_update_ip();
89 $this->maybe_update_locations();
90 $this->maybe_update_device();
91
92 if ( true == $force_update ) { //phpcs:ignore
93 $this->update_order_info();
94 }
95
96 add_action( 'wp', [ $this, 'maybe_update_data' ], 9999 );
97 }
98
99 /**
100 * If updating is required start to update.
101 *
102 * @since 1.1.0
103 */
104 public function maybe_update_data() {
105 if ( empty( self::$new_data ) ) {
106 return;
107 }
108
109 if ( ! is_user_logged_in() && is_array( self::$new_data ) ) {
110 setcookie( 'sellkit_contact_segmentation', wp_json_encode( array_merge( Contact_Data::$historical_data, self::$new_data ) ), 2147483647, '/' );
111 return;
112 }
113
114 if ( empty( Contact_Data::$historical_data ) && ! empty( self::$new_data ) ) {
115 self::$new_data['email'] = Contact_Data::get_user_email();
116
117 sellkit()->db->insert( 'contact_segmentation', self::$new_data );
118
119 update_user_meta( get_current_user_id(), self::SELLKIT_CONTACT_SEGMENTATION_FORCE_UPDATE, false );
120
121 return;
122 }
123
124 self::update_contact_segmentation( self::$new_data, Contact_Data::get_user_email() );
125 }
126
127 /**
128 * Get the class instance.
129 *
130 * @since 1.1.0
131 */
132 public static function get_instance() {
133 if ( null === self::$instance ) {
134 self::$instance = new self();
135 }
136
137 return self::$instance;
138 }
139
140 /**
141 * Update utm data.
142 *
143 * @since 1.1.0
144 */
145 private function update_utm_data() {
146 $utm_source = sellkit_htmlspecialchars( INPUT_GET, 'utm_source' );
147 $utm_campaign = sellkit_htmlspecialchars( INPUT_GET, 'utm_campaign' );
148 $utm_medium = sellkit_htmlspecialchars( INPUT_GET, 'utm_medium' );
149 $utm_content = sellkit_htmlspecialchars( INPUT_GET, 'utm_content' );
150 $utm_term = sellkit_htmlspecialchars( INPUT_GET, 'utm_term' );
151
152 if ( $this->check_utm_is_new( $utm_source, 'utm_source' ) ) {
153 self::$new_data['utm_source'] = $utm_source;
154 }
155
156 if ( $this->check_utm_is_new( $utm_campaign, 'utm_campaign' ) ) {
157 self::$new_data['utm_campaign'] = $utm_campaign;
158 }
159
160 if ( $this->check_utm_is_new( $utm_content, 'utm_content' ) ) {
161 self::$new_data['utm_content'] = $utm_content;
162 }
163
164 if ( $this->check_utm_is_new( $utm_term, 'utm_term' ) ) {
165 self::$new_data['utm_term'] = $utm_term;
166 }
167
168 if ( $this->check_utm_is_new( $utm_medium, 'utm_medium' ) ) {
169 self::$new_data['utm_medium'] = $utm_medium;
170 }
171 }
172
173 /**
174 * Update locations data from API.
175 *
176 * @since 1.1.0
177 */
178 private function maybe_update_locations() {
179 $ip = sellkit_get_ip();
180
181 if ( ! empty( $this->data['ip'] ) && $ip === $this->data['ip'] ) {
182 return;
183 }
184
185 $response = wp_remote_get( 'https://location.stg.growmatik.ai/v1', [
186 'timeout' => 10,
187 'body' => [
188 'apiKey' => self::SELLKIT_CONTACT_SEGMENTATION_LOCATION_API,
189 'ip' => $ip,
190 ],
191 ] );
192
193 $response_code = wp_remote_retrieve_response_code( $response );
194
195 if ( ! is_wp_error( $response ) && ! empty( $response['body'] ) && 200 === (int) $response_code ) {
196 $location_data = json_decode( $response['body'] );
197 self::$new_data['visitor_country'] = strtolower( $location_data->country );
198 self::$new_data['visitor_city'] = strtolower( $location_data->city );
199 }
200
201 self::$new_data['updated_at'] = time();
202 }
203
204 /**
205 * Updates device types.
206 *
207 * @since 1.1.0
208 */
209 private function maybe_update_device() {
210 $detector = new Mobile_Detect();
211 $current_display = 'desktop';
212
213 if ( $detector->isMobile() ) {
214 $current_display = 'mobile';
215 }
216
217 if ( $detector->isTablet() ) {
218 $current_display = 'tablet';
219 }
220
221 if ( ! empty( $this->data['user_device'] ) && $this->data['user_device'] === $current_display ) {
222 return;
223 }
224
225 self::$new_data['user_device'] = $current_display;
226 }
227
228 /**
229 * Update browser language.
230 *
231 * @since 1.1.0
232 */
233 private function update_browser_language() {
234 $data = [];
235
236 if ( ! empty( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ) {
237 $data = explode( ',', $_SERVER['HTTP_ACCEPT_LANGUAGE'] ); // phpcs:ignore
238 }
239
240 $language = ! empty( $data[0] ) ? $data[0] : '';
241
242 if ( empty( $language ) ) {
243 return;
244 }
245
246 if ( empty( $this->data['browser_language'] ) || ( $this->data['browser_language'] !== $language ) ) {
247 self::$new_data['browser_language'] = $language;
248 }
249 }
250
251 /**
252 * Updates the IP if it's changed.
253 *
254 * @since 1.2.1
255 */
256 private function maybe_update_ip() {
257 $ip = sellkit_get_ip();
258
259 if ( ! empty( $this->data['ip'] ) && $this->data['ip'] === $ip ) {
260 return;
261 }
262
263 self::$new_data['ip'] = $ip;
264 }
265
266 /**
267 * Checks utm value.
268 *
269 * @param string $new_utm New Utm.
270 * @param string $type Utm type.
271 * @return bool
272 */
273 private function check_utm_is_new( $new_utm, $type ) {
274 if ( empty( $new_utm ) ) {
275 return false;
276 }
277
278 if ( empty( $this->data[ $type ] ) ) {
279 return true;
280 }
281
282 if ( $new_utm !== $this->data[ $type ] ) {
283 return true;
284 }
285 }
286
287 /**
288 * Updates user information.
289 *
290 * @since 1.1.0
291 */
292 public function update_order_info() {
293 $current_user = wp_get_current_user();
294
295 $args = [
296 'customer_id' => $current_user->ID,
297 'post_status' => 'completed',
298 'post_type' => 'shop_order',
299 'limit' => -1,
300 'orderby' => 'id',
301 'order' => 'DESC',
302 ];
303
304 $orders = wc_get_orders( $args );
305 $order_number = count( $orders );
306 $total_spent = 0;
307 $first_order = ! empty( end( $orders ) ) ? end( $orders ) : '';
308 $last_order = ! empty( $orders[0] ) ? $orders[0] : '';
309 $purchased_products = [];
310 $purchased_categories = [];
311 $billing_countries = [];
312 $billing_cities = [];
313 $shipping_countries = [];
314 $shipping_cities = [];
315
316 foreach ( $orders as $order ) {
317 $total_spent += $order->get_total();
318 $billing_countries[] = $order->get_billing_country();
319 $billing_cities[] = $order->get_billing_city();
320 $shipping_countries[] = $order->get_shipping_country();
321 $shipping_cities[] = $order->get_shipping_city();
322
323 foreach ( $order->get_items() as $item ) {
324 $purchased_products[] = $item['product_id'];
325
326 $term_list = wp_get_post_terms( $item['product_id'], 'product_cat', [ 'fields' => 'ids' ] );
327 $purchased_categories = array_merge( $purchased_categories, $term_list );
328 }
329 }
330
331 $first_order_date = $first_order ? strtotime( $first_order->get_date_completed()->date( 'Y-m-d H:i:s' ) ) : '';
332 $last_order_date = $last_order ? strtotime( $last_order->get_date_completed()->date( 'Y-m-d H:i:s' ) ) : '';
333
334 self::$new_data['total_order_count'] = $order_number;
335 self::$new_data['total_spent'] = $total_spent;
336 self::$new_data['first_order_date'] = $first_order_date;
337 self::$new_data['last_order_date'] = $last_order_date;
338 self::$new_data['purchased_product'] = array_unique( $purchased_products );
339 self::$new_data['billing_country'] = array_unique( $billing_countries );
340 self::$new_data['billing_city'] = array_unique( $billing_cities );
341 self::$new_data['shipping_country'] = array_unique( $shipping_countries );
342 self::$new_data['shipping_city'] = array_unique( $shipping_cities );
343 self::$new_data['purchased_category'] = array_unique( $purchased_categories );
344
345 if ( $order_number > 0 ) {
346 self::$new_data['user_type'] = 'customer';
347 return;
348 }
349
350 self::$new_data['user_type'] = 'lead';
351 }
352
353 /**
354 * Force update data.
355 *
356 * @since 1.1.0
357 * @param string $order_id Order id.
358 */
359 public static function force_update_data( $order_id ) {
360 $user_id = get_post_meta( $order_id, '_customer_user', true );
361
362 if ( empty( $user_id ) ) {
363 $order = wc_get_order( $order_id );
364 $user = $order->get_billing_email();
365 $user_id = email_exists( $user );
366 }
367
368 if ( ! empty( $user_id ) ) {
369 update_user_meta( $user_id, self::SELLKIT_CONTACT_SEGMENTATION_FORCE_UPDATE, true );
370 }
371 }
372
373 /**
374 * Update viewed products. it should be done in wp hook so we have to update the data directly.
375 * it would be better to update these data with the main updating process.
376 *
377 * @since 1.1.0
378 */
379 public function update_viewed_details() {
380 $old_data = Contact_Data::$historical_data;
381
382 if ( is_singular( 'product' ) ) {
383 $product_id = get_the_ID();
384 $new_products = [];
385
386 if ( ! empty( $old_data['viewed_product'] ) ) {
387 $new_products = $old_data['viewed_product'];
388 }
389
390 if ( empty( $old_data['viewed_product'] ) || ! in_array( $product_id, $old_data['viewed_product'], false ) ) { // phpcs:ignore
391 self::$new_data['viewed_product'] = array_unique( array_merge( [ $product_id ], $new_products ) );
392 }
393 }
394
395 $queries = get_queried_object();
396 if ( ! empty( $queries->taxonomy ) && 'product_cat' === $queries->taxonomy ) {
397 $current_cat_id = $queries->term_id;
398 $new_cats = [];
399
400 if ( ! empty( $old_data['viewed_category'] ) ) {
401 $new_cats = $old_data['viewed_category'];
402 }
403
404 if ( empty( $old_data['viewed_category'] ) || ! in_array( $current_cat_id, $old_data['viewed_category'], false ) ) { // phpcs:ignore
405 self::$new_data['viewed_category'] = array_unique( array_merge( [ $current_cat_id ], $new_cats ) );
406 }
407 }
408 }
409
410 /**
411 * Updates contact segmentation data.
412 *
413 * @since 1.1.0
414 * @param array $data Data.
415 * @param string $email Email.
416 */
417 public static function update_contact_segmentation( $data, $email ) {
418 sellkit()->db->update( 'contact_segmentation', $data, [ 'email' => $email ] );
419
420 update_user_meta( get_current_user_id(), self::SELLKIT_CONTACT_SEGMENTATION_FORCE_UPDATE, false );
421 }
422 }
423