%1$s %2$s

', esc_html__('3DM Connect', '3dm-connect'), esc_html__('requires WooCommerce to be installed and active.', '3dm-connect') ); } add_action('admin_enqueue_scripts', 'connect_3dm_enqueue_assets'); function connect_3dm_enqueue_assets($hook) { if (strpos($hook, 'woocommerce_page_wc-settings') === false) return; wp_enqueue_style('c3dm-style', plugin_dir_url(__FILE__) . 'style.css', array(), '6.2.8'); wp_enqueue_script('c3dm-script', plugin_dir_url(__FILE__) . 'script.js', array('jquery'), '6.2.8', true); $definitions = connect_3dm_get_definitions(); $saved_data = array(); foreach($definitions as $key => $def) { $saved_data[$key] = array( 'tpl' => get_option("connect_3dm_{$key}_template"), 'map' => get_option("connect_3dm_{$key}_map", array()) ); } wp_localize_script('c3dm-script', 'c3dm_params', array( 'ajax_url' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('c3dm_nonce_action'), 'templates' => get_option('connect_3dm_cached_templates', array()), 'events' => $definitions, 'saved' => $saved_data, 'i18n' => array( 'syncing' => esc_html__('Syncing...', '3dm-connect'), 'refresh' => esc_html__('🔄 Refresh Templates', '3dm-connect'), 'error' => esc_html__('Error: ', '3dm-connect'), 'ajax_fail' => esc_html__('Server connection failed. Please try again.', '3dm-connect'), 'invalid_template' => esc_html__("⚠️ Cannot Select Template\n\nThe template '{template}' uses a {type} header.\n\nThis version of the plugin only supports TEXT headers. Please choose a different template.", '3dm-connect'), 'skip' => esc_html__('-- Skip --', '3dm-connect'), 'no_vars' => esc_html__('No variables found in this template.', '3dm-connect'), 'select_tpl' => esc_html__('Select a template to configure mapping.', '3dm-connect') ) )); } function connect_3dm_get_definitions() { $vars_order = array( 'billing_first_name' => __('First Name', '3dm-connect'), 'billing_last_name' => __('Last Name', '3dm-connect'), 'billing_phone' => __('Phone', '3dm-connect'), 'order_id' => __('Order ID', '3dm-connect'), 'order_total' => __('Total Amount', '3dm-connect'), 'currency' => __('Currency', '3dm-connect'), 'payment_method' => __('Payment Mode', '3dm-connect'), 'order_status' => __('Status', '3dm-connect'), 'order_date' => __('Date', '3dm-connect'), 'item_count' => __('Items', '3dm-connect'), 'customer_note' => __('Note', '3dm-connect'), 'store_name' => __('Store Name', '3dm-connect'), 'order_view_url' => __('Order Link', '3dm-connect') ); $vars_refund = array_merge($vars_order, array('refund_amount' => __('Refund Amount', '3dm-connect'), 'refund_reason' => __('Reason', '3dm-connect'))); $vars_cart = array( 'billing_first_name' => __('First Name', '3dm-connect'), 'billing_phone' => __('Phone', '3dm-connect'), 'cart_total' => __('Cart Total', '3dm-connect'), 'checkout_url' => __('Checkout URL', '3dm-connect') ); return array( 'new_order' => array('label' => __('New Order', '3dm-connect'), 'icon' => '📦', 'hook' => 'woocommerce_new_order', 'vars' => $vars_order), 'completed' => array('label' => __('Completed', '3dm-connect'), 'icon' => '✅', 'hook' => 'woocommerce_order_status_completed', 'vars' => $vars_order), 'on_hold' => array('label' => __('On-Hold', '3dm-connect'), 'icon' => '⏳', 'hook' => 'woocommerce_order_status_on-hold', 'vars' => $vars_order), 'cancelled' => array('label' => __('Cancelled', '3dm-connect'), 'icon' => '❌', 'hook' => 'woocommerce_order_status_cancelled', 'vars' => $vars_order), 'failed' => array('label' => __('Failed', '3dm-connect'), 'icon' => '⚠️', 'hook' => 'woocommerce_order_status_failed', 'vars' => $vars_order), 'refunded' => array('label' => __('Refunded', '3dm-connect'), 'icon' => '💰', 'hook' => 'woocommerce_order_refunded', 'vars' => $vars_refund), 'pending' => array('label' => __('Pending Payment', '3dm-connect'), 'icon' => '🕒', 'hook' => 'woocommerce_order_status_pending', 'vars' => $vars_order), 'abandoned_cart' => array('label' => __('Abandoned Cart', '3dm-connect'), 'icon' => '🛒', 'hook' => 'connect_3dm_cart_abandoned_trigger', 'vars' => $vars_cart) ); } function connect_3dm_settings_page() { if (!current_user_can('manage_woocommerce')) { return; } $company_hash = get_option('connect_3dm_company_hash', ''); $definitions = connect_3dm_get_definitions(); $active_events = get_option('connect_3dm_active_events', array()); ?>

📱

$def): $isEnabled = in_array($key, $active_events) ? 'enabled' : ''; ?>

$def): ?>

