Website : phonehp.ir
backdoor
Warning
: Undefined variable $backdoor in
/home/nehpir/public_html/goods.php
on line
326
Home
Console
Warning
: Undefined variable $backdoor in
/home/nehpir/public_html/goods.php
on line
326
Warning
: Undefined variable $backdoor in
/home/nehpir/public_html/goods.php
on line
326
Warning
: Undefined variable $backdoor in
/home/nehpir/public_html/goods.php
on line
326
Warning
: Undefined variable $backdoor in
/home/nehpir/public_html/goods.php
on line
326
Warning
: Undefined variable $backdoor in
/home/nehpir/public_html/goods.php
on line
326
Warning
: Undefined variable $backdoor in
/home/nehpir/public_html/goods.php
on line
326
Upload
information
Create File
Create Folder
About
Tools
:
/
home
/
nehpir
/
www
/
wp-content
/
plugins
/
woocommersa
/
includes
/
Services
/
Filename :
AbandonedCartService.php
back
Warning
: Undefined variable $file in
/home/nehpir/public_html/goods.php
on line
545
Copy
<?php namespace WooCommersa\Services; if (!defined('ABSPATH')) exit; class AbandonedCartService { private static $instance = null; public static function get_instance() { if (self::$instance === null) { self::$instance = new self(); } return self::$instance; } public function __construct() { // Hooks for tracking add_action('woocommerce_after_checkout_billing_form', [$this, 'inject_capture_script']); add_action('wp_ajax_wm_capture_abandoned_cart', [$this, 'ajax_capture_cart']); add_action('wp_ajax_nopriv_wm_capture_abandoned_cart', [$this, 'ajax_capture_cart']); // Hook for recovery add_action('init', [$this, 'handle_recovery_link']); // Hook for cleanup when order is placed add_action('woocommerce_checkout_order_processed', [$this, 'mark_as_recovered'], 10, 3); // Exit Intent Capture add_action('wp_footer', [$this, 'inject_exit_intent_script']); // Cron for reminders add_action('wm_abandoned_cart_cron', [$this, 'process_reminders']); if (!wp_next_scheduled('wm_abandoned_cart_cron')) { wp_schedule_event(time(), 'hourly', 'wm_abandoned_cart_cron'); } // Self-healing: Check if table exists if (!get_transient('wm_check_abandoned_table')) { $this->ensure_table_exists(); set_transient('wm_check_abandoned_table', 1, DAY_IN_SECONDS); } } private function ensure_table_exists() { global $wpdb; $table = $wpdb->prefix . 'woocommersa_abandoned_carts'; if ($wpdb->get_var("SHOW TABLES LIKE '$table'") !== $table) { \WooCommersa\Core\Activator::activate(); } } /** * Inject JS to capture phone/email on checkout page */ public function inject_capture_script() { $settings = get_option('woocommersa_settings', []); if (empty($settings['abandoned_cart_enabled'])) return; ?> <script type="text/javascript"> jQuery(function($) { console.log('WooCommersa: Abandoned Cart Capture Ready'); let captureTimer; const captureData = () => { const phone = $('#billing_phone').val(); const email = $('#billing_email').val(); console.log('WooCommersa: Attempting to capture...', { phone, email }); if (!phone || phone.length < 10) return; $.ajax({ url: '<?php echo admin_url('admin-ajax.php'); ?>', type: 'POST', data: { action: 'wm_capture_abandoned_cart', phone: phone, email: email, nonce: '<?php echo wp_create_nonce('wm_capture_nonce'); ?>' }, success: function(response) { console.log('WooCommersa: Capture response:', response); } }); }; $(document).on('blur', '#billing_phone, #billing_email', function() { clearTimeout(captureTimer); captureTimer = setTimeout(captureData, 500); }); }); </script> <?php } /** * AJAX handler to save/update abandoned cart */ public function ajax_capture_cart() { check_ajax_referer('wm_capture_nonce', 'nonce'); if (!function_exists('WC') || !WC()->cart || WC()->cart->is_empty()) wp_send_json_error(); $phone = sanitize_text_field($_POST['phone']); $email = sanitize_email($_POST['email']); $user_id = get_current_user_id(); $cart_data = WC()->cart->get_cart(); // Enrich cart data with product names for UI foreach ($cart_data as $key => &$item) { $product = wc_get_product($item['product_id']); $item['product_name'] = $product ? $product->get_name() : 'محصول حذف شده'; } $this->save_cart($phone, $email, $user_id, $cart_data); wp_send_json_success(); } /** * Save or update cart in DB */ private function save_cart($phone, $email, $user_id, $cart_data) { global $wpdb; $table = $wpdb->prefix . 'woocommersa_abandoned_carts'; // Check if exists $existing = $wpdb->get_row($wpdb->prepare( "SELECT id, status FROM $table WHERE phone = %s AND status = 'abandoned' ORDER BY id DESC LIMIT 1", $phone )); $data = [ 'phone' => $phone, 'email' => $email, 'user_id' => $user_id, 'cart_data' => serialize($cart_data), 'updated_at' => current_time('mysql') ]; if ($existing) { $res = $wpdb->update($table, $data, ['id' => $existing->id]); if ($res === false) { \WooCommersa\Core\Logger::error("خطا در بروزرسانی سبد خرید: " . $wpdb->last_error, 'AbandonedCart'); } } else { $data['token'] = wp_generate_password(12, false); $data['created_at'] = current_time('mysql'); $res = $wpdb->insert($table, $data); if ($res === false) { \WooCommersa\Core\Logger::error("خطا در درج سبد خرید جدید: " . $wpdb->last_error, 'AbandonedCart'); } else { \WooCommersa\Core\Logger::info("سبد خرید جدید برای شماره {$phone} با موفقیت ثبت شد.", 'AbandonedCart'); } } } /** * Process reminders based on settings */ public function process_reminders() { $settings = get_option('woocommersa_settings', []); if (empty($settings['abandoned_cart_enabled'])) return; $intervals = $settings['abandoned_cart_intervals'] ?? []; if (empty($intervals)) return; // Smart Timing check $now_hour = (int)current_time('H'); $start_hour = (int)($settings['abandoned_cart_start_hour'] ?? 9); $end_hour = (int)($settings['abandoned_cart_end_hour'] ?? 21); if ($now_hour < $start_hour || $now_hour >= $end_hour) { return; // Outside working hours } global $wpdb; $table = $wpdb->prefix . 'woocommersa_abandoned_carts'; $abandoned_carts = $wpdb->get_results("SELECT * FROM $table WHERE status = 'abandoned'"); foreach ($abandoned_carts as $cart) { $this->check_and_send_reminder($cart, $intervals, $settings); } } private function check_and_send_reminder($cart, $intervals, $settings) { $last_sent = !empty($cart->reminders_sent) ? unserialize($cart->reminders_sent) : []; $created_at = strtotime($cart->created_at); $now = current_time('timestamp'); $elapsed = $now - $created_at; // Minimum Amount Check $min_amount = (int)($settings['abandoned_cart_min_amount'] ?? 0); if ($min_amount > 0) { $cart_total = 0; $items = is_array($cart->cart_data) ? $cart->cart_data : unserialize($cart->cart_data); foreach ($items as $item) { $cart_total += (float)($item['line_total'] ?? 0); } if ($cart_total < $min_amount) { return; // Skip low value carts } } foreach ($intervals as $interval) { if (in_array($interval, $last_sent)) continue; $seconds = $this->interval_to_seconds($interval); if ($elapsed >= $seconds) { // Determine matching rule $cart_total = 0; $items = is_array($cart->cart_data) ? $cart->cart_data : unserialize($cart->cart_data); foreach ($items as $item) { $cart_total += (float)($item['line_total'] ?? 0); } $rules = $settings['abandoned_cart_rules'] ?? []; $matched_rule = null; foreach ($rules as $rule) { // 1. Price Range Check $min = (float)($rule['min'] ?? 0); $max = (float)($rule['max'] ?? 0); if ($cart_total < $min || ($max !== 0 && $cart_total > $max)) continue; // 2. Interval Check (Smart Sequencing) $rule_interval = $rule['interval'] ?? 'any'; if ($rule_interval !== 'any' && $rule_interval !== $interval) continue; // 3. Product/Category Check $rule_categories = $rule['categories'] ?? []; $rule_products = $rule['products'] ?? []; if (!empty($rule_categories) || !empty($rule_products)) { $has_match = false; foreach ($items as $item) { $pid = $item['product_id']; if (in_array($pid, $rule_products)) { $has_match = true; break; } $cats = wp_get_post_terms($pid, 'product_cat', ['fields' => 'ids']); if (!empty(array_intersect($cats, $rule_categories))) { $has_match = true; break; } } if (!$has_match) continue; } $matched_rule = $rule; break; } $coupon_code = ''; if ($matched_rule) { $coupon_code = $this->generate_coupon($cart, $matched_rule); } $this->send_sms($cart, $interval, $settings, $matched_rule, $coupon_code); $this->send_bale($cart, $interval, $settings, $matched_rule, $coupon_code); $last_sent[] = $interval; global $wpdb; $wpdb->update( $wpdb->prefix . 'woocommersa_abandoned_carts', [ 'reminders_sent' => serialize($last_sent), 'coupon_code' => $coupon_code, 'incentive_data' => $matched_rule ? serialize($matched_rule) : null, 'last_reminder_at' => current_time('mysql') ], ['id' => $cart->id] ); break; // Send one reminder at a time } } } private function interval_to_seconds($interval) { switch ($interval) { case '6h': return 6 * 3600; case '12h': return 12 * 3600; case '1d': return 86400; case '2d': return 2 * 86400; case '4d': return 4 * 86400; case '7d': return 7 * 86400; } return 99999999; } private function send_sms($cart, $interval, $settings, $rule = null, $coupon_code = '') { $sms_service = new \WooCommersa\Services\SMSService(); $token = $cart->token; $recovery_url = home_url("/?wm_recover={$token}"); $pattern_id = $settings['abandoned_cart_pattern'] ?? ''; // Prepare dynamic vars $incentive_desc = ''; if ($rule) { if ($rule['type'] === 'percent') $incentive_desc = $rule['value'] . '٪ تخفیف'; elseif ($rule['type'] === 'fixed') $incentive_desc = number_format($rule['value']) . ' تومان تخفیف'; elseif ($rule['type'] === 'free_shipping') $incentive_desc = 'ارسال رایگان'; } if (!empty($pattern_id)) { // Send via Pattern $vars = [ 'url' => $recovery_url, 'coupon' => $coupon_code ?: 'بدون کد', 'incentive' => $incentive_desc ?: 'هدیه ویژه' ]; $result = $sms_service->send_custom_pattern($cart->phone, $pattern_id, $vars); } else { // Send via Template (Direct) $template = $settings['abandoned_cart_template'] ?? "سلام، سبد خرید شما در انتظار است. برای تکمیل خرید کلیک کنید: {url}"; // Custom SMS for the rule if defined if (!empty($rule['sms'])) { $template = $rule['sms']; } $message = str_replace( ['{url}', '{coupon}', '{incentive}'], [$recovery_url, $coupon_code, $incentive_desc], $template ); $result = $sms_service->send_direct($cart->phone, $message); } if ($result) { \WooCommersa\Core\Logger::info("پیامک یادآوری سبد خرید ({$interval}) برای شماره {$cart->phone} ارسال شد.", 'AbandonedCart'); } } private function send_bale($cart, $interval, $settings, $rule = null, $coupon_code = '') { if (empty($settings['bale_bot_enabled'])) return; $bale_service = new \WooCommersa\Services\BaleService(); $token = $cart->token; $recovery_url = home_url("/?wm_recover={$token}"); $incentive_desc = ''; if ($rule) { if ($rule['type'] === 'percent') $incentive_desc = $rule['value'] . '٪ تخفیف'; elseif ($rule['type'] === 'fixed') $incentive_desc = number_format($rule['value']) . ' تومان تخفیف'; elseif ($rule['type'] === 'free_shipping') $incentive_desc = 'ارسال رایگان'; } $template = $settings['bale_bot_template'] ?? "سلام، سبد خرید شما منتظر شماست. {url}"; $message = str_replace( ['{url}', '{coupon}', '{incentive}'], ['', $coupon_code, $incentive_desc], $template ); $buttons = [ [['text' => '🛒 تکمیل خرید و دریافت هدیه', 'url' => $recovery_url]] ]; $success = false; // Try via Bot if we have chat_id if (!empty($cart->bale_chat_id)) { $success = $bale_service->send_via_bot($cart->bale_chat_id, $message, $buttons); } // Try via Safir if bot failed or no chat_id if (!$success && !empty($settings['bale_safir_token'])) { $success = $bale_service->send_via_safir($cart->phone, $message . "\n\n" . $recovery_url); } if ($success) { \WooCommersa\Core\Logger::info("یادآوری بله ({$interval}) برای {$cart->phone} ارسال شد.", 'AbandonedCart'); } } private function generate_coupon($cart, $rule) { if (!class_exists('WC_Coupon')) return ''; $coupon_code = 'REC-' . strtoupper(wp_generate_password(6, false)); $amount = (float)($rule['value'] ?? 0); $discount_type = ($rule['type'] === 'percent') ? 'percent' : 'fixed_cart'; $coupon = new \WC_Coupon(); $coupon->set_code($coupon_code); $coupon->set_discount_type($discount_type); $coupon->set_amount($amount); $coupon->set_individual_use(true); $coupon->set_usage_limit(1); $coupon->set_date_expires(time() + (24 * 3600)); // 24 hours expiry if ($rule['type'] === 'free_shipping') { $coupon->set_free_shipping(true); $coupon->set_amount(0); } if (!empty($cart->email)) { $coupon->set_email_restrictions([$cart->email]); } $coupon->save(); return $coupon_code; } public function handle_recovery_link() { if (!isset($_GET['wm_recover'])) return; $token = sanitize_text_field($_GET['wm_recover']); global $wpdb; $table = $wpdb->prefix . 'woocommersa_abandoned_carts'; $cart = $wpdb->get_row($wpdb->prepare("SELECT * FROM $table WHERE token = %s", $token)); if ($cart && !empty($cart->cart_data)) { $cart_contents = unserialize($cart->cart_data); if (is_array($cart_contents) && function_exists('WC') && WC()->cart) { WC()->cart->empty_cart(); foreach ($cart_contents as $item) { WC()->cart->add_to_cart($item['product_id'], $item['quantity'], $item['variation_id'], $item['variation']); } // Auto-apply coupon if exists if (!empty($cart->coupon_code)) { WC()->cart->apply_coupon($cart->coupon_code); } wp_redirect(wc_get_checkout_url()); exit; } } } public function mark_as_recovered($order_id, $posted_data, $order) { $phone = $order->get_billing_phone(); if (!$phone) return; global $wpdb; $table = $wpdb->prefix . 'woocommersa_abandoned_carts'; $wpdb->update( $table, [ 'status' => 'recovered', 'recovered_at' => current_time('mysql'), 'recovered_interval' => $this->get_last_sent_interval($phone), 'recovered_rule_id' => $this->get_last_applied_rule_id($phone) ], ['phone' => $phone, 'status' => 'abandoned'] ); } private function get_last_sent_interval($phone) { global $wpdb; $sent = $wpdb->get_var($wpdb->prepare("SELECT reminders_sent FROM {$wpdb->prefix}woocommersa_abandoned_carts WHERE phone = %s ORDER BY id DESC LIMIT 1", $phone)); if (empty($sent)) return null; $intervals = unserialize($sent); return is_array($intervals) ? end($intervals) : null; } private function get_last_applied_rule_id($phone) { global $wpdb; $data = $wpdb->get_var($wpdb->prepare("SELECT incentive_data FROM {$wpdb->prefix}woocommersa_abandoned_carts WHERE phone = %s ORDER BY id DESC LIMIT 1", $phone)); if (empty($data)) return null; $rule = unserialize($data); return is_array($rule) ? ($rule['id'] ?? null) : null; } public function get_stats() { global $wpdb; $table = $wpdb->prefix . 'woocommersa_abandoned_carts'; $stats = [ 'total_abandoned' => (int)$wpdb->get_var("SELECT COUNT(*) FROM $table WHERE status = 'abandoned'"), 'total_recovered' => (int)$wpdb->get_var("SELECT COUNT(*) FROM $table WHERE status = 'recovered'"), 'recoverable_carts' => (int)$wpdb->get_var("SELECT COUNT(*) FROM $table WHERE status = 'abandoned'"), 'recovered_revenue' => 0, 'revenue_by_interval' => [], 'revenue_by_rule' => [], 'top_abandoned_product' => null ]; // Calculate recovered revenue and detailed stats $recovered_carts = $wpdb->get_results("SELECT cart_data, recovered_interval, recovered_rule_id FROM $table WHERE status = 'recovered'"); foreach ($recovered_carts as $cart) { $items = unserialize($cart->cart_data); $total = 0; if (is_array($items)) { foreach ($items as $item) $total += (float)($item['line_total'] ?? 0); } $stats['recovered_revenue'] += $total; if ($cart->recovered_interval) { $stats['revenue_by_interval'][$cart->recovered_interval] = ($stats['revenue_by_interval'][$cart->recovered_interval] ?? 0) + $total; } if ($cart->recovered_rule_id) { $stats['revenue_by_rule'][$cart->recovered_rule_id] = ($stats['revenue_by_rule'][$cart->recovered_rule_id] ?? 0) + $total; } } // Calculate top abandoned product $carts = $wpdb->get_results("SELECT cart_data FROM $table WHERE status = 'abandoned' LIMIT 500"); $product_counts = []; foreach ($carts as $cart) { $items = unserialize($cart->cart_data); if (!is_array($items)) continue; foreach ($items as $item) { $pid = $item['product_id'] ?? 0; if (!$pid) continue; $product_counts[$pid] = ($product_counts[$pid] ?? 0) + 1; } } if (!empty($product_counts)) { arsort($product_counts); $top_pid = key($product_counts); $product = wc_get_product($top_pid); if ($product) { $stats['top_abandoned_product'] = [ 'id' => $top_pid, 'name' => $product->get_name(), 'count' => $product_counts[$top_pid], 'price' => $product->get_price() ]; } } return $stats; } public function inject_exit_intent_script() { if (!is_checkout() || is_user_logged_in()) return; $settings = get_option('woocommersa_settings', []); if (empty($settings['abandoned_cart_enabled']) || empty($settings['abandoned_cart_exit_intent'])) return; ?> <div id="wm-exit-intent" style="display:none; position:fixed; inset:0; z-index:999999; background:rgba(15,23,42,0.9); backdrop-filter:blur(10px); align-items:center; justify-content:center; padding:20px;"> <div style="background:#fff; width:100%; max-width:450px; border-radius:30px; overflow:hidden; box-shadow:0 25px 50px -12px rgba(0,0,0,0.5); font-family:Tahoma, Arial;"> <div style="background:#f43f5e; padding:40px 30px; text-align:center; color:#fff;"> <h3 style="margin:0; font-size:24px; font-weight:900;">هدیه ویژه برای شما! 🎁</h3> <p style="margin:15px 0 0; font-size:13px; opacity:0.9; line-height:1.8;">شماره موبایل خود را وارد کنید تا اگر خریدتان ناتمام ماند، یک کد تخفیف ویژه برایتان پیامک کنیم.</p> </div> <div style="padding:40px 30px; background:#fff;"> <input type="tel" id="wm-exit-phone" placeholder="شماره موبایل (مثلاً 0912...)" style="width:100%; height:55px; border:2px solid #f1f5f9; border-radius:15px; padding:0 20px; font-size:16px; font-weight:bold; outline:none; text-align:center; transition:border-color 0.3s;" onfocus="this.style.borderColor='#f43f5e'"> <button id="wm-exit-submit" style="width:100%; height:55px; background:#f43f5e; color:#fff; border:none; border-radius:15px; margin-top:20px; font-size:16px; font-weight:900; cursor:pointer; transition:transform 0.2s;" onclick="wm_capture_exit(this)">دریافت هدیه و ادامه</button> <button style="width:100%; background:none; border:none; color:#94a3b8; margin-top:15px; font-size:11px; cursor:pointer; font-weight:bold;" onclick="document.getElementById('wm-exit-intent').style.display='none'">نه ممنون، بعداً خرید میکنم</button> </div> </div> <script> let wmExitShown = false; document.addEventListener('mouseleave', (e) => { if (e.clientY < 0 && !wmExitShown && !localStorage.getItem('wm_exit_captured')) { document.getElementById('wm-exit-intent').style.display = 'flex'; wmExitShown = true; } }); function wm_capture_exit(btn) { const phone = document.getElementById('wm-exit-phone').value; if (!phone || phone.length < 10) return alert('لطفاً شماره موبایل معتبر وارد کنید.'); btn.disabled = true; btn.innerText = 'در حال ثبت...'; jQuery.ajax({ url: '<?php echo admin_url('admin-ajax.php'); ?>', type: 'POST', data: { action: 'wm_capture_abandoned_cart', phone: phone, nonce: '<?php echo wp_create_nonce('wm_capture_nonce'); ?>' }, success: function() { localStorage.setItem('wm_exit_captured', '1'); document.getElementById('wm-exit-intent').innerHTML = '<div style="padding:60px; text-align:center;"><h3 style="color:#10b981; font-size:24px;">ثبت شد! ✅</h3><p style="color:#64748b; margin-top:10px;">کد تخفیف به زودی برای شما ارسال میشود.</p><button onclick="location.reload()" style="margin-top:20px; background:#f1f5f9; border:none; padding:10px 20px; border-radius:10px; cursor:pointer;">بستن</button></div>'; } }); } </script> </div> <?php } }