Changeset 3419727
- Timestamp:
- 12/15/2025 06:03:00 AM (4 months ago)
- Location:
- gratisfaction-all-in-one-loyalty-contests-referral-program-for-woocommerce/trunk
- Files:
-
- 2 added
- 6 edited
-
css/gr-frontend.css (added)
-
grconnect.php (modified) (134 diffs)
-
includes/grwoo-api.php (modified) (25 diffs)
-
includes/grwoo-functions.php (modified) (7 diffs)
-
includes/grwoo-http-request-handler.php (modified) (2 diffs)
-
js/gr-apply-discount.js (added)
-
js/grconnect.js (modified) (4 diffs)
-
readme.txt (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
gratisfaction-all-in-one-loyalty-contests-referral-program-for-woocommerce/trunk/grconnect.php
r3404633 r3419727 3 3 /** 4 4 * @package Gratisfaction Connect 5 * @version 4. 5.55 * @version 4.6.0 6 6 */ 7 7 /* … … 9 9 Plugin URI: http://appsmav.com 10 10 Description: Loyalty + Referral + Rewards + Birthdays and Anniversaries + Giveaways + Sweepstakes. 11 Version: 4. 5.511 Version: 4.6.0 12 12 Author: Appsmav 13 13 Author URI: http://appsmav.com … … 34 34 // Load PHP compatibility polyfills for older versions 35 35 if (!function_exists('array_column')) { 36 include_once(GR_PLUGIN_BASE_PATH . '/includes/compat/array_column.php'); 36 $array_column_file = GR_PLUGIN_BASE_PATH . '/includes/compat/array_column.php'; 37 if (file_exists($array_column_file)) { 38 include_once($array_column_file); 39 } 37 40 } 38 41 if (!function_exists('hash_equals')) { 39 include_once(GR_PLUGIN_BASE_PATH . '/includes/compat/hash_equals.php'); 42 $hash_equals_file = GR_PLUGIN_BASE_PATH . '/includes/compat/hash_equals.php'; 43 if (file_exists($hash_equals_file)) { 44 include_once($hash_equals_file); 45 } 40 46 } 41 47 … … 47 53 const REDEEM_COUPON = 'GRPAYPOINTS'; 48 54 49 public static $_plugin_version = '4. 5.5';55 public static $_plugin_version = '4.6.0'; 50 56 public static $_callback_url = 'https://gratisfaction.appsmav.com/'; 51 57 public static $_api_version = 'newapi/v2/'; … … 59 65 { 60 66 try { 67 // Include required files first (before any hooks that might use them) 68 $this->include_files(); 69 61 70 // register actions 62 71 add_action('admin_init', [$this, 'admin_init']); 63 72 add_action('admin_menu', [$this, 'add_menu']); 64 73 add_action('plugins_loaded', [$this, 'woohook_init']); 74 75 // Enqueue global JavaScript for cart/checkout 76 // Use priority 5 to ensure it runs early enough 77 add_action('wp_enqueue_scripts', [$this, 'gr_enqueue_apply_discount_handler'], 5); 65 78 66 79 register_activation_hook(__FILE__, [$this, 'activate_endpoints']); … … 76 89 add_filter('woocommerce_cart_totals_coupon_label', [$this, 'coupon_label']); 77 90 add_filter('woocommerce_coupon_is_valid', [$this, 'validate_apply_coupon']); 91 92 // Clear success message when GR coupon is removed 93 add_action('woocommerce_removed_coupon', [$this, 'clear_success_message_on_coupon_removal'], 10, 1); 78 94 79 95 // display points on a separate tab on user's account page … … 88 104 add_action('rest_api_init', [$this, 'register_rest_routes'], 10); 89 105 106 // AJAX handlers for block-based cart/checkout 107 add_action('wp_ajax_get_gr_loyalty_points_blocks', [$this, 'gr_ajax_get_loyalty_points_blocks']); 108 add_action('wp_ajax_nopriv_get_gr_loyalty_points_blocks', [$this, 'gr_ajax_get_loyalty_points_blocks']); 109 90 110 // Handle plugin upgrades 91 add_action('upgrader_process_complete', [$this, 'gr_handle_plugin_upgrade'], 10, 2); 111 add_action('upgrader_process_complete', [$this, 'gr_handle_plugin_upgrade'], 10, 2); 92 112 } catch (Exception $ex) { 93 113 } … … 103 123 // Check if our plugin was updated 104 124 $plugin_path = plugin_basename(__FILE__); 105 if (isset($options['plugins']) && is_array($options['plugins']) && in_array($plugin_path, $options['plugins'], true)) {125 if (isset($options['plugins']) && is_array($options['plugins']) && appsmav_in_array($plugin_path, $options['plugins'], true)) { 106 126 107 127 // Safely get plugin data … … 140 160 { 141 161 try { 162 // Only register REST routes if WP_REST_Controller exists (WordPress 4.4+) 163 if (!class_exists('Grwoo_API')) { 164 return; 165 } 142 166 $route = new Grwoo_API(); 143 167 $route->register_apis(); … … 169 193 } 170 194 195 /** 196 * Enqueue global JavaScript handler for Apply Points button 197 * Works for both shortcode and block-based cart/checkout pages 198 * 199 * @return void 200 */ 201 public function gr_enqueue_apply_discount_handler() 202 { 203 try { 204 // Ensure WooCommerce is active 205 if (!function_exists('is_cart') || !function_exists('is_checkout')) { 206 return; 207 } 208 209 // Check if this is a cart or checkout page (traditional or block) 210 $is_cart_page = is_cart() || is_checkout(); 211 212 // Check if this is a product page (for "Restrictions apply" tooltip) 213 $is_product_page = function_exists('is_product') && is_product(); 214 215 // Additional check for pages with cart/checkout shortcodes 216 $has_cart_shortcode = false; 217 global $post; 218 if (!$is_cart_page && isset($post->post_content)) { 219 // Check if page content has woocommerce cart or checkout shortcode 220 $has_cart_shortcode = ( 221 appsmav_strpos($post->post_content, '[woocommerce_cart') !== false || 222 appsmav_strpos($post->post_content, '[woocommerce_checkout') !== false 223 ); 224 } 225 226 // Additional check for pages with cart/checkout BLOCKS (Gutenberg) 227 $has_cart_block = false; 228 if (!$is_cart_page && !$has_cart_shortcode && function_exists('has_block') && isset($post->ID)) { 229 // Check if page has WooCommerce cart or checkout blocks 230 $has_cart_block = ( 231 has_block('woocommerce/cart', $post->ID) || 232 has_block('woocommerce/checkout', $post->ID) 233 ); 234 } 235 236 // Additional check for block-based cart/checkout pages using sanitized URL 237 $is_cart_checkout_url = false; 238 if (isset($_SERVER['REQUEST_URI'])) { 239 $request_uri = sanitize_text_field(appsmav_unslash($_SERVER['REQUEST_URI'])); 240 241 // Get actual cart/checkout slugs from WooCommerce (works with custom slugs & translations) 242 $cart_page_id = function_exists('wc_get_page_id') ? wc_get_page_id('cart') : 0; 243 $checkout_page_id = function_exists('wc_get_page_id') ? wc_get_page_id('checkout') : 0; 244 245 $cart_slug = ($cart_page_id > 0) ? get_post_field('post_name', $cart_page_id) : 'cart'; 246 $checkout_slug = ($checkout_page_id > 0) ? get_post_field('post_name', $checkout_page_id) : 'checkout'; 247 248 // Check URL with actual slugs (handles custom slugs, translations, subdirectories) 249 $is_cart_checkout_url = (appsmav_strpos($request_uri, '/' . $cart_slug) !== false) || 250 (appsmav_strpos($request_uri, '/' . $checkout_slug) !== false) || 251 (appsmav_strpos($request_uri, 'page_id=' . $cart_page_id) !== false) || 252 (appsmav_strpos($request_uri, 'page_id=' . $checkout_page_id) !== false); 253 } 254 255 // Combine all cart/checkout detection methods (4-layer detection) 256 $is_cart_or_checkout = $is_cart_page || $has_cart_shortcode || $has_cart_block || $is_cart_checkout_url; 257 258 // Exit early if not a cart/checkout/product page 259 if (!$is_cart_or_checkout && !$is_product_page) { 260 return; 261 } 262 263 // Check if this is a block-based page 264 $is_block_page = gr_is_cart_or_checkout_block(); 265 266 // Enqueue FSE-compatible styles for cart/checkout pages 267 // Styles moved from inline to CSS file for FSE theme.json compatibility 268 wp_enqueue_style( 269 'gr-frontend-styles', 270 plugins_url('/css/gr-frontend.css', __FILE__), 271 array(), 272 self::$_plugin_version 273 ); 274 275 // Prepare script dependencies 276 $dependencies = array('jquery'); 277 // Script dependency on jquery only (WC Blocks loaded independently) 278 279 // Enqueue the script (consolidated frontend cart/checkout handler) 280 $script_url = plugins_url('/js/gr-apply-discount.js', __FILE__); 281 282 wp_enqueue_script( 283 'gr-apply-discount-handler', 284 $script_url, 285 $dependencies, 286 self::$_plugin_version, 287 true 288 ); 289 290 // Get the current GR coupon code from session (safe fallback) 291 $redeem_coupon = self::REDEEM_COUPON; 292 try { 293 if (!empty(WC()->session)) { 294 $session_coupon = WC()->session->get('gr_paybypoints_coupon', self::REDEEM_COUPON); 295 if (!empty($session_coupon)) { 296 $redeem_coupon = $session_coupon; 297 } 298 } 299 } catch (Exception $coupon_ex) { 300 // Use default if session not available 301 $redeem_coupon = self::REDEEM_COUPON; 302 } 303 304 // Get WooCommerce cart and checkout URLs 305 $cart_url = ''; 306 $checkout_url = ''; 307 if (function_exists('wc_get_cart_url')) { 308 $cart_url = wc_get_cart_url(); 309 } 310 if (function_exists('wc_get_checkout_url')) { 311 $checkout_url = wc_get_checkout_url(); 312 } 313 314 // Pass data to JavaScript with properly escaped values 315 wp_localize_script('gr-apply-discount-handler', 'grconnect_vars', array( 316 'ajax_url' => esc_url(admin_url('admin-ajax.php')), 317 'apply_nonce' => wp_create_nonce('apply_gr_discount'), 318 'remove_nonce' => wp_create_nonce('remove_gr_discount'), 319 'blocks_nonce' => wp_create_nonce('gr_blocks_loyalty_data'), 320 'cart_details_nonce' => wp_create_nonce('gr_nonce'), 321 'gr_coupon' => (string) $redeem_coupon, // Authoritative GR coupon code for exact matching 322 'coupon_removed_message' => esc_html__('Coupon removed successfully.', 'gratisfaction'), 323 'is_cart_page' => $is_cart_or_checkout, // Detected in PHP - handles all cases 324 'is_block_page' => $is_block_page, // Block page detection from PHP 325 'cart_url' => esc_url($cart_url), // Official WooCommerce cart URL 326 'checkout_url' => esc_url($checkout_url), // Official WooCommerce checkout URL 327 'use_php_hooks' => $has_cart_shortcode // TRUE ONLY for shortcode pages, NOT official cart/checkout 328 )); 329 330 // If block page, also pass grBlocksData for block-specific functionality 331 if ($is_block_page) { 332 // Generate fresh nonce for blocks 333 $fresh_nonce = wp_create_nonce('gr_blocks_loyalty_data'); 334 335 wp_localize_script('gr-apply-discount-handler', 'grBlocksData', array( 336 'ajax_url' => esc_url(admin_url('admin-ajax.php')), 337 'nonce' => $fresh_nonce, 338 'cart_url' => esc_url(wc_get_cart_url()), 339 'is_checkout' => is_checkout() ? '1' : '0' 340 )); 341 } 342 } catch (Exception $ex) { 343 // Silently catch exception 344 } 345 } 346 171 347 public function get_discount_error_message($message, $message_code, $coupon) 172 348 { … … 175 351 return $message; 176 352 353 // Safe object method call - verify coupon is object and has get_code method 354 if (empty($coupon) || !is_object($coupon) || !method_exists($coupon, 'get_code')) 355 return $message; 356 177 357 $coupon = appsmav_strtolower($coupon->get_code()); 178 358 $redeem_coupon = appsmav_strtolower(WC()->session->get('gr_paybypoints_coupon', self::REDEEM_COUPON)); … … 190 370 return $message; 191 371 372 // Safe object method call - verify coupon is object and has get_code method 373 if (empty($coupon) || !is_object($coupon) || !method_exists($coupon, 'get_code')) 374 return $message; 375 192 376 $coupon = appsmav_strtolower($coupon->get_code()); 377 $redeem_coupon = appsmav_strtolower(WC()->session->get('gr_paybypoints_coupon', self::REDEEM_COUPON)); 378 if ($coupon === $redeem_coupon) { 379 // Message code 1 = Coupon applied successfully 380 if (WC_Coupon::WC_COUPON_SUCCESS === $message_code) { 381 // Return message from session - WooCommerce will display it via notice system 382 // For classic pages: WooCommerce displays notices automatically during fragment refresh 383 // For block pages: JavaScript handles display (but filter still needs to return message for consistency) 384 return __(WC()->session->get('gr_redeemed_status_msg'), 'gratisfaction'); 385 } 386 387 // Message code 2 = Coupon removed successfully 388 if (2 === $message_code) { 389 // Return translatable message from language files 390 return __('Coupon removed successfully.', 'gratisfaction'); 391 } 392 } 393 } catch (Exception $ex) { 394 395 } 396 return $message; 397 } 398 399 /** 400 * Clear success message when GR coupon is removed 401 * This prevents "Reward added Successfully" from showing after coupon removal 402 * 403 * @param string $coupon_code The coupon code that was removed 404 */ 405 public function clear_success_message_on_coupon_removal($coupon_code) 406 { 407 try { 408 if (empty(WC()->session)) { 409 return; 410 } 411 412 // Check if the removed coupon is our GR coupon 193 413 $redeem_coupon = WC()->session->get('gr_paybypoints_coupon', self::REDEEM_COUPON); 194 if ($coupon === appsmav_strtolower($redeem_coupon)) { 195 if (WC_Coupon::WC_COUPON_SUCCESS === $message_code) 196 return __(WC()->session->get('gr_redeemed_status_msg'), 'gratisfaction'); 414 if (appsmav_strtolower($coupon_code) === appsmav_strtolower($redeem_coupon)) { 415 // Clear the success message from session 416 WC()->session->set('gr_redeemed_status_msg', ''); 417 418 // Also clear WooCommerce notices that contain the success message 419 if (function_exists('wc_get_notices') && function_exists('wc_clear_notices') && function_exists('wc_add_notice')) { 420 $all_notices = wc_get_notices(); 421 422 // Clear all notices 423 wc_clear_notices(); 424 425 // Re-add only notices that are NOT the "Reward added" message 426 if (!empty($all_notices)) { 427 foreach ($all_notices as $notice_type => $notices) { 428 if (is_array($notices)) { 429 foreach ($notices as $notice) { 430 $notice_text = is_array($notice) && isset($notice['notice']) ? $notice['notice'] : $notice; 431 $notice_text_lower = appsmav_strtolower($notice_text); 432 433 // Skip if this is the "Reward added" success message 434 if (appsmav_strpos($notice_text_lower, 'reward added') !== false || 435 appsmav_strpos($notice_text_lower, 'added successfully') !== false) { 436 continue; // Don't re-add this notice 437 } 438 439 // Re-add other notices 440 wc_add_notice($notice_text, $notice_type); 441 } 442 } 443 } 444 } 445 } 197 446 } 198 447 } catch (Exception $ex) { 199 200 } 201 return $message; 448 // Silently fail 449 } 202 450 } 203 451 … … 287 535 if (empty($ratio)) 288 536 $ratio = self::gr_get_currency_ratio(); 537 538 // Prevent division by zero 539 if (empty($ratio) || $ratio == 0) { 540 $ratio = 1; 541 } 289 542 290 543 // Set redeem points in descriptions … … 371 624 if ($is_blocked_role) 372 625 { 373 echo WC()->session->get('no_records_found', 'No Activites Found');626 echo esc_html(WC()->session->get('no_records_found', 'No Activites Found')); 374 627 return; 375 628 } … … 395 648 396 649 if(!empty($resp)) 397 $resp = json_decode($resp, true); 650 $resp = appsmav_json_decode($resp, true); 651 652 // Ensure $resp is always an array to prevent "Cannot access offset of type string on string" error 653 if(!is_array($resp)) 654 $resp = array(); 398 655 399 656 if(!empty($resp['error'])) … … 420 677 $this->get_settings_api(); 421 678 422 echo $style.'<div class="rewardsActivities"><h3>'. WC()->session->get('label_life_time_points', 'My Life Time Points').'</h3><ul class="pointsCon clearfix">423 <li><label>'. WC()->session->get('label_available_points', 'Redeemable points').'</label><span class="titlePoints">'.$resp['user_points'].'</span></li>424 <li><label>'. WC()->session->get('label_exclusion_points', 'Latest Exclusion period points').'</label><span class="titlePoints">'.$resp['exclusion_points'].'</span></li>425 <li><label>'. WC()->session->get('label_total_points', 'Total points').'</label><span class="titlePoints">'.$resp['total_points'].'</span></li>426 <li><label>'. WC()->session->get('label_redeemed_points', 'Redeemed points').'</label><span class="titlePoints">'.$resp['redeem_points'].'</span></li>679 echo $style.'<div class="rewardsActivities"><h3>'.esc_html(WC()->session->get('label_life_time_points', 'My Life Time Points')).'</h3><ul class="pointsCon clearfix"> 680 <li><label>'.esc_html(WC()->session->get('label_available_points', 'Redeemable points')).'</label><span class="titlePoints">'.absint($resp['user_points']).'</span></li> 681 <li><label>'.esc_html(WC()->session->get('label_exclusion_points', 'Latest Exclusion period points')).'</label><span class="titlePoints">'.absint($resp['exclusion_points']).'</span></li> 682 <li><label>'.esc_html(WC()->session->get('label_total_points', 'Total points')).'</label><span class="titlePoints">'.absint($resp['total_points']).'</span></li> 683 <li><label>'.esc_html(WC()->session->get('label_redeemed_points', 'Redeemed points')).'</label><span class="titlePoints">'.absint($resp['redeem_points']).'</span></li> 427 684 </ul></div>'; 428 685 } 429 686 catch(Exception $e) 430 687 { 431 echo WC()->session->get('no_records_found', 'No Activites Found');688 echo esc_html(WC()->session->get('no_records_found', 'No Activites Found')); 432 689 } 433 690 … … 601 858 602 859 $order = new WC_Order($order_id); 860 if (empty($order) || !($order instanceof WC_Order)) { 861 return; 862 } 603 863 604 864 $param = []; … … 610 870 $user_email = $ordered_user->get('user_email'); 611 871 612 //$param['user_email'] = $user_email; 613 $param['roles'] = (empty($ordered_user) || !is_object($ordered_user)) ? [] : $ordered_user->roles; 872 $param['roles'] = ($ordered_user instanceof WP_User) ? $ordered_user->roles : []; 614 873 615 874 if (version_compare( WC_VERSION, '3.7', '<' )) … … 628 887 { 629 888 $points = appsmav_explode('_', $gr_applied_points); 630 $param['redeem_points'] = empty($points['1']) ? 0 : $points['1']; 631 $param['redeem_charges'] = empty($points['2']) ? 0 : $points['2']; 889 // Ensure $points is array and has required elements (explode returns numeric indices: 0, 1, 2...) 890 if (is_array($points) && count($points) >= 3) { 891 $param['redeem_points'] = isset($points[1]) ? absint($points[1]) : 0; 892 $param['redeem_charges'] = isset($points[2]) ? floatval($points[2]) : 0; 893 } else { 894 $param['redeem_points'] = 0; 895 $param['redeem_charges'] = 0; 896 } 632 897 } 633 898 … … 640 905 $param['subtotal'] = $order->get_subtotal(); 641 906 $param['total'] = $order->get_total(); 642 //$param['total'] = $order->get_total() - $order->get_total_refunded();643 644 // Full refund, set total amount for points deduction.645 // if ($param['total'] <= 0)646 // $param['total'] = $order->get_total();647 648 907 $param['refunded'] = $order->get_total_refunded(); 649 908 $param['shipping'] = $order->get_shipping_total(); … … 656 915 if (!empty($refunds) && is_array($refunds)) { 657 916 foreach ($refunds as $key => $refund) { 658 if (empty($refund) || ! is_object($refund)) {917 if (empty($refund) || !($refund instanceof WC_Order_Refund)) { 659 918 continue; 660 919 } 920 661 921 $refundData[$key]['refund'] = self::sanitize_refund_data_for_api($refund->get_data()); 922 662 923 $refund_items = $refund->get_items(); 663 924 if (!empty($refund_items) && is_array($refund_items)) { 664 925 foreach ($refund_items as $item_id => $item) { 665 if (empty($item) || ! is_object($item)) {926 if (empty($item) || !($item instanceof WC_Order_Item)) { 666 927 continue; 667 928 } 929 668 930 $refundData[$key]['line_items'][$item_id] = self::sanitize_refund_data_for_api($item->get_data()); 669 931 } … … 700 962 { 701 963 $order_data = $order->get_data(); 702 $param['name'] = empty($order_data['billing']['first_name']) ? '' : $order_data['billing']['first_name']; 964 $billing_data = isset($order_data['billing']) && is_array($order_data['billing']) ? $order_data['billing'] : array(); 965 $param['name'] = empty($billing_data['first_name']) ? '' : $billing_data['first_name']; 703 966 $param['number'] = empty($order_data['number']) ? '' : $order_data['number']; 704 $param['first_name'] = empty($order_data['billing']['first_name']) ? '' : $order_data['billing']['first_name']; 705 $param['last_name'] = empty($order_data['billing']['last_name']) ? '' : $order_data['billing']['last_name']; 706 $param['postcode'] = empty($order_data['billing']['postcode']) ? '' : $order_data['billing']['postcode']; 707 $param['country'] = empty($order_data['billing']['country']) ? '' : $order_data['billing']['country']; 708 } 709 710 $param['created_date'] = $order->get_date_created()->format('c'); 967 $param['first_name'] = empty($billing_data['first_name']) ? '' : $billing_data['first_name']; 968 $param['last_name'] = empty($billing_data['last_name']) ? '' : $billing_data['last_name']; 969 $param['postcode'] = empty($billing_data['postcode']) ? '' : $billing_data['postcode']; 970 $param['country'] = empty($billing_data['country']) ? '' : $billing_data['country']; 971 } 972 973 // Safe date handling - prevent fatal if get_date_created() returns null 974 $date_created = $order->get_date_created(); 975 $param['created_date'] = (is_object($date_created) && method_exists($date_created, 'format')) 976 ? $date_created->format('c') 977 : current_time('c'); 711 978 $param['user_ip'] = $order->get_customer_ip_address(); 712 979 $param['email'] = !empty($user_email) ? $user_email : $order->get_billing_email(); … … 722 989 723 990 if( appsmav_get_parent_id($order) === 0 && get_post_meta( $order_id, 'has_wcmp_sub_order', true ) == '1'){ 724 $param['comment'] = 'Main WCMp Order Id ' . str_replace('wc-', '', sanitize_text_field($_REQUEST['order_status'])) . ' - ' . $order_id . ' From ' . get_option('grconnect_shop_id', 0).' total '.$param['total']; 991 $order_status_safe = isset($_REQUEST['order_status']) ? sanitize_text_field($_REQUEST['order_status']) : $param['order_status']; 992 $param['comment'] = 'Main WCMp Order Id ' . appsmav_str_replace('wc-', '', $order_status_safe) . ' - ' . $order_id . ' From ' . get_option('grconnect_shop_id', 0).' total '.$param['total']; 725 993 $param['total'] = 0; 726 994 $param['subtotal'] = 0; … … 733 1001 { 734 1002 if( appsmav_get_parent_id($order) === 0 && get_post_meta( $order_id, 'has_sub_order', true ) == '1'){ 735 $param['comment'] = 'Main Dokan Order Id ' . str_replace('wc-', '', sanitize_text_field($_REQUEST['order_status'])) . ' - ' . $order_id . ' From ' . get_option('grconnect_shop_id', 0).' total '.$param['total']; 1003 $order_status_safe = isset($_REQUEST['order_status']) ? sanitize_text_field($_REQUEST['order_status']) : $param['order_status']; 1004 $param['comment'] = 'Main Dokan Order Id ' . appsmav_str_replace('wc-', '', $order_status_safe) . ' - ' . $order_id . ' From ' . get_option('grconnect_shop_id', 0).' total '.$param['total']; 736 1005 $param['total'] = 0; 737 1006 $param['subtotal'] = 0; … … 768 1037 // Set up the settings for this plugin 769 1038 $order = new WC_Order($order_id); 1039 if (empty($order) || !($order instanceof WC_Order)) { 1040 return; 1041 } 770 1042 771 1043 $param = []; … … 865 1137 { 866 1138 $order_data = $order->get_data(); 867 $param['name'] = empty($order_data['billing']['first_name']) ? '' : $order_data['billing']['first_name']; 1139 $billing_data = isset($order_data['billing']) && is_array($order_data['billing']) ? $order_data['billing'] : array(); 1140 $param['name'] = empty($billing_data['first_name']) ? '' : $billing_data['first_name']; 868 1141 $param['number'] = empty($order_data['number']) ? '' : $order_data['number']; 869 $param['first_name'] = empty($ order_data['billing']['first_name']) ? '' : $order_data['billing']['first_name'];870 $param['last_name'] = empty($ order_data['billing']['last_name']) ? '' : $order_data['billing']['last_name'];871 $param['postcode'] = empty($ order_data['billing']['postcode']) ? '' : $order_data['billing']['postcode'];872 $param['country'] = empty($ order_data['billing']['country']) ? '' : $order_data['billing']['country'];1142 $param['first_name'] = empty($billing_data['first_name']) ? '' : $billing_data['first_name']; 1143 $param['last_name'] = empty($billing_data['last_name']) ? '' : $billing_data['last_name']; 1144 $param['postcode'] = empty($billing_data['postcode']) ? '' : $billing_data['postcode']; 1145 $param['country'] = empty($billing_data['country']) ? '' : $billing_data['country']; 873 1146 } 874 1147 … … 880 1153 $param['comment'] = 'Order Id - ' . $order_id . ' From ' . get_option('siteurl'); 881 1154 $param['status'] = 'Add'; 882 $param['created_date'] = $order->get_date_created()->format('c'); 1155 // Safe date handling - prevent fatal if get_date_created() returns null 1156 $date_created = $order->get_date_created(); 1157 $param['created_date'] = (is_object($date_created) && method_exists($date_created, 'format')) 1158 ? $date_created->format('c') 1159 : current_time('c'); 883 1160 $param['user_ip'] = $order->get_customer_ip_address(); 884 1161 … … 902 1179 903 1180 if( appsmav_get_parent_id($order) === 0 && get_post_meta( $order_id, 'has_wcmp_sub_order', true ) == '1'){ 904 $param['comment'] = 'Main WCMp Order Id ' . str_replace('wc-', '', sanitize_text_field($_REQUEST['order_status'])) . ' - ' . $order_id . ' From ' . get_option('grconnect_shop_id', 0).' total '.$param['total']; 1181 $order_status_safe = isset($_REQUEST['order_status']) ? sanitize_text_field($_REQUEST['order_status']) : $param['order_status']; 1182 $param['comment'] = 'Main WCMp Order Id ' . appsmav_str_replace('wc-', '', $order_status_safe) . ' - ' . $order_id . ' From ' . get_option('grconnect_shop_id', 0).' total '.$param['total']; 905 1183 $param['total'] = 0; 906 1184 $param['subtotal'] = 0; … … 913 1191 { 914 1192 if( appsmav_get_parent_id($order) === 0 && get_post_meta( $order_id, 'has_sub_order', true ) == '1'){ 915 $param['comment'] = 'Main Dokan Order Id ' . str_replace('wc-', '', sanitize_text_field($_REQUEST['order_status'])) . ' - ' . $order_id . ' From ' . get_option('grconnect_shop_id', 0).' total '.$param['total']; 1193 $order_status_safe = isset($_REQUEST['order_status']) ? sanitize_text_field($_REQUEST['order_status']) : $param['order_status']; 1194 $param['comment'] = 'Main Dokan Order Id ' . appsmav_str_replace('wc-', '', $order_status_safe) . ' - ' . $order_id . ' From ' . get_option('grconnect_shop_id', 0).' total '.$param['total']; 916 1195 $param['total'] = 0; 917 1196 $param['subtotal'] = 0; … … 951 1230 // Set up the settings for this plugin 952 1231 $user = get_userdata($customer_id); 1232 if (empty($user) || !($user instanceof WP_User)) { 1233 return; 1234 } 1235 953 1236 $shop_id = get_option('grconnect_shop_id', 0); 954 1237 … … 965 1248 $param['first_name'] = empty($user->first_name) ? '' : $user->first_name; 966 1249 $param['last_name'] = empty($user->last_name) ? '' : $user->last_name; 967 $param['user_ip'] = empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR'];1250 $param['user_ip'] = !empty($_SERVER['REMOTE_ADDR']) ? sanitize_text_field(appsmav_unslash($_SERVER['REMOTE_ADDR'])) : ''; 968 1251 $param['customer_id'] = $customer_id; 969 1252 $param['id_shop'] = $shop_id; … … 981 1264 982 1265 if(!empty($resp)) 983 $resp = json_decode($resp, true); 1266 $resp = appsmav_json_decode($resp, true); 1267 1268 // Ensure $resp is always an array to prevent "Cannot access offset of type string on string" error 1269 if(!is_array($resp)) 1270 $resp = array(); 984 1271 985 1272 if(!empty($resp['error'])) … … 1009 1296 1010 1297 $user = get_userdata($customer_id); 1298 if (empty($user) || !($user instanceof WP_User)) { 1299 return; 1300 } 1011 1301 1012 1302 $param['email'] = $user->user_email; … … 1015 1305 $param['last_name'] = empty($user->last_name) ? '' : $user->last_name; 1016 1306 1017 $param['user_ip'] = empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR'];1307 $param['user_ip'] = !empty($_SERVER['REMOTE_ADDR']) ? sanitize_text_field(appsmav_unslash($_SERVER['REMOTE_ADDR'])) : ''; 1018 1308 $param['customer_id'] = $customer_id; 1019 1309 $param['id_shop'] = $grShopId; … … 1031 1321 1032 1322 if(!empty($resp)) 1033 $resp = json_decode($resp, true); 1323 $resp = appsmav_json_decode($resp, true); 1324 1325 // Ensure $resp is always an array to prevent "Cannot access offset of type string on string" error 1326 if (!is_array($resp)) 1327 $resp = array(); 1034 1328 1035 1329 if(!empty($resp['error'])) … … 1062 1356 // Set up the settings for this plugin 1063 1357 $action = isset($_REQUEST['action']) ? sanitize_text_field($_REQUEST['action']) : ''; 1064 if(!empty($action) && $action == 'woocommerce_delete_refund') 1065 { 1358 if (!empty($action) && $action === 'woocommerce_delete_refund') { 1066 1359 $refund = new WC_Order_Refund($refund_id); 1067 if (empty($refund) || ! is_object($refund)) {1360 if (empty($refund) || !($refund instanceof WC_Order_Refund)) { 1068 1361 return; 1069 1362 } 1363 1070 1364 $parent_id = appsmav_get_parent_id($refund); 1071 1365 if (empty($parent_id)) { 1072 1366 return; 1073 1367 } 1368 1074 1369 $order = new WC_Order($parent_id); 1075 if (empty($order) || ! is_object($order)) {1370 if (empty($order) || !($order instanceof WC_Order)) { 1076 1371 return; 1077 1372 } … … 1105 1400 } 1106 1401 1107 $param['created_date'] = $order->get_date_created()->format('c'); 1402 // Safe date handling - prevent fatal if get_date_created() returns null 1403 $date_created = $order->get_date_created(); 1404 $param['created_date'] = (is_object($date_created) && method_exists($date_created, 'format')) 1405 ? $date_created->format('c') 1406 : current_time('c'); 1108 1407 $param['user_ip'] = $order->get_customer_ip_address(); 1109 1408 $param['email'] = !empty($email) ? $email : $order->get_billing_email(); … … 1126 1425 { 1127 1426 $order_data = $order->get_data(); 1128 $param['name'] = empty($order_data['billing']['first_name']) ? '' : $order_data['billing']['first_name']; 1427 $billing_data = isset($order_data['billing']) && is_array($order_data['billing']) ? $order_data['billing'] : array(); 1428 $param['name'] = empty($billing_data['first_name']) ? '' : $billing_data['first_name']; 1129 1429 $param['number'] = empty($order_data['number']) ? '' : $order_data['number']; 1130 $param['first_name'] = empty($ order_data['billing']['first_name']) ? '' : $order_data['billing']['first_name'];1131 $param['last_name'] = empty($ order_data['billing']['last_name']) ? '' : $order_data['billing']['last_name'];1132 $param['postcode'] = empty($ order_data['billing']['postcode']) ? '' : $order_data['billing']['postcode'];1133 $param['country'] = empty($ order_data['billing']['country']) ? '' : $order_data['billing']['country'];1430 $param['first_name'] = empty($billing_data['first_name']) ? '' : $billing_data['first_name']; 1431 $param['last_name'] = empty($billing_data['last_name']) ? '' : $billing_data['last_name']; 1432 $param['postcode'] = empty($billing_data['postcode']) ? '' : $billing_data['postcode']; 1433 $param['country'] = empty($billing_data['country']) ? '' : $billing_data['country']; 1134 1434 } 1135 1435 … … 1187 1487 $grCampId = get_option('grconnect_secret'); 1188 1488 $grPayload = get_option('grconnect_payload'); 1189 if (empty($grShopId) || empty($grAppId) || empty($grCampId) || empty($grPayload) || empty($_REQUEST['refund_amount'])) 1489 1490 // Check refund amount exists and is valid 1491 $refund_amount = isset($_REQUEST['refund_amount']) ? sanitize_text_field(appsmav_unslash($_REQUEST['refund_amount'])) : ''; 1492 1493 if (empty($grShopId) || empty($grAppId) || empty($grCampId) || empty($grPayload) || empty($refund_amount)) 1190 1494 return; 1191 1495 1192 1496 // Set up the settings for this plugin 1193 1497 $order = new WC_Order($order_id); 1194 if (empty($order) || ! is_object($order)) {1498 if (empty($order) || !($order instanceof WC_Order)) { 1195 1499 return; 1196 1500 } … … 1228 1532 if (!empty($refunds) && is_array($refunds)) { 1229 1533 foreach ($refunds as $key => $refund) { 1230 if (empty($refund) || ! is_object($refund)) {1534 if (empty($refund) || !($refund instanceof WC_Order_Refund)) { 1231 1535 continue; 1232 1536 } … … 1266 1570 $param['tax'] = $order->get_total_tax(); 1267 1571 1268 $param['created_date'] = $order->get_date_created()->format('c'); 1572 // Safe date handling - prevent fatal if get_date_created() returns null 1573 $date_created = $order->get_date_created(); 1574 $param['created_date'] = (is_object($date_created) && method_exists($date_created, 'format')) 1575 ? $date_created->format('c') 1576 : current_time('c'); 1269 1577 $param['user_ip'] = $order->get_customer_ip_address(); 1270 1578 $param['email'] = !empty($user_email) ? $user_email : $order->get_billing_email(); … … 1286 1594 { 1287 1595 $order_data = $order->get_data(); 1288 $param['name'] = empty($order_data['billing']['first_name']) ? '' : $order_data['billing']['first_name']; 1596 $billing_data = isset($order_data['billing']) && is_array($order_data['billing']) ? $order_data['billing'] : array(); 1597 $param['name'] = empty($billing_data['first_name']) ? '' : $billing_data['first_name']; 1289 1598 $param['number'] = empty($order_data['number']) ? '' : $order_data['number']; 1290 $param['first_name'] = empty($ order_data['billing']['first_name']) ? '' : $order_data['billing']['first_name'];1291 $param['last_name'] = empty($ order_data['billing']['last_name']) ? '' : $order_data['billing']['last_name'];1292 $param['postcode'] = empty($ order_data['billing']['postcode']) ? '' : $order_data['billing']['postcode'];1293 $param['country'] = empty($ order_data['billing']['country']) ? '' : $order_data['billing']['country'];1599 $param['first_name'] = empty($billing_data['first_name']) ? '' : $billing_data['first_name']; 1600 $param['last_name'] = empty($billing_data['last_name']) ? '' : $billing_data['last_name']; 1601 $param['postcode'] = empty($billing_data['postcode']) ? '' : $billing_data['postcode']; 1602 $param['country'] = empty($billing_data['country']) ? '' : $billing_data['country']; 1294 1603 } 1295 1604 … … 1425 1734 1426 1735 $order = new WC_Order($order_id); 1427 if (empty($order) )1736 if (empty($order) || !($order instanceof WC_Order) || empty($order->get_id())) 1428 1737 throw new Exception('Order details not found'); 1429 1738 … … 1439 1748 foreach($couponsArr as $coupon_code) 1440 1749 { 1441 $redeem_coupon = WC()->session->get('gr_paybypoints_coupon', self::REDEEM_COUPON); 1442 if(appsmav_strtolower($coupon_code) === appsmav_strtolower($redeem_coupon)) 1750 $coupon_code_lower = appsmav_strtolower($coupon_code); 1751 $redeem_coupon = appsmav_strtolower(WC()->session->get('gr_paybypoints_coupon', self::REDEEM_COUPON)); 1752 if($coupon_code_lower === $redeem_coupon) 1443 1753 { 1444 1754 WC()->session->set('gr_applied_points', ''); … … 1453 1763 1454 1764 //pbp appeding more variable is the config files 1765 // Cache session object for performance and safety 1766 $wc_session = WC()->session; 1767 $wc_cart = WC()->cart; 1768 1455 1769 $cart_count = 0; 1456 if (!empty( WC()->cart)) {1457 $cart_items = WC()->cart->get_cart();1458 $cart_count = (is_array($cart_items) || $cart_items instanceof Countable) ? count($cart_items) : 0;1459 1460 // Reset the session values if discount is not there1461 if ( empty(WC()->session->get('gr_user_max_discount', 0))) {1770 if (!empty($wc_cart)) { 1771 $cart_items = $wc_cart->get_cart(); 1772 $cart_count = (is_array($cart_items) || ($cart_items instanceof Countable)) ? count($cart_items) : 0; 1773 1774 // Validate session exists before accessing 1775 if (!empty($wc_session)) { 1462 1776 self::gr_calc_point_value(); 1463 1777 } … … 1465 1779 1466 1780 // Do not move this to above 1467 $discounted_amount = WC()->session->get('gr_user_max_discount', 0); 1468 $is_discount_applied = WC()->session->get('gr_discount_applied', 0); 1781 // Safely retrieve session values with fallbacks 1782 $discounted_amount = !empty($wc_session) ? $wc_session->get('gr_user_max_discount', 0) : 0; 1783 $is_discount_applied = !empty($wc_session) ? $wc_session->get('gr_discount_applied', 0) : 0; 1469 1784 $cart_url = !empty(wc_get_cart_url()) ? esc_url(wc_get_cart_url()) : ''; 1470 $gr_sdk_version = !empty(WC()->session->get('gr_sdk_version', 0)) ? WC()->session->get('gr_sdk_version', 0) : self::$_plugin_version; 1471 $gr_widget_config_version = !empty(WC()->session->get('gr_widget_config_version', 0)) ? WC()->session->get('gr_widget_config_version', 0) : WC()->session->get('gr_api_sess', 0); 1785 $gr_sdk_version = !empty($wc_session) && $wc_session->get('gr_sdk_version', 0) ? $wc_session->get('gr_sdk_version', 0) : self::$_plugin_version; 1786 $gr_widget_config_version = !empty($wc_session) && $wc_session->get('gr_widget_config_version', 0) ? $wc_session->get('gr_widget_config_version', 0) : (!empty($wc_session) ? $wc_session->get('gr_api_sess', 0) : 0); 1787 1788 // Check actual cart state for is_discount_applied on page load 1789 // Session flags can be stale, so verify coupon is actually in cart 1790 if (!empty($wc_session) && !empty($wc_cart)) { 1791 $redeem_coupon_raw = $wc_session->get('gr_paybypoints_coupon', self::REDEEM_COUPON); 1792 // Sanitize coupon code to prevent potential issues 1793 $redeem_coupon = sanitize_text_field($redeem_coupon_raw); 1794 $cart_has_coupon = $wc_cart->has_discount($redeem_coupon); 1795 1796 // CART IS SOURCE OF TRUTH: If coupon is in cart, it's applied 1797 // Override session flag with actual cart state for accuracy 1798 if ($cart_has_coupon) { 1799 $is_discount_applied = 1; 1800 } else { 1801 // Coupon not in cart, ensure flag is cleared 1802 $is_discount_applied = 0; 1803 } 1804 } 1472 1805 1473 1806 $config = [ … … 1492 1825 'sdk_version' => esc_js($gr_sdk_version), 1493 1826 'version' => esc_js($gr_widget_config_version), 1494 1495 ] ,1827 'ajax_url' => esc_js(admin_url('admin-ajax.php')) 1828 ] 1496 1829 ]; 1497 1830 … … 1508 1841 } 1509 1842 } 1510 1511 echo '<script>var AMGRConfig = ' . wp_json_encode($config) . '; 1843 1844 // Safely retrieve session values with validation 1845 // Include currency_ratio so SDK can calculate points worth correctly for multi-currency stores 1846 $currency_ratio = self::gr_get_currency_ratio(); 1847 1848 // Get current WooCommerce currency for display (overrides app.json currency) 1849 $current_currency = get_woocommerce_currency(); 1850 $currency_symbol = get_woocommerce_currency_symbol($current_currency); 1851 1852 $config['pay_points'] = [ 1853 'redeem_restrict_terms' => !empty($wc_session) ? esc_js($wc_session->get('gr_pay_redeem_restrict_terms', '')) : '', 1854 'label_redeem_restriction_apply' => !empty($wc_session) ? esc_js($wc_session->get('gr_label_redeem_restriction_apply', '')) : '', 1855 'paybypoints_coupon' => !empty($wc_session) ? esc_js($wc_session->get('gr_paybypoints_coupon', self::REDEEM_COUPON)) : esc_js(self::REDEEM_COUPON), 1856 'currency_ratio' => esc_js($currency_ratio), 1857 'current_currency' => esc_js($current_currency), 1858 'current_currency_symbol' => esc_js($currency_symbol) 1859 ]; 1860 1861 // Output config and load SDK script 1862 echo '<script type="text/javascript"> 1863 var AMGRConfig = ' . appsmav_json_encode($config) . '; 1512 1864 (function(d, s, id) { 1513 1865 var js, amjs = d.getElementsByTagName(s)[0]; 1514 1866 if (d.getElementById(id)) return; 1515 js = d.createElement(s); js.id = id; js.async = true; 1516 js.src = "' . esc_url(self::$_c_sdk_url) . '?v=' . esc_attr($gr_sdk_version) . '"; 1867 js = d.createElement(s); 1868 js.id = id; 1869 js.async = true; 1870 js.src = ' . appsmav_json_encode(esc_url(self::$_c_sdk_url) . '?v=' . esc_attr($gr_sdk_version)) . '; 1517 1871 amjs.parentNode.insertBefore(js, amjs); 1518 1872 }(document, "script", "gratisfaction-sdk")); … … 1543 1897 $order_id = sanitize_text_field($_GET['order-received']); 1544 1898 } elseif (isset($_GET['key']) && !empty($_GET['key']) && version_compare( WC_VERSION, '5.9', '>=' )) { 1545 $order_id = wc_get_order_id_by_order_key( sanitize_text_field( $_GET['key']) );1899 $order_id = wc_get_order_id_by_order_key( sanitize_text_field(appsmav_unslash($_GET['key'])) ); 1546 1900 } else { 1547 $url = $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI']; 1548 $template_name = strpos($url,'/order-received/') === false ? '/view-order/' : '/order-received/'; 1549 if (strpos($url,$template_name) !== false) { 1550 $start = strpos($url,$template_name); 1551 $first_part = substr($url, $start+appsmav_strlen($template_name)); 1552 $order_id = substr($first_part, 0, strpos($first_part, '/')); 1901 $server_name = !empty($_SERVER['SERVER_NAME']) ? sanitize_text_field(appsmav_unslash($_SERVER['SERVER_NAME'])) : ''; 1902 $request_uri = !empty($_SERVER['REQUEST_URI']) ? esc_url_raw(appsmav_unslash($_SERVER['REQUEST_URI'])) : ''; 1903 $url = $server_name . $request_uri; 1904 1905 // Get WooCommerce endpoint slugs (works with custom endpoints & translations) 1906 $order_received_slug = function_exists('get_option') ? get_option('woocommerce_checkout_order_received_endpoint', 'order-received') : 'order-received'; 1907 $view_order_slug = function_exists('get_option') ? get_option('woocommerce_myaccount_view_order_endpoint', 'view-order') : 'view-order'; 1908 1909 // Determine which endpoint is in the URL 1910 $template_name = (appsmav_strpos($url, '/' . $order_received_slug . '/') !== false) ? '/' . $order_received_slug . '/' : '/' . $view_order_slug . '/'; 1911 1912 if (appsmav_strpos($url, $template_name) !== false) { 1913 $start = appsmav_strpos($url, $template_name); 1914 $first_part = appsmav_substr($url, $start+appsmav_strlen($template_name)); 1915 $order_id = appsmav_substr($first_part, 0, appsmav_strpos($first_part, '/')); 1553 1916 } 1554 1917 } … … 1626 1989 try { 1627 1990 $app_config = gr_get_app_config(); 1628 if (empty($app_config['points']['loyalty_campaign_enabled']) || empty($app_config['reviews']['global_review_enabled']) || empty($app_config['reviews']['is_testimonial_enabled'])) { 1991 if (empty(appsmav_get_nested_array_value($app_config, ['points', 'loyalty_campaign_enabled'], 0)) || 1992 empty(appsmav_get_nested_array_value($app_config, ['reviews', 'global_review_enabled'], 0)) || 1993 empty(appsmav_get_nested_array_value($app_config, ['reviews', 'is_testimonial_enabled'], 0))) { 1629 1994 return; 1630 1995 } 1631 1996 1632 1997 $post_meta = get_post_meta($post_id); 1633 if (!isset($post_meta['email']) || empty($post_meta['email'][0]) || self::_isActiveCampaign() === false) 1998 // Safe array access - check if email exists and is array before accessing index 1999 $email_meta = isset($post_meta['email']) && is_array($post_meta['email']) ? $post_meta['email'] : array(); 2000 if (empty($email_meta[0]) || self::_isActiveCampaign() === false) 1634 2001 return; 1635 2002 … … 1647 2014 $review_details['comment_ID'] = !empty($post->ID) ? $post->ID : 0; 1648 2015 $review_details['comment_post_ID'] = !empty($post->ID) ? $post->ID : 0; 1649 $review_details['comment_author_email'] = !empty($post_meta['email'][0]) ? $post_meta['email'][0] : '';2016 $review_details['comment_author_email'] = (isset($post_meta['email']) && is_array($post_meta['email']) && isset($post_meta['email'][0])) ? $post_meta['email'][0] : ''; 1650 2017 $review_details['comment_date'] = !empty($post->post_date) ? $post->post_date : ''; 1651 2018 … … 1653 2020 $review_details['comment_approved'] = !empty($post->post_status == 'publish') ? 1 : 0; 1654 2021 $review_details['comment_status'] = $comment_status; 1655 $review_details['rating'] = !empty($post_meta['star_rating'][0]) ? $post_meta['star_rating'][0] : '';2022 $review_details['rating'] = (isset($post_meta['star_rating']) && is_array($post_meta['star_rating']) && isset($post_meta['star_rating'][0])) ? $post_meta['star_rating'][0] : ''; 1656 2023 $review_details['product_key'] = !empty($testimonial_key) ? $testimonial_key : ''; 1657 2024 $review_details['product_url'] = !empty($testimonial_url) ? $testimonial_url : ''; … … 1661 2028 1662 2029 // Check the user role is allowed to proceed 1663 $user = get_user_by('email', $post_meta['email'][0]); 2030 $user_email = (isset($post_meta['email']) && is_array($post_meta['email']) && isset($post_meta['email'][0])) ? $post_meta['email'][0] : ''; 2031 $user = !empty($user_email) ? get_user_by('email', $user_email) : false; 1664 2032 if (!empty($user)) { 1665 2033 $is_blocked_role = self::is_restricted_user_role($user->roles); … … 1739 2107 1740 2108 // Render the settings template 1741 if( in_array('woocommerce/woocommerce.php', apply_filters('active_plugins', get_option('active_plugins'))))2109 if(appsmav_in_array('woocommerce/woocommerce.php', apply_filters('active_plugins', get_option('active_plugins')))) 1742 2110 { 1743 2111 $frame_url = 'about:blank'; … … 1786 2154 $this->get_settings_api(1); 1787 2155 } 2156 1788 2157 self::gr_calc_point_value(); 1789 2158 } … … 1813 2182 $gr_pbp_auto_apply = WC()->session->get('gr_pbp_auto_apply', 0); 1814 2183 $gr_pbp_auto_apply_done = WC()->session->get('gr_pbp_auto_apply_done', 0); // First time only we need to set automatically 1815 if (empty($gr_pbp_auto_apply_done) && !empty($gr_pbp_auto_apply)) 2184 $manually_removed = WC()->session->get('gr_user_manually_removed_discount', 0); 2185 2186 // Only auto-apply if: not done yet, auto-apply is enabled, AND user didn't manually remove discount 2187 if (empty($gr_pbp_auto_apply_done) && !empty($gr_pbp_auto_apply) && empty($manually_removed)) 1816 2188 { 1817 $applied_coupons = WC()->cart->applied_coupons; 1818 if (empty($applied_coupons)) 2189 $cart_object = !empty(WC()->cart) ? WC()->cart : null; 2190 if (!empty($cart_object)) { 2191 $applied_coupons = $cart_object->applied_coupons; 2192 } else { 2193 $applied_coupons = array(); 2194 } 2195 if (empty($applied_coupons) && !empty($cart_object)) 1819 2196 { 1820 WC()->cart->add_discount($redeem_coupon); 2197 if (method_exists($cart_object, 'apply_coupon')) { 2198 $cart_object->apply_coupon($redeem_coupon); 2199 } else { 2200 $cart_object->add_discount($redeem_coupon); 2201 } 1821 2202 WC()->session->set('gr_pbp_auto_apply_done', 1); 1822 2203 WC()->session->set('gr_discount_applied', 1); … … 1841 2222 } 1842 2223 1843 $email = sanitize_email( $_POST['grconnect_login_email'] );2224 $email = isset($_POST['grconnect_login_email']) ? sanitize_email($_POST['grconnect_login_email']) : ''; 1844 2225 if(empty($email) || !filter_var($email, FILTER_VALIDATE_EMAIL)) 1845 2226 throw new Exception("Please enter valid email"); … … 1856 2237 $params['email'] = $email; 1857 2238 $params['admin_email'] = $adminEmail; 1858 $params['password'] = sanitize_text_field( $_POST['grconnect_login_pwd'] );2239 $params['password'] = isset($_POST['grconnect_login_pwd']) ? sanitize_text_field($_POST['grconnect_login_pwd']) : ''; 1859 2240 $params['shop_url'] = get_option('siteurl'); 1860 2241 $params['plugin_version'] = self::$_plugin_version; … … 1866 2247 1867 2248 if(!empty($resp)) 1868 $resp = json_decode($resp, true); 2249 $resp = appsmav_json_decode($resp, true); 2250 2251 // Ensure $resp is always an array to prevent "Cannot access offset of type string on string" error 2252 if(!is_array($resp)) 2253 $resp = array(); 1869 2254 1870 2255 if(empty($resp['error']) && !empty($resp['id_shop'])) … … 1909 2294 } 1910 2295 1911 die(json_encode($res)); 2296 echo appsmav_json_encode($res); 2297 wp_die(); 1912 2298 } 1913 2299 … … 1943 2329 1944 2330 if(!empty($res)) 1945 $res = json_decode($res, true); 2331 $res = appsmav_json_decode($res, true); 2332 2333 // Ensure $res is always an array to prevent "Cannot access offset of type string on string" error 2334 if(!is_array($res)) 2335 $res = array(); 1946 2336 1947 2337 if(!empty($res['is_shop']) && $res['is_shop'] == 1) … … 2003 2393 $params['date_format'] = 'd/m/Y'; //Dummy$p['grappsmav_reg_date_format']; 2004 2394 $params['exclusion_period'] = 0; //$p['grconnect_reg_exclusion_period']; 2005 $params['app_lang'] = str_replace('-', '_', get_bloginfo('language'));2395 $params['app_lang'] = appsmav_str_replace('-', '_', get_bloginfo('language')); 2006 2396 2007 2397 $myaccount_page_id = get_option('woocommerce_myaccount_page_id'); … … 2018 2408 2019 2409 if(!empty($res)) 2020 $res = json_decode($res, true); 2410 $res = appsmav_json_decode($res, true); 2411 2412 // Ensure $res is always an array to prevent "Cannot access offset of type string on string" error 2413 if(!is_array($res)) 2414 $res = array(); 2021 2415 2022 2416 if(empty($res['error']) && !empty($res['id_shop'])) … … 2057 2451 } 2058 2452 2059 die(json_encode($res)); 2453 echo appsmav_json_encode($res); 2454 wp_die(); 2060 2455 } 2061 2456 … … 2085 2480 $shop_credentials['payload'] = get_option('grconnect_payload', 0); 2086 2481 } 2087 // Get client IP address with fallback to empty string if not available2088 $ip_address = !empty($_SERVER['REMOTE_ADDR']) ? filter_var($_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP) : '';2089 // Prepare and execute API request to get auto-login token2482 // Get client IP address with fallback to empty string if not available 2483 $ip_address = !empty($_SERVER['REMOTE_ADDR']) ? filter_var(appsmav_unslash($_SERVER['REMOTE_ADDR']), FILTER_VALIDATE_IP) : ''; 2484 // Prepare and execute API request to get auto-login token 2090 2485 $httpObj = (new HttpRequestHandler) 2091 2486 ->setPostData([ … … 2099 2494 // Process API response if not empty 2100 2495 if (!empty($resp)) { 2101 $response = json_decode($resp, true);2496 $response = appsmav_json_decode($resp, true); 2102 2497 // Check if JSON is valid and is an array 2103 if ( json_last_error() === JSON_ERROR_NONE &&is_array($response)) {2498 if (is_array($response)) { 2104 2499 if (empty($response['error']) && !empty($response['token'])) { 2105 2500 $token = $response['token']; … … 2126 2521 } 2127 2522 } 2128 } catch (Exception $ex) { 2129 $res['message'] = 'Error: ' . $ex->getMessage(); 2130 $res['error'] = 1; 2131 } 2132 die(json_encode($res)); 2133 } 2134 2523 } catch (Exception $ex) { 2524 $res['message'] = 'Error: ' . $ex->getMessage(); 2525 $res['error'] = 1; 2526 } 2527 2528 echo appsmav_json_encode($res); 2529 wp_die(); 2530 } 2531 2135 2532 public function gr_show_redeem_points_lable() 2136 2533 { … … 2191 2588 self::gr_calc_point_value(); 2192 2589 2193 echo '<style>.gr_rewards_remove_discount{opacity:.6}#gr_checkout_redeem_lable{text-align:right;}.grPointsRedeem{padding:10px;border:1px dashed;}</style>';2194 2195 2590 if(WC()->session->get('gr_user_max_discount', 0) > 0 && WC()->session->get('gr_user_deduct_points', 0) >= 1) 2196 2591 { 2197 2592 $discount = WC()->session->get('gr_user_max_discount', 0); 2198 2593 $points = WC()->session->get('gr_user_deduct_points', 0); 2199 $redeem_point_lable = str_replace('{points}', $points, $redeem_point_lable); 2200 $redeem_point_lable = str_replace('{points_value}', wc_price($discount), $redeem_point_lable); 2201 2202 $point_lable = ($points > 1) ? WC()->session->get('gr_points_lable', '') : WC()->session->get('gr_point_lable', ''); 2203 $redeem_point_lable = '<p class="grPointsRedeem" id="gr_checkout_lable_top">' . str_replace('{points_label}', $point_lable, $redeem_point_lable); 2204 2594 $label = WC()->session->get('gr_redeem_point_per_dollar_lable', ''); 2205 2595 $extra_pay_points = WC()->session->get('gr_user_extra_pay_points', 0); 2206 $extra_pay_apply = '';2207 $extra_pay_confirm = 'display:none;';2208 if (!empty($extra_pay_points))2209 {2210 $extra_pay_apply = 'display:none;';2211 $extra_pay_confirm = '';2212 $extra_point_lable = ($extra_pay_points > 1) ? WC()->session->get('gr_points_lable', '') : WC()->session->get('gr_point_lable', '');2213 2214 2596 $extra_points_info = WC()->session->get('gr_redeem_extra_point_info', ''); 2215 $extra_points_info = str_replace('{points}', $extra_pay_points, $extra_points_info); 2216 $extra_points_info = str_replace('{points_label}', $extra_point_lable, $extra_points_info); 2217 $redeem_point_lable .= '<span style="color:red;"><br>'.$extra_points_info.'</span>'; 2218 } 2219 2220 $redeem_point_lable .= '</p>'; 2221 2222 // add 'Apply Discount' button 2223 if(WC()->session->get('gr_user_applied_discount', 0) == 0) 2224 { 2225 $btn_agree = WC()->session->get('gr_btn_redeem_confirm', ''); 2226 $redeem_point_lable .= '<form class="gr_apply_discount" action="' . esc_url(get_permalink(wc_get_page_id('cart'))) . '" method="post">'; 2227 $redeem_point_lable .= '<input type="hidden" name="gr_rewards_apply_discount" class="gr_rewards_apply_discount" value="1" />'; 2228 $redeem_point_lable .= '<input type="submit" class="button gr_rewards_apply_discount_confirm" style="'.$extra_pay_confirm.'" value="'.$btn_agree.'" />'; 2229 $redeem_point_lable .= '<input type="submit" class="button gr_rewards_apply_discount" style="'.$extra_pay_apply.'" value="' . WC()->session->get('gr_redeem_btn_text', '') . '" />'; 2230 $redeem_point_lable .= '</form>'; 2231 2597 $is_applied = (WC()->session->get('gr_user_applied_discount', 0) != 0); 2598 2599 // Prepare data array 2600 $render_data = array( 2601 'discount' => $discount, 2602 'points' => $points, 2603 'label' => $label, 2604 'extra_pay_points' => $extra_pay_points, 2605 'extra_points_info' => $extra_points_info, 2606 'is_applied' => $is_applied, 2607 'is_checkout' => $is_checkout_page, 2608 'is_block_page' => false // Classic page 2609 ); 2610 2611 // Use shared rendering method 2612 $redeem_point_lable = $this->gr_render_loyalty_banner_html($render_data); 2613 2614 // Handle session reset for non-applied state (lines 2184-2198) 2615 if(!$is_applied) { 2232 2616 WC()->session->set('gr_user_max_discount', 0); 2233 2617 WC()->session->set('gr_user_deduct_points', 0); 2234 } 2235 else 2236 { 2237 $redeem_point_lable = ''; 2238 2239 if(WC()->session->get('gr_user_applied_discount', 0) != $discount) 2240 { 2618 } else { 2619 $redeem_point_lable = ''; 2620 2621 if(WC()->session->get('gr_user_applied_discount', 0) != $discount) { 2241 2622 WC()->session->set('gr_user_max_discount', $discount); 2242 2623 WC()->session->set('gr_user_deduct_points', $points); 2243 2244 2624 $gr_user_max_discount = WC()->session->get('gr_user_max_discount', 0); 2245 2625 WC()->session->set('gr_user_applied_discount', (!empty($gr_user_max_discount) ? $gr_user_max_discount : 0)); 2246 2626 } 2247 2248 2627 } 2249 2628 2250 2629 echo '<div id="gr_checkout_redeem_lable">' . $redeem_point_lable . '</div>'; 2251 2252 wc_enqueue_js(" 2253 var gr_busy = false; 2254 jQuery('body').on('click', '.gr_rewards_apply_discount', function(e) { 2255 e.preventDefault(); 2256 jQuery('#gr_checkout_lable_top').next('.error-msg').remove(); 2257 2258 if(gr_busy) 2259 return false; 2260 2261 gr_busy = true; 2262 jQuery.post( 2263 '" . admin_url('admin-ajax.php') . "', 2264 { 2265 action:'apply_gr_discount', 2266 security: '". wp_create_nonce('apply_gr_discount') ."' 2267 }, 2268 function(response){ 2269 gr_busy = false; 2270 2271 if (typeof response.success != 'undefined' && response.success === false) { 2272 var errorMessage = (typeof response.data == 'undefined') ? 'Sorry, you are not allowed!' : response.data; 2273 jQuery('#gr_checkout_lable_top').after('<span class=\"error-msg\">'+ errorMessage +'</span>'); 2274 return false; 2275 } 2276 2277 if('".$is_checkout_page."' == '1') 2278 { 2279 jQuery('#gr_checkout_redeem_lable').hide(); 2280 jQuery('body').trigger('update_checkout'); 2281 return false; 2282 } 2283 else 2284 { 2285 var obj = jQuery(\"[name='update_cart']\"); 2286 jQuery('body').trigger('wc_update_cart'); 2287 2288 if(obj.length > 0) 2289 jQuery('body').trigger('wc_update_cart'); 2290 } 2291 }, 'json'); 2292 return false; 2293 }); 2294 2295 jQuery('body').on('click', '.gr_rewards_apply_discount_confirm', function(e) { 2296 e.preventDefault(); 2297 2298 jQuery('.gr_rewards_apply_discount_confirm').hide(); 2299 jQuery('.gr_rewards_apply_discount').fadeIn('slow'); 2300 return false; 2301 }); 2302 "); 2630 2631 // Mark banner as displayed to prevent duplicates from other hooks 2632 $banner_displayed = true; 2303 2633 } 2304 2634 } 2305 2635 catch(Exception $e) 2306 2636 { 2307 2308 } 2637 } 2638 } 2639 2640 /** 2641 * PURE PHP: Inject banner into WooCommerce Blocks (cart/checkout) 2642 * Intercepts block rendering and adds banner HTML before the block 2643 * Works for ALL block-based cart pages (official + custom URLs) 2644 * 2645 * @param string $block_content The rendered block content 2646 * @param array $block The block data (name, attributes, etc.) 2647 * @return string Modified block content with banner injected 2648 */ 2649 public function gr_inject_banner_into_blocks($block_content, $block) 2650 { 2651 try { 2652 // Prevent multiple injections per request (banner should appear only once) 2653 static $banner_injected = false; 2654 if ($banner_injected) { 2655 return $block_content; 2656 } 2657 2658 // Only process cart and checkout blocks 2659 if (!isset($block['blockName'])) { 2660 return $block_content; 2661 } 2662 2663 $block_name = $block['blockName']; 2664 2665 // Target WooCommerce cart and checkout blocks 2666 if ($block_name !== 'woocommerce/cart' && $block_name !== 'woocommerce/checkout') { 2667 return $block_content; 2668 } 2669 2670 // Skip ONLY pages with shortcodes (they use traditional hooks) 2671 // Shortcode pages have [woocommerce_cart] which internally renders blocks 2672 // We must detect shortcode pages and let traditional hooks handle them 2673 // This prevents double injection: render_block + traditional hooks 2674 // BUT: Official block pages (/cart/, /checkout/) should still use render_block 2675 global $post; 2676 2677 // Skip pages with cart/checkout shortcodes (they use traditional hooks) 2678 if (isset($post->post_content)) { 2679 $has_shortcode = ( 2680 appsmav_strpos($post->post_content, '[woocommerce_cart') !== false || 2681 appsmav_strpos($post->post_content, '[woocommerce_checkout') !== false 2682 ); 2683 2684 if ($has_shortcode) { 2685 return $block_content; // Let traditional hooks handle shortcode pages 2686 } 2687 } 2688 // If no shortcode found, this is a pure block page - proceed with render_block injection 2689 2690 // Check if we should show the banner (same logic as gr_show_redeem_points_lable) 2691 if ( 2692 empty(WC()->session) || WC()->session->get('gr_loyalty_campaign_enabled', 0) != 1 || 2693 WC()->session->get('redeem_point_enabled') == 0 || self::_isActiveCampaign() === false 2694 || empty(WC()->cart) 2695 ) { 2696 return $block_content; 2697 } 2698 2699 // Show redeem label only for points 2700 if(WC()->session->get('gr_pbp_mode', 'points') != 'points') { 2701 return $block_content; 2702 } 2703 2704 // Check user permissions 2705 if(is_user_logged_in()) 2706 { 2707 $user = wp_get_current_user(); 2708 $is_blocked_role = self::is_restricted_user_role($user->roles); 2709 if ($is_blocked_role) 2710 return $block_content; 2711 } 2712 else if (WC()->session->get('gr_disable_non_loggedin', 0) == 1) 2713 { 2714 return $block_content; 2715 } 2716 2717 // Check if coupon is applied (same logic as gr_show_redeem_points_lable) 2718 $items = WC()->cart->get_cart(); 2719 $redeem_coupon = WC()->session->get('gr_paybypoints_coupon', self::REDEEM_COUPON); 2720 2721 if(!WC()->cart->has_discount($redeem_coupon)) 2722 { 2723 WC()->session->set('gr_user_applied_discount', 0); 2724 WC()->session->set('gr_discount_applied', 0); 2725 } 2726 else if(WC()->session->get('gr_user_applied_discount', 0) <= 0 || empty($items)) 2727 { 2728 WC()->cart->remove_coupon($redeem_coupon); 2729 WC()->session->set('gr_user_max_discount', 0); 2730 WC()->session->set('gr_user_deduct_points', 0); 2731 } 2732 2733 // Calculate point value 2734 self::gr_calc_point_value(); 2735 2736 // Generate banner HTML if points available 2737 if(WC()->session->get('gr_user_max_discount', 0) > 0 && WC()->session->get('gr_user_deduct_points', 0) >= 1) 2738 { 2739 $discount = WC()->session->get('gr_user_max_discount', 0); 2740 $points = WC()->session->get('gr_user_deduct_points', 0); 2741 $label = WC()->session->get('gr_redeem_point_per_dollar_lable', ''); 2742 $extra_pay_points = WC()->session->get('gr_user_extra_pay_points', 0); 2743 $extra_points_info = WC()->session->get('gr_redeem_extra_point_info', ''); 2744 $is_applied = (WC()->session->get('gr_user_applied_discount', 0) != 0); 2745 2746 $is_checkout_page = ($block_name === 'woocommerce/checkout') ? 1 : 0; 2747 2748 // Prepare data array 2749 $render_data = array( 2750 'discount' => $discount, 2751 'points' => $points, 2752 'label' => $label, 2753 'extra_pay_points' => $extra_pay_points, 2754 'extra_points_info' => $extra_points_info, 2755 'is_applied' => $is_applied, 2756 'is_checkout' => $is_checkout_page, 2757 'is_block_page' => true // Block page 2758 ); 2759 2760 // Use shared rendering method 2761 $redeem_point_lable = $this->gr_render_loyalty_banner_html($render_data); 2762 2763 // Handle session reset for non-applied state 2764 if(!$is_applied) { 2765 WC()->session->set('gr_user_max_discount', 0); 2766 WC()->session->set('gr_user_deduct_points', 0); 2767 } else { 2768 $redeem_point_lable = ''; 2769 2770 if(WC()->session->get('gr_user_applied_discount', 0) != $discount) { 2771 WC()->session->set('gr_user_max_discount', $discount); 2772 WC()->session->set('gr_user_deduct_points', $points); 2773 $gr_user_max_discount = WC()->session->get('gr_user_max_discount', 0); 2774 WC()->session->set('gr_user_applied_discount', (!empty($gr_user_max_discount) ? $gr_user_max_discount : 0)); 2775 } 2776 } 2777 2778 // Inject banner BEFORE the block content 2779 $banner_html = '<div id="gr_checkout_redeem_lable">' . $redeem_point_lable . '</div>'; 2780 $block_content = $banner_html . $block_content; 2781 2782 // Mark banner as injected to prevent duplicates 2783 $banner_injected = true; 2784 } 2785 2786 return $block_content; 2787 2788 } catch(Exception $e) { 2789 // Silently fail - return original block content 2790 return $block_content; 2791 } 2792 } 2793 2794 /** 2795 * Render loyalty points HTML for both shortcode and blocks 2796 * BUSINESS LOGIC: Separated from presentation layer 2797 * 2798 * @param array $data Associative array with keys: 2799 * - discount (float): Discount amount 2800 * - points (int): Points to redeem 2801 * - label (string): Label template from session 2802 * - extra_pay_points (int): Extra points needed 2803 * - extra_points_info (string): Extra points message 2804 * - is_applied (bool): Whether discount is already applied 2805 * - is_checkout (bool): Whether this is checkout page 2806 * - is_block_page (bool): Whether this is a block page (optional, defaults to false) 2807 * @return string HTML output (not echoed) 2808 */ 2809 public function gr_render_loyalty_banner_html($data) { 2810 // Extract and validate data 2811 $discount = isset($data['discount']) ? floatval($data['discount']) : 0; 2812 $points = isset($data['points']) ? absint($data['points']) : 0; 2813 $label = isset($data['label']) ? wp_kses_post($data['label']) : ''; 2814 2815 if ($discount <= 0 || $points < 1) { 2816 return ''; 2817 } 2818 2819 // Check if discount is already applied 2820 $is_applied = isset($data['is_applied']) ? $data['is_applied'] : false; 2821 2822 if ($is_applied) { 2823 // Applied state: Return empty HTML (like traditional implementation) 2824 $html = ''; 2825 } else { 2826 // Build label with replacements (reuse existing logic from lines 2151-2172) 2827 $redeem_point_lable = appsmav_str_replace('{points}', $points, $label); 2828 $redeem_point_lable = appsmav_str_replace('{points_value}', wc_price($discount), $redeem_point_lable); 2829 2830 $point_lable = ($points > 1) 2831 ? wp_kses_post(WC()->session->get('gr_points_lable', '')) 2832 : wp_kses_post(WC()->session->get('gr_point_lable', '')); 2833 2834 $html = '<p class="grPointsRedeem" id="gr_checkout_lable_top">' 2835 . appsmav_str_replace('{points_label}', $point_lable, $redeem_point_lable); 2836 } 2837 2838 // Extra points logic (lines 2157-2170) - only for unapplied state 2839 if (!$is_applied) { 2840 $extra_pay_points = isset($data['extra_pay_points']) ? absint($data['extra_pay_points']) : 0; 2841 $extra_pay_apply = ''; 2842 $extra_pay_confirm = 'display:none;'; 2843 2844 if (!empty($extra_pay_points)) { 2845 $extra_pay_apply = 'display:none;'; 2846 $extra_pay_confirm = ''; 2847 $extra_point_lable = ($extra_pay_points > 1) 2848 ? WC()->session->get('gr_points_lable', '') 2849 : WC()->session->get('gr_point_lable', ''); 2850 2851 $extra_points_info = isset($data['extra_points_info']) ? $data['extra_points_info'] : ''; 2852 $extra_points_info = appsmav_str_replace('{points}', $extra_pay_points, $extra_points_info); 2853 $extra_points_info = appsmav_str_replace('{points_label}', $extra_point_lable, $extra_points_info); 2854 $html .= '<span class="gr_redeem_extra_point_info"><br>' . esc_html($extra_points_info) . '</span>'; 2855 } 2856 $html .= '</p>'; 2857 2858 // Add form/buttons if not applied (lines 2175-2186) 2859 $btn_agree = WC()->session->get('gr_btn_redeem_confirm', 'Apply Discount'); 2860 $btn_text = WC()->session->get('gr_redeem_btn_text', 'Apply Points'); 2861 2862 // Ensure we have button text 2863 if (empty($btn_text)) { 2864 $btn_text = 'Apply Points'; 2865 } 2866 if (empty($btn_agree)) { 2867 $btn_agree = 'Apply Discount'; 2868 } 2869 2870 $is_checkout_attr = isset($data['is_checkout']) && $data['is_checkout'] ? '1' : '0'; 2871 // Set form action to current page (cart or checkout) 2872 $form_action = $is_checkout_attr ? get_permalink(wc_get_page_id('checkout')) : get_permalink(wc_get_page_id('cart')); 2873 $html .= '<form class="gr_apply_discount" action="' . esc_url($form_action) . '" method="post" data-is-checkout="' . esc_attr($is_checkout_attr) . '">'; 2874 $html .= '<input type="hidden" name="gr_rewards_apply_discount" value="1" />'; 2875 $html .= '<input type="submit" class="button gr_rewards_apply_discount_confirm" style="' . esc_attr($extra_pay_confirm) . '" value="' . esc_attr($btn_agree) . '" />'; 2876 // Use standard WooCommerce button class for consistent styling (white background) on both classic and block pages 2877 $html .= '<input type="submit" class="button gr_rewards_apply_discount" style="' . esc_attr($extra_pay_apply) . '" value="' . esc_attr($btn_text) . '" />'; 2878 $html .= '</form>'; 2879 } 2880 2881 return $html; 2309 2882 } 2310 2883 … … 2371 2944 } 2372 2945 2373 if(WC()->session->get('gr_discount_applied') != 1) 2374 { 2375 $redeem_coupon = WC()->session->get('gr_paybypoints_coupon', self::REDEEM_COUPON); 2376 WC()->cart->add_discount($redeem_coupon); 2946 // Check if coupon is already in cart 2947 $redeem_coupon = WC()->session->get('gr_paybypoints_coupon', self::REDEEM_COUPON); 2948 $cart_has_coupon = WC()->cart->has_discount($redeem_coupon); 2949 2950 if(!$cart_has_coupon) 2951 { 2952 // Apply coupon using WooCommerce's built-in system 2953 // This automatically triggers 'woocommerce_coupon_message' filter (see get_discount_applied_message() at line 535) 2954 // The filter returns our custom message, and WooCommerce adds it as a notice to session 2955 WC()->cart->apply_coupon($redeem_coupon); 2956 2957 // Set session flags 2377 2958 WC()->session->set('gr_discount_applied', 1); 2378 } 2959 WC()->session->set('gr_user_applied_discount', 1); 2960 2961 // Clear the manual removal flag since user is now applying discount 2962 WC()->session->set('gr_user_manually_removed_discount', 0); 2963 } 2964 2965 // Get success message for JSON response (used by block pages) 2966 $success_message = WC()->session->get('gr_redeemed_status_msg', ''); 2967 if (empty($success_message)) { 2968 $success_message = __('Reward added Successfully', 'gratisfaction'); 2969 } 2970 2971 // IMPORTANT: Do NOT call wc_add_notice() here! 2972 // Classic pages: WooCommerce already added notice via 'woocommerce_coupon_message' filter when apply_coupon() was called 2973 // Block pages: JavaScript manually displays the message from JSON response (success_message below) 2974 // Adding wc_add_notice() here would create DUPLICATE messages on classic pages 2975 2976 // Return success response 2977 wp_send_json_success(array( 2978 'message' => 'Discount applied', 2979 'success_message' => $success_message, // For block pages - JavaScript will display this 2980 'nonces' => array( 2981 'apply_nonce' => wp_create_nonce('apply_gr_discount'), 2982 'remove_nonce' => wp_create_nonce('remove_gr_discount'), 2983 'cart_details_nonce' => wp_create_nonce('gr_nonce') 2984 ) 2985 )); 2379 2986 } 2380 2987 catch(Exception $e) … … 2393 3000 $msg = self::_checkNonce('','',0); 2394 3001 if (!empty($msg)) { 2395 wp_send_json_error($msg); 3002 echo appsmav_json_encode(array('success' => false, 'data' => $msg)); 3003 wp_die(); 2396 3004 } 2397 3005 … … 2402 3010 $discount = WC()->session->get('gr_user_max_discount', 0); 2403 3011 $points = WC()->session->get('gr_user_deduct_points', 0); 2404 $redeem_point_lable = str_replace('{points}', $points, $redeem_point_lable);2405 $redeem_point_lable = str_replace('{points_value}', wc_price($discount), $redeem_point_lable);3012 $redeem_point_lable = appsmav_str_replace('{points}', $points, $redeem_point_lable); 3013 $redeem_point_lable = appsmav_str_replace('{points_value}', wc_price($discount), $redeem_point_lable); 2406 3014 $point_lable = ($points > 1) ? WC()->session->get('gr_points_lable', '') : WC()->session->get('gr_point_lable', ''); 2407 $redeem_point_lable = str_replace('{points_label}', $point_lable, $redeem_point_lable); 3015 $redeem_point_lable = appsmav_str_replace('{points_label}', $point_lable, $redeem_point_lable); 3016 3017 $res = array(); 2408 3018 $res['msg'] = $redeem_point_lable; 2409 3019 2410 die(json_encode($res)); 3020 echo appsmav_json_encode($res); 3021 wp_die(); 2411 3022 2412 3023 } catch (Exception $ex) { … … 2419 3030 try 2420 3031 { 3032 // Early exit for unsigned users - loyalty points only available for logged-in users 3033 if (!is_user_logged_in()) { 3034 return; 3035 } 3036 2421 3037 if (empty(WC()->session) || WC()->session->get('redeem_point_enabled') == 0) 2422 3038 return; … … 2425 3041 $msg = self::_checkNonce('','',0); 2426 3042 if (!empty($msg)) { 2427 wp_send_json_error($msg); 2428 } 2429 3043 echo appsmav_json_encode(array('success' => false, 'data' => $msg)); 3044 wp_die(); 3045 } 3046 3047 // This ensures fresh calculation after cart changes, coupon removal, or idle time 2430 3048 self::gr_calc_point_value(); 3049 3050 // Check if coupon is actually in cart 3051 $redeem_coupon = WC()->session->get('gr_paybypoints_coupon', self::REDEEM_COUPON); 3052 $cart_has_coupon = WC()->cart->has_discount($redeem_coupon); 2431 3053 2432 3054 $res = array( 2433 3055 'discounted_amount' => WC()->session->get('gr_user_max_discount', 0), 2434 3056 'points' => WC()->session->get('gr_user_deduct_points', 0), 2435 'cart_count' => (is_array(WC()->cart->get_cart()) || WC()->cart->get_cart() instanceof Countable) ? count(WC()->cart->get_cart()) : 0,3057 'cart_count' => (is_array(WC()->cart->get_cart()) || (WC()->cart->get_cart() instanceof Countable)) ? count(WC()->cart->get_cart()) : 0, 2436 3058 'is_discount_applied' => 0, 2437 'extra_pbp' => WC()->session->get('gr_user_extra_pay_points', 0) 3059 'extra_pbp' => WC()->session->get('gr_user_extra_pay_points', 0), 3060 'redeem_restrict_terms' => WC()->session->get('gr_pay_redeem_restrict_terms', ''), 3061 'label_redeem_restriction_apply' => WC()->session->get('gr_label_redeem_restriction_apply', '') 2438 3062 ); 2439 3063 … … 2441 3065 //while cart is empty this session should set to 0 2442 3066 WC()->session->set('gr_discount_applied', 0); 2443 }2444 //To update the popup in checkut page2445 if (sanitize_text_field($_POST['event_type']) == 'remove_coupon') {2446 3067 $res['is_discount_applied'] = 0; 2447 } else if (!empty($res['discounted_amount'])) { 2448 $res['is_discount_applied'] = WC()->session->get('gr_discount_applied', 0); 2449 } 2450 2451 die(json_encode($res)); 3068 $res['discounted_amount'] = 0; 3069 $res['points'] = 0; 3070 $res['extra_pbp'] = 0; 3071 } else { 3072 // CART IS SOURCE OF TRUTH: Check if coupon is actually in cart 3073 if ($cart_has_coupon && !empty($res['discounted_amount'])) { 3074 // Coupon is in cart and has discount - it's applied 3075 $res['is_discount_applied'] = 1; 3076 } else { 3077 // Coupon not in cart - discount is not applied 3078 $res['is_discount_applied'] = 0; 3079 // Keep discounted_amount and points values (from gr_user_max_discount and gr_user_deduct_points) 3080 // These represent the AVAILABLE discount that CAN be applied, not what IS applied 3081 // This allows popup to show "Use X points for a Y discount" correctly 3082 // Only reset extra_pbp to prevent "additional points" message when no discount is applied 3083 $res['extra_pbp'] = 0; 3084 } 3085 } 3086 3087 // Add fresh nonces to response for next action (prevents "Sorry, you are not allowed!" after multiple actions) 3088 $res['nonces'] = array( 3089 'apply_nonce' => wp_create_nonce('apply_gr_discount'), 3090 'remove_nonce' => wp_create_nonce('remove_gr_discount'), 3091 'cart_details_nonce' => wp_create_nonce('gr_nonce') 3092 ); 3093 3094 echo appsmav_json_encode($res); 3095 wp_die(); 2452 3096 } 2453 3097 catch (Exception $ex) 2454 3098 { } 3099 } 3100 3101 /** 3102 * AJAX handler: Get loyalty points data for blocks 3103 * Handles: wp_ajax_get_gr_loyalty_points_blocks 3104 * Handles: wp_ajax_nopriv_get_gr_loyalty_points_blocks 3105 */ 3106 public function gr_ajax_get_loyalty_points_blocks() { 3107 try { 3108 // 0) Early exit for unsigned users - loyalty points only available for logged-in users 3109 if (!is_user_logged_in()) { 3110 wp_send_json_error(array('message' => 'User not logged in.')); 3111 return; 3112 } 3113 3114 // 1) Strict CSRF verification - check nonce exists first 3115 if (!isset($_POST['nonce'])) { 3116 wp_send_json_error(array('message' => 'Missing security token.')); 3117 return; 3118 } 3119 3120 // 2) Verify nonce matches the action 3121 $nonce_valid = check_ajax_referer('gr_blocks_loyalty_data', 'nonce', false); 3122 if (!$nonce_valid) { 3123 wp_send_json_error(array('message' => 'Invalid or expired security token.')); 3124 return; 3125 } 3126 3127 // Check WooCommerce session and cart 3128 if (empty(WC()->session) || empty(WC()->cart)) { 3129 wp_send_json_error(array('message' => 'Session or cart not available.')); 3130 return; 3131 } 3132 3133 // Check if campaign is active 3134 $campaign_enabled = WC()->session->get('gr_loyalty_campaign_enabled', 0); 3135 $redeem_enabled = WC()->session->get('redeem_point_enabled'); 3136 $is_custom_cart = isset($_POST['is_custom_cart']) ? (int)$_POST['is_custom_cart'] : 0; 3137 3138 if ($campaign_enabled != 1 || $redeem_enabled == 0) { 3139 wp_send_json_error(array('message' => 'Campaign not active.')); 3140 return; 3141 } 3142 3143 // Check PBP mode 3144 $pbp_mode = WC()->session->get('gr_pbp_mode', 'points'); 3145 if ($pbp_mode != 'points') { 3146 wp_send_json_error(array('message' => 'Points mode not active.')); 3147 return; 3148 } 3149 3150 // Calculate point values including extra points 3151 // This ensures gr_user_extra_pay_points is calculated for minimum point restrictions 3152 // Calculate point values following the same pattern as traditional pages 3153 $this->gr_calc_point_value(); 3154 3155 // Fetch data from session (AFTER calculation) 3156 $discount = WC()->session->get('gr_user_max_discount', 0); 3157 $points = WC()->session->get('gr_user_deduct_points', 0); 3158 $label = WC()->session->get('gr_redeem_point_per_dollar_lable', ''); 3159 3160 // Only return data if there are points to redeem 3161 if ($discount <= 0 || $points < 1) { 3162 wp_send_json_error(array('message' => 'No points available.')); 3163 return; 3164 } 3165 3166 // Prepare render data 3167 $is_checkout_page = is_checkout(); 3168 3169 // Check if discount is actually applied by checking if coupon exists in cart 3170 $redeem_coupon = WC()->session->get('gr_paybypoints_coupon', self::REDEEM_COUPON); 3171 3172 // Cart is the source of truth for applied discount status 3173 // Ensure cart totals are current and check discount status 3174 WC()->cart->calculate_totals(); 3175 $is_applied = WC()->cart->has_discount($redeem_coupon); 3176 3177 // Detect if this is a block page (for button styling) 3178 $is_block_page = function_exists('gr_is_cart_or_checkout_block') && gr_is_cart_or_checkout_block(); 3179 3180 $render_data = array( 3181 'discount' => $discount, 3182 'points' => $points, 3183 'label' => $label, 3184 'extra_pay_points' => WC()->session->get('gr_user_extra_pay_points', 0), 3185 'extra_points_info' => WC()->session->get('gr_redeem_extra_point_info', ''), 3186 'is_applied' => $is_applied, 3187 'is_checkout' => $is_checkout_page, 3188 'is_block_page' => $is_block_page 3189 ); 3190 3191 // Generate HTML using shared method 3192 $html = $this->gr_render_loyalty_banner_html($render_data); 3193 3194 // HTML sanitized via gr_render_loyalty_banner_html() 3195 3196 // Wrap in div (styles are in css/grconnect.css for FSE compatibility) 3197 $final_html = '<div id="gr_checkout_redeem_lable">' . $html . '</div>'; 3198 3199 // Return JSON response with ALL data needed for both banner AND widget config 3200 // This allows JavaScript to use just 1 AJAX call instead of 2 3201 $response_data = array( 3202 // Banner data 3203 'discount' => $discount, 3204 'points' => $points, 3205 'html' => $final_html, 3206 'is_checkout' => $is_checkout_page ? '1' : '0', 3207 3208 // Widget config data (same as gr_get_cart_details) 3209 'is_discount_applied' => $is_applied ? 1 : 0, 3210 'cart_count' => (is_array(WC()->cart->get_cart()) || (WC()->cart->get_cart() instanceof Countable)) ? count(WC()->cart->get_cart()) : 0, 3211 'extra_pbp' => WC()->session->get('gr_user_extra_pay_points', 0), 3212 'redeem_restrict_terms' => WC()->session->get('gr_pay_redeem_restrict_terms', ''), 3213 'label_redeem_restriction_apply' => WC()->session->get('gr_label_redeem_restriction_apply', ''), 3214 3215 // Return fresh nonces to prevent expiration after 12-24 hours 3216 'new_nonce' => wp_create_nonce('gr_blocks_loyalty_data'), // For banner updates (block pages only) 3217 'nonces' => array( 3218 'apply_nonce' => wp_create_nonce('apply_gr_discount'), 3219 'remove_nonce' => wp_create_nonce('remove_gr_discount'), 3220 'cart_details_nonce' => wp_create_nonce('gr_nonce') 3221 ) 3222 ); 3223 wp_send_json_success($response_data); 3224 3225 } catch (Exception $e) { 3226 wp_send_json_error(array('message' => 'Server error occurred.')); 3227 } 2455 3228 } 2456 3229 … … 2554 3327 } 2555 3328 2556 if ( (is_array($list_products) || $list_products instanceof Countable) && count($list_products)>0 )3329 if ( (is_array($list_products) || ($list_products instanceof Countable)) && count($list_products)>0 ) 2557 3330 { 2558 3331 $product_price_range = array(); … … 2618 3391 2619 3392 $redeem_point_lable = WC()->session->get('gr_redeem_point_product_per_dollar_lable'); 2620 $redeem_point_lable = str_replace('{points}', $discount, $redeem_point_lable);3393 $redeem_point_lable = appsmav_str_replace('{points}', $discount, $redeem_point_lable); 2621 3394 $point_lable = ($max_points > 1) ? WC()->session->get('gr_points_lable', '') : WC()->session->get('gr_point_lable', ''); 2622 $redeem_point_lable = str_replace('{points_label}', $point_lable, $redeem_point_lable);3395 $redeem_point_lable = appsmav_str_replace('{points_label}', $point_lable, $redeem_point_lable); 2623 3396 2624 3397 $redeem_restrict_terms = WC()->session->get('gr_pay_redeem_restrict_terms', ''); … … 2628 3401 $redeem_point_lable .= ' <span style="position:relative;"><span class="gr_restriction_apply_title" style="cursor: pointer;border-bottom: 1px dashed;color: initial;display: inline-block;"><b>'.$label_redeem_restriction_apply.'</b></span>'; 2629 3402 $redeem_point_lable .= '<span style="display:none;" class="gr_restriction_apply_message"><span>'.$redeem_restrict_terms.'</span><span class="gr_restriction_apply_close">×</span></span></span>'; 2630 2631 wc_enqueue_js("2632 jQuery('body').on('click', '.gr_restriction_apply_title', function(e) {2633 e.preventDefault();2634 jQuery('.gr_restriction_apply_message').toggle();2635 return false;2636 });2637 2638 jQuery('body').on('click', '.gr_restriction_apply_close', function(e) {2639 e.preventDefault();2640 jQuery('.gr_restriction_apply_message').hide();2641 return false;2642 });2643 ");2644 3403 } 2645 3404 … … 2674 3433 if(!empty(WC()->session->get('gr_earn_exclude_products', ''))){ 2675 3434 $earn_exclude_products = appsmav_explode(',', appsmav_trim(WC()->session->get('gr_earn_exclude_products', ''))); 2676 $earn_exclude_products = array_map('trim', $earn_exclude_products); 3435 $earn_exclude_products = appsmav_array_map('trim', $earn_exclude_products); 3436 // Ensure result is still an array after array_map 3437 if (!is_array($earn_exclude_products)) { 3438 $earn_exclude_products = array(); 3439 } 2677 3440 } 2678 3441 … … 2683 3446 WC()->session->get('gr_purchase_theme_status', 0) == 0 || 2684 3447 self::_isActiveCampaign() === false || 2685 in_array($product->get_id(), $earn_exclude_products)3448 (is_array($earn_exclude_products) && appsmav_in_array($product->get_id(), $earn_exclude_products)) 2686 3449 ) 2687 3450 return; … … 2719 3482 } 2720 3483 2721 if ( (is_array($list_products) || $list_products instanceof Countable) && count($list_products)>0 )3484 if ( (is_array($list_products) || ($list_products instanceof Countable)) && count($list_products)>0 ) 2722 3485 { 2723 3486 $product_price_range = array(); … … 2728 3491 if (wc_tax_enabled()) 2729 3492 { 2730 if (wc_prices_include_tax() && in_array($reward_type, array('TOTAL_EXCLUDE_TAX','SUBTOTAL')))3493 if (wc_prices_include_tax() && appsmav_in_array($reward_type, array('TOTAL_EXCLUDE_TAX','SUBTOTAL'))) 2731 3494 $p_price = wc_get_price_excluding_tax($product, array('qty' => 1, 'price' => $p_price)); 2732 else if (!wc_prices_include_tax() && ! in_array($reward_type, array('TOTAL_EXCLUDE_TAX','SUBTOTAL')))3495 else if (!wc_prices_include_tax() && !appsmav_in_array($reward_type, array('TOTAL_EXCLUDE_TAX','SUBTOTAL'))) 2733 3496 $p_price = wc_get_price_including_tax($product, array('qty' => 1, 'price' => $p_price)); 2734 3497 } … … 2764 3527 $p_tax = $tax_inc - $tax_exc; 2765 3528 2766 if (wc_prices_include_tax() && in_array($reward_type, array('TOTAL_EXCLUDE_TAX','SUBTOTAL')))3529 if (wc_prices_include_tax() && appsmav_in_array($reward_type, array('TOTAL_EXCLUDE_TAX','SUBTOTAL'))) 2767 3530 $p_price = $p_price - $p_tax; 2768 else if (!wc_prices_include_tax() && ! in_array($reward_type, array('TOTAL_EXCLUDE_TAX','SUBTOTAL')))3531 else if (!wc_prices_include_tax() && !appsmav_in_array($reward_type, array('TOTAL_EXCLUDE_TAX','SUBTOTAL'))) 2769 3532 $p_price = $p_price + $p_tax; 2770 3533 } … … 2782 3545 2783 3546 $earn_point_lable = WC()->session->get('gr_earn_point_per_dollar_lable', ''); 2784 $earn_point_lable = str_replace('{points}', $point_earn, $earn_point_lable);2785 $earn_point_lable = str_replace('{points_label}', $point_lable, $earn_point_lable);3547 $earn_point_lable = appsmav_str_replace('{points}', $point_earn, $earn_point_lable); 3548 $earn_point_lable = appsmav_str_replace('{points_label}', $point_lable, $earn_point_lable); 2786 3549 2787 3550 $label_color = WC()->session->get('gr_label_color', '#4bb543'); … … 2795 3558 } 2796 3559 2797 p rivatefunction gr_calc_point_value()3560 public function gr_calc_point_value() 2798 3561 { 2799 3562 try … … 2815 3578 WC()->session->set('gr_user_extra_pay_points', 0); 2816 3579 2817 $redeem_point = WC()->session->get('gr_redeem_point_per_dollar', 0)/$ratio; 3580 // Prevent division by zero in currency conversion 3581 $redeem_point = (!empty($ratio) && $ratio != 0) 3582 ? WC()->session->get('gr_redeem_point_per_dollar', 0) / $ratio 3583 : 0; 2818 3584 $cart_total = WC()->cart->subtotal; 2819 $discount = floor(WC()->session->get('gr_user_points', 0) / $redeem_point); 3585 // Prevent division by zero if redeem_point is 0 3586 $discount = (!empty($redeem_point) && $redeem_point != 0) 3587 ? floor(WC()->session->get('gr_user_points', 0) / $redeem_point) 3588 : 0; 2820 3589 2821 3590 $applied_coupons = WC()->cart->applied_coupons; … … 2870 3639 if (!empty($max_point) ) 2871 3640 { 2872 $discount_max_point = floor($max_point / $redeem_point); 3641 // Prevent division by zero if redeem_point is 0 3642 $discount_max_point = (!empty($redeem_point) && $redeem_point != 0) 3643 ? floor($max_point / $redeem_point) 3644 : 0; 2873 3645 if ($discount > $discount_max_point) 2874 3646 { … … 2942 3714 return; 2943 3715 2944 WC()->session->set('gr_api_sess', empty($app_config['date_updated']) ? 0 : $app_config['date_updated']); 2945 WC()->session->set('gr_loyalty_campaign_enabled', empty($app_config['points']['loyalty_campaign_enabled']) ? 0 : $app_config['points']['loyalty_campaign_enabled']); 2946 WC()->session->set('earn_point_enabled', empty($app_config['points']['earn_point_enabled']) ? 0 : $app_config['points']['earn_point_enabled']); 2947 WC()->session->set('gr_purchase_theme_status', empty($app_config['points']['purchase_theme_status']) ? 0 : $app_config['points']['purchase_theme_status']); 2948 WC()->session->set('redeem_point_enabled', empty($app_config['points']['redeem_purchase_status']) ? 0 : $app_config['points']['redeem_purchase_status']); 2949 WC()->session->set('gr_roundoff_type', empty($app_config['points']['gr_roundoff_type']) ? 'ROUND' : $app_config['points']['gr_roundoff_type']); 2950 WC()->session->set('gr_redeem_point_per_dollar', empty($app_config['points']['redeem_point_per_dollar']) ? 0 : $app_config['points']['redeem_point_per_dollar']); 3716 WC()->session->set('gr_api_sess', isset($app_config['date_updated']) ? $app_config['date_updated'] : 0); 3717 // Safe nested array access - check if parent keys exist before accessing nested values 3718 $points_config = isset($app_config['points']) && is_array($app_config['points']) ? $app_config['points'] : array(); 3719 $lang_config = isset($app_config['lang']) && is_array($app_config['lang']) ? $app_config['lang'] : array(); 3720 3721 WC()->session->set('gr_loyalty_campaign_enabled', isset($points_config['loyalty_campaign_enabled']) ? $points_config['loyalty_campaign_enabled'] : 0); 3722 WC()->session->set('earn_point_enabled', isset($points_config['earn_point_enabled']) ? $points_config['earn_point_enabled'] : 0); 3723 WC()->session->set('gr_purchase_theme_status', isset($points_config['purchase_theme_status']) ? $points_config['purchase_theme_status'] : 0); 3724 WC()->session->set('redeem_point_enabled', isset($points_config['redeem_purchase_status']) ? $points_config['redeem_purchase_status'] : 0); 3725 WC()->session->set('gr_roundoff_type', isset($points_config['gr_roundoff_type']) ? $points_config['gr_roundoff_type'] : 'ROUND'); 3726 WC()->session->set('gr_redeem_point_per_dollar', isset($points_config['redeem_point_per_dollar']) ? $points_config['redeem_point_per_dollar'] : 0); 2951 3727 WC()->session->set('gr_redeem_theme_status', empty($app_config['points']['redeem_theme_status']) ? 0 : $app_config['points']['redeem_theme_status']); 2952 WC()->session->set('minimum_order_value', empty($app_config['points']['minimum_order_value']) ? 0 : $app_config['points']['minimum_order_value']);2953 WC()->session->set('gr_redeem_point_per_dollar_lable', empty($app_config['lang']['redeem_point_per_dollar_lable']) ? '' : appsmav_stripslashes($app_config['lang']['redeem_point_per_dollar_lable']));2954 WC()->session->set('gr_redeem_point_product_per_dollar_lable', empty($app_config['lang']['redeem_point_product_per_dollar_lable']) ? '' : appsmav_stripslashes($app_config['lang']['redeem_point_product_per_dollar_lable']));2955 WC()->session->set('gr_earn_point_per_dollar', empty($app_config['points']['earn_point_per_dollar']) ? 0 : $app_config['points']['earn_point_per_dollar']);2956 WC()->session->set('gr_earn_point_per_dollar_lable', empty($app_config['lang']['earn_point_per_dollar_lable']) ? '' : appsmav_stripslashes($app_config['lang']['earn_point_per_dollar_lable']));2957 WC()->session->set('gr_point_lable', empty($app_config['lang']['point_lable']) ? '' : $app_config['lang']['point_lable']);2958 WC()->session->set('gr_points_lable', empty($app_config['lang']['points_lable']) ? '' : $app_config['lang']['points_lable']);2959 WC()->session->set('gr_redeem_btn_text', empty($app_config['lang']['redeem_btn_text']) ? '' : $app_config['lang']['redeem_btn_text']);2960 WC()->session->set('gr_redeemed_btn_text', empty($app_config['lang']['redeemed_btn_text']) ? '' : $app_config['lang']['redeemed_btn_text']);2961 WC()->session->set('gr_redeemed_status_msg', empty($app_config['lang']['redeemed_status_msg']) ? '' : $app_config['lang']['redeemed_status_msg']);2962 WC()->session->set('label_redeemed_points', empty($app_config['lang']['label_redeemed_points']) ? '' : $app_config['lang']['label_redeemed_points']);2963 WC()->session->set('label_life_time_points', empty($app_config['lang']['label_life_time_points']) ? '' : $app_config['lang']['label_life_time_points']);2964 WC()->session->set('label_available_points', empty($app_config['lang']['label_available_points']) ? '' : $app_config['lang']['label_available_points']);2965 WC()->session->set('error_more_points_required', empty($app_config['lang']['error_more_points_required']) ? '' : $app_config['lang']['error_more_points_required']);2966 WC()->session->set('label_exclusion_points', empty($app_config['lang']['label_exclusion_points']) ? '' : $app_config['lang']['label_exclusion_points']);2967 WC()->session->set('label_total_points', empty($app_config['lang']['label_total_points']) ? '' : $app_config['lang']['label_total_points']);2968 WC()->session->set('no_records_found', empty($app_config['lang']['no_records_found']) ? '' : $app_config['lang']['no_records_found']);2969 WC()->session->set('gr_loyalty_menu_name', appsmav_stripslashes( $app_config['lang']['loyalty_menu_name']));2970 WC()->session->set('gr_loyalty_date_start', empty($app_config['loyalty']['date_start']) ? 0 : $app_config['loyalty']['date_start']);2971 WC()->session->set('gr_loyalty_date_end', empty($app_config['loyalty']['date_end']) ? 0 : $app_config['loyalty']['date_end']);2972 WC()->session->set('gr_loyalty_timezone', empty($app_config['loyalty']['timezone']) ? 'America/Chicago' : $app_config['loyalty']['timezone']);2973 WC()->session->set('gr_loyalty_is_open', empty($app_config['loyalty']['is_open']) ? 0 : $app_config['loyalty']['is_open']);2974 WC()->session->set('gr_global_review_enabled', empty($app_config['reviews']['global_review_enabled']) ? 0 : $app_config['reviews']['global_review_enabled']);2975 WC()->session->set('gr_global_review_points', empty($app_config['reviews']['global_review_points']) ? 0 : $app_config['reviews']['global_review_points']);2976 WC()->session->set('gr_global_review_lable_enabled', empty($app_config['reviews']['global_review_lable_enabled']) ? 0 : $app_config['reviews']['global_review_lable_enabled']);2977 WC()->session->set('gr_global_review_lable', empty($app_config['reviews']['global_review_lable']) ? '' : $app_config['reviews']['global_review_lable']);2978 WC()->session->set('gr_restricted_user_roles', empty($app_config['points']['gr_restricted_user_roles'])?array():$app_config['points']['gr_restricted_user_roles']);2979 WC()->session->set('gr_roles_restrict_type', empty($app_config['points']['gr_roles_restrict_type'])?'restrict':$app_config['points']['gr_roles_restrict_type']);2980 WC()->session->set('gr_disable_non_loggedin', empty($app_config['points']['gr_disable_non_loggedin'])?0:$app_config['points']['gr_disable_non_loggedin']);2981 WC()->session->set('gr_reward_points_type', empty($app_config['points']['reward_points_type']) ? 'SUBTOTAL' : $app_config['points']['reward_points_type']);3728 WC()->session->set('minimum_order_value', isset($points_config['minimum_order_value']) ? $points_config['minimum_order_value'] : 0); 3729 WC()->session->set('gr_redeem_point_per_dollar_lable', isset($lang_config['redeem_point_per_dollar_lable']) ? appsmav_stripslashes($lang_config['redeem_point_per_dollar_lable']) : ''); 3730 WC()->session->set('gr_redeem_point_product_per_dollar_lable', isset($lang_config['redeem_point_product_per_dollar_lable']) ? appsmav_stripslashes($lang_config['redeem_point_product_per_dollar_lable']) : ''); 3731 WC()->session->set('gr_earn_point_per_dollar', isset($points_config['earn_point_per_dollar']) ? $points_config['earn_point_per_dollar'] : 0); 3732 WC()->session->set('gr_earn_point_per_dollar_lable', isset($lang_config['earn_point_per_dollar_lable']) ? appsmav_stripslashes($lang_config['earn_point_per_dollar_lable']) : ''); 3733 WC()->session->set('gr_point_lable', isset($lang_config['point_lable']) ? $lang_config['point_lable'] : ''); 3734 WC()->session->set('gr_points_lable', isset($lang_config['points_lable']) ? $lang_config['points_lable'] : ''); 3735 WC()->session->set('gr_redeem_btn_text', isset($lang_config['redeem_btn_text']) ? $lang_config['redeem_btn_text'] : ''); 3736 WC()->session->set('gr_redeemed_btn_text', isset($lang_config['redeemed_btn_text']) ? $lang_config['redeemed_btn_text'] : ''); 3737 WC()->session->set('gr_redeemed_status_msg', isset($lang_config['redeemed_status_msg']) ? $lang_config['redeemed_status_msg'] : ''); 3738 WC()->session->set('label_redeemed_points', isset($lang_config['label_redeemed_points']) ? $lang_config['label_redeemed_points'] : ''); 3739 WC()->session->set('label_life_time_points', isset($lang_config['label_life_time_points']) ? $lang_config['label_life_time_points'] : ''); 3740 WC()->session->set('label_available_points', isset($lang_config['label_available_points']) ? $lang_config['label_available_points'] : ''); 3741 WC()->session->set('error_more_points_required', isset($lang_config['error_more_points_required']) ? $lang_config['error_more_points_required'] : ''); 3742 WC()->session->set('label_exclusion_points', isset($lang_config['label_exclusion_points']) ? $lang_config['label_exclusion_points'] : ''); 3743 WC()->session->set('label_total_points', isset($lang_config['label_total_points']) ? $lang_config['label_total_points'] : ''); 3744 WC()->session->set('no_records_found', isset($lang_config['no_records_found']) ? $lang_config['no_records_found'] : ''); 3745 WC()->session->set('gr_loyalty_menu_name', appsmav_stripslashes(appsmav_get_nested_array_value($app_config, ['lang', 'loyalty_menu_name'], ''))); 3746 WC()->session->set('gr_loyalty_date_start', appsmav_get_nested_array_value($app_config, ['loyalty', 'date_start'], 0)); 3747 WC()->session->set('gr_loyalty_date_end', appsmav_get_nested_array_value($app_config, ['loyalty', 'date_end'], 0)); 3748 WC()->session->set('gr_loyalty_timezone', appsmav_get_nested_array_value($app_config, ['loyalty', 'timezone'], 'America/Chicago')); 3749 WC()->session->set('gr_loyalty_is_open', appsmav_get_nested_array_value($app_config, ['loyalty', 'is_open'], 0)); 3750 WC()->session->set('gr_global_review_enabled', appsmav_get_nested_array_value($app_config, ['reviews', 'global_review_enabled'], 0)); 3751 WC()->session->set('gr_global_review_points', appsmav_get_nested_array_value($app_config, ['reviews', 'global_review_points'], 0)); 3752 WC()->session->set('gr_global_review_lable_enabled', appsmav_get_nested_array_value($app_config, ['reviews', 'global_review_lable_enabled'], 0)); 3753 WC()->session->set('gr_global_review_lable', appsmav_get_nested_array_value($app_config, ['reviews', 'global_review_lable'], '')); 3754 WC()->session->set('gr_restricted_user_roles', appsmav_get_nested_array_value($app_config, ['points', 'gr_restricted_user_roles'], array())); 3755 WC()->session->set('gr_roles_restrict_type', appsmav_get_nested_array_value($app_config, ['points', 'gr_roles_restrict_type'], 'restrict')); 3756 WC()->session->set('gr_disable_non_loggedin', appsmav_get_nested_array_value($app_config, ['points', 'gr_disable_non_loggedin'], 0)); 3757 WC()->session->set('gr_reward_points_type', appsmav_get_nested_array_value($app_config, ['points', 'reward_points_type'], 'SUBTOTAL')); 2982 3758 WC()->session->set('gr_redeem_restrict_status', isset($app_config['points']['gr_redeem_restrict_status']) ? $app_config['points']['gr_redeem_restrict_status'] : 0); 2983 3759 WC()->session->set('gr_redeem_min_point', isset($app_config['points']['gr_redeem_min_point']) ? $app_config['points']['gr_redeem_min_point'] : 0); … … 2985 3761 WC()->session->set('gr_redeem_cart_percent', isset($app_config['points']['gr_redeem_cart_percent']) ? $app_config['points']['gr_redeem_cart_percent'] : ''); 2986 3762 WC()->session->set('gr_label_color', isset($app_config['points']['gr_label_color']) ? $app_config['points']['gr_label_color'] : '#4bb543'); 2987 WC()->session->set('gr_is_redeem_individual', empty($app_config['points']['gr_is_redeem_individual'])?0:$app_config['points']['gr_is_redeem_individual']);2988 WC()->session->set('gr_pay_exclude_products', empty($app_config['points']['gr_pay_exclude_products'])?array():$app_config['points']['gr_pay_exclude_products']);2989 WC()->session->set('gr_pay_exclude_categories', empty($app_config['points']['gr_pay_exclude_categories'])?array():$app_config['points']['gr_pay_exclude_categories']);2990 WC()->session->set('gr_pay_redeem_restrict_terms', empty($app_config['points']['gr_pay_redeem_restrict_terms'])?'':$app_config['points']['gr_pay_redeem_restrict_terms']);2991 WC()->session->set('gr_label_redeem_restriction_apply', empty($app_config['lang']['gr_label_redeem_restriction_apply'])?'':appsmav_stripslashes($app_config['lang']['gr_label_redeem_restriction_apply']));2992 WC()->session->set('gr_btn_redeem_confirm', empty($app_config['lang']['gr_btn_redeem_confirm'])?'':appsmav_stripslashes($app_config['lang']['gr_btn_redeem_confirm']));2993 WC()->session->set('gr_redeem_extra_point_info', empty($app_config['lang']['gr_redeem_extra_point_info'])?'':appsmav_stripslashes($app_config['lang']['gr_redeem_extra_point_info']));2994 WC()->session->set('gr_pbp_auto_apply', empty($app_config['points']['gr_pbp_auto_apply'])?0:$app_config['points']['gr_pbp_auto_apply']);2995 WC()->session->set('gr_earn_exclude_products', empty($app_config['points']['gr_earn_exclude_products'])?'':$app_config['points']['gr_earn_exclude_products']);2996 WC()->session->set('gr_pbp_mode', empty($app_config['points']['gr_pbp_mode'])?'points':$app_config['points']['gr_pbp_mode']);3763 WC()->session->set('gr_is_redeem_individual', appsmav_get_nested_array_value($app_config, ['points', 'gr_is_redeem_individual'], 0)); 3764 WC()->session->set('gr_pay_exclude_products', appsmav_get_nested_array_value($app_config, ['points', 'gr_pay_exclude_products'], array())); 3765 WC()->session->set('gr_pay_exclude_categories', appsmav_get_nested_array_value($app_config, ['points', 'gr_pay_exclude_categories'], array())); 3766 WC()->session->set('gr_pay_redeem_restrict_terms', appsmav_get_nested_array_value($app_config, ['points', 'gr_pay_redeem_restrict_terms'], '')); 3767 WC()->session->set('gr_label_redeem_restriction_apply', appsmav_stripslashes(appsmav_get_nested_array_value($app_config, ['lang', 'gr_label_redeem_restriction_apply'], ''))); 3768 WC()->session->set('gr_btn_redeem_confirm', appsmav_stripslashes(appsmav_get_nested_array_value($app_config, ['lang', 'gr_btn_redeem_confirm'], ''))); 3769 WC()->session->set('gr_redeem_extra_point_info', appsmav_stripslashes(appsmav_get_nested_array_value($app_config, ['lang', 'gr_redeem_extra_point_info'], ''))); 3770 WC()->session->set('gr_pbp_auto_apply', appsmav_get_nested_array_value($app_config, ['points', 'gr_pbp_auto_apply'], 0)); 3771 WC()->session->set('gr_earn_exclude_products', appsmav_get_nested_array_value($app_config, ['points', 'gr_earn_exclude_products'], '')); 3772 WC()->session->set('gr_pbp_mode', appsmav_get_nested_array_value($app_config, ['points', 'gr_pbp_mode'], 'points')); 2997 3773 2998 3774 $redeem_coupon = WC()->session->get('gr_paybypoints_coupon', self::REDEEM_COUPON); 2999 if (!empty($redeem_coupon) && !empty($app_config['points']['gr_paybypoints_coupon']) && 3000 $redeem_coupon != $app_config['points']['gr_paybypoints_coupon']) 3775 $paybypoints_coupon = isset($app_config['points']['gr_paybypoints_coupon']) ? $app_config['points']['gr_paybypoints_coupon'] : ''; 3776 if (!empty($redeem_coupon) && !empty($paybypoints_coupon) && 3777 $redeem_coupon != $paybypoints_coupon) 3001 3778 { 3002 3779 WC()->session->set('gr_paybypoints_coupon_old', $redeem_coupon); … … 3068 3845 3069 3846 if(is_array($response) && !empty($response['body'])) 3070 $ret = json_decode($response['body'], true);3847 $ret = appsmav_json_decode($response['body'], true); 3071 3848 else 3072 $ret['error'] = 1; 3849 $ret = array('error' => 1); 3850 3851 // Ensure $ret is always an array to prevent "Cannot access offset of type string on string" error 3852 if(!is_array($ret)) 3853 $ret = array('error' => 1); 3073 3854 3074 3855 if(isset($ret['error']) && $ret['error'] != 1) … … 3128 3909 'gr_label_redeem_restriction_apply' => $ret['gr_label_redeem_restriction_apply'], 3129 3910 'gr_btn_redeem_confirm' => $ret['gr_btn_redeem_confirm'], 3911 'gr_redeem_extra_point_info' => isset($ret['gr_redeem_extra_point_info']) ? $ret['gr_redeem_extra_point_info'] : '', 3130 3912 'loyalty_menu_name' => empty($ret['loyalty_menu_name']) ? 'GR Loyalty' : appsmav_stripslashes($ret['loyalty_menu_name']) 3131 3913 ), … … 3222 4004 $date_start = WC()->session->get('gr_loyalty_date_start', 0); 3223 4005 $date_end = WC()->session->get('gr_loyalty_date_end', 0); 3224 $timezone = WC()->session->get('gr_loyalty_timezone', 'America/Chicago');4006 $timezone_raw = WC()->session->get('gr_loyalty_timezone', 'America/Chicago'); 3225 4007 $is_open = WC()->session->get('gr_loyalty_is_open', 0); 3226 4008 } else { … … 3228 4010 $date_start = !empty($app_config['loyalty']['date_start']) ? $app_config['loyalty']['date_start'] : 0; 3229 4011 $date_end = !empty($app_config['loyalty']['date_end']) ? $app_config['loyalty']['date_end'] : 0; 3230 $timezone = !empty($app_config['loyalty']['timezone']) ? $app_config['loyalty']['timezone'] : 'America/Chicago';4012 $timezone_raw = !empty($app_config['loyalty']['timezone']) ? $app_config['loyalty']['timezone'] : 'America/Chicago'; 3231 4013 $is_open = !empty($app_config['loyalty']['is_open']) ? $app_config['loyalty']['is_open'] : 0; 3232 4014 } 3233 4015 3234 if (!empty($date_start)) { 3235 $currentDate = new \DateTime("now"); 3236 $currentDate->setTimezone(new DateTimeZone($timezone)); 3237 3238 $startDate = DateTime::createFromFormat('U', $date_start); 3239 $startDate->setTimezone(new DateTimeZone($timezone)); 3240 3241 $endDate = 0; 3242 if (empty($is_open)) { 3243 $endDate = DateTime::createFromFormat('U', $date_end); 3244 $endDate->setTimezone(new DateTimeZone($timezone)); 3245 } 3246 3247 if ($startDate > $currentDate || (!empty($endDate) && $currentDate > $endDate)) { 3248 return false; 3249 } 3250 } 4016 if (!empty($date_start)) { 4017 // Ensure timezone is a valid string to prevent fatal error 4018 // Validate immediately after retrieval to prevent any non-string values from propagating 4019 if (!is_string($timezone_raw) || empty($timezone_raw)) { 4020 $timezone = 'America/Chicago'; 4021 } else { 4022 $timezone = $timezone_raw; 4023 } 4024 4025 // Validate timezone is in valid format 4026 try { 4027 $timezoneObj = new DateTimeZone($timezone); 4028 } catch (Exception $tz_exception) { 4029 // Invalid timezone string, use default 4030 $timezone = 'America/Chicago'; 4031 $timezoneObj = new DateTimeZone($timezone); 4032 } 4033 4034 $currentDate = new DateTime("now"); 4035 $currentDate->setTimezone($timezoneObj); 4036 4037 $startDate = DateTime::createFromFormat('U', $date_start); 4038 // Validate DateTime creation succeeded 4039 if ($startDate !== false) { 4040 $startDate->setTimezone($timezoneObj); 4041 } else { 4042 // Invalid date format, allow campaign 4043 return true; 4044 } 4045 4046 $endDate = 0; 4047 if (empty($is_open)) { 4048 $endDate = DateTime::createFromFormat('U', $date_end); 4049 // Validate DateTime creation succeeded 4050 if ($endDate !== false) { 4051 $endDate->setTimezone($timezoneObj); 4052 } else { 4053 // Invalid end date, treat as open-ended 4054 $endDate = 0; 4055 } 4056 } 4057 4058 if ($startDate > $currentDate || (!empty($endDate) && $currentDate > $endDate)) { 4059 return false; 4060 } 4061 } 3251 4062 } 3252 4063 catch(Exception $e) … … 3260 4071 try 3261 4072 { 3262 $ip = !empty($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR']: '';4073 $ip = !empty($_SERVER['REMOTE_ADDR']) ? sanitize_text_field(appsmav_unslash($_SERVER['REMOTE_ADDR'])) : ''; 3263 4074 $ip_details = array('ip' => $ip, 'city' => '', 'region_name' => '', 'country_code' => 'US'); 3264 4075 … … 3275 4086 3276 4087 if (!empty($res)) { 3277 $ipLocArr = json_decode($res, true); 3278 3279 if (!empty($ipLocArr['geoplugin_request']) && $ipLocArr['geoplugin_request'] == $ip && in_array($ipLocArr['geoplugin_status'], array(200, 206))) { 4088 $ipLocArr = appsmav_json_decode($res, true); 4089 4090 // Check if valid array and status matches 4091 if (is_array($ipLocArr) && !empty($ipLocArr['geoplugin_request']) && 4092 isset($ipLocArr['geoplugin_status']) && 4093 $ipLocArr['geoplugin_request'] == $ip && 4094 appsmav_in_array($ipLocArr['geoplugin_status'], array(200, 206))) { 3280 4095 $ip_details['ip'] = empty($ipLocArr['geoplugin_request']) ? $ip : $ipLocArr['geoplugin_request']; 3281 4096 $ip_details['city'] = empty($ipLocArr['geoplugin_city']) ? null : $ipLocArr['geoplugin_city']; … … 3299 4114 { 3300 4115 $resArr = array('gr_reg' => 4, 'message' => 'Enter valid email address'); 3301 die(json_encode($resArr)); 4116 echo appsmav_json_encode($resArr); 4117 wp_die(); 3302 4118 } 3303 4119 … … 3331 4147 $params['date_format'] = 'd/m/Y'; //Dummy$p['grappsmav_reg_date_format']; 3332 4148 $params['exclusion_period'] = 0; //$p['grconnect_reg_exclusion_period']; 3333 $params["app_lang"] = str_replace('-', '_', get_bloginfo('language'));4149 $params["app_lang"] = appsmav_str_replace('-', '_', get_bloginfo('language')); 3334 4150 $myaccount_page_id = get_option('woocommerce_myaccount_page_id'); 3335 4151 $myaccount_page_url = get_permalink($myaccount_page_id); … … 3342 4158 $res = $httpObj->getResponse(); 3343 4159 4160 $resArr = array(); 3344 4161 if(!empty($res)) 3345 $resArr = json_decode($res, true); 4162 $resArr = appsmav_json_decode($res, true); 4163 4164 // Ensure $resArr is always an array to prevent "Cannot access offset of type string on string" error 4165 if(!is_array($resArr)) 4166 $resArr = array(); 3346 4167 3347 4168 if(isset($resArr['error']) && $resArr['error'] == 0) … … 3370 4191 } 3371 4192 3372 die( json_encode($resArr));4193 die(appsmav_json_encode($resArr)); 3373 4194 } catch (Exception $ex) { 3374 4195 … … 3400 4221 $params['last_name'] = empty($param['last_name']) ? '' : $param['last_name']; 3401 4222 $params['comment'] = isset($param['comment']) ? $param['comment'] : ''; 3402 $params["app_lang"] = str_replace('-', '_', get_bloginfo('language'));4223 $params["app_lang"] = appsmav_str_replace('-', '_', get_bloginfo('language')); 3403 4224 $allparam = implode('#WP#', $paramSalt); 3404 4225 $params['salt'] = md5($allparam); … … 3442 4263 3443 4264 if(!empty($res)) 3444 $res = json_decode($res, true); 4265 $res = appsmav_json_decode($res, true); 4266 4267 // Ensure $res is always an array to prevent "Cannot access offset of type string on string" error 4268 if(!is_array($res)) 4269 $res = array(); 3445 4270 3446 4271 if(!empty($res['error'])) … … 3483 4308 // Data for product review 3484 4309 $params['app'] = 'WP'; 3485 $params["app_lang"] = str_replace('-', '_', get_bloginfo('language'));4310 $params["app_lang"] = appsmav_str_replace('-', '_', get_bloginfo('language')); 3486 4311 $params['id_shop'] = $grShopId; 3487 4312 $params['currency'] = get_option('woocommerce_currency', 'USD'); … … 3494 4319 3495 4320 if(!empty($res)) 3496 $res = json_decode($res, true); 4321 $res = appsmav_json_decode($res, true); 4322 4323 // Ensure $res is always an array to prevent "Cannot access offset of type string on string" error 4324 if(!is_array($res)) 4325 $res = array(); 3497 4326 3498 4327 if(!empty($res['error'])) … … 3528 4357 $coupon_code = sanitize_text_field($_REQUEST['gr_coupon_code']); 3529 4358 if(!empty(WC()->cart) && !empty($coupon_code)) { 3530 WC()->cart->add_discount($coupon_code); 4359 if (method_exists(WC()->cart, 'apply_coupon')) { 4360 WC()->cart->apply_coupon($coupon_code); 4361 } else { 4362 WC()->cart->add_discount($coupon_code); 4363 } 3531 4364 } 3532 4365 } … … 3545 4378 if (!empty($roles) && !empty($app_config['points']['gr_restricted_user_roles'])) 3546 4379 { 3547 $blocked_roles = array_intersect ($roles, $app_config['points']['gr_restricted_user_roles']); 3548 if ($restrict_type == 'restrict' && !empty($blocked_roles) && (is_array($blocked_roles) || $blocked_roles instanceof Countable) && count($blocked_roles) > 0) 4380 // Ensure both parameters are arrays before using array_intersect 4381 $roles_array = is_array($roles) ? $roles : array(); 4382 $restricted_roles = (isset($app_config['points']['gr_restricted_user_roles']) && is_array($app_config['points']['gr_restricted_user_roles'])) 4383 ? $app_config['points']['gr_restricted_user_roles'] 4384 : array(); 4385 $blocked_roles = appsmav_array_intersect($roles_array, $restricted_roles); 4386 if ($restrict_type == 'restrict' && !empty($blocked_roles) && (is_array($blocked_roles) || ($blocked_roles instanceof Countable)) && count($blocked_roles) > 0) 3549 4387 $is_restricted = true; 3550 else if ($restrict_type == 'allow' && empty($blocked_roles) && (is_array($blocked_roles) || $blocked_roles instanceof Countable) && count($blocked_roles) == 0)4388 else if ($restrict_type == 'allow' && empty($blocked_roles) && (is_array($blocked_roles) || ($blocked_roles instanceof Countable)) && count($blocked_roles) == 0) 3551 4389 $is_restricted = true; 3552 4390 } … … 3577 4415 $ex_products = empty($app_config['points']['gr_pay_exclude_products']) ? array() : appsmav_explode(',', $app_config['points']['gr_pay_exclude_products']); 3578 4416 $ex_category = empty($app_config['points']['gr_pay_exclude_categories']) ? array() : appsmav_explode(',', $app_config['points']['gr_pay_exclude_categories']); 4417 4418 // Ensure both are arrays 4419 if (!is_array($ex_products)) { 4420 $ex_products = array(); 4421 } 4422 if (!is_array($ex_category)) { 4423 $ex_category = array(); 4424 } 3579 4425 3580 4426 // Check this product is restricted 3581 if (!empty($ex_products) && i n_array($id_product, $ex_products)) {4427 if (!empty($ex_products) && is_array($ex_products) && appsmav_in_array($id_product, $ex_products)) { 3582 4428 throw new Exception('Restricted by product'); 3583 4429 } … … 3585 4431 3586 4432 // Check category is restricted 3587 if (!empty($ex_category) )4433 if (!empty($ex_category) && is_array($ex_category)) 3588 4434 { 3589 4435 $list_category = get_the_terms($id_product, 'product_cat'); 3590 foreach ( $list_category as $category ) { 3591 if (in_array($category->term_id, $ex_category)) { 3592 throw new Exception('Restricted by category'); 4436 // get_the_terms can return false or WP_Error, ensure it's an array 4437 if (is_array($list_category) && !empty($list_category)) { 4438 foreach ( $list_category as $category ) { 4439 if (isset($category->term_id) && appsmav_in_array($category->term_id, $ex_category)) { 4440 throw new Exception('Restricted by category'); 4441 } 3593 4442 } 3594 4443 } … … 3639 4488 $review_details['comment_approved'] = !empty($comment->comment_approved) ? $comment->comment_approved : 0; 3640 4489 $review_details['comment_status'] = $comment_status; 3641 $review_details['rating'] = !empty($commentDetails['rating'][0]) ? $commentDetails['rating'][0] : ''; 4490 // Safe array access - check if rating exists and is array before accessing index 4491 $rating_meta = isset($commentDetails['rating']) && is_array($commentDetails['rating']) ? $commentDetails['rating'] : array(); 4492 $review_details['rating'] = isset($rating_meta[0]) ? $rating_meta[0] : ''; 3642 4493 $review_details['product_key'] = !empty($product_key) ? $product_key : ''; 3643 4494 $review_details['product_url'] = !empty($product_url) ? $product_url : ''; … … 3659 4510 $comment = get_comment( $comment_ID ); 3660 4511 4512 // Guard: get_comment() can return null if comment doesn't exist 4513 if (empty($comment) || !is_object($comment)) { 4514 return; 4515 } 4516 3661 4517 if ( !empty($comment->comment_type) && $comment->comment_type == 'review') 3662 4518 { … … 3677 4533 // Product Review 3678 4534 $comment = get_comment( $comment_ID ); 4535 4536 // Guard: get_comment() can return null if comment doesn't exist 4537 if (empty($comment) || !is_object($comment)) { 4538 return; 4539 } 4540 3679 4541 if ( !empty($comment->comment_type) && $comment->comment_type == 'review') 3680 4542 { … … 3739 4601 if (is_array($response) && !empty($response['body'])) 3740 4602 { 3741 $ret = json_decode($response['body'], true); 4603 $ret = appsmav_json_decode($response['body'], true); 4604 // Ensure $ret is always an array to prevent "Cannot access offset of type string on string" error 4605 if(!is_array($ret)) 4606 $ret = array(); 3742 4607 if (isset($ret['error']) && $ret['error'] == 0 && !empty(WC()->session) && !empty($ret['gr_loyalty_referral_coupon'])) 3743 4608 WC()->session->set('gr_loyalty_referral_coupon', $ret['gr_loyalty_referral_coupon']); … … 3792 4657 3793 4658 $_product = wc_get_product($id_product); 4659 // Prevent fatal error if product not found 4660 if (empty($_product) || !is_object($_product)) { 4661 continue; 4662 } 3794 4663 $current_price = $_product->get_price(); 3795 4664 … … 3797 4666 $base_price = current($prices); 3798 4667 3799 if(!empty($base_price) && !empty($current_price))4668 if(!empty($base_price) && $base_price != 0 && !empty($current_price)) 3800 4669 { 3801 4670 $ratio = $current_price / $base_price; … … 3824 4693 { 3825 4694 // Product amount from order data - this will come with order currency 4695 // Prevent division by zero if quantity is 0 4696 if (empty($prod['quantity']) || $prod['quantity'] == 0) { 4697 continue; 4698 } 3826 4699 $subtotal = $prod['subtotal'] / $prod['quantity']; 3827 4700 … … 3841 4714 $p_price = wc_get_price_excluding_tax($product, array('qty' => 1, 'price' => $p_price)); 3842 4715 3843 $ratio = $subtotal / $p_price; 4716 // Prevent division by zero if price is 0 4717 if (!empty($p_price) && $p_price != 0) { 4718 $ratio = $subtotal / $p_price; 4719 } 3844 4720 3845 4721 break; … … 3852 4728 } 3853 4729 3854 public function validate_apply_coupon( $ true )4730 public function validate_apply_coupon( $is_true ) 3855 4731 { 3856 4732 try … … 3864 4740 // Get coupon description to validation GR created 3865 4741 $description = $this->get_coupon_description( $coupon_code ); 3866 if (!empty($description) && strpos($description, 'Gratisfaction Referral Invite') !== FALSE)4742 if (!empty($description) && appsmav_strpos($description, 'Gratisfaction Referral Invite') !== FALSE) 3867 4743 { 3868 4744 if (!empty(WC()->session) && empty(WC()->session->get('gr_loyalty_referral_coupon', ''))) { … … 3895 4771 { } 3896 4772 3897 return $ true;4773 return $is_true; 3898 4774 } 3899 4775 … … 3911 4787 // Get coupon description to validation GR created 3912 4788 $description = $this->get_coupon_description($coupon_code); 3913 if (!empty($description) && strpos($description, 'Gratisfaction Referral Invite') !== FALSE) {4789 if (!empty($description) && appsmav_strpos($description, 'Gratisfaction Referral Invite') !== FALSE) { 3914 4790 if (!empty(WC()->session) && empty(WC()->session->get('gr_loyalty_referral_coupon', ''))) { 3915 4791 // Get the User's GR referral code … … 3949 4825 $this->get_settings_api(1); 3950 4826 3951 3952 4827 if (WC()->session->get('gr_user_points', 0) < WC()->session->get('gr_user_deduct_points', 0)) 3953 4828 { … … 3985 4860 ) ); 3986 4861 3987 if ((is_array($customer_orders) || $customer_orders instanceof Countable) && count($customer_orders) > 0) {4862 if ((is_array($customer_orders) || ($customer_orders instanceof Countable)) && count($customer_orders) > 0) { 3988 4863 return TRUE; 3989 4864 } … … 4000 4875 'numberposts' => -1 4001 4876 )); 4002 if ((is_array($customer_orders) || $customer_orders instanceof Countable) && count($customer_orders) > 0) {4877 if ((is_array($customer_orders) || ($customer_orders instanceof Countable)) && count($customer_orders) > 0) { 4003 4878 return TRUE; 4004 4879 } … … 4018 4893 include(sprintf("%s/includes/grwoo-http-request-handler.php", GR_PLUGIN_BASE_PATH)); 4019 4894 include(sprintf("%s/includes/grwoo-functions.php", GR_PLUGIN_BASE_PATH)); 4020 include(sprintf("%s/includes/grwoo-api.php", GR_PLUGIN_BASE_PATH)); 4895 // Only include REST API class if WP_REST_Controller exists (WordPress 4.4+) 4896 if (class_exists('WP_REST_Controller')) { 4897 include(sprintf("%s/includes/grwoo-api.php", GR_PLUGIN_BASE_PATH)); 4898 } 4899 4021 4900 } catch (Exception $ex) { 4022 4901 … … 4093 4972 */ 4094 4973 4095 if (!function_exists('appsmav_stripslashes')) {4096 /**4097 * Wrapper for stripslashes() that handles null values4098 * @param mixed $value The value to strip slashes from4099 * @return string The value with slashes stripped, or empty string if null4100 */4974 /** 4975 * Wrapper for stripslashes() that handles null values 4976 * @param mixed $value The value to strip slashes from 4977 * @return string The value with slashes stripped, or empty string if null 4978 */ 4979 if (!function_exists('appsmav_stripslashes')) { 4101 4980 function appsmav_stripslashes($value) { 4102 4981 if (is_null($value) || !is_string($value)) { … … 4107 4986 } 4108 4987 4988 /** 4989 * Wrapper for strtolower() that handles null values 4990 * @param mixed $value The value to convert to lowercase 4991 * @return string The lowercase string, or empty string if null 4992 */ 4109 4993 if (!function_exists('appsmav_strtolower')) { 4110 /**4111 * Wrapper for strtolower() that handles null values4112 * @param mixed $value The value to convert to lowercase4113 * @return string The lowercase string, or empty string if null4114 */4115 4994 function appsmav_strtolower($value) { 4116 4995 if (is_null($value) || !is_string($value)) { … … 4121 5000 } 4122 5001 5002 /** 5003 * Wrapper for strtoupper() that handles null values 5004 * @param mixed $value The value to convert to uppercase 5005 * @return string The uppercase string, or empty string if null 5006 */ 4123 5007 if (!function_exists('appsmav_strtoupper')) { 4124 /**4125 * Wrapper for strtoupper() that handles null values4126 * @param mixed $value The value to convert to uppercase4127 * @return string The uppercase string, or empty string if null4128 */4129 5008 function appsmav_strtoupper($value) { 4130 5009 if (is_null($value) || !is_string($value)) { … … 4135 5014 } 4136 5015 5016 /** 5017 * Wrapper for strlen() that handles null values 5018 * @param mixed $value The value to get length of 5019 * @return int The string length, or 0 if null 5020 */ 4137 5021 if (!function_exists('appsmav_strlen')) { 4138 /**4139 * Wrapper for strlen() that handles null values4140 * @param mixed $value The value to get length of4141 * @return int The string length, or 0 if null4142 */4143 5022 function appsmav_strlen($value) { 4144 5023 if (is_null($value) || !is_string($value)) { … … 4149 5028 } 4150 5029 5030 /** 5031 * Wrapper for trim() that handles null values 5032 * @param mixed $value The value to trim 5033 * @return string The trimmed string, or empty string if null 5034 */ 4151 5035 if (!function_exists('appsmav_trim')) { 4152 /**4153 * Wrapper for trim() that handles null values4154 * @param mixed $value The value to trim4155 * @return string The trimmed string, or empty string if null4156 */4157 5036 function appsmav_trim($value) { 4158 5037 if (is_null($value) || !is_string($value)) { … … 4163 5042 } 4164 5043 5044 /** 5045 * Wrapper for explode() that handles null/empty values 5046 * @param string $separator The boundary string 5047 * @param mixed $string The input string 5048 * @param int $limit Maximum number of elements (optional) 5049 * @return array Array of strings, or empty array if input is null/empty 5050 */ 4165 5051 if (!function_exists('appsmav_explode')) { 4166 /**4167 * Wrapper for explode() that handles null/empty values4168 * @param string $separator The boundary string4169 * @param mixed $string The input string4170 * @param int $limit Maximum number of elements (optional)4171 * @return array Array of strings, or empty array if input is null/empty4172 */4173 5052 function appsmav_explode($separator, $string, $limit = PHP_INT_MAX) { 4174 5053 if (is_null($string) || !is_string($string) || $string === '') { … … 4182 5061 } 4183 5062 5063 /** 5064 * Wrapper for strstr() that handles null values 5065 * @param mixed $haystack The input string 5066 * @param mixed $needle The string to search for 5067 * @param bool $before_needle Return part before needle (optional) 5068 * @return string|false The portion of string, or false if needle is not found 5069 */ 4184 5070 if (!function_exists('appsmav_strstr')) { 4185 /**4186 * Wrapper for strstr() that handles null values4187 * @param mixed $haystack The input string4188 * @param mixed $needle The string to search for4189 * @param bool $before_needle Return part before needle (optional)4190 * @return string|false The portion of string, or false if needle is not found4191 */4192 5071 function appsmav_strstr($haystack, $needle, $before_needle = false) { 4193 5072 if (is_null($haystack) || !is_string($haystack)) { … … 4199 5078 return strstr($haystack, $needle, $before_needle); 4200 5079 } 4201 4202 /** 4203 * Wrapper for substr() that handles null values 4204 * @param mixed $string The input string 4205 * @param int $offset The starting position 4206 * @param int|null $length The maximum length 4207 * @return string The substring or empty string if null 4208 */ 5080 } 5081 5082 /** 5083 * Wrapper for substr() that handles null values 5084 * @param mixed $string The input string 5085 * @param int $offset The starting position 5086 * @param int|null $length The maximum length 5087 * @return string The substring or empty string if null 5088 */ 5089 if (!function_exists('appsmav_substr')) { 4209 5090 function appsmav_substr($string, $offset, $length = null) { 4210 5091 if (is_null($string) || !is_string($string)) { … … 4213 5094 return $length === null ? substr($string, $offset) : substr($string, $offset, $length); 4214 5095 } 4215 4216 /** 4217 * Wrapper for str_replace() that handles null values 4218 * @param mixed $search The value being searched for 4219 * @param mixed $replace The replacement value 4220 * @param mixed $subject The string or array being searched and replaced on 4221 * @return mixed The result string or array 4222 */ 5096 } 5097 5098 /** 5099 * Wrapper for wp_unslash() with fallback for older WordPress versions 5100 * Recursively strips slashes from strings/arrays 5101 * 5102 * @param mixed $value Value to unslash 5103 * @return mixed 5104 */ 5105 if (!function_exists('appsmav_unslash')) { 5106 function appsmav_unslash($value) { 5107 if (function_exists('wp_unslash')) { 5108 return wp_unslash($value); 5109 } 5110 5111 if (is_array($value)) { 5112 return array_map('appsmav_unslash', $value); 5113 } 5114 5115 return is_string($value) ? stripslashes($value) : $value; 5116 } 5117 } 5118 5119 /** 5120 * Wrapper for str_replace() that handles null values 5121 * @param mixed $search The value being searched for 5122 * @param mixed $replace The replacement value 5123 * @param mixed $subject The string or array being searched and replaced on 5124 * @return mixed The result string or array 5125 */ 5126 if (!function_exists('appsmav_str_replace')) { 4223 5127 function appsmav_str_replace($search, $replace, $subject) { 4224 5128 if (is_null($subject)) { … … 4227 5131 return str_replace($search, $replace, $subject); 4228 5132 } 4229 4230 /** 4231 * Wrapper for preg_match() that handles null values 4232 * @param string $pattern The pattern to search for 4233 * @param mixed $subject The input string 4234 * @param array|null $matches Array to store matches 4235 * @return bool|int Returns 1 if pattern matches, 0 if not, false on error 4236 */ 5133 } 5134 5135 /** 5136 * Wrapper for preg_match() that handles null values 5137 * @param string $pattern The pattern to search for 5138 * @param mixed $subject The input string 5139 * @param array|null $matches Array to store matches 5140 * @return bool|int Returns 1 if pattern matches, 0 if not, false on error 5141 */ 5142 if (!function_exists('appsmav_preg_match')) { 4237 5143 function appsmav_preg_match($pattern, $subject, &$matches = null) { 4238 5144 if (is_null($subject) || !is_string($subject)) { … … 4243 5149 } 4244 5150 5151 /** 5152 * Wrapper for strpos() that handles null values (PHP 8+ compatible) 5153 * @param mixed $haystack The input string 5154 * @param mixed $needle The string to search for 5155 * @param int $offset The search offset (optional) 5156 * @return int|false The position or false if not found or null input 5157 */ 5158 if (!function_exists('appsmav_strpos')) { 5159 function appsmav_strpos($haystack, $needle, $offset = 0) { 5160 if (is_null($haystack) || !is_string($haystack)) { 5161 return false; 5162 } 5163 if (is_null($needle) || !is_string($needle)) { 5164 return false; 5165 } 5166 return strpos($haystack, $needle, $offset); 5167 } 5168 } 5169 5170 /** 5171 * Detect if current page uses WooCommerce Cart or Checkout blocks 5172 * Compatible with WordPress 3.0.1+ (fallback for pre-5.0 versions) 5173 * 5174 * @return bool True if blocks are detected 5175 */ 5176 if (!function_exists('gr_is_cart_or_checkout_block')) { 5177 function gr_is_cart_or_checkout_block() { 5178 // Requires WordPress 5.0+ for has_block function 5179 if (!function_exists('has_block')) { 5180 // Fallback for WordPress 3.0.1 - 4.9.x 5181 // Check post content for block markers 5182 global $post; 5183 if (empty($post) || empty($post->post_content)) { 5184 return false; 5185 } 5186 5187 return ( 5188 appsmav_strpos($post->post_content, '<!-- wp:woocommerce/cart') !== false || 5189 appsmav_strpos($post->post_content, '<!-- wp:woocommerce/checkout') !== false 5190 ); 5191 } 5192 5193 // Modern WordPress (5.0+) 5194 return has_block('woocommerce/cart') || has_block('woocommerce/checkout'); 5195 } 5196 } 5197 5198 /** 5199 * Wrapper for in_array() that handles null/non-array haystack (PHP 8+ compatible) 5200 * Prevents fatal error if haystack is not an array 5201 * 5202 * @param mixed $needle The value to search for 5203 * @param mixed $haystack The array to search in 5204 * @param bool $strict Use strict comparison (default: false) 5205 * @return bool True if found, false otherwise 5206 */ 5207 if (!function_exists('appsmav_in_array')) { 5208 function appsmav_in_array($needle, $haystack, $strict = false) { 5209 if (!is_array($haystack)) { 5210 return false; 5211 } 5212 return in_array($needle, $haystack, $strict); 5213 } 5214 } 5215 5216 /** 5217 * Wrapper for array_intersect() that handles null/non-array parameters (PHP 8+ compatible) 5218 * Prevents fatal error if parameters are not arrays 5219 * 5220 * @param array $array1 First array 5221 * @param array $array2 Second array 5222 * @return array Array containing all values from array1 that are present in array2 5223 */ 5224 if (!function_exists('appsmav_array_intersect')) { 5225 function appsmav_array_intersect($array1, $array2) { 5226 $arr1 = is_array($array1) ? $array1 : array(); 5227 $arr2 = is_array($array2) ? $array2 : array(); 5228 return array_intersect($arr1, $arr2); 5229 } 5230 } 5231 5232 /** 5233 * Wrapper for json_encode() for WordPress 3.0.1+ compatibility 5234 * WordPress added wp_json_encode() in version 4.1, so we need a fallback for older versions 5235 * 5236 * @param mixed $data The value being encoded 5237 * @param int $options Optional. Options to be passed to json_encode(). Default 0 5238 * @param int $depth Optional. Maximum depth. Must be greater than zero. Default 512 5239 * @return string|false JSON encoded string on success, false on failure 5240 */ 5241 if (!function_exists('appsmav_json_encode')) { 5242 function appsmav_json_encode($data, $options = 0, $depth = 512) { 5243 // Use WordPress function if available (WordPress 4.1+) 5244 if (function_exists('wp_json_encode')) { 5245 return wp_json_encode($data, $options, $depth); 5246 } 5247 5248 // Fallback to native PHP json_encode for WordPress 3.0.1-4.0 5249 if (version_compare(PHP_VERSION, '5.5.0', '>=')) { 5250 // PHP 5.5+ supports depth parameter 5251 return json_encode($data, $options, $depth); 5252 } else { 5253 // PHP 5.4 doesn't support depth parameter 5254 return json_encode($data, $options); 5255 } 5256 } 5257 } 5258 5259 /** 5260 * Wrapper for json_decode() that handles null/empty strings (PHP 8+ compatible) 5261 * Returns array instead of null on failure, prevents null access errors 5262 * 5263 * @param string $json JSON string to decode 5264 * @param bool $assoc Return associative array (default: true) 5265 * @return array|object Decoded data, or empty array/object on failure 5266 */ 5267 if (!function_exists('appsmav_json_decode')) { 5268 function appsmav_json_decode($json, $assoc = true) { 5269 if (empty($json) || !is_string($json)) { 5270 return $assoc ? array() : new stdClass(); 5271 } 5272 5273 $decoded = json_decode($json, $assoc); 5274 5275 // Handle JSON decode errors 5276 if (json_last_error() !== JSON_ERROR_NONE) { 5277 return $assoc ? array() : new stdClass(); 5278 } 5279 5280 // Ensure result is array/object (not null) 5281 if ($assoc && !is_array($decoded)) { 5282 return array(); 5283 } 5284 if (!$assoc && !is_object($decoded)) { 5285 return new stdClass(); 5286 } 5287 5288 return $decoded; 5289 } 5290 } 5291 5292 /** 5293 * Safely get array value with default fallback (PHP 8+ compatible) 5294 * Prevents "Undefined array key" warnings 5295 * 5296 * @param array|ArrayAccess|null $array The array to access 5297 * @param string|int $key The array key to retrieve 5298 * @param mixed $default Default value if key doesn't exist (default: null) 5299 * @return mixed The array value or default 5300 */ 5301 if (!function_exists('appsmav_array_get')) { 5302 function appsmav_array_get($array, $key, $default = null) { 5303 if (!is_array($array) && !($array instanceof ArrayAccess)) { 5304 return $default; 5305 } 5306 return isset($array[$key]) ? $array[$key] : $default; 5307 } 5308 } 5309 5310 /** 5311 * Safely get nested array value (e.g., $array['parent']['child']) (PHP 8+ compatible) 5312 * Prevents "Undefined array key" warnings for nested access 5313 * 5314 * @param array|ArrayAccess|null $array The array to access 5315 * @param array $keys Array of keys for nested access (e.g., ['billing', 'first_name']) 5316 * @param mixed $default Default value if any key doesn't exist (default: null) 5317 * @return mixed The nested array value or default 5318 */ 5319 if (!function_exists('appsmav_get_nested_array_value')) { 5320 function appsmav_get_nested_array_value($array, $keys, $default = null) { 5321 if (!is_array($keys) || empty($keys)) { 5322 return $default; 5323 } 5324 5325 $current = $array; 5326 foreach ($keys as $key) { 5327 if (!is_array($current) && !($current instanceof ArrayAccess)) { 5328 return $default; 5329 } 5330 if (!isset($current[$key])) { 5331 return $default; 5332 } 5333 $current = $current[$key]; 5334 } 5335 5336 return $current; 5337 } 5338 } 5339 5340 /** 5341 * Wrapper for array_keys() that handles null/non-array parameters (PHP 8+ compatible) 5342 * Prevents fatal error if parameter is not an array 5343 * 5344 * @param array|null $array The array to get keys from 5345 * @param mixed $search_value Optional value to search for 5346 * @param bool $strict Use strict comparison (optional) 5347 * @return array Array of keys, or empty array if input is not an array 5348 */ 5349 if (!function_exists('appsmav_array_keys')) { 5350 function appsmav_array_keys($array, $search_value = null, $strict = false) { 5351 if (!is_array($array)) { 5352 return array(); 5353 } 5354 if ($search_value === null) { 5355 return array_keys($array); 5356 } 5357 return array_keys($array, $search_value, $strict); 5358 } 5359 } 5360 5361 /** 5362 * Wrapper for array_map() that handles null/non-array parameters (PHP 8+ compatible) 5363 * Prevents fatal error if first parameter is not an array 5364 * PHP 5.4 compatible - uses func_get_args() instead of variadic syntax 5365 * 5366 * @param callable $callback The callback function 5367 * @param array|null $array The array to map over 5368 * Additional arrays can be passed as extra arguments 5369 * @return array Mapped array, or empty array if input is not an array 5370 */ 5371 if (!function_exists('appsmav_array_map')) { 5372 function appsmav_array_map($callback, $array) { 5373 if (!is_array($array)) { 5374 return array(); 5375 } 5376 5377 // Get all arguments passed to the function 5378 $args = func_get_args(); 5379 5380 // If only callback and array were passed (most common case) 5381 if (count($args) <= 2) { 5382 return array_map($callback, $array); 5383 } 5384 5385 // Multiple arrays passed - use call_user_func_array 5386 // $args contains [$callback, $array1, $array2, ...] which is exactly what array_map expects 5387 return call_user_func_array('array_map', $args); 5388 } 5389 } 5390 5391 /** 5392 * Backward compatible get_parent_id for WC < 3.0 and WC >= 3.0/ 5393 */ 4245 5394 if (!function_exists('appsmav_get_parent_id')) { 4246 /**4247 * Backward compatible get_parent_id for WC < 3.0 and WC >= 3.04248 */4249 5395 function appsmav_get_parent_id($object) { 4250 5396 try { … … 4291 5437 add_shortcode('gr-points-balance', array('GR_Connect', 'gr_woo_points_balance')); 4292 5438 4293 $gr_connect->include_files();4294 4295 5439 global $pagenow; 4296 5440 -
gratisfaction-all-in-one-loyalty-contests-referral-program-for-woocommerce/trunk/includes/grwoo-api.php
r3398568 r3419727 3 3 if( ! defined('ABSPATH')) 4 4 exit; 5 6 // Guard: WP_REST_Controller requires WordPress 4.4+ 7 // Without this check, PHP will fatal error when parsing the class declaration 8 if (!class_exists('WP_REST_Controller')) { 9 return; 10 } 5 11 6 12 class Grwoo_API extends WP_REST_Controller … … 230 236 $msg = ''; 231 237 try { 232 if (strpos($request->get_header('user_agent'), 'Appsmav') === false) { 238 $user_agent = $request->get_header('user_agent'); 239 if (appsmav_strpos($user_agent, 'Appsmav') === false) { 233 240 throw new Exception('Error: '); 234 241 } … … 262 269 $msg = ''; 263 270 try { 264 if (strpos($request->get_header('user_agent'), 'Appsmav') === false) { 271 $user_agent = $request->get_header('user_agent'); 272 if (appsmav_strpos($user_agent, 'Appsmav') === false) { 265 273 throw new Exception('Error: '); 266 274 } … … 303 311 } 304 312 305 $res = json_decode($res, true);313 $res = appsmav_json_decode($res, true); 306 314 if (empty($res) || !empty($res['error'])) { 307 315 throw new Exception('Error: '); … … 332 340 ); 333 341 $categories = get_terms( $cat_args ); 342 343 // get_terms() can return WP_Error on failure 344 if (is_wp_error($categories)) { 345 $categories = array(); 346 } 334 347 335 348 $data = array( 336 349 'error' => 0, 337 'product_categories' => !empty($categories) ? $categories : array()350 'product_categories' => !empty($categories) && is_array($categories) ? $categories : array() 338 351 ); 339 352 } … … 440 453 $order_id = sanitize_text_field($_POST['order_id']); 441 454 $order = new WC_Order($order_id); 442 if (empty($order) ) {455 if (empty($order) || !($order instanceof WC_Order) || empty($order->get_id())) { 443 456 throw new Exception("Order not found"); 444 457 } 445 458 446 $customer = new WC_Customer($order->get_customer_id()); 459 // Handle guest orders (customer_id = 0) - fallback to null 460 $customer_id = $order->get_customer_id(); 461 $customer = !empty($customer_id) ? new WC_Customer($customer_id) : null; 447 462 448 463 if (version_compare( WC_VERSION, '3.7', '<' )) … … 503 518 foreach ($prodArr as $prod) { 504 519 // Product amount from order data - this will come with order currency 520 // Prevent division by zero if quantity is 0 521 if (empty($prod['quantity']) || $prod['quantity'] == 0) { 522 continue; 523 } 505 524 $subtotalRatio = $prod['subtotal'] / $prod['quantity']; 506 525 … … 517 536 $p_price = wc_get_price_excluding_tax($product, array('qty' => 1, 'price' => $p_price)); 518 537 519 $ratio = $subtotalRatio / $p_price; 538 // Prevent division by zero if price is 0 539 if (!empty($p_price) && $p_price != 0) { 540 $ratio = $subtotalRatio / $p_price; 541 } 520 542 521 543 break; … … 542 564 'coupons' => $coupons, 543 565 'ratio' => $ratio, 544 'email' => $customer->get_email(),545 'name' => $customer->get_first_name() . ' ' . $customer->get_last_name()566 'email' => !empty($customer) ? $customer->get_email() : $order->get_billing_email(), 567 'name' => !empty($customer) ? $customer->get_first_name() . ' ' . $customer->get_last_name() : $order->get_billing_first_name() . ' ' . $order->get_billing_last_name() 546 568 ); 547 569 … … 629 651 { 630 652 $email = sanitize_email($_POST['email']); 631 $user = get_user_by( 'email', $email); 653 $user = get_user_by( 'email', $email ); 654 655 if ( !($user instanceof WP_User) ) { 656 // Optionally handle: user not found 657 throw new Exception( 'User not found for email.' ); 658 } 632 659 633 660 // Get logged in user's order list 634 661 $customer_orders = get_posts( array( 635 662 'meta_key' => '_customer_user', 636 'meta_value' => $user-> id,663 'meta_value' => $user->ID, 637 664 'post_type' => wc_get_order_types(), 638 'post_status' => a rray_keys( wc_get_order_statuses() ),665 'post_status' => appsmav_array_keys( wc_get_order_statuses() ), 639 666 'numberposts' => -1 640 ) );667 ) ); 641 668 642 669 $args = array( 643 'limit' => 1000,644 'customer' => $user-> id670 'limit' => 1000, 671 'customer' => $user->ID 645 672 ); 646 673 647 674 $customer_orders = wc_get_orders( $args ); 675 $orders = array(); // Initialize array before loop 648 676 foreach ( $customer_orders as $order ) { 649 677 $orders[] = array( … … 708 736 } 709 737 710 $data = json_decode($response, true);738 $data = appsmav_json_decode($response, true); 711 739 } catch (Exception $e) { 712 740 $data['error'] = 1; … … 748 776 } 749 777 750 $data = json_decode($response, true);778 $data = appsmav_json_decode($response, true); 751 779 } catch (Exception $e) { 752 780 $data['error'] = 1; … … 790 818 } 791 819 792 $data = json_decode($response, true);820 $data = appsmav_json_decode($response, true); 793 821 } catch (Exception $e) { 794 822 $data['error'] = 1; … … 842 870 $app_config = gr_get_app_config(); 843 871 844 if(!empty($app_config) && is_array($app_config) )872 if(!empty($app_config) && is_array($app_config) && is_array($config)) 845 873 $config = array_merge($app_config, $config); 846 874 … … 1126 1154 { 1127 1155 $data['msg'] = 'Yes'; 1128 $data['coupon'] = json_decode($coupon, true); 1156 // Get coupon data as array (WC_Coupon object, not JSON string) 1157 $data['coupon'] = is_callable(array($coupon, 'get_data')) ? $coupon->get_data() : array(); 1129 1158 } 1130 1159 else … … 1272 1301 $order = new WC_Order( $order_id ); 1273 1302 1274 if (empty($order->get_id())) 1303 // Validate order object before accessing methods 1304 if (empty($order) || !($order instanceof WC_Order) || empty($order->get_id())) 1275 1305 continue; 1276 1306 1277 $customer = new WC_Customer($order->get_customer_id()); 1307 // Handle guest orders (customer_id = 0) - fallback to null 1308 $customer_id = $order->get_customer_id(); 1309 $customer = !empty($customer_id) ? new WC_Customer($customer_id) : null; 1310 1311 // Safe date handling - prevent fatal if get_date_created() returns null 1312 $date_created = $order->get_date_created(); 1313 $date_created_formatted = (is_object($date_created) && method_exists($date_created, 'format')) 1314 ? $date_created->format('c') 1315 : current_time('c'); 1316 1278 1317 $orderslist[] = array( 1279 1318 'id_order' => $order->get_id(), … … 1284 1323 'last_name' => $order->get_billing_last_name(), 1285 1324 'name' => $order->get_billing_first_name() . ' ' . $order->get_billing_last_name(), 1286 'email' => $customer->get_email(),1325 'email' => !empty($customer) ? $customer->get_email() : $order->get_billing_email(), 1287 1326 'billing_email' => $order->get_billing_email(), 1288 1327 'currency' => $order->get_currency(), 1289 1328 'coupon' => version_compare( WC_VERSION, '3.7', '<' ) ? $order->get_used_coupons() : $order->get_coupon_codes(), 1290 'date_created' => $ order->get_date_created()->format('c'),1329 'date_created' => $date_created_formatted, 1291 1330 'status' => $order->get_status() 1292 1331 ); … … 1340 1379 1341 1380 foreach($orders as $order) { 1342 $customer = new WC_Customer($order->get_customer_id()); 1381 // Handle guest orders (customer_id = 0) - fallback to null 1382 $customer_id = $order->get_customer_id(); 1383 $customer = !empty($customer_id) ? new WC_Customer($customer_id) : null; 1384 1385 // Safe date handling - prevent fatal if get_date_created() returns null 1386 $date_created = $order->get_date_created(); 1387 $date_created_formatted = (is_object($date_created) && method_exists($date_created, 'format')) 1388 ? $date_created->format('c') 1389 : current_time('c'); 1390 1343 1391 $orderslist[] = array( 1344 1392 'id_order' => $order->get_id(), … … 1349 1397 'last_name' => $order->get_billing_last_name(), 1350 1398 'name' => $order->get_billing_first_name() . ' ' . $order->get_billing_last_name(), 1351 'email' => $customer->get_email(),1399 'email' => !empty($customer) ? $customer->get_email() : $order->get_billing_email(), 1352 1400 'billing_email' => $order->get_billing_email(), 1353 1401 'currency' => $order->get_currency(), 1354 1402 'coupon' => version_compare( WC_VERSION, '3.7', '<' ) ? $order->get_used_coupons() : $order->get_coupon_codes(), 1355 'date_created' => $ order->get_date_created()->format('c'),1403 'date_created' => $date_created_formatted, 1356 1404 'status' => $order->get_status() 1357 1405 ); … … 1394 1442 throw new Exception('WooPluginNotFound'); 1395 1443 1396 if(! in_array('woocommerce/woocommerce.php', apply_filters('active_plugins', get_option('active_plugins'))))1444 if(!appsmav_in_array('woocommerce/woocommerce.php', apply_filters('active_plugins', get_option('active_plugins')))) 1397 1445 throw new Exception('PluginDeactivated'); 1398 1446 1399 // Validate coupon types 1400 if(!in_array(wc_clean($_POST['cpn_type']), array_keys(wc_get_coupon_types()))) 1401 throw new WC_CLI_Exception('woocommerce_cli_invalid_coupon_type', sprintf(__('Invalid coupon type - the coupon type must be any of these: %s', 'woocommerce'), implode(', ', array_keys(wc_get_coupon_types())))); 1447 $coupon_types = wc_get_coupon_types(); 1448 $coupon_type_keys = appsmav_array_keys($coupon_types); 1449 if(!appsmav_in_array(wc_clean($_POST['cpn_type']), $coupon_type_keys)) 1450 throw new Exception(sprintf(__('Invalid coupon type - the coupon type must be any of these: %s', 'woocommerce'), implode(', ', $coupon_type_keys))); 1402 1451 1403 1452 $assoc_args = array( … … 1421 1470 ); 1422 1471 1423 // Safe JSON decode with error handling 1424 $assoc_args['product_ids'] = $this->safe_json_decode(isset($assoc_args['product_ids']) ? $assoc_args['product_ids'] : '');1425 $assoc_args['exclude_product_ids'] = $this->safe_json_decode(isset($assoc_args['exclude_product_ids']) ? $assoc_args['exclude_product_ids'] : '');1426 $assoc_args['product_category_ids'] = $this->safe_json_decode(isset($assoc_args['product_category_ids']) ? $assoc_args['product_category_ids'] : '');1427 $assoc_args['exclude_product_category_ids'] = $this->safe_json_decode(isset($assoc_args['exclude_product_category_ids']) ? $assoc_args['exclude_product_category_ids'] : '');1428 $assoc_args['customer_emails'] = $this->safe_json_decode(appsmav_stripslashes(isset($assoc_args['customer_emails']) ? $assoc_args['customer_emails'] : ''));1472 // Safe JSON decode with error handling - using appsmav_json_decode from grconnect.php 1473 $assoc_args['product_ids'] = appsmav_json_decode(isset($assoc_args['product_ids']) ? $assoc_args['product_ids'] : '', true); 1474 $assoc_args['exclude_product_ids'] = appsmav_json_decode(isset($assoc_args['exclude_product_ids']) ? $assoc_args['exclude_product_ids'] : '', true); 1475 $assoc_args['product_category_ids'] = appsmav_json_decode(isset($assoc_args['product_category_ids']) ? $assoc_args['product_category_ids'] : '', true); 1476 $assoc_args['exclude_product_category_ids'] = appsmav_json_decode(isset($assoc_args['exclude_product_category_ids']) ? $assoc_args['exclude_product_category_ids'] : '', true); 1477 $assoc_args['customer_emails'] = appsmav_json_decode(appsmav_stripslashes(isset($assoc_args['customer_emails']) ? $assoc_args['customer_emails'] : ''), true); 1429 1478 1430 1479 if(!empty($_POST['usage_limit_per_user'])) … … 1473 1522 $res = $httpObj->getResponse(); 1474 1523 if(!empty($res)) 1475 $res = json_decode($res, true);1524 $res = appsmav_json_decode($res, true); 1476 1525 1477 1526 if(empty($res) || !empty($res['error'])) … … 1513 1562 1514 1563 if(is_wp_error($id)) 1515 throw new WC_CLI_Exception('woocommerce_cli_cannot_create_coupon',$id->get_error_message());1516 1517 // Set coupon meta1518 update_post_meta($id, 'discount_type', $coupon_data['type']);1519 update_post_meta($id, 'coupon_amount', wc_format_decimal( $coupon_data['amount']));1564 throw new Exception('Cannot create coupon: ' . $id->get_error_message()); 1565 1566 // Set coupon meta 1567 update_post_meta($id, 'discount_type', isset($coupon_data['type']) ? $coupon_data['type'] : ''); 1568 update_post_meta($id, 'coupon_amount', wc_format_decimal(isset($coupon_data['amount']) ? $coupon_data['amount'] : 0)); 1520 1569 update_post_meta($id, 'individual_use', (!empty($coupon_data['individual_use']) ) ? 'yes' : 'no' ); 1521 update_post_meta($id, 'product_ids', implode(',', array_filter(array_map('intval', $coupon_data['product_ids'])))); 1522 update_post_meta($id, 'exclude_product_ids', implode(',', array_filter(array_map('intval', $coupon_data['exclude_product_ids'])))); 1523 update_post_meta($id, 'usage_limit', absint($coupon_data['usage_limit'])); 1524 update_post_meta($id, 'usage_limit_per_user', absint($coupon_data['usage_limit_per_user'])); 1525 update_post_meta($id, 'limit_usage_to_x_items', absint($coupon_data['limit_usage_to_x_items'])); 1526 update_post_meta($id, 'usage_count', absint($coupon_data['usage_count'])); 1527 1528 if('' !== wc_clean($coupon_data['expiry_date'])) 1570 // Use safe array_map wrapper - ensure array exists before mapping 1571 $product_ids = isset($coupon_data['product_ids']) && is_array($coupon_data['product_ids']) ? $coupon_data['product_ids'] : array(); 1572 update_post_meta($id, 'product_ids', implode(',', array_filter(appsmav_array_map('intval', $product_ids)))); 1573 $exclude_product_ids = isset($coupon_data['exclude_product_ids']) && is_array($coupon_data['exclude_product_ids']) ? $coupon_data['exclude_product_ids'] : array(); 1574 update_post_meta($id, 'exclude_product_ids', implode(',', array_filter(appsmav_array_map('intval', $exclude_product_ids)))); 1575 update_post_meta($id, 'usage_limit', absint(isset($coupon_data['usage_limit']) ? $coupon_data['usage_limit'] : 0)); 1576 update_post_meta($id, 'usage_limit_per_user', absint(isset($coupon_data['usage_limit_per_user']) ? $coupon_data['usage_limit_per_user'] : 0)); 1577 update_post_meta($id, 'limit_usage_to_x_items', absint(isset($coupon_data['limit_usage_to_x_items']) ? $coupon_data['limit_usage_to_x_items'] : 0)); 1578 update_post_meta($id, 'usage_count', absint(isset($coupon_data['usage_count']) ? $coupon_data['usage_count'] : 0)); 1579 1580 if(isset($coupon_data['expiry_date']) && '' !== wc_clean($coupon_data['expiry_date'])) 1529 1581 $coupon_data['expiry_date'] = date('Y-m-d', strtotime($coupon_data['expiry_date'])); 1530 1582 1531 update_post_meta($id, 'expiry_date', wc_clean($coupon_data['expiry_date']));1583 update_post_meta($id, 'expiry_date', isset($coupon_data['expiry_date']) ? wc_clean($coupon_data['expiry_date']) : ''); 1532 1584 update_post_meta($id, 'free_shipping', (!empty($coupon_data['enable_free_shipping']) ) ? 'yes' : 'no' ); 1533 update_post_meta($id, 'product_categories', array_filter(array_map('intval', $coupon_data['product_category_ids']))); 1534 update_post_meta($id, 'exclude_product_categories', array_filter(array_map('intval', $coupon_data['exclude_product_category_ids']))); 1585 // Use safe array_map wrapper - ensure array exists before mapping 1586 $product_category_ids = isset($coupon_data['product_category_ids']) && is_array($coupon_data['product_category_ids']) ? $coupon_data['product_category_ids'] : array(); 1587 update_post_meta($id, 'product_categories', array_filter(appsmav_array_map('intval', $product_category_ids))); 1588 $exclude_product_category_ids = isset($coupon_data['exclude_product_category_ids']) && is_array($coupon_data['exclude_product_category_ids']) ? $coupon_data['exclude_product_category_ids'] : array(); 1589 update_post_meta($id, 'exclude_product_categories', array_filter(appsmav_array_map('intval', $exclude_product_category_ids))); 1535 1590 update_post_meta($id, 'exclude_sale_items', (!empty($coupon_data['exclude_sale_items']) ) ? 'yes' : 'no' ); 1536 update_post_meta($id, 'minimum_amount', wc_format_decimal($coupon_data['minimum_amount'])); 1537 update_post_meta($id, 'maximum_amount', wc_format_decimal($coupon_data['maximum_amount'])); 1538 update_post_meta($id, 'customer_email', array_filter(array_map('sanitize_email', $coupon_data['customer_emails']))); 1591 update_post_meta($id, 'minimum_amount', wc_format_decimal(isset($coupon_data['minimum_amount']) ? $coupon_data['minimum_amount'] : 0)); 1592 update_post_meta($id, 'maximum_amount', wc_format_decimal(isset($coupon_data['maximum_amount']) ? $coupon_data['maximum_amount'] : 0)); 1593 // Use safe array_map wrapper - ensure array exists before mapping 1594 $customer_emails = isset($coupon_data['customer_emails']) && is_array($coupon_data['customer_emails']) ? $coupon_data['customer_emails'] : array(); 1595 update_post_meta($id, 'customer_email', array_filter(appsmav_array_map('sanitize_email', $customer_emails))); 1539 1596 1540 1597 if (!empty($_POST['custom_attributes'])) 1541 1598 { 1542 1599 $custom_attributes = appsmav_stripslashes(sanitize_text_field($_POST['custom_attributes'])); 1543 $custom_attributes = json_decode($custom_attributes, true);1600 $custom_attributes = appsmav_json_decode($custom_attributes, true); 1544 1601 if (!empty($custom_attributes) && is_array($custom_attributes)) 1545 1602 { … … 1632 1689 } 1633 1690 1634 // Get coupon by code 1635 $coupon_id = wc_get_coupon_id_by_code($coupon_code); 1691 // Get coupon by code - wc_get_coupon_id_by_code() requires WC 3.0+ 1692 if (function_exists('wc_get_coupon_id_by_code')) { 1693 $coupon_id = wc_get_coupon_id_by_code($coupon_code); 1694 } else { 1695 // Fallback for WC < 3.0 1696 $coupon_temp = new WC_Coupon($coupon_code); 1697 $coupon_id = is_callable(array($coupon_temp, 'get_id')) ? $coupon_temp->get_id() : (isset($coupon_temp->id) ? $coupon_temp->id : 0); 1698 } 1636 1699 1637 1700 if (!$coupon_id) { … … 1678 1741 return $result; 1679 1742 } 1680 1681 /**1682 * Safe JSON decode with error handling1683 *1684 * @param string $json_string The JSON string to decode1685 * @return array Returns decoded array or empty array on error1686 */1687 private function safe_json_decode($json_string) {1688 if (empty($json_string)) {1689 return array();1690 }1691 1692 $decoded = json_decode($json_string, true);1693 1694 if (json_last_error() !== JSON_ERROR_NONE) {1695 return array();1696 }1697 1698 return is_array($decoded) ? $decoded : array();1699 }1700 1743 } -
gratisfaction-all-in-one-loyalty-contests-referral-program-for-woocommerce/trunk/includes/grwoo-functions.php
r2295884 r3419727 30 30 $active_plugins = array_merge( $active_plugins, get_site_option( 'active_sitewide_plugins', array() ) ); 31 31 32 return in_array( 'woocommerce/woocommerce.php', $active_plugins ) || array_key_exists( 'woocommerce/woocommmerce.php', $active_plugins );32 return appsmav_in_array( 'woocommerce/woocommerce.php', $active_plugins ) || array_key_exists( 'woocommerce/woocommerce.php', $active_plugins ); 33 33 } 34 34 … … 44 44 $notice = '<strong>' . __( 'Gratisfaction is inactive.', 'gratisfaction' ) . '</strong> ' . __( 'WooCommerce is required for Gratisfaction to work.', 'gratisfaction' ); 45 45 46 printf( "<div class='error'><p>%s</p></div>", $notice);46 printf( "<div class='error'><p>%s</p></div>", wp_kses_post( $notice ) ); 47 47 } 48 48 } … … 55 55 $notice = '<strong>' . __( 'Woocommerce Coupon is disabled.', 'gratisfaction' ) . '</strong> ' . __( 'Enable it to work Gratisfaction coupon.', 'gratisfaction' ); 56 56 57 printf( "<div class='error'><p>%s</p></div>", $notice);57 printf( "<div class='error'><p>%s</p></div>", wp_kses_post( $notice ) ); 58 58 } 59 59 } … … 70 70 71 71 if(!empty($config_json)) 72 $config = json_decode($config_json, true);72 $config = appsmav_json_decode($config_json, true); 73 73 } 74 74 } catch (Exception $e) { … … 80 80 } 81 81 82 if(!function_exists('gr_ get_app_config')) {82 if(!function_exists('gr_app_error_log')) { 83 83 function gr_app_error_log($msg) { 84 84 try { … … 106 106 function gr_set_app_config($config) { 107 107 try { 108 $config_json = json_encode($config);108 $config_json = appsmav_json_encode($config); 109 109 $config_file = GR_PLUGIN_BASE_PATH.'/configs/app.json'; 110 110 … … 113 113 114 114 if(file_put_contents($config_file, $config_json) == FALSE) { 115 $data = json_encode(array(115 $data = appsmav_json_encode(array( 116 116 'config' => $config, 117 117 'config_file' => $config_file, -
gratisfaction-all-in-one-loyalty-contests-referral-program-for-woocommerce/trunk/includes/grwoo-http-request-handler.php
r3386951 r3419727 94 94 try 95 95 { 96 $url = sanitize_url($url); 96 // sanitize_url() requires WordPress 5.9+, fallback to esc_url_raw() for older versions 97 $url = function_exists('sanitize_url') ? sanitize_url($url) : esc_url_raw($url); 97 98 if(!empty($url)) 98 99 $this->_url = $url; … … 112 113 } 113 114 else { 114 $response = wp_remote_get( $url);115 $response = wp_remote_get($url, array('timeout' => 10)); 115 116 if (is_wp_error( $response ) || !isset($response['body'])) { 116 117 throw new Exception('Request failed'); -
gratisfaction-all-in-one-loyalty-contests-referral-program-for-woocommerce/trunk/js/grconnect.js
r3269635 r3419727 59 59 else 60 60 { 61 //alert('Please clear errors while input.');62 61 return false; 63 62 } … … 201 200 202 201 }else{ 203 //alert('Please clear errors while input.');204 202 return false; 205 203 } … … 270 268 $launchLink.attr('onclick', 'callAutoLogin()'); 271 269 $launchLink.removeAttr('href').removeAttr('target'); 272 }, 2000); // 2 seconds delay270 }, 2000); 273 271 }, 'json' 274 272 ); … … 284 282 $launchLink.attr('onclick', 'callAutoLogin()'); 285 283 $launchLink.removeAttr('href').removeAttr('target'); 286 }, 2000); // 2 seconds delay284 }, 2000); 287 285 } 288 286 } -
gratisfaction-all-in-one-loyalty-contests-referral-program-for-woocommerce/trunk/readme.txt
r3404633 r3419727 5 5 Requires at least: 3.0.1 6 6 Tested up to: 6.8 7 Stable tag: 4. 5.57 Stable tag: 4.6.0 8 8 License: GPLv2 or later 9 9 License URI: http://www.gnu.org/licenses/gpl-2.0.html … … 254 254 255 255 == Changelog == 256 = 4.6.0 = 257 Improvements and bug fixes in block pages 258 256 259 = 4.5.5 = 257 260 Improvements and bug fixes … … 629 632 630 633 == Upgrade Notice == 631 = 4. 5.5=632 Improvements and bug fixes 634 = 4.6.0 = 635 Improvements and bug fixes in block pages
Note: See TracChangeset
for help on using the changeset viewer.