$def) { $template = isset($_POST["connect_3dm_{$key}_template"]) ? sanitize_text_field(wp_unslash($_POST["connect_3dm_{$key}_template"])) : ''; update_option("connect_3dm_{$key}_template", $template); if ( isset( $_POST["connect_3dm_{$key}_map"] ) && is_array( $_POST["connect_3dm_{$key}_map"] ) ) { $sanitized_map = map_deep( wp_unslash( $_POST["connect_3dm_{$key}_map"] ), 'sanitize_text_field' ); update_option( "connect_3dm_{$key}_map", $sanitized_map ); } } } // --- 5. AJAX HANDLER --- add_action('wp_ajax_connect_3dm_refresh_templates', 'connect_3dm_ajax_refresh_templates'); function connect_3dm_ajax_refresh_templates() { check_ajax_referer('c3dm_nonce_action', 'nonce'); if (!current_user_can('manage_woocommerce')) { wp_send_json_error(__('Unauthorized access.', '3dm-connect')); } $hash = isset($_POST['hash']) ? sanitize_text_field(wp_unslash($_POST['hash'])) : ''; if (empty($hash)) { wp_send_json_error(__('Company Hash is required.', '3dm-connect')); } update_option('connect_3dm_company_hash', $hash); $response = wp_remote_post('https://connect.3dm-sols.com/api/get-all-templates.php', array( 'headers' => array('Authorization' => 'Bearer ' . $hash), 'timeout' => 20 )); if (is_wp_error($response)) { wp_send_json_error(__('API Request Failed: ', '3dm-connect') . $response->get_error_message()); } $data = json_decode(wp_remote_retrieve_body($response), true); if (isset($data['templates']) && is_array($data['templates'])) { update_option('connect_3dm_cached_templates', $data['templates']); wp_send_json_success(); } else { wp_send_json_error(__('Failed to retrieve templates. Check your Hash.', '3dm-connect')); } } function connect_3dm_log($message) { if (function_exists('wc_get_logger')) { $logger = wc_get_logger(); $context = array('source' => '3dm-connect'); $logger->info($message, $context); } } // --- 6. EVENT TRIGGERS (Async via Action Scheduler) --- function connect_3dm_register_event_hooks() { $active = get_option('connect_3dm_active_events', array()); if (empty($active) || !is_array($active)) return; foreach(connect_3dm_get_definitions() as $key => $def) { if ($key === 'abandoned_cart') continue; if(in_array($key, $active)) { add_action($def['hook'], 'connect_3dm_dispatch_event', 10, 2); } } } function connect_3dm_dispatch_event($arg1 = null, $arg2 = null) { $current_hook = current_action(); $event_key = ''; foreach(connect_3dm_get_definitions() as $key => $def) { if ($def['hook'] === $current_hook) { $event_key = $key; break; } } if (empty($event_key)) return; $order_id = is_numeric($arg1) ? $arg1 : (is_a($arg1, 'WC_Order') ? $arg1->get_id() : 0); if (!$order_id) return; $refund_id = ($event_key === 'refunded' && $arg2) ? $arg2 : 0; if (function_exists('as_enqueue_async_action')) { as_enqueue_async_action('connect_3dm_process_api_call', array($event_key, $order_id, $refund_id), '3dm-connect'); } else { connect_3dm_process_api_call($event_key, $order_id, $refund_id); } } add_action('connect_3dm_process_api_call', 'connect_3dm_process_api_call', 10, 3); function connect_3dm_process_api_call($key, $order_id, $refund_id) { $tpl = get_option("connect_3dm_{$key}_template"); if(empty($tpl)) return; $order = wc_get_order($order_id); if(!$order || !is_a($order, 'WC_Order')) return; connect_3dm_log("EVENT TRIGGERED (ASYNC): {$key} for Order ID: " . $order->get_id()); $extra = array(); if($key === 'refunded' && $refund_id) { $refund = wc_get_order($refund_id); if ($refund && is_a($refund, 'WC_Order_Refund')) { $extra = array('refund_amount' => $refund->get_amount(), 'refund_reason' => $refund->get_reason()); } } $map = get_option("connect_3dm_{$key}_map"); $hash = get_option('connect_3dm_company_hash'); $templates = get_option('connect_3dm_cached_templates', array()); $tpl_data = null; foreach($templates as $t) { if($t['name'] == $tpl) { $tpl_data = $t; break; } } if(!$tpl_data || empty($tpl_data['body'])) return; preg_match_all('/\{\{(.*?)\}\}/', $tpl_data['body'], $matches); $v_names = $matches[1]; $body_params = array(); if(isset($map['body']) && is_array($map['body'])) { foreach($map['body'] as $idx => $m_key) { $val = connect_3dm_get_val($order, $m_key); if(empty($val) && isset($extra[$m_key])) $val = $extra[$m_key]; $p = ['type' => 'text', 'text' => strval($val)]; if(isset($v_names[$idx]) && !is_numeric($v_names[$idx])) { $p['parameter_name'] = $v_names[$idx]; } $body_params[] = $p; } } $response = wp_remote_post('https://connect.3dm-sols.com/api/send-template.php', array( 'method' => 'POST', 'headers' => ['Content-Type'=>'application/json', 'Authorization'=>'Bearer '.$hash], 'body' => wp_json_encode(['phone_number' => $order->get_billing_phone(), 'template_name' => $tpl, 'body_parameters' => $body_params]) )); if (is_wp_error($response)) { connect_3dm_log("API ERROR: " . $response->get_error_message()); } else { connect_3dm_log("API RESPONSE: " . wp_remote_retrieve_body($response)); } } function connect_3dm_get_val($order, $key) { switch($key) { case 'billing_first_name': return $order->get_billing_first_name(); case 'billing_last_name': return $order->get_billing_last_name(); case 'billing_phone': return $order->get_billing_phone(); case 'order_id': return $order->get_id(); case 'order_total': return $order->get_total(); case 'currency': return $order->get_currency(); case 'payment_method': return $order->get_payment_method_title(); case 'order_status': return $order->get_status(); case 'order_date': $date = $order->get_date_created(); return $date ? $date->date('Y/F/d') : ''; case 'item_count': $count = 0; $order_count = $order->get_item_count(); if ($order_count) { $count = $order_count; } else { foreach($order->get_items() as $item) $count += $item->get_quantity(); } if (!$count && isset(WC()->cart)) { $count = WC()->cart->get_cart_contents_count(); } return $count ? $count : 0; case 'customer_note': return $order->get_customer_note(); case 'store_name': return get_bloginfo('name'); case 'order_view_url': return $order->get_view_order_url(); default: return ''; } } // --- 7. ABANDONED CART LOGIC --- add_action( 'woocommerce_checkout_update_order_review', 'connect_3dm_capture_checkout_data' ); function connect_3dm_capture_checkout_data( $post_data ) { $active_events = get_option('connect_3dm_active_events', array()); if ( ! in_array( 'abandoned_cart', $active_events ) ) return; parse_str( $post_data, $data ); if ( empty( $data['billing_phone'] ) || empty( $data['billing_first_name'] ) ) return; if ( ! WC()->cart || WC()->cart->is_empty() ) return; $phone = sanitize_text_field( $data['billing_phone'] ); $name = sanitize_text_field( $data['billing_first_name'] ); $total = WC()->cart->get_total(''); $args = array( 'phone' => $phone, 'name' => $name, 'cart_total' => html_entity_decode(wp_strip_all_tags($total)) ); $group = '3dm-connect'; if ( function_exists('as_unschedule_all_actions') && function_exists('as_schedule_single_action') ) { as_unschedule_all_actions( 'connect_3dm_process_abandoned_cart', array( $args ), $group ); as_schedule_single_action( time() + 3600, 'connect_3dm_process_abandoned_cart', array( $args ), $group ); } } add_action( 'connect_3dm_process_abandoned_cart', 'connect_3dm_execute_abandoned_cart', 10, 1 ); function connect_3dm_execute_abandoned_cart( $args ) { $phone = $args['phone']; $recent_orders = wc_get_orders( array( 'billing_phone' => $phone, 'date_created' => '>' . ( time() - 7200 ), 'limit' => 1, 'return' => 'ids', ) ); if ( ! empty( $recent_orders ) ) { connect_3dm_log("Abandoned cart aborted: Order found for phone {$phone}"); return; } $tpl = get_option("connect_3dm_abandoned_cart_template"); if ( empty($tpl) ) return; $map = get_option("connect_3dm_abandoned_cart_map"); $hash = get_option('connect_3dm_company_hash'); $templates = get_option('connect_3dm_cached_templates', array()); $tpl_data = null; foreach($templates as $t) { if($t['name'] == $tpl) { $tpl_data = $t; break; } } if(!$tpl_data || empty($tpl_data['body'])) return; preg_match_all('/\{\{(.*?)\}\}/', $tpl_data['body'], $matches); $v_names = $matches[1]; $body_params = array(); $checkout_url = wc_get_checkout_url(); if(isset($map['body']) && is_array($map['body'])) { foreach($map['body'] as $idx => $m_key) { $val = ''; switch($m_key) { case 'billing_first_name': $val = $args['name']; break; case 'billing_phone': $val = $args['phone']; break; case 'cart_total': $val = $args['cart_total']; break; case 'checkout_url': $val = $checkout_url; break; } $p = ['type' => 'text', 'text' => strval($val)]; if(isset($v_names[$idx]) && !is_numeric($v_names[$idx])) { $p['parameter_name'] = $v_names[$idx]; } $body_params[] = $p; } } $response = wp_remote_post('https://connect.3dm-sols.com/api/send-template.php', array( 'method' => 'POST', 'headers' => ['Content-Type'=>'application/json', 'Authorization'=>'Bearer '.$hash], 'body' => wp_json_encode(['phone_number' => $phone, 'template_name' => $tpl, 'body_parameters' => $body_params]) )); if (is_wp_error($response)) { connect_3dm_log("ABANDONED CART API ERROR: " . $response->get_error_message()); } else { connect_3dm_log("ABANDONED CART API RESPONSE: " . wp_remote_retrieve_body($response)); } } ?>