PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 1.1.5
MxChat – AI Chatbot & Content Generation for WordPress v1.1.5
3.2.21 3.2.20 3.2.19 3.2.18 3.2.17 3.2.16 3.2.15 3.2.14 3.2.12 3.2.13 3.2.11 3.2.10 3.2.9 3.2.8 3.2.7 3.2.6 3.2.5 3.2.4 3.2.3 3.2.2 3.2.1 2.0.3 2.0.4 2.0.5 2.0.6 All 152 releases
mxchat-basic / includes / class-mxchat-integrator.php

class-mxchat-integrator.php in MxChat – AI Chatbot & Content Generation for WordPress 1.1.5, at includes/class-mxchat-integrator.php

657 lines 23.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if (!defined('ABSPATH')) {
3 exit;
4 }
5
6 class MxChat_Integrator {
7 private $options;
8 private $chat_count;
9
10 public function __construct() {
11 $this->options = get_option('mxchat_options');
12 $this->chat_count = get_option('mxchat_chat_count', 0);
13
14 // Add WooCommerce hooks
15 add_action('wp_insert_post', array($this, 'mxchat_handle_product_change'), 10, 3);
16
17 // Ensure embeddings are removed when a product is moved to trash or permanently deleted
18 add_action('wp_trash_post', array($this, 'mxchat_handle_product_delete'));
19 add_action('before_delete_post', array($this, 'mxchat_handle_product_delete'));
20
21 add_action('wp_enqueue_scripts', array($this, 'mxchat_enqueue_scripts_styles'));
22 add_action('wp_ajax_mxchat_handle_chat_request', array($this, 'mxchat_handle_chat_request'));
23 add_action('wp_ajax_nopriv_mxchat_handle_chat_request', array($this, 'mxchat_handle_chat_request'));
24 add_action('wp_ajax_mxchat_dismiss_pre_chat_message', array($this, 'mxchat_dismiss_pre_chat_message'));
25 add_action('wp_ajax_nopriv_mxchat_dismiss_pre_chat_message', array($this, 'mxchat_dismiss_pre_chat_message'));
26
27 if (!wp_next_scheduled('mxchat_reset_rate_limits')) {
28 wp_schedule_event(time(), 'daily', 'mxchat_reset_rate_limits');
29 }
30
31 add_action('mxchat_reset_rate_limits', array($this, 'mxchat_reset_rate_limits'));
32 }
33
34 public function mxchat_handle_product_change($post_id, $post, $update) {
35 // Ensure this is a product post type
36 if ($post->post_type !== 'product') {
37 return;
38 }
39
40 // Only generate embeddings if the product is published
41 if ($post->post_status === 'publish') {
42 // Delay the embedding slightly to ensure all product data is available
43 add_action('shutdown', function() use ($post_id) {
44 $product = wc_get_product($post_id);
45 if ($product && $product->get_price() !== '') {
46 $this->mxchat_store_product_embedding($product);
47 } else {
48 // Optionally, log or handle the case where product data is incomplete
49 error_log("Product {$post_id} does not have complete data. Embedding not generated.");
50 }
51 });
52 }
53 }
54
55 public function mxchat_handle_product_delete($post_id) {
56 if (get_post_type($post_id) !== 'product') {
57 return;
58 }
59
60 global $wpdb;
61 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
62
63 // Delete the embedding associated with this product
64 $wpdb->delete($table_name, array('source_url' => get_permalink($post_id)), array('%s'));
65 }
66
67 private function mxchat_store_product_embedding($product) {
68 if (isset($this->options['enable_woocommerce_integration']) && $this->options['enable_woocommerce_integration'] === '1') {
69
70 $source_url = get_permalink($product->get_id());
71 $regular_price = $product->get_regular_price();
72 $sale_price = $product->get_sale_price();
73 $price = $sale_price ?: $regular_price;
74
75 $description = $product->get_description() . "\n\n" .
76 "Short Description: " . $product->get_short_description() . "\n" .
77 "Price: " . $regular_price . "\n" .
78 "Sale Price: " . ($sale_price ?: 'N/A') . "\n" .
79 "SKU: " . $product->get_sku();
80
81 global $wpdb;
82 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
83
84 // Delete any existing embedding for this product
85 $wpdb->delete($table_name, array('source_url' => $source_url), array('%s'));
86
87 // Submit the new content and embedding to the database
88 MxChat_Utils::submit_content_to_db($description, $source_url, $this->options['api_key']);
89 }
90 }
91
92
93
94
95
96 private function mxchat_increment_chat_count() {
97 $chat_count = get_option('mxchat_chat_count', 0);
98 $chat_count++;
99 update_option('mxchat_chat_count', $chat_count);
100 }
101
102 public function mxchat_fetch_conversation_history_for_ajax($session_id) {
103 global $wpdb;
104 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
105
106 // Prepare and execute the query safely
107 $chat_transcripts = $wpdb->get_results(
108 $wpdb->prepare("SELECT * FROM $table_name WHERE session_id = %s ORDER BY timestamp ASC", sanitize_text_field($session_id))
109 );
110
111 // Check if results are empty
112 if (empty($chat_transcripts)) {
113 return [];
114 }
115
116 // Build the conversation history
117 $conversation_history = [];
118 foreach ($chat_transcripts as $transcript) {
119 $conversation_history[] = [
120 'role' => $transcript->role,
121 'content' => $transcript->message
122 ];
123 }
124
125 return $conversation_history;
126 }
127
128
129 private function mxchat_save_chat_message($session_id, $role, $message) {
130 global $wpdb;
131 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
132
133 $user_id = is_user_logged_in() ? get_current_user_id() : 0;
134 $user_identifier = MxChat_User::mxchat_get_user_identifier();
135 $user_email = MxChat_User::mxchat_get_user_email();
136
137 $wpdb->insert($table_name, [
138 'user_id' => $user_id,
139 'user_identifier' => $user_identifier,
140 'user_email' => $user_email,
141 'session_id' => $session_id,
142 'role' => $role,
143 'message' => $message,
144 'timestamp' => current_time('mysql', 1)
145 ]);
146 }
147
148 public function mxchat_handle_chat_request() {
149 global $wpdb;
150
151 // Get and sanitize the user identifier
152 $user_id = $this->mxchat_get_user_identifier();
153 $user_id = sanitize_key($user_id);
154
155 // Setup rate limiting
156 $rate_limit_transient_key = 'mxchat_chat_limit_' . $user_id;
157 $chat_count = get_transient($rate_limit_transient_key) ?: 0;
158 $session_transient_key = 'mxchat_chat_session_' . $user_id;
159 $session_id = get_transient($session_transient_key) ?: uniqid('mxchat_chat_', true);
160
161 set_transient($session_transient_key, $session_id, DAY_IN_SECONDS);
162
163 // Check rate limit
164 $rate_limit_option = $this->options['rate_limit'] ?? 'unlimited';
165 if ($rate_limit_option !== 'unlimited' && $chat_count >= intval($rate_limit_option)) {
166 wp_send_json_error(array('message' => $this->options['rate_limit_message'] ?? 'Rate limit exceeded. Please try again later.'));
167 wp_die();
168 }
169 set_transient($rate_limit_transient_key, $chat_count + 1, DAY_IN_SECONDS);
170
171 // Validate and sanitize the incoming message
172 if (empty($_POST['message'])) {
173 wp_send_json_error('No message received');
174 wp_die();
175 }
176
177 $message = sanitize_text_field($_POST['message']);
178 $this->mxchat_save_chat_message($session_id, 'user', $message);
179
180 // Track email capture and WooCommerce flows with individual transients
181 $email_capture_prompt = get_transient('mxchat_email_capture_' . $user_id);
182 $interaction_count = get_transient('mxchat_email_interaction_count_' . $user_id) ?: 0;
183 $woocommerce_prompt = get_transient('mxchat_woocommerce_prompt_' . $user_id);
184
185 // Handle email capture flow
186 if ($email_capture_prompt) {
187 if (preg_match('/[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}/i', $message, $matches)) {
188 $email = $matches[0];
189
190 $this->add_email_to_loops($email);
191 $response = $this->options['email_capture_response'] ?? 'Thank you for providing your email! You\'ve been added to our list.';
192
193 delete_transient('mxchat_email_capture_' . $user_id);
194 delete_transient('mxchat_email_interaction_count_' . $user_id);
195 delete_transient('mxchat_woocommerce_prompt_' . $user_id);
196
197 $this->mxchat_save_chat_message($session_id, 'bot', $response);
198 wp_send_json(['message' => $response]);
199 wp_die();
200 } else {
201 if ($interaction_count >= 3) {
202 delete_transient('mxchat_email_capture_' . $user_id);
203 delete_transient('mxchat_email_interaction_count_' . $user_id);
204 // Allow the conversation to continue normally without forcing email capture
205 } else {
206 set_transient('mxchat_email_interaction_count_' . $user_id, ++$interaction_count, 5 * MINUTE_IN_SECONDS);
207 }
208 }
209 }
210
211 // Handle WooCommerce add-to-cart flow
212 if (class_exists('WooCommerce') && stripos($message, 'add to cart') !== false) {
213 $last_product_id = get_transient('mxchat_last_discussed_product_' . $user_id);
214
215 if ($last_product_id) {
216 $added = WC()->cart->add_to_cart($last_product_id);
217 $product = wc_get_product($last_product_id);
218
219 if ($added) {
220 $response = "The product '{$product->get_name()}' has been added to your cart. To proceed to checkout, please type 'checkout'.";
221 set_transient('mxchat_checkout_prompt_' . $user_id, true, 5 * MINUTE_IN_SECONDS);
222
223 delete_transient('mxchat_email_capture_' . $user_id); // Clear email capture state to switch to cart flow
224 $this->mxchat_save_chat_message($session_id, 'bot', $response);
225 wp_send_json(['message' => $response]);
226 wp_die();
227 } else {
228 $response = "Sorry, I couldn't add the product to your cart. Please try again.";
229 $this->mxchat_save_chat_message($session_id, 'bot', $response);
230 wp_send_json(['message' => $response]);
231 wp_die();
232 }
233 } else {
234 $response = "I couldn't find the product to add. Please mention the product name again.";
235 $this->mxchat_save_chat_message($session_id, 'bot', $response);
236 wp_send_json(['message' => $response]);
237 wp_die();
238 }
239 }
240
241 // Handle checkout response
242 if (class_exists('WooCommerce') && stripos($message, 'checkout') !== false) {
243 $checkout_prompt = get_transient('mxchat_checkout_prompt_' . $user_id);
244 if ($checkout_prompt && WC()->cart->get_cart_contents_count() > 0) {
245 $checkout_url = wc_get_checkout_url();
246 $response = "Great! Redirecting you to the checkout page...";
247 delete_transient('mxchat_checkout_prompt_' . $user_id);
248
249 $this->mxchat_save_chat_message($session_id, 'bot', $response);
250 wp_send_json(['message' => $response, 'redirect_url' => $checkout_url]);
251 wp_die();
252 } else {
253 $response = "It seems like there is no active checkout prompt or no items in your cart. Please add a product to the cart first.";
254 $this->mxchat_save_chat_message($session_id, 'bot', $response);
255 wp_send_json(['message' => $response]);
256 wp_die();
257 }
258 }
259
260
261 // Check for trigger keywords to initiate email capture
262 $trigger_keywords = explode(',', $this->options['trigger_keywords'] ?? '');
263
264 // Ensure there are actual keywords defined
265 if (!empty($trigger_keywords) && $trigger_keywords[0] !== '') {
266 foreach ($trigger_keywords as $keyword) {
267 if (stripos($message, trim($keyword)) !== false) {
268 $response = $this->options['triggered_phrase_response'] ?? "Would you like to join our mailing list? Please provide your email below.";
269 set_transient('mxchat_email_capture_' . $user_id, true, 5 * MINUTE_IN_SECONDS);
270
271 $this->mxchat_save_chat_message($session_id, 'bot', $response);
272 wp_send_json(['message' => $response]);
273 wp_die();
274 }
275 }
276 }
277
278 // Store product discussion in transient
279 $last_discussed_product_id = MxChat_WooCommerce::mxchat_extract_product_id_from_message($message);
280 if ($last_discussed_product_id) {
281 set_transient('mxchat_last_discussed_product_' . $user_id, $last_discussed_product_id, 3600); // Cache for 1 hour
282 }
283
284 // Standard chat processing
285 $user_message_embedding = $this->mxchat_generate_embedding($message, $this->options['api_key']);
286 if (!is_array($user_message_embedding)) {
287 wp_send_json_error('Error processing your message.');
288 wp_die();
289 }
290
291 $relevant_content = $this->mxchat_find_relevant_content($user_message_embedding);
292 $conversation_history = $this->mxchat_fetch_conversation_history_for_ajax($session_id);
293 $this->mxchat_increment_chat_count();
294 $response = $this->mxchat_generate_response($relevant_content, $this->options['api_key'], $conversation_history);
295
296 $this->mxchat_save_chat_message($session_id, 'bot', $response);
297 wp_send_json(['message' => $response]);
298 wp_die();
299 }
300
301
302 // Function to add the captured email to Loops
303 private function add_email_to_loops($email) {
304 $api_key = $this->options['loops_api_key'];
305 $mailing_list_id = $this->options['loops_mailing_list'];
306
307 $data = array(
308 'email' => $email,
309 'subscribed' => true,
310 'source' => 'MxChat AI Chatbot',
311 'mailingLists' => array($mailing_list_id => true),
312 );
313
314 $url = "https://app.loops.so/api/v1/contacts/create";
315 $args = array(
316 'body' => json_encode($data),
317 'headers' => array(
318 'Authorization' => 'Bearer ' . $api_key,
319 'Content-Type' => 'application/json',
320 ),
321 'method' => 'POST',
322 'timeout' => 45,
323 );
324
325 wp_remote_post($url, $args);
326 }
327
328
329 private function mxchat_get_user_identifier() {
330 return MxChat_User::mxchat_get_user_identifier();
331 }
332
333
334
335 private function mxchat_generate_embedding($text, $api_key) {
336 $endpoint = 'https://api.openai.com/v1/embeddings';
337
338 $body = wp_json_encode([
339 'input' => $text,
340 'model' => 'text-embedding-ada-002'
341 ]);
342
343 $args = [
344 'body' => $body,
345 'headers' => [
346 'Content-Type' => 'application/json',
347 'Authorization' => 'Bearer ' . $api_key,
348 ],
349 'timeout' => 60,
350 'redirection' => 5,
351 'blocking' => true,
352 'httpversion' => '1.0',
353 'sslverify' => true,
354 ];
355
356 $response = wp_remote_post($endpoint, $args);
357
358 if (is_wp_error($response)) {
359 return null;
360 }
361
362 $response_body = json_decode(wp_remote_retrieve_body($response), true);
363
364 if (isset($response_body['data'][0]['embedding']) && is_array($response_body['data'][0]['embedding'])) {
365 return $response_body['data'][0]['embedding'];
366 } else {
367 return null;
368 }
369 }
370
371 private function mxchat_find_relevant_content($user_embedding) {
372 global $wpdb;
373 $system_prompt_table = $wpdb->prefix . 'mxchat_system_prompt_content';
374
375 // Define a cache key for embeddings
376 $cache_key = 'mxchat_system_prompt_embeddings';
377
378 // Attempt to get the embeddings from the cache
379 $embeddings = wp_cache_get($cache_key, 'mxchat_system_prompts');
380
381 if ($embeddings === false) {
382 // Cache miss, query the database and cache the results
383 $query = "SELECT id, embedding_vector FROM {$system_prompt_table}";
384 $embeddings = $wpdb->get_results($query);
385
386 if ($embeddings === null || empty($embeddings)) {
387 error_log("No embeddings found in the database.");
388 return null; // Return null to handle no embeddings gracefully
389 }
390
391 // Cache the results if successful
392 wp_cache_set($cache_key, $embeddings, 'mxchat_system_prompts', 3600); // Cache for 1 hour
393 }
394
395 $most_relevant_id = null;
396 $highest_similarity = -INF;
397
398 foreach ($embeddings as $embedding) {
399 $database_embedding = maybe_unserialize($embedding->embedding_vector);
400
401 // Debugging: Log the embeddings
402 // if (!is_array($database_embedding)) {
403 // error_log("Invalid database embedding format for ID {$embedding->id}: " . print_r($database_embedding, true));
404 // continue;
405 // }
406
407 if (is_array($user_embedding)) {
408 $similarity = $this->mxchat_calculate_cosine_similarity($user_embedding, $database_embedding);
409
410 // Debugging: Log the similarity score
411 // error_log("Calculated similarity for ID {$embedding->id}: {$similarity}");
412
413 if ($similarity > $highest_similarity) {
414 $highest_similarity = $similarity;
415 $most_relevant_id = $embedding->id;
416 }
417 } else {
418 // error_log("User embedding is not an array. Embedding data: " . print_r($user_embedding, true));
419 }
420 }
421
422 if ($most_relevant_id !== null) {
423 // Fetch content with product links
424 return $this->fetch_content_with_product_links($most_relevant_id);
425 }
426
427 error_log("No relevant content found. Most relevant ID was null.");
428 return null; // Return null if no relevant content is found
429 }
430
431
432 private function fetch_content_with_product_links($most_relevant_id) {
433 global $wpdb;
434 $system_prompt_table = $wpdb->prefix . 'mxchat_system_prompt_content';
435
436 // Fetch the article content and associated product URL
437 $query = $wpdb->prepare("SELECT article_content, source_url FROM {$system_prompt_table} WHERE id = %d", $most_relevant_id);
438 $result = $wpdb->get_row($query);
439
440 if ($result) {
441 // Append the product link to the content if available
442 $content = $result->article_content;
443 if (!empty($result->source_url)) {
444 $content .= "\n\nFor more details, check out this product: " . esc_url($result->source_url);
445 }
446 return $content;
447 }
448
449 return null;
450 }
451
452
453 private function mxchat_generate_response($relevant_content, $api_key, $conversation_history) {
454 if (!$relevant_content) {
455 return "I'm sorry, I couldn't find relevant information on that topic.";
456 }
457
458 $system_prompt_instructions = isset($this->options['system_prompt_instructions']) ? $this->options['system_prompt_instructions'] : '';
459
460 $content_with_instructions = $system_prompt_instructions . " " . $relevant_content;
461
462 array_unshift($conversation_history, [
463 'role' => 'system',
464 'content' => "Here are your instructions: " . $content_with_instructions
465 ]);
466
467 foreach ($conversation_history as &$message) {
468 if ($message['role'] === 'bot') {
469 $message['role'] = 'assistant';
470 }
471 }
472
473 $api_url = 'https://api.openai.com/v1/chat/completions';
474
475 $body = json_encode([
476 'model' => isset($this->options['model']) ? $this->options['model'] : 'gpt-3.5',
477 'messages' => $conversation_history,
478 ]);
479
480 $args = [
481 'body' => $body,
482 'headers' => [
483 'Content-Type' => 'application/json',
484 'Authorization' => 'Bearer ' . $api_key,
485 ],
486 'timeout' => 60,
487 'redirection' => 5,
488 'blocking' => true,
489 'httpversion' => '1.0',
490 'sslverify' => true,
491 ];
492
493 $response = wp_remote_post($api_url, $args);
494
495 if (is_wp_error($response)) {
496 return "Sorry, there was an error processing your request.";
497 }
498
499 $response_body = json_decode(wp_remote_retrieve_body($response), true);
500
501 if (isset($response_body['choices'][0]['message']['content'])) {
502 if (isset($response_body['usage'])) {
503 $prompt_tokens = $response_body['usage']['prompt_tokens'];
504 $total_tokens = $response_body['usage']['total_tokens'];
505 }
506 return trim($response_body['choices'][0]['message']['content']);
507 } else {
508 return "Sorry, I couldn't process that request.";
509 }
510 }
511
512
513 public function mxchat_dismiss_pre_chat_message() {
514 // Get and sanitize the user identifier
515 $user_id = $this->mxchat_get_user_identifier();
516 $user_id = sanitize_key($user_id);
517
518 // Set a transient to track that the user has dismissed the pre-chat message
519 $transient_key = 'mxchat_pre_chat_message_dismissed_' . $user_id;
520 set_transient($transient_key, true, DAY_IN_SECONDS);
521
522 wp_send_json_success();
523 }
524
525
526
527 private function mxchat_calculate_cosine_similarity($vectorA, $vectorB) {
528 if (!is_array($vectorA) || !is_array($vectorB) || empty($vectorA) || empty($vectorB)) {
529 return 0;
530 }
531
532 $dotProduct = array_sum(array_map(function ($a, $b) {
533 return $a * $b;
534 }, $vectorA, $vectorB));
535 $normA = sqrt(array_sum(array_map(function ($a) {
536 return $a * $a;
537 }, $vectorA)));
538 $normB = sqrt(array_sum(array_map(function ($b) {
539 return $b * $b;
540 }, $vectorB)));
541
542 if ($normA == 0 || $normB == 0) {
543 return 0;
544 }
545
546 return $dotProduct / ($normA * $normB);
547 }
548
549 public function mxchat_enqueue_scripts_styles() {
550 // Define version numbers for the styles and scripts
551 $chat_style_version = '1.1.5'; // Replace with your actual version
552 $chat_script_version = '1.1.5'; // Replace with your actual version
553
554 // Correct path to the script file
555 wp_enqueue_script(
556 'mxchat-chat-js', // Handle for the script
557 plugin_dir_url(__FILE__) . '../js/chat-script.js', // Correct path using __FILE__
558 array('jquery'), // Dependencies
559 $chat_script_version, // Version for cache busting
560 true // Load script in footer
561 );
562
563 // Enqueue the CSS file similarly
564 wp_enqueue_style(
565 'mxchat-chat-css', // Handle for the style
566 plugin_dir_url(__FILE__) . '../css/chat-style.css', // Correct path using __FILE__
567 array(), // No dependencies
568 $chat_style_version // Version for cache busting
569 );
570
571 // Fetch options from the database
572 $this->options = get_option('mxchat_options');
573
574 // Prepare settings to pass to JavaScript
575 $style_settings = array(
576 'ajax_url' => admin_url('admin-ajax.php'),
577 'nonce' => wp_create_nonce('mxchat_chat_nonce'), // Nonce for security
578 'rate_limit_message' => $this->options['rate_limit_message'] ?? 'Rate limit exceeded. Please try again later.',
579 'appendWidgetToBody' => $this->options['append_to_body'] ?? 'off'
580 );
581
582 // Localize the script with necessary data
583 wp_localize_script('mxchat-chat-js', 'mxchatChat', $style_settings);
584 }
585
586
587
588 public function mxchat_reset_rate_limits() {
589 global $wpdb;
590
591 // Define a cache key pattern for rate limits
592 $cache_key_pattern = 'mxchat_chat_limit_%';
593
594 // Retrieve all option names matching the pattern
595 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
596 $option_names = $wpdb->get_col("SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE 'mxchat_chat_limit_%'");
597
598 // db call ok; no-cache ok
599 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- db call ok
600 $wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE 'mxchat_chat_limit_%'");
601
602 // Clear the relevant cache entries
603 foreach ($option_names as $option_name) {
604 wp_cache_delete($option_name, 'options');
605 }
606
607 // Optionally, clear a general cache if you have one
608 wp_cache_delete('mxchat_all_chat_limits', 'options');
609 }
610
611
612 private function mxchat_fetch_woocommerce_products() {
613 // Ensure WooCommerce is active
614 if (!class_exists('WooCommerce')) {
615 return [];
616 }
617
618 $args = array(
619 'post_type' => 'product',
620 'post_status' => 'publish',
621 'posts_per_page' => -1,
622 );
623
624 $products = get_posts($args);
625 $product_data = [];
626
627 foreach ($products as $product) {
628 $product_id = $product->ID;
629 $product_obj = wc_get_product($product_id);
630
631 $product_data[] = array(
632 'id' => $product_id,
633 'name' => $product_obj->get_name(),
634 'description' => $product_obj->get_description(),
635 'short_description' => $product_obj->get_short_description(),
636 'url' => get_permalink($product_id),
637 'price' => $product_obj->get_regular_price(),
638 'sale_price' => $product_obj->get_sale_price(),
639 'stock_status' => $product_obj->get_stock_status(),
640 'sku' => $product_obj->get_sku(),
641 'in_stock' => $product_obj->is_in_stock(),
642 'total_sales' => $product_obj->get_total_sales(),
643 );
644 }
645
646 return $product_data;
647 }
648
649
650
651
652
653
654
655 }
656 ?>
657