PluginProbe
MStore API – Create Native Android & iOS Apps On The Cloud / 4.21.2
MStore API – Create Native Android & iOS Apps On The Cloud v4.21.2
4.21.3 4.21.2 4.21.1 trunk 1.1.5 2.9.4 2.9.5 2.9.6 2.9.7 2.9.8 2.9.9 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 3.1.2 3.1.3 All 192 releases
mstore-api / mstore-api.php

mstore-api.php in MStore API – Create Native Android & iOS Apps On The Cloud 4.21.2, at mstore-api.php

1,846 lines 73.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Plugin Name: MStore API
4 * Plugin URI: https://github.com/inspireui/mstore-api
5 * Description: The MStore API Plugin which is used for the FluxBuilder and FluxStore Mobile App
6 * Version: 4.21.2
7 * Author: FluxBuilder
8 * Author URI: https://fluxbuilder.com
9 *
10 * Text Domain: MStore-Api
11 */
12
13 defined('ABSPATH') or wp_die('No script kiddies please!');
14
15
16 // use MStoreCheckout\Templates\MDetect;
17
18 include plugin_dir_path(__FILE__) . "templates/class-mobile-detect.php";
19 include plugin_dir_path(__FILE__) . "templates/class-rename-generate.php";
20
21 // ---------------------------------------------------------------------------
22 // Always-loaded files: helpers, side-effect controllers, and the listing-REST
23 // API controller (which registers 'init' hooks that must fire before rest_api_init).
24 // ---------------------------------------------------------------------------
25 include_once plugin_dir_path(__FILE__) . "functions/index.php";
26 include_once plugin_dir_path(__FILE__) . "functions/utils.php";
27 // Guarded: when a release is packaged without this file the bare include_once
28 // emits warnings that get prepended to every REST response and break the JSON.
29 // The two callers below fall back gracefully when the class is absent.
30 if (file_exists(plugin_dir_path(__FILE__) . "controllers/helpers/icon-helper.php")) {
31 include_once plugin_dir_path(__FILE__) . "controllers/helpers/icon-helper.php";
32 }
33 include_once plugin_dir_path(__FILE__) . "controllers/listing-rest-api/class.api.fields.php";
34 include_once plugin_dir_path(__FILE__) . "controllers/helpers/firebase-message-helper.php";
35 include_once plugin_dir_path(__FILE__) . "controllers/helpers/firebase-phone-auth-helper.php";
36 include_once plugin_dir_path(__FILE__) . "controllers/helpers/pure-taxonomies-helper.php";
37 include_once plugin_dir_path(__FILE__) . "controllers/helpers/fibosearch-woo-rest-integration.php";
38 // Side-effect controllers: register hooks that must fire on non-REST requests too.
39 include_once plugin_dir_path(__FILE__) . "controllers/flutter-review.php"; // duplicate_comment_id hook
40 include_once plugin_dir_path(__FILE__) . "controllers/flutter-points-offline-store.php"; // wc_points_rewards_event_description hook
41 include_once plugin_dir_path(__FILE__) . "controllers/flutter-smart-cod.php"; // woocommerce_order_after_calculate_totals hook
42
43 // ---------------------------------------------------------------------------
44 // Pure-REST controllers: deferred to rest_api_init (priority 0) so they are
45 // only loaded when an actual REST API request is being processed. The
46 // constructors inside each file call add_action('rest_api_init', ..., 10), which
47 // still fires within the same rest_api_init cycle because priority 10 > 0.
48 // ---------------------------------------------------------------------------
49 add_action('rest_api_init', 'mstore_load_rest_controllers', 0);
50 function mstore_load_rest_controllers()
51 {
52 $p = plugin_dir_path(__FILE__);
53 include_once $p . "controllers/flutter-app.php";
54 include_once $p . "controllers/flutter-user.php";
55 include_once $p . "controllers/flutter-home.php";
56 include_once $p . "controllers/flutter-booking.php";
57 include_once $p . "controllers/flutter-vendor-admin.php";
58 include_once $p . "controllers/flutter-woo.php";
59 include_once $p . "controllers/flutter-delivery.php";
60 include_once $p . "controllers/flutter-tera-wallet.php";
61 include_once $p . "controllers/flutter-paytm.php";
62 include_once $p . "controllers/flutter-paystack.php";
63 include_once $p . "controllers/flutter-flutterwave.php";
64 include_once $p . "controllers/flutter-myfatoorah.php";
65 include_once $p . "controllers/flutter-midtrans.php";
66 include_once $p . "controllers/flutter-modempay.php";
67 include_once $p . "controllers/flutter-paid-memberships-pro.php";
68 include_once $p . "controllers/flutter-blog.php";
69 include_once $p . "controllers/flutter-wholesale.php";
70 include_once $p . "controllers/flutter-stripe.php";
71 include_once $p . "controllers/flutter-notification.php";
72 include_once $p . "controllers/flutter-thawani.php";
73 include_once $p . "controllers/flutter-expresspay.php";
74 include_once $p . "controllers/flutter-2c2p.php";
75 include_once $p . "controllers/flutter-cc-avenue.php";
76 include_once $p . "controllers/flutter-flow-flow.php";
77 include_once $p . "controllers/flutter-store-locator.php";
78 include_once $p . "controllers/flutter-composite-products.php";
79 include_once $p . "controllers/flutter-b2bking.php";
80 include_once $p . "controllers/flutter-fib.php";
81 include_once $p . "controllers/flutter-auction.php";
82 include_once $p . "controllers/flutter-iyzico.php";
83 include_once $p . "controllers/flutter-phonepe.php";
84 include_once $p . "controllers/flutter-cashfree.php";
85 include_once $p . "controllers/flutter-discount-rules.php";
86 include_once $p . "controllers/flutter-checkout.php";
87 include_once $p . "controllers/flutter-razorpay.php";
88 include_once $p . "controllers/flutter-fibo-search.php";
89 include_once $p . "controllers/flutter-paypal.php";
90 include_once $p . "controllers/flutter-rental.php";
91 }
92
93 if ( is_readable( __DIR__ . '/vendor/autoload.php' ) ) {
94 require __DIR__ . '/vendor/autoload.php';
95 }
96
97 class MstoreCheckOut
98 {
99 public $version = '4.21.2';
100
101 public function __construct()
102 {
103 define('MSTORE_CHECKOUT_VERSION', $this->version);
104 define('MSTORE_PLUGIN_FILE', __FILE__);
105
106 /**
107 * Prepare data before checkout by webview
108 */
109 add_action('template_redirect', 'flutter_prepare_checkout');
110
111 add_action( 'pre_get_posts', 'set_author_in_for_vendor_staff' );
112
113 include_once(ABSPATH . 'wp-admin/includes/plugin.php');
114 //include_once(ABSPATH . 'wp-includes/pluggable.php');
115
116 // //migrate old versions to re-verify purchase code automatically
117 // verifyPurchaseCodeAuto();
118
119 add_filter( 'get_avatar_url', array( $this, 'filter_avatar' ), 10, 3 );
120
121 if (is_plugin_active('woocommerce/woocommerce.php') == false) {
122 return 0;
123 }
124 add_action('woocommerce_init', 'woocommerce_mstore_init');
125 function woocommerce_mstore_init()
126 {
127 include_once plugin_dir_path(__FILE__) . "controllers/flutter-order.php";
128 include_once plugin_dir_path(__FILE__) . "controllers/flutter-multi-vendor.php";
129 include_once plugin_dir_path(__FILE__) . "controllers/flutter-vendor.php";
130 include_once plugin_dir_path(__FILE__) . "controllers/helpers/delivery-wcfm-helper.php";
131 include_once plugin_dir_path(__FILE__) . "controllers/helpers/vendor-admin-woo-helper.php";
132 include_once plugin_dir_path(__FILE__) . "controllers/helpers/vendor-admin-wcfm-helper.php";
133 include_once plugin_dir_path(__FILE__) . "controllers/helpers/vendor-admin-dokan-helper.php";
134 include_once plugin_dir_path(__FILE__) . "controllers/flutter-customer.php";
135 include_once plugin_dir_path(__FILE__) . "functions/video-setting-embed.php";
136 }
137
138 add_filter('wp_rest_cache/allowed_endpoints', array($this, 'wprc_add_flutter_api_endpoints'));
139
140 $order = filter_has_var(INPUT_GET, 'code') && strlen(filter_input(INPUT_GET, 'code')) > 0 ? true : false;
141 if ($order) {
142 add_filter('woocommerce_is_checkout', '__return_true');
143 }
144
145 /*
146 add_filter( 'woocommerce_get_item_data', 'display_custom_product_field_data_mstore_api', 10, 2 );
147
148 function display_custom_product_field_data_mstore_api( $cart_data, $cart_item ) {
149
150 if( !empty( $cart_data ) ){
151 $custom_items = $cart_data;
152
153 $code = sanitize_text_field($_GET['code']) ?: get_transient( 'mstore_code' );
154 set_transient( 'mstore_code', $code, 600 );
155
156 global $wpdb;
157 $table_name = $wpdb->prefix . "mstore_checkout";
158 $item = $wpdb->get_row("SELECT * FROM $table_name WHERE code = '$code'");
159 if ($item) {
160 $data = json_decode(urldecode(base64_decode($item->order)), true);
161 $line_items = $data['line_items'];
162 $product_ids = [];
163 foreach($line_items as $line => $item) {
164 $product_ids[$item['product_id']] = $item;
165 }
166
167 if (array_key_exists($cart_item['product_id'], $product_ids)) {
168 if ($varian = $product_ids[$cart_item['product_id']]) {
169 $variations = $varian['meta_data'];
170 foreach($variations as $v => $f) {
171 preg_match('#\((.*?)\)#', $f['key'], $match);
172 $val = $match[1];
173 $custom_items[] = array(
174 'key' => $f['value'],
175 'value' => $val,
176 'display' => $val,
177 );
178 }
179 }
180 }
181 }
182
183 return $custom_items;
184 }
185 return $cart_data;
186 }
187
188
189 add_action( 'woocommerce_before_calculate_totals', 'add_custom_price_mstore_api' );
190
191 function add_custom_price_mstore_api( $cart_object ) {
192 foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
193 $add_price = 0;
194 if ($variations = $cart_item['variation']) {
195 foreach($variations as $v => $f) {
196 preg_match('#\((.*?)\)#', $v, $match);
197 if(is_array($match) && array_key_exists(1,$match)){
198 $val = $match[1];
199 $cents = filter_var($val, FILTER_SANITIZE_NUMBER_INT);
200 if(is_numeric($cents)){
201 $add_price += floatval($cents / 100);
202 }
203 }
204 }
205 }
206 $new_price = $cart_item['data']->get_price() + $add_price;
207 $cart_item['data']->set_price($new_price);
208 }
209 }
210 */
211
212 add_action('wp_print_scripts', array($this, 'handle_received_order_page'));
213
214 //add meta box shipping location in order detail
215 add_action('add_meta_boxes', 'mv_add_meta_boxes');
216 if (!function_exists('mv_add_meta_boxes')) {
217 function mv_add_meta_boxes()
218 {
219 add_meta_box('mv_other_fields', __('Shipping Location', 'woocommerce'), 'mv_add_other_fields_for_packaging', 'shop_order', 'side', 'core');
220 }
221 }
222 // Adding Meta field in the meta container admin shop_order pages
223 if (!function_exists('mv_add_other_fields_for_packaging')) {
224 function mv_add_other_fields_for_packaging()
225 {
226 global $post;
227 $note = $post->post_excerpt;
228 $items = explode("\n", $note);
229 if (strpos($items[0], "URL:") !== false) {
230 $url = str_replace("URL:", "", $items[0]);
231 echo esc_html('<iframe width="600" height="500" src="' . esc_url($url) . '"></iframe>');
232 }
233 }
234 }
235
236 register_activation_hook(__FILE__, array($this, 'create_custom_mstore_table'));
237
238 // Setup Ajax action hook
239 add_action('wp_ajax_mstore_delete_json_file', array($this, 'mstore_delete_json_file'));
240 add_action('wp_ajax_mstore_delete_apple_file', array($this, 'mstore_delete_apple_file'));
241 add_action('wp_ajax_mstore_delete_firebase_file', array($this, 'mstore_delete_firebase_file'));
242 add_action('wp_ajax_mstore_update_limit_product', array($this, 'mstore_update_limit_product'));
243 add_action('wp_ajax_mstore_update_new_order_title', array($this, 'mstore_update_new_order_title'));
244 add_action('wp_ajax_mstore_update_new_order_message', array($this, 'mstore_update_new_order_message'));
245 add_action('wp_ajax_mstore_update_status_order_title', array($this, 'mstore_update_status_order_title'));
246 add_action('wp_ajax_mstore_update_status_order_message', array($this, 'mstore_update_status_order_message'));
247 add_action('wp_ajax_mstore_update_delivery_order_title', array($this, 'mstore_update_delivery_order_title'));
248 add_action('wp_ajax_mstore_update_delivery_order_message', array($this, 'mstore_update_delivery_order_message'));
249 add_action('wp_ajax_mstore_update_delivery_order_unassign_message', array($this, 'mstore_update_delivery_order_unassign_message'));
250
251 // Booking notification settings (Listeo)
252 add_action('wp_ajax_mstore_update_new_booking_title', array($this, 'mstore_update_new_booking_title'));
253 add_action('wp_ajax_mstore_update_new_booking_message', array($this, 'mstore_update_new_booking_message'));
254 add_action('wp_ajax_mstore_update_status_booking_title', array($this, 'mstore_update_status_booking_title'));
255 add_action('wp_ajax_mstore_update_status_booking_message', array($this, 'mstore_update_status_booking_message'));
256
257 // listen changed order status to notify
258 add_action('woocommerce_order_status_changed', array($this, 'track_order_status_changed'), 9, 4);
259 add_action('woocommerce_checkout_update_order_meta', array($this, 'track_new_order'));
260 add_action('woocommerce_rest_insert_shop_order_object', array($this, 'track_api_new_order'), 10, 4);
261
262 // Listeo booking notifications
263 add_action('listeo_booking_created', array($this, 'track_new_booking'), 10, 1);
264 add_action('listeo_mail_to_owner_new_reservation', array($this, 'track_new_booking_from_listeo_mail'), 10, 1);
265 add_action('listeo_mail_to_owner_new_instant_reservation', array($this, 'track_new_booking_from_listeo_mail'), 10, 1);
266 add_action('listeo_mail_to_user_pay', array($this, 'track_booking_status_pay_to_confirm_from_listeo_mail'), 10, 1);
267 add_action('listeo_mail_to_user_paid', array($this, 'track_booking_status_paid_from_listeo_mail'), 10, 1);
268 add_action('listeo_mail_to_user_canceled', array($this, 'track_booking_status_cancelled_from_listeo_mail'), 10, 1);
269
270 //WCFM - WooCommerce Frontend Manager - Delivery
271 //Handle listen to assign delivery boy on the website
272 add_action( 'wcfmd_after_delivery_boy_assigned', array($this, 'track_delivery_boy_assigned'), 400, 6 );
273 }
274
275 public function wprc_add_flutter_api_endpoints($allowed_endpoints){
276 if ( ! isset( $allowed_endpoints[ 'wc/v3' ] ) || ! in_array( 'products', $allowed_endpoints[ 'wc/v3' ] ) ) {
277 $allowed_endpoints[ 'wc/v3' ][] = 'products';
278 $allowed_endpoints[ 'wc/v3' ][] = 'products/categories';
279 $allowed_endpoints[ 'wc/v3' ][] = 'products/attributes';
280 }
281 if ( ! isset( $allowed_endpoints[ 'wc/v2' ] ) || ! in_array( 'products', $allowed_endpoints[ 'wc/v2' ] ) ) {
282 $allowed_endpoints[ 'wc/v2' ][] = 'products';
283 $allowed_endpoints[ 'wc/v2' ][] = 'products/categories';
284 $allowed_endpoints[ 'wc/v2' ][] = 'products/attributes';
285 }
286 return $allowed_endpoints;
287 }
288
289 public function filter_avatar($url, $id_or_email, $args)
290 {
291 $finder = false;
292 $user = false;
293
294 // Early return if id_or_email is empty
295 if (empty($id_or_email)) {
296 return $url;
297 }
298
299 if (is_numeric($id_or_email)) {
300 $user = get_user_by('id', absint($id_or_email));
301 } elseif (is_string($id_or_email) && is_email($id_or_email)) {
302 $user = get_user_by('email', $id_or_email);
303 } elseif ($id_or_email instanceof WP_User) {
304 $user = $id_or_email;
305 } elseif ($id_or_email instanceof WP_Post) {
306 $user = get_user_by('id', (int)$id_or_email->post_author);
307 } elseif ($id_or_email instanceof WP_Comment) {
308 if (!empty($id_or_email->user_id)) {
309 $user = get_user_by('id', (int)$id_or_email->user_id);
310 }
311 }
312
313 if ($user && !is_wp_error($user)) {
314 $avatar = get_user_meta($user->ID, 'user_avatar', true);
315 if (isset($avatar) && $avatar !== "" && !is_bool($avatar) && !empty($avatar[0])) {
316 return $avatar[0];
317 }
318 }
319
320 return $url;
321 }
322
323 function mstore_delete_json_file(){
324 if(checkIsAdmin(get_current_user_id())){
325 $id = sanitize_text_field($_REQUEST['id']);
326 $nonce = sanitize_text_field($_REQUEST['nonce']);
327 FlutterUtils::delete_config_file($id, $nonce);
328 }else{
329 wp_send_json_error('No Permission',401);
330 }
331 }
332
333 function mstore_delete_apple_file(){
334 if(checkIsAdmin(get_current_user_id())){
335 $nonce = sanitize_text_field($_REQUEST['nonce']);
336 FlutterAppleSignInUtils::delete_config_file($nonce);
337 wp_send_json('success',200);
338 }else{
339 wp_send_json_error('No Permission',401);
340 }
341 }
342
343 function mstore_delete_firebase_file(){
344 if(checkIsAdmin(get_current_user_id())){
345 $nonce = sanitize_text_field($_REQUEST['nonce']);
346 FirebaseMessageHelper::delete_config_file($nonce);
347 wp_send_json('success',200);
348 }else{
349 wp_send_json_error('No Permission',401);
350 }
351 }
352
353 function mstore_update_limit_product()
354 {
355 $nonce = sanitize_text_field($_REQUEST['nonce']);
356 if(checkIsAdmin(get_current_user_id()) && wp_verify_nonce($nonce, 'update_limit_product')){
357 $limit = sanitize_text_field($_REQUEST['limit']);
358 if (is_numeric($limit)) {
359 update_option("mstore_limit_product", intval($limit));
360 }
361 }else{
362 wp_send_json_error('No Permission',401);
363 }
364 }
365
366 function mstore_update_new_order_title()
367 {
368 $nonce = sanitize_text_field($_REQUEST['nonce']);
369 if(checkIsAdmin(get_current_user_id()) && wp_verify_nonce($nonce, 'update_new_order_title')){
370 $title = sanitize_text_field($_REQUEST['title']);
371 update_option("mstore_new_order_title", $title);
372 }else{
373 wp_send_json_error('No Permission',401);
374 }
375 }
376
377 function mstore_update_new_order_message()
378 {
379 $nonce = sanitize_text_field($_REQUEST['nonce']);
380 if(checkIsAdmin(get_current_user_id()) && wp_verify_nonce($nonce, 'update_new_order_message')){
381 $message = sanitize_text_field($_REQUEST['message']);
382 update_option("mstore_new_order_message", $message);
383 }else{
384 wp_send_json_error('No Permission',401);
385 }
386 }
387
388 function mstore_update_status_order_title()
389 {
390 $nonce = sanitize_text_field($_REQUEST['nonce']);
391 if(checkIsAdmin(get_current_user_id()) && wp_verify_nonce($nonce, 'update_status_order_title')){
392 $title = sanitize_text_field($_REQUEST['title']);
393 update_option("mstore_status_order_title", $title);
394 }else{
395 wp_send_json_error('No Permission',401);
396 }
397 }
398
399 function mstore_update_status_order_message()
400 {
401 $nonce = sanitize_text_field($_REQUEST['nonce']);
402 if(checkIsAdmin(get_current_user_id()) && wp_verify_nonce($nonce, 'update_status_order_message')){
403 $message = sanitize_text_field($_REQUEST['message']);
404 update_option("mstore_status_order_message", $message);
405 }else{
406 wp_send_json_error('No Permission',401);
407 }
408 }
409
410 function mstore_update_delivery_order_title()
411 {
412 $nonce = sanitize_text_field($_REQUEST['nonce']);
413 if(checkIsAdmin(get_current_user_id()) && wp_verify_nonce($nonce, 'update_delivery_order_title')){
414 $title = sanitize_text_field($_REQUEST['title']);
415 update_option("mstore_delivery_order_title", $title);
416 wp_send_json('success',200);
417 }else{
418 wp_send_json_error('No Permission',401);
419 }
420 }
421
422 // Booking notification settings
423 function mstore_update_new_booking_title()
424 {
425 $nonce = sanitize_text_field($_REQUEST['nonce']);
426 if(checkIsAdmin(get_current_user_id()) && wp_verify_nonce($nonce, 'update_new_booking_title')){
427 $title = sanitize_text_field($_REQUEST['title']);
428 update_option("mstore_new_booking_title", $title);
429 }else{
430 wp_send_json_error('No Permission',401);
431 }
432 }
433
434 function mstore_update_delivery_order_message()
435 {
436 $nonce = sanitize_text_field($_REQUEST['nonce']);
437 if(checkIsAdmin(get_current_user_id()) && wp_verify_nonce($nonce, 'update_delivery_order_message')){
438 $message = sanitize_textarea_field($_REQUEST['message']);
439 update_option("mstore_delivery_order_message", $message);
440 }else{
441 wp_send_json_error('No Permission',401);
442 }
443 }
444
445 function mstore_update_new_booking_message()
446 {
447 $nonce = sanitize_text_field($_REQUEST['nonce']);
448 if(checkIsAdmin(get_current_user_id()) && wp_verify_nonce($nonce, 'update_new_booking_message')){
449 $message = sanitize_text_field($_REQUEST['message']);
450 update_option("mstore_new_booking_message", $message);
451 }else{
452 wp_send_json_error('No Permission',401);
453 }
454 }
455
456 function mstore_update_delivery_order_unassign_message()
457 {
458 $nonce = sanitize_text_field($_REQUEST['nonce']);
459 if(checkIsAdmin(get_current_user_id()) && wp_verify_nonce($nonce, 'update_delivery_order_unassign_message')){
460 $message = sanitize_textarea_field($_REQUEST['message']);
461 update_option("mstore_delivery_order_unassign_message", $message);
462 }else{
463 wp_send_json_error('No Permission',401);
464 }
465 }
466
467 function mstore_update_status_booking_title()
468 {
469 $nonce = sanitize_text_field($_REQUEST['nonce']);
470 if(checkIsAdmin(get_current_user_id()) && wp_verify_nonce($nonce, 'update_status_booking_title')){
471 $title = sanitize_text_field($_REQUEST['title']);
472 update_option("mstore_status_booking_title", $title);
473 }else{
474 wp_send_json_error('No Permission',401);
475 }
476 }
477
478 function mstore_update_status_booking_message()
479 {
480 $nonce = sanitize_text_field($_REQUEST['nonce']);
481 if(checkIsAdmin(get_current_user_id()) && wp_verify_nonce($nonce, 'update_status_booking_message')){
482 $message = sanitize_text_field($_REQUEST['message']);
483 update_option("mstore_status_booking_message", $message);
484 }else{
485 wp_send_json_error('No Permission',401);
486 }
487 }
488
489 // Booking tracking functions
490 function track_new_booking($booking_id)
491 {
492 $this->track_new_booking_once($booking_id);
493 }
494
495 function track_new_booking_from_listeo_mail($mail_args)
496 {
497 if (!is_array($mail_args) || !isset($mail_args['booking'])) {
498 return;
499 }
500
501 $booking = $mail_args['booking'];
502 $booking_id = $this->extract_booking_id_from_payload($booking);
503
504 if ($booking_id <= 0) {
505 $booking_id = $this->resolve_booking_id_from_payload($booking);
506 }
507
508 $this->track_new_booking_once($booking_id);
509 }
510
511 private function extract_booking_id_from_payload($booking)
512 {
513 $payload = is_object($booking) ? (array) $booking : $booking;
514 if (!is_array($payload)) {
515 return 0;
516 }
517
518 if (isset($payload['id'])) {
519 return absint($payload['id']);
520 }
521
522 if (isset($payload['ID'])) {
523 return absint($payload['ID']);
524 }
525
526 if (isset($payload['booking_id'])) {
527 return absint($payload['booking_id']);
528 }
529
530 return 0;
531 }
532
533 private function resolve_booking_id_from_payload($booking)
534 {
535 $payload = is_object($booking) ? (array) $booking : $booking;
536 if (!is_array($payload)) {
537 return 0;
538 }
539
540 $owner_id = isset($payload['owner_id']) ? absint($payload['owner_id']) : 0;
541 $listing_id = isset($payload['listing_id']) ? absint($payload['listing_id']) : 0;
542 if ($owner_id <= 0 || $listing_id <= 0) {
543 return 0;
544 }
545
546 global $wpdb;
547 $table_name = $wpdb->prefix . 'bookings_calendar';
548
549 $date_start = isset($payload['date_start']) ? sanitize_text_field($payload['date_start']) : '';
550 $date_end = isset($payload['date_end']) ? sanitize_text_field($payload['date_end']) : '';
551 $created = isset($payload['created']) ? sanitize_text_field($payload['created']) : '';
552
553 $booking_id = 0;
554
555 if ($date_start !== '' && $date_end !== '') {
556 $booking_id = (int) $wpdb->get_var($wpdb->prepare(
557 "SELECT id FROM {$table_name} WHERE owner_id = %d AND listing_id = %d AND date_start = %s AND date_end = %s ORDER BY id DESC LIMIT 1",
558 $owner_id,
559 $listing_id,
560 $date_start,
561 $date_end
562 ));
563 }
564
565 if ($booking_id <= 0 && $created !== '') {
566 $booking_id = (int) $wpdb->get_var($wpdb->prepare(
567 "SELECT id FROM {$table_name} WHERE owner_id = %d AND listing_id = %d AND created = %s ORDER BY id DESC LIMIT 1",
568 $owner_id,
569 $listing_id,
570 $created
571 ));
572 }
573
574 if ($booking_id <= 0) {
575 $booking_id = (int) $wpdb->get_var($wpdb->prepare(
576 "SELECT id FROM {$table_name} WHERE owner_id = %d AND listing_id = %d ORDER BY id DESC LIMIT 1",
577 $owner_id,
578 $listing_id
579 ));
580 }
581
582 return $booking_id;
583 }
584
585 private function track_new_booking_once($booking_id)
586 {
587 $booking_id = absint($booking_id);
588 if ($booking_id <= 0) {
589 return;
590 }
591
592 // Avoid duplicate push when multiple Listeo hooks fire for the same booking.
593 $transient_key = 'mstore_booking_push_sent_' . $booking_id;
594 if (get_transient($transient_key)) {
595 return;
596 }
597
598 set_transient($transient_key, 1, 10 * MINUTE_IN_SECONDS);
599 sendNewBookingNotificationToOwner($booking_id);
600 }
601
602 function track_booking_status_pay_to_confirm_from_listeo_mail($mail_args)
603 {
604 $this->track_booking_status_from_listeo_mail($mail_args, 'pay_to_confirm');
605 }
606
607 function track_booking_status_paid_from_listeo_mail($mail_args)
608 {
609 $this->track_booking_status_from_listeo_mail($mail_args, 'paid');
610 }
611
612 function track_booking_status_cancelled_from_listeo_mail($mail_args)
613 {
614 $this->track_booking_status_from_listeo_mail($mail_args, 'cancelled');
615 }
616
617 private function track_booking_status_from_listeo_mail($mail_args, $status)
618 {
619 if (!is_array($mail_args) || !isset($mail_args['booking'])) {
620 return;
621 }
622
623 $booking = $mail_args['booking'];
624 $booking_id = $this->extract_booking_id_from_payload($booking);
625
626 if ($booking_id <= 0) {
627 $booking_id = $this->resolve_booking_id_from_payload($booking);
628 }
629
630 if ($booking_id <= 0 || empty($status)) {
631 return;
632 }
633
634 // Avoid duplicate push when multiple Listeo hooks fire for the same status.
635 $status_key = sanitize_key($status);
636 $transient_key = 'mstore_booking_status_push_last_' . $booking_id;
637 if (get_transient($transient_key) === $status_key) {
638 return;
639 }
640
641 set_transient($transient_key, $status_key, 10 * MINUTE_IN_SECONDS);
642 trackBookingStatusChanged($booking_id, $status);
643 }
644
645 // update order via website
646 function track_order_status_changed($id, $previous_status, $next_status)
647 {
648 trackOrderStatusChanged($id, $previous_status, $next_status);
649 }
650
651 // new order via website
652 function track_new_order($order_id)
653 {
654 trackNewOrder($order_id);
655 }
656
657 // New order or update order via API
658 function track_api_new_order($object, $request, $creating)
659 {
660 if ($creating) {
661 trackNewOrder($object->get_id());
662
663 // Update order attributes. Requires WooCommerce 8.5.0 or later.
664 // And make sure you have enabled `Order Attributes` in WooCommerce
665 // Settings > Advanced > Features.
666 // See: https://woocommerce.com/document/order-attribution-tracking/
667 do_action('woocommerce_store_api_checkout_update_order_from_request', $object, $request);
668 } else {
669 $body = $request->get_body_params();
670 if (isset($body['status'])) {
671 sendNotificationForOrderStatusUpdated($object->id, $body['status']);
672 }
673 }
674 }
675
676 function track_delivery_boy_assigned( $order_id, $order_item_id, $wcfm_tracking_data, $product_id, $wcfm_delivery_boy, $wcfm_messages ) {
677 $notification_message = strip_tags($wcfm_messages);
678 $title = "You have new notification";
679 pushNotificationForDeliveryBoy($wcfm_delivery_boy, $title, $notification_message);
680 }
681
682 public function handle_received_order_page()
683 {
684 // default return true for getting checkout library working
685 if (is_order_received_page()) {
686 $detect = new MDetect;
687 if ($detect->isMobile()) {
688 wp_register_style('mstore-order-custom-style', plugins_url('assets/css/mstore-order-style.css', MSTORE_PLUGIN_FILE));
689 wp_enqueue_style('mstore-order-custom-style');
690 }
691 }
692
693 }
694
695 function create_custom_mstore_table()
696 {
697 global $wpdb;
698 // include upgrade-functions for maybe_create_table;
699 if (!function_exists('maybe_create_table')) {
700 require_once ABSPATH . 'wp-admin/includes/upgrade.php';
701 }
702 $charset_collate = $wpdb->get_charset_collate();
703 $table_name = $wpdb->prefix . 'mstore_checkout';
704 $sql = "CREATE TABLE $table_name (
705 id mediumint(9) NOT NULL AUTO_INCREMENT,
706 `code` tinytext NOT NULL,
707 `order` text NOT NULL,
708 PRIMARY KEY (id)
709 ) $charset_collate;";
710 $success = maybe_create_table($table_name, $sql);
711 $this->ensure_checkout_template_exists();
712 }
713
714 private function ensure_checkout_template_exists()
715 {
716 $path = get_stylesheet_directory() . '/templates';
717 if (!is_dir($path)) {
718 wp_mkdir_p($path);
719 }
720
721 if (!is_dir($path)) {
722 error_log('MStore API: failed to create active theme templates directory at ' . $path);
723 return false;
724 }
725
726 $templatePath = plugin_dir_path(__FILE__) . 'templates/mstore-api-template.php';
727 $targetPath = $path . '/mstore-api-template.php';
728
729 if (!is_readable($templatePath)) {
730 error_log('MStore API: checkout template source is missing or unreadable at ' . $templatePath);
731 return false;
732 }
733
734 if (file_exists($targetPath)) {
735 return true;
736 }
737
738 if (!copy($templatePath, $targetPath)) {
739 error_log('MStore API: failed to copy checkout template to ' . $targetPath);
740 return false;
741 }
742
743 return true;
744 }
745 }
746
747 $mstoreCheckOut = new MstoreCheckOut();
748
749 // use JO\Module\Templater\Templater;
750 include plugin_dir_path(__FILE__) . "templates/class-templater.php";
751
752 add_action('plugins_loaded', 'load_mstore_templater');
753 function load_mstore_templater()
754 {
755
756 // add our new custom templates
757 $my_templater = new Templater(
758 array(
759 // YOUR_PLUGIN_DIR or plugin_dir_path(__FILE__)
760 'plugin_directory' => plugin_dir_path(__FILE__),
761 // should end with _ > prefix_
762 'plugin_prefix' => 'plugin_prefix_',
763 // templates directory inside your plugin
764 'plugin_template_directory' => 'templates',
765 )
766 );
767 $my_templater->add(
768 array(
769 'page' => array(
770 'mstore-api-template.php' => 'Page Custom Template',
771 ),
772 )
773 )->register();
774 }
775
776 //custom rest api
777 function flutter_users_routes()
778 {
779 $controller = new FlutterUserController();
780 $controller->register_routes();
781 }
782
783 add_action('rest_api_init', 'flutter_users_routes');
784
785 /// FluxBuilder Troubleshooting only 🤔
786 add_action('rest_api_init', 'mstore_check_payment_routes');
787 function mstore_check_payment_routes()
788 {
789 register_rest_route('order', '/verify', array(
790 'methods' => 'GET',
791 'callback' => 'mstore_check_payment',
792 'permission_callback' => function () {
793 return true;
794 },
795 )
796 );
797 }
798
799 function mstore_check_payment()
800 {
801 return true;
802 }
803
804
805 // Add menu Setting
806 add_action('admin_menu', 'mstore_plugin_setup_menu');
807
808 function mstore_plugin_setup_menu()
809 {
810 add_menu_page('MStore Api', 'MStore Api', 'manage_options', 'mstore-plugin', 'mstore_init');
811 }
812
813 function mstore_init()
814 {
815 load_template(dirname(__FILE__) . '/templates/mstore-api-admin-page.php');
816 }
817
818 // Register Woo/REST response filters only when a REST request is actually being processed.
819 // Previously these were registered globally, causing WordPress to invoke them on every
820 // page load, admin request, and cron job even when no REST call was made.
821 add_action('rest_api_init', 'mstore_register_rest_filters');
822 function mstore_register_rest_filters()
823 {
824 add_filter('woocommerce_rest_prepare_product_variation_object', 'custom_woocommerce_rest_prepare_product_variation_object', 20, 3);
825 add_filter('woocommerce_rest_prepare_product_object', 'flutter_custom_change_product_response', 20, 3);
826 add_filter('woocommerce_rest_prepare_product_review', 'custom_product_review', 20, 3);
827 add_filter('woocommerce_rest_prepare_product_cat', 'custom_product_category', 20, 3);
828 add_filter('woocommerce_rest_prepare_shop_order_object', 'custom_woocommerce_rest_prepare_shop_order_object', 10, 1);
829 add_filter('woocommerce_rest_prepare_shop_order_object', 'flutter_custom_change_order_response', 20, 3);
830 // Exclude checkout-draft from REST API order listing.
831 // HPOS (WooCommerce 7+) expands 'any' status using wc_get_order_statuses(), so we
832 // filter that function to remove checkout-draft only during REST API requests.
833 add_filter('wc_order_statuses', 'flutter_remove_draft_from_order_statuses');
834 // Add image support for WordPress blog categories
835 add_filter('rest_prepare_category', 'flutter_add_image_to_category', 20, 3);
836 add_filter('woocommerce_rest_prepare_product_attribute', 'flutter_custom_change_product_attribute', 20, 3);
837 add_filter('woocommerce_rest_prepare_product_tag', 'flutter_custom_change_product_taxonomy', 20, 3);
838 add_filter('woocommerce_rest_prepare_product_brand', 'flutter_custom_change_product_taxonomy', 20, 3);
839 add_filter('woocommerce_rest_product_object_query', 'flutter_custom_rest_product_object_query', 10, 2);
840 add_filter('woocommerce_rest_product_tag_query', 'flutter_custom_rest_product_tag_query', 10, 2);
841 add_filter('woocommerce_rest_product_brand_query', 'flutter_custom_rest_product_brand_query', 10, 2);
842 add_filter('rest_product_collection_params', 'flutter_custom_rest_product_collection_params', 10, 1);
843 add_filter('posts_pre_query', 'flutter_custom_posts_pre_query', 10, 2);
844 add_filter('found_posts', 'flutter_custom_found_posts', 20, 2);
845 }
846 function flutter_remove_draft_from_order_statuses($statuses)
847 {
848 unset($statuses['wc-checkout-draft']);
849 return $statuses;
850 }
851
852 /**
853 * WooCommerce REST API: Random sorting for products.
854 *
855 * rest_{post_type}_collection_params
856 *
857 * @param array $params
858 * @return array
859 */
860 function flutter_custom_rest_product_collection_params($params)
861 {
862 $params['orderby']['enum'][] = 'rand';
863 return $params;
864 }
865
866 function flutter_custom_rest_product_tag_query($args, $request)
867 {
868 return flutter_custom_rest_product_taxomomy_query($args, $request, 'product_tag');
869 }
870
871 function flutter_custom_rest_product_brand_query($args, $request)
872 {
873 return flutter_custom_rest_product_taxomomy_query($args, $request, 'product_brand');
874 }
875
876 function flutter_custom_rest_product_taxomomy_query($args, $request, $taxonomy)
877 {
878 // If include or exclude parameters are explicitly set in the request, don't override them
879 if (!empty($request['include']) || !empty($request['exclude'])) {
880 return $args;
881 }
882
883 $hide_empty = isset($args['hide_empty']) && $args['hide_empty'] == true;
884
885 // `include` parameter can be an array or comma separated string
886 $include = wp_parse_id_list($args['include']);
887
888 // Get product count for all tags, brands related to the requested params (based on category, brand, etc.)
889 $terms = get_filtered_term_product_counts($request, $taxonomy, [], $hide_empty);
890
891 // Trick: Use the `include` parameter to fix the API problem below
892 // The page = 1 has no visible items (count = 0), the app does not show
893 // anything (include loadmore button). If the page = 2 has visible items,
894 // cannot do anything in app because it do not show load more button
895 foreach ($terms as $key => $term) {
896 if ($hide_empty) {
897 if ($term['term_count'] > 0) {
898 $include[] = $term['term_count_id'];
899 }
900 } else {
901 $include[] = $term['term_count_id'];
902 }
903 }
904
905 $args['include'] = implode(',', wp_parse_id_list($include));
906 return $args;
907 }
908
909 function flutter_custom_rest_product_object_query($args, $request)
910 {
911 $attrs = $request['attributes'];
912
913 if (is_string($attrs) && !empty($attrs)) {
914 $attrs = json_decode($attrs, true);
915 }
916
917 // Attributes filter.
918 if (!empty($attrs)) {
919 foreach ($attrs as $attr_key => $attr_value) {
920 $args['tax_query'][] = [
921 'taxonomy' => $attr_key,
922 'field' => 'term_id',
923 'terms' => explode(',', $attr_value),
924 ];
925 }
926 }
927
928 return $args;
929 }
930
931 function custom_product_category($response, $object, $request)
932 {
933 $id = $response->data['id'];
934 $children = get_term_children($id, 'product_cat');
935
936 if (empty($children)) {
937 $response->data['has_children'] = false;
938 } else {
939 $response->data['has_children'] = true;
940 }
941 return $response;
942 }
943
944 /**
945 * Add image field to WordPress blog category REST API response
946 */
947 function flutter_add_image_to_category($response, $category, $request)
948 {
949 $image_id = get_term_meta($category->term_id, 'category_image_id', true);
950 $image_url = $image_id ? wp_get_attachment_image_url($image_id, 'full') : '';
951 $response->data['image'] = $image_url;
952
953 return $response;
954 }
955
956 /**
957 * Add image field to category form (Add new category)
958 */
959 add_action('category_add_form_fields', 'flutter_add_category_image_field', 10, 2);
960 function flutter_add_category_image_field($taxonomy)
961 {
962 ?>
963 <div class="form-field term-group">
964 <label for="category-image-id"><?php esc_html_e('Image', 'mstore-api'); ?></label>
965 <input type="hidden" id="category-image-id" name="category-image-id" value="">
966 <div id="category-image-wrapper"></div>
967 <p>
968 <button type="button" class="button button-secondary flutter_category_media_button">
969 <?php esc_html_e('Add Image', 'mstore-api'); ?>
970 </button>
971 <button type="button" class="button button-secondary flutter_category_media_remove">
972 <?php esc_html_e('Remove Image', 'mstore-api'); ?>
973 </button>
974 </p>
975 </div>
976 <?php
977 }
978
979 /**
980 * Add image field to category form (Edit category)
981 */
982 add_action('category_edit_form_fields', 'flutter_edit_category_image_field', 10, 2);
983 function flutter_edit_category_image_field($term, $taxonomy)
984 {
985 $image_id = get_term_meta($term->term_id, 'category_image_id', true);
986 ?>
987 <tr class="form-field term-group-wrap">
988 <th scope="row">
989 <label for="category-image-id"><?php esc_html_e('Image', 'mstore-api'); ?></label>
990 </th>
991 <td>
992 <input type="hidden" id="category-image-id" name="category-image-id" value="<?php echo esc_attr($image_id); ?>">
993 <div id="category-image-wrapper">
994 <?php
995 if ($image_id) {
996 echo wp_get_attachment_image($image_id, 'thumbnail');
997 }
998 ?>
999 </div>
1000 <p>
1001 <button type="button" class="button button-secondary flutter_category_media_button">
1002 <?php esc_html_e('Add Image', 'mstore-api'); ?>
1003 </button>
1004 <button type="button" class="button button-secondary flutter_category_media_remove">
1005 <?php esc_html_e('Remove Image', 'mstore-api'); ?>
1006 </button>
1007 </p>
1008 </td>
1009 </tr>
1010 <?php
1011 }
1012
1013 /**
1014 * Save category image
1015 */
1016 add_action('created_category', 'flutter_save_category_image', 10, 2);
1017 add_action('edited_category', 'flutter_save_category_image', 10, 2);
1018 function flutter_save_category_image($term_id, $tt_id)
1019 {
1020 if (!isset($_POST['category-image-id'])) {
1021 return;
1022 }
1023
1024 $image_id = absint($_POST['category-image-id']);
1025 update_term_meta($term_id, 'category_image_id', $image_id);
1026 }
1027
1028 /**
1029 * Enqueue media uploader scripts for category image
1030 */
1031 add_action('admin_enqueue_scripts', 'flutter_category_image_admin_scripts');
1032 function flutter_category_image_admin_scripts($hook)
1033 {
1034 if (!in_array($hook, ['edit-tags.php', 'term.php'], true)) {
1035 return;
1036 }
1037
1038 wp_enqueue_media();
1039 wp_enqueue_script(
1040 'flutter-category-image',
1041 plugin_dir_url(__FILE__) . 'assets/js/mstore-inspireui.js',
1042 array('jquery'),
1043 '1.0.0',
1044 true
1045 );
1046 }
1047
1048 /**
1049 * Add image column to category list table
1050 */
1051 add_filter('manage_edit-category_columns', 'flutter_add_category_image_column');
1052 function flutter_add_category_image_column($columns)
1053 {
1054 $new_columns = array();
1055 foreach ($columns as $key => $value) {
1056 if ($key === 'description') {
1057 $new_columns['image'] = __('Image', 'mstore-api');
1058 }
1059 $new_columns[$key] = $value;
1060 }
1061 return $new_columns;
1062 }
1063
1064 /**
1065 * Display image in category list table column
1066 */
1067 add_filter('manage_category_custom_column', 'flutter_display_category_image_column', 10, 3);
1068 function flutter_display_category_image_column($content, $column_name, $term_id)
1069 {
1070 if ($column_name !== 'image') {
1071 return $content;
1072 }
1073
1074 $image_id = get_term_meta($term_id, 'category_image_id', true);
1075
1076 if (!$image_id) {
1077 return '';
1078 }
1079
1080 return wp_get_attachment_image(
1081 $image_id,
1082 'thumbnail',
1083 false,
1084 array('style' => 'width:50px;height:50px;object-fit:cover;')
1085 );
1086 }
1087
1088 function custom_product_review($response, $object, $request)
1089 {
1090 if(is_plugin_active('woo-photo-reviews/woo-photo-reviews.php') || is_plugin_active('woocommerce-photo-reviews/woocommerce-photo-reviews.php')){
1091 $id = $response->data['id'];
1092 $image_post_ids = get_comment_meta( $id, 'reviews-images', true );
1093 $image_arr = array();
1094 if(!is_string($image_post_ids)){
1095 foreach( $image_post_ids as $image_post_id ) {
1096 $image_arr[] = wp_get_original_image_url( $image_post_id );
1097 }
1098 }
1099 $response->data['images'] = $image_arr;
1100 }
1101 return $response;
1102 }
1103
1104 function flutter_custom_change_order_response($response, $object, $request)
1105 {
1106 return customOrderResponse($response, $object, $request);
1107 }
1108
1109 function flutter_custom_change_product_response($response, $object, $request)
1110 {
1111 return customProductResponse($response, $object, $request);
1112 }
1113
1114 function flutter_custom_change_product_attribute($response, $item, $request)
1115 {
1116 $taxonomy = wc_attribute_taxonomy_name($item->attribute_name);
1117
1118 // Combine all attribute terms into the return result when getting
1119 // attributes. Reduce api calls to get sub-attributes from the app
1120 $options = get_terms([
1121 'taxonomy' => $taxonomy,
1122 'hide_empty' => false,
1123 ]);
1124
1125 // Get list count of attribute terms based on attribute.
1126 $terms = get_filtered_term_product_counts($request, $taxonomy);
1127
1128 $is_visible = false;
1129 // Show this attribute if any attribute terms have product quantity > 0
1130 foreach ($terms as $key => $term) {
1131 $count = (int)$term['term_count'];
1132 if ($is_visible == false && $count > 0) {
1133 $is_visible = true;
1134 }
1135 // Update term count for options
1136 foreach ($options as $option) {
1137 if ($option->term_id == $term['term_count_id']) {
1138 $option->count = $count;
1139 break;
1140 }
1141 }
1142 }
1143
1144 $response->data['terms'] = $options;
1145
1146 $response->data['is_visible'] = $is_visible;
1147
1148 return $response;
1149 }
1150
1151 /// Custom response for a product tag or brand
1152 function flutter_custom_change_product_taxonomy($response, $item, $request)
1153 {
1154 // There is only a maximum of 1 entry because term_ids is an array of a
1155 // unique id
1156 $terms = get_filtered_term_product_counts($request, $item->taxonomy, $item->term_id);
1157
1158 if (!empty($terms)) {
1159 $term = $terms[0];
1160 $count = (int)$term['term_count'];
1161 $response->data['count'] = $count;
1162 $response->data['is_visible'] = $count > 0;
1163 } else {
1164 $response->data['count'] = 0;
1165 $response->data['is_visible'] = false;
1166 }
1167
1168 return $response;
1169 }
1170
1171 function custom_get_attribute_taxonomy_name( $slug, $product ) {
1172 // Format slug so it matches attributes of the product.
1173 $slug = wc_attribute_taxonomy_slug( $slug );
1174 $attributes = $product->get_attributes();
1175 $attribute = false;
1176
1177 // pa_ attributes.
1178 if ( isset( $attributes[ wc_attribute_taxonomy_name( $slug ) ] ) ) {
1179 $attribute = $attributes[ wc_attribute_taxonomy_name( $slug ) ];
1180 } elseif ( isset( $attributes[ $slug ] ) ) {
1181 $attribute = $attributes[ $slug ];
1182 }
1183
1184 if ( ! $attribute ) {
1185 return $slug;
1186 }
1187
1188 // Taxonomy attribute name.
1189 if ( $attribute->is_taxonomy() ) {
1190 $taxonomy = $attribute->get_taxonomy_object();
1191 return $taxonomy->attribute_label;
1192 }
1193
1194 // Custom product attribute name.
1195 return $attribute->get_name();
1196 }
1197
1198 function custom_woocommerce_rest_prepare_product_variation_object($response, $object, $request)
1199 {
1200
1201 global $woocommerce_wpml;
1202
1203 //update correct product price with tax setting
1204 $response->data['price'] = wc_get_price_to_display( $object );
1205 $response->data['regular_price'] = wc_get_price_to_display( $object, array( 'price' => $object->get_regular_price() ) );
1206 $response->data['sale_price'] = wc_get_price_to_display( $object, array( 'price' => $object->get_sale_price() ) );
1207
1208 $is_purchased = false;
1209 if (isset($request['user_id'])) {
1210 $user_id = $request['user_id'];
1211 $user_data = get_userdata($user_id);
1212 $user_email = $user_data->user_email;
1213 $is_purchased = wc_customer_bought_product($user_email, $user_id, $response->data['id']);
1214 }
1215 $response->data['is_purchased'] = $is_purchased;
1216 if (!empty($woocommerce_wpml->multi_currency) && !empty($woocommerce_wpml->settings['currencies_order'])) {
1217
1218 $price = $response->data['price'];
1219
1220 foreach ($woocommerce_wpml->settings['currency_options'] as $key => $currency) {
1221 $rate = (float)$currency["rate"];
1222 $response->data['multi-currency-prices'][$key]['price'] = $rate == 0 ? $price : sprintf("%.2f", $price * $rate);
1223 }
1224 }
1225
1226 /*Update product price for subscription product*/
1227 if($object->get_type() == 'subscription_variation'){
1228 $meta_data = $response->data['meta_data'];
1229 $sign_up_fee = null;
1230 foreach ($meta_data as $meta_data_item) {
1231 if ($meta_data_item->get_data()["key"] == "_subscription_sign_up_fee") {
1232 $sign_up_fee = $meta_data_item->get_data()["value"];
1233 }
1234 }
1235 if($sign_up_fee != null){
1236 $response->data['regular_price']= $sign_up_fee;
1237 $response->data['price']= $sign_up_fee;
1238 }
1239 }
1240
1241 if (class_exists('WooCommerceWholeSalePrices')) {
1242 $meta_data = $response->data['meta_data'];
1243 foreach ($meta_data as $v) {
1244 $key = is_array($v) ? $v['key'] : $v->__get('key');
1245 if ((strpos($key, '_wholesale_price') !== false && strpos($key, '_have_wholesale_price') === false) || (strpos($key, '_wholesale_sale_price') !== false && strpos($key, '_have_wholesale_sale_price') === false)) {
1246 if($v->__get('value')){
1247 $wholesale_price = wc_get_price_to_display( $object, array( 'price' => $v->__get('value') ) );
1248 $v->__set("value", number_format($wholesale_price, 2, '.', ''));
1249 $v->apply_changes();
1250 }
1251 }
1252 }
1253 }
1254
1255 $variation_product = wc_get_product( $response->data['id'] );
1256 if($variation_product) {
1257 $_product = wc_get_product( $variation_product->get_parent_id() );
1258 $attributes = array();
1259
1260 foreach ( $variation_product->get_variation_attributes() as $attribute_name => $attribute ) {
1261 $name = str_replace( 'attribute_', '', $attribute_name );
1262
1263 if ( empty( $attribute ) && '0' !== $attribute ) {
1264 continue;
1265 }
1266
1267 // Taxonomy-based attributes are prefixed with `pa_`, otherwise simply `attribute_`.
1268 if ( 0 === strpos( $attribute_name, 'attribute_pa_' ) ) {
1269 $option_term = get_term_by( 'slug', $attribute, $name );
1270 $attributes[] = array(
1271 'id' => wc_attribute_taxonomy_id_by_name( $name ),
1272 'name' => custom_get_attribute_taxonomy_name( $name, $_product ),
1273 'option' => $option_term && ! is_wp_error( $option_term ) ? $option_term->name : $attribute,
1274 );
1275 } else {
1276 $attributes[] = array(
1277 'id' => 0,
1278 'name' => custom_get_attribute_taxonomy_name( $name, $_product ),
1279 'option' => $attribute,
1280 );
1281 }
1282 }
1283 $response->data['attributes'] = $attributes;
1284 }
1285 return $response;
1286 }
1287
1288 /**
1289 * Wordpress REST API: Support Rest API with Relevanssi.
1290 *
1291 * Attaches to 'the_posts' filter hook, checks to see if there's a place for a
1292 * search and runs relevanssi_do_query() if there is.
1293 *
1294 * https://www.relevanssi.com/user-manual/using-relevanssi-outside-search-pages/
1295 *
1296 * @param array $posts An array of post objects.
1297 * @param WP_Query $query The WP_Query object, default false.
1298 */
1299 function flutter_custom_posts_pre_query($posts, $query)
1300 {
1301 if ($query->is_search() && defined('REST_REQUEST') && REST_REQUEST) {
1302 if (function_exists('relevanssi_do_query')) {
1303 $posts = relevanssi_do_query($query);
1304 $query->relevanssi_found_posts = $query->found_posts;
1305 return $posts;
1306 }
1307 }
1308 return $posts;
1309 }
1310
1311 /**
1312 * Wordpress REST API: Customize `found_posts` in Rest API with Relevanssi.
1313 *
1314 * @param int $found_posts The number of posts found.
1315 * @param WP_Query $query The WP_Query object.
1316 */
1317 function flutter_custom_found_posts($found_posts, $query)
1318 {
1319 if ($query->is_search() && defined('REST_REQUEST') && REST_REQUEST) {
1320 if (function_exists('relevanssi_do_query')) {
1321 return $query->relevanssi_found_posts;
1322 }
1323 }
1324 return $found_posts;
1325 }
1326
1327
1328 // Prepare data before checkout by webview
1329 function flutter_prepare_checkout()
1330 {
1331 // Early exit: Only process if checkout parameters are present
1332 $has_checkout_params = isset($_GET['mobile']) || isset($_GET['code']) || isset($_GET['cookie']);
1333
1334 // Only parse referer as fallback if $_GET is truly empty (preserves legitimate query args)
1335 if (!$has_checkout_params && empty($_GET) && isset($_SERVER['HTTP_REFERER'])) {
1336 $url_components = parse_url($_SERVER['HTTP_REFERER']);
1337 if (isset($url_components['query'])) {
1338 parse_str($url_components['query'], $params);
1339 // Extract only the checkout-related keys from referer; don't overwrite all $_GET
1340 foreach (['mobile', 'code', 'cookie'] as $key) {
1341 if (isset($params[$key])) {
1342 $_GET[$key] = $params[$key];
1343 }
1344 }
1345 $has_checkout_params = isset($_GET['mobile']) || isset($_GET['code']) || isset($_GET['cookie']);
1346 }
1347 }
1348
1349 // Exit early if no checkout-related parameters found - prevents database thrashing on regular page loads
1350 if (!$has_checkout_params) {
1351 return;
1352 }
1353
1354 if (isset($_GET['mobile']) && isset($_GET['code'])) {
1355
1356 $code = sanitize_text_field($_GET['code']);
1357 global $wpdb;
1358 $table_name = $wpdb->prefix . "mstore_checkout";
1359 $sql = $wpdb->prepare("SELECT * FROM $table_name WHERE code = %s", $code);
1360 $item = $wpdb->get_row($sql);
1361 if ($item) {
1362 $data = json_decode(urldecode(base64_decode($item->order)), true);
1363 } else {
1364 return var_dump("Can't not get the order");
1365 }
1366
1367 $shipping = isset($data['shipping']) ? $data['shipping'] : NULL;
1368 $billing = isset($data['billing']) ? $data['billing'] : $shipping;
1369
1370 if (isset($data['token'])) {
1371 // Validate the cookie token
1372 $userId = validateCookieLogin($data['token']);
1373 if(!is_wp_error($userId)){
1374 if (isset($billing)) {
1375 // Batch update user meta to reduce database queries
1376 $user_meta = get_user_meta($userId);
1377 $billing_fields = array('first_name', 'last_name', 'company', 'address_1', 'address_2', 'city', 'state', 'postcode', 'country', 'email', 'phone');
1378 foreach ($billing_fields as $field) {
1379 if (isset($billing[$field]) && !empty($billing[$field])) {
1380 // Only update if value has changed (reduces unnecessary DB writes)
1381 $current_billing = isset($user_meta['billing_' . $field][0]) ? $user_meta['billing_' . $field][0] : '';
1382 if ($current_billing !== $billing[$field]) {
1383 update_user_meta($userId, 'billing_' . $field, $billing[$field]);
1384 }
1385 $current_shipping = isset($user_meta['shipping_' . $field][0]) ? $user_meta['shipping_' . $field][0] : '';
1386 if ($current_shipping !== $billing[$field]) {
1387 update_user_meta($userId, 'shipping_' . $field, $billing[$field]);
1388 }
1389 }
1390 }
1391 } else {
1392 // Batch retrieve user meta instead of 22 individual queries
1393 $user_meta = get_user_meta($userId);
1394 $billing_fields = array('first_name', 'last_name', 'company', 'address_1', 'address_2', 'city', 'state', 'postcode', 'country', 'email', 'phone');
1395
1396 $billing = array();
1397 $shipping = array();
1398
1399 foreach ($billing_fields as $field) {
1400 $billing[$field] = isset($user_meta['billing_' . $field][0]) ? $user_meta['billing_' . $field][0] : '';
1401 $shipping[$field] = isset($user_meta['shipping_' . $field][0]) ? $user_meta['shipping_' . $field][0] : '';
1402 }
1403
1404 if (isset($billing["first_name"]) && !isset($shipping["first_name"])) {
1405 $shipping = $billing;
1406 }
1407 if (!isset($billing["first_name"]) && isset($shipping["first_name"])) {
1408 $billing = $shipping;
1409 }
1410 }
1411
1412 // Check user and authentication
1413 $user = get_userdata($userId);
1414 if ($user && (!is_user_logged_in() || get_current_user_id() !== $userId)) {
1415 wp_set_current_user($userId, $user->user_login);
1416 wp_set_auth_cookie($userId);
1417
1418 header("Refresh:0");
1419 }
1420 cleanupAppointmentCartData($userId);
1421 }
1422 } else {
1423 if (is_user_logged_in()) {
1424 wp_logout();
1425 wp_set_current_user(0);
1426 header("Refresh:0");
1427 }
1428 }
1429
1430 if (is_plugin_active('woocommerce/woocommerce.php') == true) {
1431 //header("Content-Security-Policy: frame-ancestors 'self' *.yourdomain.com");
1432 global $woocommerce;
1433 WC()->session->set('refresh_totals', true);
1434 WC()->cart->empty_cart();
1435
1436 if(class_exists('WC_Points_Rewards_Discount') && !empty($data['fee_lines'])){
1437 foreach ($data['fee_lines'] as $fee) {
1438 if($fee['name'] == 'Cart Discount'){
1439 list($points, $monetary_value) = explode(':', get_option('wc_points_rewards_redeem_points_ratio', ''));
1440 $cart_price_rate = floatval($monetary_value);
1441 $cart_points_rate = intval($points);
1442
1443 WC()->session->set( 'wc_points_rewards_discount_amount', intval($fee['total']) * $cart_points_rate / $cart_price_rate * (-1) );
1444 // generate and set unique discount code
1445 $discount_code = WC_Points_Rewards_Discount::generate_discount_code();
1446 // apply the discount
1447 WC()->cart->add_discount( $discount_code );
1448 }
1449 }
1450 }
1451
1452 $products = $data['line_items'];
1453
1454 buildCartItemData($products, function($productId, $quantity, $variationId, $attributes, $cart_item_data){
1455 global $woocommerce;
1456 $woocommerce->cart->add_to_cart($productId, $quantity, $variationId, $attributes, $cart_item_data);
1457 });
1458
1459 if (isset($shipping)) {
1460 $woocommerce->customer->set_shipping_first_name($shipping["first_name"]);
1461 $woocommerce->customer->set_shipping_last_name($shipping["last_name"]);
1462 $woocommerce->customer->set_shipping_company($shipping["company"]);
1463 $woocommerce->customer->set_shipping_address_1($shipping["address_1"]);
1464 $woocommerce->customer->set_shipping_address_2($shipping["address_2"]);
1465 $woocommerce->customer->set_shipping_city($shipping["city"]);
1466 $woocommerce->customer->set_shipping_state($shipping["state"]);
1467 $woocommerce->customer->set_shipping_postcode($shipping["postcode"]);
1468 $woocommerce->customer->set_shipping_country($shipping["country"]);
1469 }
1470
1471 if (isset($billing)) {
1472 $woocommerce->customer->set_billing_first_name($billing["first_name"]);
1473 $woocommerce->customer->set_billing_last_name($billing["last_name"]);
1474 $woocommerce->customer->set_billing_company($billing["company"]);
1475 $woocommerce->customer->set_billing_address_1($billing["address_1"]);
1476 $woocommerce->customer->set_billing_address_2($billing["address_2"]);
1477 $woocommerce->customer->set_billing_city($billing["city"]);
1478 $woocommerce->customer->set_billing_state($billing["state"]);
1479 $woocommerce->customer->set_billing_postcode($billing["postcode"]);
1480 $woocommerce->customer->set_billing_country($billing["country"]);
1481 $woocommerce->customer->set_billing_email($billing["email"]);
1482 $woocommerce->customer->set_billing_phone($billing["phone"]);
1483 }
1484
1485 if (!empty($data['coupon_lines'])) {
1486 $coupons = $data['coupon_lines'];
1487 foreach ($coupons as $coupon) {
1488 $woocommerce->cart->add_discount($coupon['code']);
1489 }
1490 }
1491
1492 if (!empty($data['shipping_lines'])) {
1493 $shippingLines = $data['shipping_lines'];
1494 $shippingMethods = [];
1495 foreach ($shippingLines as $key => $shippingLine) {
1496 if (is_plugin_active('wc-multivendor-marketplace/wc-multivendor-marketplace.php') && !empty($shippingLine['meta_data'])) {
1497 $seller_meta = array_filter($shippingLine['meta_data'],function($item){
1498 return $item['key'] == 'seller_id';
1499 });
1500 if(!empty($seller_meta)){
1501 $shippingMethods[$seller_meta[0]['value']] = $shippingLine['method_id'];
1502 }else{
1503 $shippingMethods[] = $shippingLine['method_id'];
1504 }
1505 } else{
1506 $shippingMethods[] = $shippingLine['method_id'];
1507 }
1508 }
1509 WC()->session->set('chosen_shipping_methods', $shippingMethods);
1510 }
1511 if (!empty($data['payment_method'])) {
1512 WC()->session->set('chosen_payment_method', $data['payment_method']);
1513 }
1514
1515 if (isset($data['customer_note']) && !empty($data['customer_note'])) {
1516 $_POST["order_comments"] = sanitize_text_field($data['customer_note']);
1517 $checkout_fields = WC()->checkout->__get("checkout_fields");
1518 $checkout_fields["order"] = ["order_comments" => ["type" => "textarea", "class" => [], "label" => "Order notes", "placeholder" => "Notes about your order, e.g. special notes for delivery."]];
1519 WC()->checkout->__set("checkout_fields", $checkout_fields);
1520 }
1521 }
1522 }
1523
1524 if (isset($_GET['cookie'])) {
1525 $cookie = mstore_decode_user_cookie(sanitize_text_field($_GET['cookie']));
1526 $userId = validateCookieLogin($cookie);
1527 if (!is_wp_error($userId)) {
1528 $user = get_userdata($userId);
1529 if ($user !== false) {
1530 $cookie_elements = explode('|', $cookie);
1531 if (count($cookie_elements) == 4) {
1532 list($username, $expiration, $token, $hmac) = $cookie_elements;
1533 $_COOKIE[LOGGED_IN_COOKIE] = $cookie;
1534 wp_set_current_user($userId, $user->user_login);
1535 wp_set_auth_cookie($userId, true, '', $token);
1536 }
1537
1538 if (isset($_GET['order_detail']) && isset($_GET['order_id']) && is_plugin_active('dokan-lite/dokan.php')) {
1539 $url = add_query_arg(
1540 [
1541 'order_id' => $_GET['order_id'],
1542 '_wpnonce' => wp_create_nonce('dokan_view_order'),
1543 ],
1544 dokan_get_navigation_url('orders')
1545 );
1546 wp_safe_redirect($url);
1547 die;
1548 }
1549
1550 if (isset($_GET['vendor_admin'])) {
1551 global $wp;
1552 $request = $wp->request;
1553 wp_redirect(esc_url_raw(home_url("/" . $request)));
1554 die;
1555 }
1556 }
1557 }
1558 }
1559 }
1560
1561 function set_author_in_for_vendor_staff( $query ) {
1562 // Only relevant in admin context for Dokan vendor staff — skip all front-end and REST requests early
1563 if ( ! is_admin() ) {
1564 return;
1565 }
1566 global $post;
1567 if ( class_exists('WeDevs_Dokan') && dokan()->is_pro_exists() && isset( $query->query['post_type'] ) && $query->query['post_type'] === 'global_product_addon' && $post != null ) {
1568 $vendor = dokan_get_vendor_by_product( $post->ID );
1569
1570 // Check if $vendor is valid before proceeding
1571 if (!$vendor) {
1572 return;
1573 }
1574
1575 if ( function_exists( 'dokan_get_vendor_staff' ) ) {
1576 $vendor_staffs = dokan_get_vendor_staff( $vendor->get_id() );
1577 if ( ! in_array( $vendor->get_id(), $vendor_staffs, true ) ) {
1578 return;
1579 }
1580 $query->set( 'author__in', $vendor_staffs );
1581 }
1582 }
1583 }
1584
1585 // Add product image to order
1586 function custom_woocommerce_rest_prepare_shop_order_object($response)
1587 {
1588 if (empty($response->data) || empty($response->data['line_items'])) {
1589 return $response;
1590 }
1591 $api = new WC_REST_Products_Controller();
1592 $req = new WP_REST_Request('GET');
1593 $line_items = [];
1594 foreach ($response->data['line_items'] as $item) {
1595 $product_id = $item['product_id'];
1596 $req->set_query_params(["id" => $product_id]);
1597 $res = $api->get_item($req);
1598 $item["product_data"] = is_wp_error($res) ? null : $res->get_data();
1599
1600 if (empty($item['product_data']['images'] ?? null)) {
1601 global $wpdb;
1602 $listing_id_from_booking = 0;
1603
1604 if (!empty($item['id'])) {
1605 $meta_listing_id = wc_get_order_item_meta($item['id'], '_listing_id', true);
1606 if (!empty($meta_listing_id)) {
1607 $listing_id_from_booking = (int) $meta_listing_id;
1608 }
1609 }
1610 if ($listing_id_from_booking === 0) {
1611
1612 if (!isset($order_id)) {
1613 $order_id = isset($response->data['id']) ? absint($response->data['id']) : 0;
1614 }
1615
1616 if ($order_id > 0) {
1617 $table_name = $wpdb->prefix . 'bookings_calendar';
1618 $listing_id_from_booking = (int)$wpdb->get_var(
1619 $wpdb->prepare("SELECT listing_id FROM {$table_name} WHERE order_id = %d ORDER BY id DESC LIMIT 1", $order_id)
1620 );
1621 }
1622 }
1623
1624 if ($listing_id_from_booking > 0) {
1625 $attachment_ids = array();
1626 $featured_id = (int)get_post_thumbnail_id($listing_id_from_booking);
1627
1628 if ($featured_id > 0) {
1629 $attachment_ids[] = $featured_id;
1630 }
1631
1632 $gallery = get_post_meta($listing_id_from_booking, '_gallery', true);
1633 if (is_array($gallery)) {
1634 foreach ($gallery as $key => $val) {
1635 $attachment_id = 0;
1636 if (is_numeric($key) && (int)$key > 0) {
1637 $attachment_id = (int)$key;
1638 } elseif (is_numeric($val) && (int)$val > 0) {
1639 $attachment_id = (int)$val;
1640 }
1641
1642 if ($attachment_id > 0 && !in_array($attachment_id, $attachment_ids, true)) {
1643 $attachment_ids[] = $attachment_id;
1644 }
1645 }
1646 }
1647
1648 $images = array();
1649 foreach ($attachment_ids as $attachment_id) {
1650 $attachment = get_post($attachment_id);
1651 if (!$attachment) {
1652 continue;
1653 }
1654
1655 $image_src = wp_get_attachment_image_url($attachment_id, 'full') ?: '';
1656
1657 $images[] = array(
1658 'id' => $attachment_id,
1659 'date_created' => wc_rest_prepare_date_response($attachment->post_date, false),
1660 'date_created_gmt' => wc_rest_prepare_date_response($attachment->post_date_gmt, true),
1661 'date_modified' => wc_rest_prepare_date_response($attachment->post_modified, false),
1662 'date_modified_gmt' => wc_rest_prepare_date_response($attachment->post_modified_gmt, true),
1663 'src' => $image_src,
1664 'name' => get_the_title($attachment_id),
1665 'alt' => get_post_meta($attachment_id, '_wp_attachment_image_alt', true),
1666 'srcset' => wp_get_attachment_image_srcset($attachment_id, 'full') ?: '',
1667 'sizes' => wp_get_attachment_image_sizes($attachment_id) ?: '',
1668 'thumbnail' => wp_get_attachment_image_url($attachment_id) ?: $image_src,
1669 );
1670 }
1671
1672 if (!isset($item['product_data']) || !is_array($item['product_data'])) {
1673 $item['product_data'] = array();
1674 }
1675 $item['product_data']['images'] = $images;
1676 }
1677 }
1678 $line_items[] = $item;
1679
1680 }
1681 $response->data['line_items'] = $line_items;
1682
1683 // Get payment method
1684 $payment_method = $response->data['payment_method'];
1685 $payment_method_title = $response->data['payment_method_title'];
1686 $order_id = $response->data['id'];
1687
1688 // Get default Bank transfer info
1689 if ($payment_method == 'bacs' && class_exists('WC_Gateway_BACS')) {
1690 $bacs = new WC_Gateway_BACS();
1691 $bacs_accounts = apply_filters('woocommerce_bacs_accounts', $bacs->account_details, $order_id);
1692 $response->data['bacs_info'] = $bacs_accounts;
1693 }
1694
1695 // Get other Bank transfer info
1696 if (strpos($payment_method, 'bank_transfer') !== false && is_plugin_active('fr-multi-bank-transfer-payment-gateways-for-woocommerce/fr-multi-bank-transfer-gateways-for-woocommerce.php')) {
1697 require_once ABSPATH . 'wp-content/plugins/fr-multi-bank-transfer-payment-gateways-for-woocommerce/includes/gateways/class-fr-multi-bank-transfer-gateways-for-woocommerce-bank-transfer.php';
1698
1699 $bacs = new Fr_Multi_Bank_Transfer_Gateways_For_Woocommerce_Bank_Transfer([
1700 'id' => $payment_method,
1701 'method_title' => $payment_method_title,
1702 ]);
1703 $bacs_accounts = apply_filters('woocommerce_bacs_accounts', $bacs->account_details, $order_id);
1704 $response->data['bacs_info'] = $bacs_accounts;
1705 }
1706
1707 return $response;
1708 }
1709
1710
1711 function mstore_register_order_refund_requested_order_status()
1712 {
1713 register_post_status('wc-refund-req', array(
1714 'label' => esc_attr__('Refund Requested'),
1715 'public' => true,
1716 'show_in_admin_status_list' => true,
1717 'show_in_admin_all_list' => true,
1718 'exclude_from_search' => false,
1719 'label_count' => _n_noop('Refund requested <span class="count">(%s)</span>', 'Refund requested <span class="count">(%s)</span>')
1720 ));
1721 }
1722
1723 add_action('init', 'mstore_register_order_refund_requested_order_status');
1724
1725
1726 function mstore_add_custom_order_statuses($order_statuses)
1727 {
1728 // Create new status array.
1729 $new_order_statuses = array();
1730 // Loop though statuses.
1731 foreach ($order_statuses as $key => $status) {
1732 // Add status to our new statuses.
1733 $new_order_statuses[$key] = $status;
1734 // Add our custom statuses.
1735 if ('wc-processing' === $key) {
1736 $new_order_statuses['wc-refund-req'] = esc_attr__('Refund Requested');
1737 }
1738 }
1739
1740 return $new_order_statuses;
1741 }
1742
1743 add_filter('wc_order_statuses', 'mstore_add_custom_order_statuses');
1744
1745
1746 function custom_status_bulk_edit($actions)
1747 {
1748 // Add order status changes.
1749 $actions['mark_refund-req'] = __('Change status to refund requested');
1750
1751 return $actions;
1752 }
1753
1754 add_filter('bulk_actions-edit-shop_order', 'custom_status_bulk_edit', 20, 1);
1755
1756 add_action('rest_api_init', 'get_promptpay_qrcode_routes');
1757 function get_promptpay_qrcode_routes()
1758 {
1759 register_rest_route('promptpay', '/detail' . '/(?P<id>[\d]+)', array(
1760 'methods' => 'GET',
1761 'callback' => function($request){
1762 $available_payment_methods = WC()->payment_gateways()->payment_gateways();
1763 $paymentMethod = $available_payment_methods['thai-promptpay-easy'];
1764 $order = wc_get_order($request['id'] );
1765 $thank_msg = $paymentMethod->thank_msg;
1766 $promptpay_id = $paymentMethod->promptpay_id;
1767 $promptpay_type = $paymentMethod->promptpay_type;
1768 $promptpay_name = $paymentMethod->promptpay_name;
1769 $include_price = $paymentMethod->include_price;
1770 $image_url = get_site_url() . "/wp-content/plugins/thai-promptpay-payment-easy-gateway-plugin/images/promptpay_qrcode/promptpay-qr-l.php?type=$promptpay_type&promptpay_id=$promptpay_id";
1771
1772 if($include_price=='yes'){
1773 $price = $order->get_total();
1774 $image_url .= "&price=$price&p=1";
1775 }
1776
1777
1778 return ['thank_msg' => $thank_msg, 'qrcode_url' => $image_url, 'promptpay_id' => $promptpay_id, 'promptpay_name' => $promptpay_name, 'promptpay_type' => $paymentMethod->promptpay_type_name[$promptpay_type]];
1779 },
1780 'permission_callback' => function () {
1781 return true;
1782 },
1783 )
1784 );
1785 }
1786
1787 // Check if Woo Variation Swatches is active before adding the filter
1788 if (!function_exists('add_variation_swatches_attribute_images_to_api')) {
1789 function add_variation_swatches_attribute_images_to_api($response, $product, $request) {
1790 // First check if the plugin is active
1791 if (!function_exists('woo_variation_swatches') || !class_exists('Woo_Variation_Swatches')) {
1792 return $response;
1793 }
1794
1795 $data = $response->get_data();
1796
1797 // Only proceed if product has attributes
1798 if (empty($data['attributes'])) {
1799 return $response;
1800 }
1801
1802 $product_attributes = $product->get_attributes();
1803
1804 foreach ($data['attributes'] as $key => $attribute) {
1805 $attribute_name = $attribute['name'];
1806 $attribute_obj = isset($product_attributes[$attribute_name]) ? $product_attributes[$attribute_name] : null;
1807
1808 if ($attribute_obj && $attribute_obj->is_taxonomy()) {
1809 $terms = wp_get_post_terms($product->get_id(), $attribute_name, ['fields' => 'all']);
1810
1811 foreach ($terms as $term_key => $term) {
1812 // Get image ID from term meta set by Woo Variation Swatches
1813 $image_id = get_term_meta($term->term_id, 'product_attribute_image', true);
1814
1815 if ($image_id) {
1816 $image_size = woo_variation_swatches()->get_option('attribute_image_size', 'variation_swatches_image_size');
1817 $image_src = wp_get_attachment_image_src($image_id, $image_size);
1818
1819 if ($image_src) {
1820 // Add image data to the term in the API response
1821 $data['attributes'][$key]['options'][$term_key] = [
1822 'term_id' => $term->term_id,
1823 'name' => $term->name,
1824 'slug' => $term->slug,
1825 'image' => [
1826 'src' => $image_src[0],
1827 'width' => $image_src[1],
1828 'height' => $image_src[2]
1829 ]
1830 ];
1831 }
1832 }
1833 }
1834 }
1835 }
1836
1837 $response->set_data($data);
1838 return $response;
1839 }
1840 }
1841
1842 // Only add the filter if the function exists (as an extra check)
1843 if (function_exists('add_variation_swatches_attribute_images_to_api')) {
1844 add_filter('woocommerce_rest_prepare_product_object', 'add_variation_swatches_attribute_images_to_api', 10, 3);
1845 }
1846