PluginProbe
MStore API – Create Native Android & iOS Apps On The Cloud / trunk
MStore API – Create Native Android & iOS Apps On The Cloud vtrunk
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 trunk, at mstore-api.php

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