prefix . 'rcp_expired_orders'; $charset_collate = $wpdb->get_charset_collate(); $sql = "CREATE TABLE IF NOT EXISTS $tabla ( id INT AUTO_INCREMENT PRIMARY KEY, order_id INT NOT NULL UNIQUE, fecha_registro DATETIME DEFAULT CURRENT_TIMESTAMP, fecha_orden DATETIME, fecha_expiracion DATETIME, producto VARCHAR(255), cliente_nombre VARCHAR(255), telefono VARCHAR(100), api_enviado TINYINT(1) DEFAULT 0, api_estatus TEXT DEFAULT NULL ) $charset_collate;"; require_once ABSPATH . 'wp-admin/includes/upgrade.php'; dbDelta($sql); } // Ejecutar si URL contiene token add_action('init', 'rcp_membresias_check_url_trigger'); function rcp_membresias_check_url_trigger() { if (isset($_GET['token']) && $_GET['token'] === token) { $procesar_todo = isset($_GET['procesar_todo']) && $_GET['procesar_todo'] === '1'; $limite = isset($_GET['limite']) ? intval($_GET['limite']) : 10; $log_activo = isset($_GET['log']) && $_GET['log'] === '1'; rcp_verificar_membresias_expiradas($limite, $log_activo); echo "✅ Se han procesado las órdenes vencidas." . PHP_EOL . PHP_EOL; rcp_enviar_mensajes_ordenes_expiradas($log_activo); echo "📤 Se han enviado los mensajes a los clientes." . PHP_EOL; exit; } } // Lógica principal function rcp_verificar_membresias_expiradas($limite = 10, $log = false) { global $wpdb; // 💡 Captura parámetros GET si están definidos $limite = isset($_GET['limite']) ? intval($_GET['limite']) : $limite; $log = isset($_GET['log']) ? boolval($_GET['log']) : $log; $horas_vencimiento = isset($_GET['horas']) ? intval($_GET['horas']) : 720; $tabla_objetivo = $wpdb->prefix . 'rcp_expired_orders'; $log_path = plugin_dir_path(__FILE__) . 'logs/rcp_membresias_expiradas.txt'; if ($log) file_put_contents($log_path, "=== INICIO RCP ".date('Y-m-d H:i:s')." ===\n", FILE_APPEND); $now_str = current_time('mysql'); $fecha_limite_str = date('Y-m-d H:i:s', strtotime("+{$horas_vencimiento} hours", strtotime($now_str))); $query = " SELECT m.id AS membership_id, m.gateway_subscription_id AS order_id, m.created_date, m.expiration_date, m.customer_id, m.user_id FROM {$wpdb->prefix}rcp_memberships m WHERE m.expiration_date BETWEEN %s AND %s ORDER BY m.expiration_date ASC LIMIT %d "; $query_preparada = $wpdb->prepare($query, $now_str, $fecha_limite_str, $limite * 3); if ($log) { file_put_contents($log_path, "🕒 Rango: $now_str => $fecha_limite_str\n", FILE_APPEND); file_put_contents($log_path, "🔍 SQL: \n$query_preparada\n", FILE_APPEND); } $ordenes_procesadas = $wpdb->get_col("SELECT order_id FROM $tabla_objetivo"); $membresias = $wpdb->get_results($query_preparada); if ($log) file_put_contents($log_path, "📦 Membresías encontradas en rango: ".count($membresias)."\n", FILE_APPEND); $mapa_productos = [ 'Vip Bronce' => 'Vip Bronce acceso por 24 horas', 'Vip Plata' => 'Vip Plata acceso por 30 días', 'Vip Oro' => 'Vip Oro acceso por 365 días', ]; $procesadas = 0; foreach ($membresias as $m) { if (in_array($m->order_id, $ordenes_procesadas)) { if ($log) file_put_contents($log_path, "⚠️ Ya procesado order_id: $m->order_id\n", FILE_APPEND); continue; } // Obtener suscripción $subscription = $wpdb->get_var($wpdb->prepare( "SELECT subscription FROM {$wpdb->prefix}rcp_payments WHERE customer_id = %d ORDER BY id DESC LIMIT 1", $m->customer_id )); if (!$subscription || !isset($mapa_productos[$subscription])) { if ($log) file_put_contents($log_path, "⛔ Suscripción no válida para customer_id $m->customer_id (subscription: $subscription)\n", FILE_APPEND); continue; } $producto = $mapa_productos[$subscription]; // Obtener datos del usuario $user_data = $wpdb->get_row($wpdb->prepare( "SELECT user_email, user_nicename FROM {$wpdb->prefix}users WHERE ID = %d", $m->user_id )); if (!$user_data) { if ($log) file_put_contents($log_path, "⛔ Usuario no encontrado para user_id $m->user_id\n", FILE_APPEND); continue; } $user_email = $user_data->user_email; $usuario = $user_data->user_nicename; // Obtener contacto $contacto = $wpdb->get_row($wpdb->prepare( "SELECT f_name, l_name, contact_no FROM {$wpdb->prefix}bwf_contact WHERE email = %s", $user_email )); if (!$contacto) { if ($log) file_put_contents($log_path, "⛔ Contacto no encontrado para email $user_email\n", FILE_APPEND); continue; } $cliente_nombre = mb_convert_case(trim($contacto->f_name . ' ' . $contacto->l_name), MB_CASE_TITLE, 'UTF-8'); $telefono = rcp_formatear_numero_telefono($contacto->contact_no); // Insertar en la tabla objetivo $wpdb->insert($tabla_objetivo, [ 'order_id' => $m->order_id, 'fecha_registro' => current_time('mysql'), 'fecha_orden' => $m->created_date, 'fecha_expiracion' => $m->expiration_date, 'producto' => $producto, 'cliente_nombre' => $cliente_nombre, 'usuario' => $usuario, 'mail' => $user_email, 'telefono' => $telefono, 'api_enviado' => 0 ]); if ($log) file_put_contents($log_path, "✅ Insertada order_id $m->order_id - $producto\n", FILE_APPEND); $procesadas++; if ($procesadas >= $limite) break; sleep(1); } if ($log) file_put_contents($log_path, "=== FIN (procesadas: $procesadas) ===\n", FILE_APPEND); } function rcp_enviar_mensajes_ordenes_expiradas($log = false) { global $wpdb; $tabla = $wpdb->prefix . 'rcp_expired_orders'; $log_path = plugin_dir_path(__FILE__) . 'logs/rcp_enviar_mensajes.txt'; $ordenes = $wpdb->get_results("SELECT * FROM $tabla WHERE api_enviado = 0 LIMIT 5"); foreach ($ordenes as $orden) { $telefono = $orden->telefono; $nombre = mb_convert_case(mb_strtolower(trim($orden->cliente_nombre), 'UTF-8'), MB_CASE_TITLE, 'UTF-8'); $orden_id = $orden->order_id; $producto = $orden->producto; $fecha_orden = DateTime::createFromFormat('Y-m-d H:i:s', $orden->fecha_orden); $fecha_expiracion = DateTime::createFromFormat('Y-m-d H:i:s', $orden->fecha_expiracion); $fecha_orden_str = $fecha_orden->format('d/m/Y H:i:s'); $fecha_expiracion_str = $fecha_expiracion->format('d/m/Y H:i:s'); // Texto del mensaje $texto = "*Saludos, $nombre.*\n\n" . "Recibes este mensaje porque el día *$fecha_orden_str* en la página web de *Tecno Group* generaste un pedido para obtener acceso a la *Membresía VIP de la Página de Análisis de Precios Unitarios*.\n\n" . "Tu suscripción *$orden_id* que te da acceso a *$producto* expira el día *$fecha_expiracion_str*.\n\n" . "*Pero tenemos buenas noticias 🎉: puedes obtener un cupón con 30% de descuento en la membresía anual (acceso por 365 días).*\n\n" . "Aprovecha esta oportunidad para tener acceso completo a más de 130,000 ejemplos de precios unitarios, básicos, costos horarios y rendimientos.\n\n" . "*Para recibir el cupón: responde con #365* (muy importante incluir el #, almohadilla o símbolo de gato antes del número 365, se tiene que ver así » #365).\n\n" . "¿Tienes dudas o necesitas ayuda? Responde con la palabra *duda* y platicamos con gusto."; // URL con mensaje en el query $url = "https://n8n.primeleads.app/webhook/woocommerce-messages?text=" . rawurlencode($texto); $payload = json_encode(['number' => $telefono]); $args = [ 'method' => 'POST', 'headers' => ['Content-Type' => 'application/json'], 'body' => $payload, 'timeout' => 10 ]; if ($log) { file_put_contents($log_path, "🌐 URL enviada: $url\n", FILE_APPEND); file_put_contents($log_path, "📦 Payload: $payload\n", FILE_APPEND); } $response = wp_remote_request($url, $args); if (is_wp_error($response)) { $mensaje_respuesta = 'Error: ' . $response->get_error_message(); } else { $body = wp_remote_retrieve_body($response); $mensaje_respuesta = $body; $json = json_decode($body, true); if (is_array($json) && isset($json[0]['message'])) { $mensaje_respuesta = $json[0]['message']; } } $wpdb->update( $tabla, [ 'api_enviado' => 1, // siempre se marca como procesado 'api_estatus' => $mensaje_respuesta ], ['id' => $orden->id] ); if ($log) { $log_msg = "📢 Resultado para $telefono ($nombre): $mensaje_respuesta"; file_put_contents($log_path, $log_msg . "\n", FILE_APPEND); } sleep(1); } } function rcp_formatear_numero_telefono($numero) { $original = $numero; $cleaned = preg_replace('/\D/', '', $numero); if (preg_match('/^521\d{10}$/', $cleaned)) { return $cleaned; } if (preg_match('/^52\d{10}$/', $cleaned)) { return '521' . substr($cleaned, 2); } return $cleaned; } // Zona horaria function rcp_get_wp_timezone_string() { $timezone = get_option('timezone_string'); if ($timezone) return $timezone; $offset = (float) get_option('gmt_offset'); $hours = (int) $offset; $minutes = ($offset - $hours) * 60; return sprintf('UTC%+03d:%02d', $hours, $minutes); }