logger = $logger; $this->messageTextFactory = $messageTextFactory; $this->clientPhoneNumber = $clientPhoneNumber; $this->minioStorage = $minioStorage; $config = PluginConfigManager::create()->loadConfig(); $ipServer = $config['ipserver'] ?? 'localhost'; $apiUrl = "https://$ipServer/crm/api/v1.0/"; $client = new Client([ 'base_uri' => $apiUrl, 'verify' => false, ]); $this->ucrmApi = new UcrmApi($client, $config['apitoken'] ?? ''); } /* * Creates a PaymentIntent for OXXO in Stripe for a Customer * @param array $event_json * @param int|null $amount * @return array * @throws \GuzzleHttp\Exception\GuzzleException * @throws \Stripe\Exception\ApiErrorException * @throws Exception */ public function createOxxoPaymentIntent($event_json, $amount = null, bool $uploadToFtp = true): array { // 1. Obtener Referencia de Stripe (Lógica Común) $stripeResult = $this->createStripeReference($event_json, $amount); // Si hubo error en Stripe, retornar el resultado de error if ($stripeResult['hasError']) { return $stripeResult['data']; } // 2. Procesamiento Síncrono (Generación inmediata) // Usamos los datos obtenidos de Stripe $oxxoData = $stripeResult['data']; return $this->processSynchronousGeneration($oxxoData, $uploadToFtp); } /** * Crea la orden en el servicio Docker y retorna el ID para flujo asíncrono */ public function createAsyncOxxoOrder($event_json, $amount = null): array { // 1. Obtener Referencia de Stripe $stripeResult = $this->createStripeReference($event_json, $amount); if ($stripeResult['hasError']) { return $stripeResult['data']; } $data = $stripeResult['data']; // 2. Llamar al servicio Docker para crear la orden (POST /orders) $config = PluginConfigManager::create()->loadConfig(); $ipPuppeteer = $config['ipPuppeteer']; $portPuppeteer = $config['portPuppeteer']; $api_url = "http://$ipPuppeteer:$portPuppeteer/orders"; try { $client = new Client(['timeout' => 5]); $response = $client->post($api_url, [ 'json' => [ 'client_id' => $data['clientID'], 'amount' => $data['amount'], 'client_full_name' => $data['clientFullName'], 'oxxo_reference' => $data['oxxo_reference'], 'stripe_url' => $data['url'] ] ]); $orderData = json_decode($response->getBody()->getContents(), true); // Agregar el order_id al resultado para que el Plugin lo devuelva $data['order_id'] = $orderData['order_id'] ?? null; return $data; // Retornamos los datos básicos + order_id } catch (Exception $e) { $this->logger->error("Error creando orden asíncrona en Docker: " . $e->getMessage()); // Fallback: Retornar los datos de Stripe aunque falle el guardado en DB local (mejor que nada) $data['error'] = 'docker_order_creation_failed'; return $data; } } /** * Trigger para generar la imagen en background, subirla y actualizar la orden */ public function generateAndUploadOrder(int $orderId, string $stripeUrl, string $clientFullName, bool $uploadToFtp = true): void { $this->logger->info("Iniciando generación en background para Orden #$orderId"); $config = PluginConfigManager::create()->loadConfig(); $ipPuppeteer = $config['ipPuppeteer']; $portPuppeteer = $config['portPuppeteer']; // 1. Solicitar Generación (POST /orders/:id/generate) // Calculamos nombre de archivo $clientFullNameWithoutSpaces = str_replace(' ', '_', $clientFullName); $voucherFileName = 'voucher_'.$clientFullNameWithoutSpaces.'_' . time() . '.jpeg'; $output_filename = realpath(__DIR__ . '/../../vouchers_oxxo') . '/'. $voucherFileName; $generateUrl = "http://$ipPuppeteer:$portPuppeteer/orders/$orderId/generate"; try { $client = new Client(['timeout' => 60]); // Mayor timeout para Puppeteer $response = $client->post($generateUrl, [ 'json' => [ 'url' => $stripeUrl, 'filename' => $voucherFileName, 'clip' => ['x' => 325, 'y' => 30, 'width' => 550, 'height' => 550] ] ]); if ($response->getStatusCode() === 200) { // Guardar imagen localmente $imageContent = $response->getBody()->getContents(); file_put_contents($output_filename, $imageContent); $this->logger->info("Imagen generada y guardada localmente: $voucherFileName"); $voucherUrl = ''; // 2. Subir a S3/MinIO (si aplica) if ($uploadToFtp) { // $voucherUrl = $this->UploadVoucherToWordpressByImageFileName($voucherFileName); // $this->logger->info("Imagen subida a FTP: $voucherUrl"); // Nueva implementación MinIO $voucherUrl = $this->minioStorage->uploadFile($output_filename, $voucherFileName); $this->logger->info("Imagen subida a MinIO: $voucherUrl"); } // 3. Actualizar Orden (POST /orders/:id/complete) if ($voucherUrl) { $completeUrl = "http://$ipPuppeteer:$portPuppeteer/orders/$orderId/complete"; $client->post($completeUrl, [ 'json' => ['voucher_image_url' => $voucherUrl] ]); } } } catch (Exception $e) { $this->logger->error("Error en flujo de background para Orden #$orderId: " . $e->getMessage()); } } /** * Obtiene el estado de una orden desde el servicio Docker */ public function getOxxoOrderStatus($orderId): array { $config = PluginConfigManager::create()->loadConfig(); $ipPuppeteer = $config['ipPuppeteer']; $portPuppeteer = $config['portPuppeteer']; $api_url = "http://$ipPuppeteer:$portPuppeteer/orders/$orderId"; try { $client = new Client(['timeout' => 5]); $response = $client->get($api_url); $data = json_decode($response->getBody()->getContents(), true); // Si la URL de la imagen es relativa o local, ajustarla si es necesario // pero el servicio ya guarda la URL completa del FTP si se subió. return $data; } catch (Exception $e) { $this->logger->error("Error obteniendo estado de orden #$orderId: " . $e->getMessage()); return ['error' => 'fetch_failed', 'message' => $e->getMessage()]; } } /** * Lógica extraída de Stripe * Retorna ['hasError' => bool, 'data' => array] */ public function createStripeReference($event_json, $amount = null): array { $arrayOxxoPayment = []; $integerAmount = $amount; // Inicializar Config y Clientes (Copied logic) $configManager = PluginConfigManager::create(); $config = $configManager->loadConfig(); $StripeToken = $config['tokenstripe']; $IPServer = $config['ipserver']; $tokenCRM = $config['apitoken']; $baseUri = 'https://' . $IPServer . '/crm/api/v1.0/'; if (!isset($this->ucrmApi)) { // Re-instanciar si es necesario, aunque ya está en consructor // Pero usamos la propiedad de clase } // ... Lógica de obtención de cliente, Stripe Customer, PaymentIntent ... // [RESUMIDO: Copiaremos el tocho de código aquí, simplificado] $clientID = $event_json['client_id']; $clientFullName = ''; // --- 1. Obtener Cliente CRM --- try { $clientGuzzle = new Client([ 'base_uri' => $baseUri, 'headers' => ['X-Auth-App-Key' => $tokenCRM, 'Accept' => 'application/json'], 'verify' => false, 'timeout' => 5 ]); $resp = $clientGuzzle->get("clients/" . $clientID); $arrayClientCRM = json_decode($resp->getBody()->getContents(), true); $clientFullName = $arrayClientCRM['firstName'] . ' ' . $arrayClientCRM['lastName']; } catch (Exception $e) { return ['hasError' => true, 'data' => $this->buildErrorArray('errorGetClient', 'Error obteniendo cliente', $clientID, $clientFullName, $amount)]; } // --- 2. Obtener Email y Stripe ID --- $clientEmail = 'siip8873@gmail.com'; // Default foreach ($arrayClientCRM['contacts'] as $contact) { if (!empty($contact['email'])) { $clientEmail = $contact['email']; break; } } $stripeCustomerId = null; foreach ($arrayClientCRM['attributes'] as $attr) { if ($attr['key'] === 'stripeCustomerId') { $stripeCustomerId = $attr['value']; break; } } if (!$stripeCustomerId) { return ['hasError' => true, 'data' => $this->buildErrorArray('errorGetCustomerStripe', 'Cliente sin Stripe ID', $clientID, $clientFullName, $amount)]; } // --- 3. Calcular Monto --- if ($amount === null) { $amount = abs($arrayClientCRM['accountOutstanding']); } else { if(!is_numeric($amount)) $amount = preg_replace('/[^\d.]/', '', $amount); } if ($amount <= 10) { return ['hasError' => true, 'data' => $this->buildErrorArray('errorsinadeudo', 'Monto insuficiente o sin deuda', $clientID, $clientFullName, $amount)]; } // --- 4. Crear Payment Intent en Stripe --- try { $amountInCents = intval($amount * 100); $guzzleClient = new Client(['timeout' => 10]); // Payment Intent $piResp = $guzzleClient->post('https://api.stripe.com/v1/payment_intents', [ 'auth' => [$StripeToken, ''], 'form_params' => [ 'amount' => $amountInCents, 'currency' => 'mxn', 'payment_method_types' => ['customer_balance', 'card', 'oxxo'], 'customer' => $stripeCustomerId, 'metadata' => [ 'clientId' => $clientID, 'ucrm_client_id' => $clientID, // Backwards compatibility 'createdBy' => 'UCRM', 'paymentType' => 'card.one_time', 'signedInAdminId' => $config['idPaymentAdminCRM'] ?? 1, 'tipoPago' => 'OXXO' ], 'payment_method_options' => ['oxxo' => ['expires_after_days' => 3]] ] ]); $paymentIntent = json_decode($piResp->getBody()->getContents(), true); // Payment Method $firstName = $arrayClientCRM['firstName'] ?? ''; $lastName = $arrayClientCRM['lastName'] ?? ''; $pmResp = $guzzleClient->post('https://api.stripe.com/v1/payment_methods', [ 'auth' => [$StripeToken, ''], 'form_params' => [ 'type' => 'oxxo', 'billing_details' => ['name' => "$firstName $lastName", 'email' => $clientEmail] ] ]); $paymentMethod = json_decode($pmResp->getBody()->getContents(), true); // Confirm $confResp = $guzzleClient->post('https://api.stripe.com/v1/payment_intents/' . $paymentIntent['id'] . '/confirm', [ 'auth' => [$StripeToken, ''], 'form_params' => ['payment_method' => $paymentMethod['id']] ]); $paymentIntentConfirm = json_decode($confResp->getBody()->getContents(), true); } catch (Exception $e) { return ['hasError' => true, 'data' => $this->buildErrorArray('errorStripeApi', $e->getMessage(), $clientID, $clientFullName, $amount)]; } // --- 5. Extraer Datos OXXO --- if (!empty($paymentIntentConfirm['next_action']['oxxo_display_details'])) { $oxxoDetails = $paymentIntentConfirm['next_action']['oxxo_display_details']; return [ 'hasError' => false, 'data' => [ 'oxxo_reference' => $oxxoDetails['number'], 'url' => $oxxoDetails['hosted_voucher_url'], 'error' => '', 'clientID' => $clientID, 'clientFullName' => $clientFullName, 'amount' => $amount, 'voucher_image_url' => '' ] ]; } return ['hasError' => true, 'data' => $this->buildErrorArray('errorNoOxxoDetails', 'No se recibieron detalles OXXO', $clientID, $clientFullName, $amount)]; } private function buildErrorArray($code, $desc, $clientId, $name, $amount) { return [ 'oxxo_reference' => '', 'url' => '', 'error' => $code, 'failDescription' => $desc, 'clientID' => $clientId, 'clientFullName' => $name, 'amount' => $amount, 'voucher_image_url' => '' ]; } /** * Crea la orden en el microservicio (Paso 1 del flujo asíncrono) */ public function createOxxoOrder($arrayOxxoPayment) { $config = PluginConfigManager::create()->loadConfig(); $ipPuppeteer = $config['ipPuppeteer']; $portPuppeteer = $config['portPuppeteer']; $baseUrl = 'http://'.$ipPuppeteer.':'.$portPuppeteer; $guzzleClient = new Client(['timeout' => 10]); try { $createOrderResp = $guzzleClient->post($baseUrl . '/orders', [ 'json' => [ 'client_id' => $arrayOxxoPayment['clientID'], 'amount' => $arrayOxxoPayment['amount'], 'client_full_name' => $arrayOxxoPayment['clientFullName'], 'oxxo_reference' => $arrayOxxoPayment['oxxo_reference'], 'stripe_url' => $arrayOxxoPayment['url'] ] ]); $orderData = json_decode($createOrderResp->getBody()->getContents(), true); $arrayOxxoPayment['order_id'] = $orderData['order_id'] ?? null; return $arrayOxxoPayment; } catch (Exception $e) { $this->logger->error("Error creando orden OXXO: " . $e->getMessage()); return $arrayOxxoPayment; } } /** * Genera el voucher y lo sube (Paso 2 del flujo asíncrono) */ public function generateOxxoVoucher($arrayOxxoPayment, $uploadToFtp) { if (empty($arrayOxxoPayment['order_id'])) { $this->logger->error("Intentando generar voucher sin order_id"); return $arrayOxxoPayment; } $config = PluginConfigManager::create()->loadConfig(); $ipPuppeteer = $config['ipPuppeteer']; $portPuppeteer = $config['portPuppeteer']; $baseUrl = 'http://'.$ipPuppeteer.':'.$portPuppeteer; $guzzleClient = new Client(['timeout' => 60]); $clientFullNameWithoutSpaces = str_replace(' ', '_', $arrayOxxoPayment['clientFullName']); $voucherFileName = 'voucher_'.$clientFullNameWithoutSpaces.'_' . time() . '.jpeg'; $output_filename = realpath(__DIR__ . '/../../vouchers_oxxo') . '/'. $voucherFileName; $request_data = [ 'url' => $arrayOxxoPayment['url'], 'filename' => $voucherFileName, 'clip' => ['x' => 325, 'y' => 30, 'width' => 550, 'height' => 550] ]; try { $response = $guzzleClient->post($baseUrl . '/orders/' . $arrayOxxoPayment['order_id'] . '/generate', [ 'json' => $request_data, 'headers' => ['Accept' => 'image/jpeg, image/png'] ]); if ($response->getStatusCode() === 200) { file_put_contents($output_filename, $response->getBody()->getContents()); $arrayOxxoPayment['voucher_filename'] = $voucherFileName; if ($uploadToFtp) { $url_file = $this->minioStorage->uploadFile($output_filename, $voucherFileName); $arrayOxxoPayment['voucher_image_url'] = $url_file; try { $guzzleClient->post($baseUrl . '/orders/' . $arrayOxxoPayment['order_id'] . '/complete', [ 'json' => ['voucher_image_url' => $url_file] ]); // CLEANUP: Eliminar voucher local tras subida exitosa if (file_exists($output_filename)) { unlink($output_filename); //$this->logger->info("Limpieza: Voucher local eliminado ($voucherFileName)"); } } catch (Exception $ex) { $this->logger->warning("No se pudo actualizar URL final: " . $ex->getMessage()); } } } } catch (Exception $e) { $this->logger->error("Error generando voucher (Puppeteer): " . $e->getMessage()); } return $arrayOxxoPayment; } /** * Mantiene compatibilidad con llamadas anteriores */ private function processSynchronousGeneration($arrayOxxoPayment, $uploadToFtp) { $arrayOxxoPayment = $this->createOxxoOrder($arrayOxxoPayment); if (!empty($arrayOxxoPayment['order_id'])) { $arrayOxxoPayment = $this->generateOxxoVoucher($arrayOxxoPayment, $uploadToFtp); } return $arrayOxxoPayment; } // /** // * implement in subclass with the specific messaging provider // * @see TwilioNotifierFacade::sendWhatsApp() // */ abstract protected function sendWhatsApp( string $message ): void; function validarEmail($email) { $this->logger->debug('SE VALIDA EL EMAIL!!! ' . PHP_EOL); // Utilizar la función filter_var con el filtro FILTER_VALIDATE_EMAIL para validar el email if (filter_var($email, FILTER_VALIDATE_EMAIL)) { // Si el email es válido, devolver el email return $email; } else { // Si el email no es válido, devolver una cadena vacía return ''; } } function UploadReceiptToWordpressByImageFileName($imageFileName): string { // $log = PluginLogManager::create(); //Initialize Logger $configManager = PluginConfigManager::create(); $config = $configManager->loadConfig(); // Configuración de conexión FTP $ftp_server = $config['hostServerFTP']; $ftp_username = $config['usernameServerFTP']; $ftp_password = $config['passServerFTP']; $remote_folder = "/public_html/wp/wp-content/uploads/img/"; // Configuración de conexión FTP $remote_file = "/public_html/wp/wp-content/uploads/img/" . $imageFileName; $file_to_upload = __DIR__ . '/' . $imageFileName; $url = 'https://siip.mx/wp/wp-content/uploads/img/' . $imageFileName; // Conexión FTP $ftp_conn = ftp_connect($ftp_server) or die("No se pudo conectar al servidor FTP"); $login = ftp_login($ftp_conn, $ftp_username, $ftp_password); ftp_pasv($ftp_conn, true); // Verificar conexión y login if ($ftp_conn && $login) { print_r("Conexión FTP exitosa" . PHP_EOL); // $log->appendLog("Conexión FTP exitosa" . PHP_EOL); // Cargar archivo if (ftp_put($ftp_conn, $remote_file, $file_to_upload, FTP_BINARY)) { print_r("El archivo ha sido cargado exitosamente." . PHP_EOL); print_r("La URL es: " . $url . PHP_EOL); // $log->appendLog("El archivo ha sido cargado exitosamente." . PHP_EOL); // $log->appendLog("La URL es: " . $url . PHP_EOL); // Cerrar conexión FTP //ftp_close($ftp_conn); //return $url; } else { //$log->appendLog("Error al cargar el archivo " . PHP_EOL); print_r("Error al cargar el archivo " . PHP_EOL); ftp_close($ftp_conn); return ''; } // Obtener lista de archivos en la carpeta $files = ftp_nlist($ftp_conn, $remote_folder); if (is_array($files)) { // Eliminar la ruta del directorio de los archivos $files = array_map(function ($file) use ($remote_folder) { return str_replace($remote_folder, '', $file); }, $files); // Obtener fechas de modificación $filesWithTime = []; foreach ($files as $file) { $modifiedTime = ftp_mdtm($ftp_conn, $remote_folder . $file); if ($modifiedTime != -1) { $filesWithTime[$file] = $modifiedTime; } } // Ordenar archivos por fecha de modificación, más recientes primero arsort($filesWithTime); // Obtener los archivos a eliminar (todos menos los 5 más recientes) $filesToDelete = array_slice(array_keys($filesWithTime), 5); // Eliminar archivos antiguos foreach ($filesToDelete as $file) { if (ftp_delete($ftp_conn, $remote_folder . $file)) { print_r("Comprobante eliminado de Wordpress: " . $file . PHP_EOL); // $log->appendLog("Comprobante eliminado de Wordpress: " . $file . PHP_EOL); } else { print_r('Error al borrar comprobante' . $file . PHP_EOL); // $log->appendLog('Error al borrar comprobante' . $file . PHP_EOL); } } print_r("Archivos eliminados" . PHP_EOL); // $log->appendLog("Archivos eliminados" . PHP_EOL); ftp_close($ftp_conn); return $url; } else { print_r("No se pudo obtener la lista de archivos de la carpeta FTP" . PHP_EOL); // $log->appendLog("No se pudo obtener la lista de archivos de la carpeta FTP" . PHP_EOL); ftp_close($ftp_conn); return $url; } // Cerrar conexión FTP //ftp_close($ftp_conn); //return ''; } else { print_r("No se pudo conectar o iniciar sesión en el servidor FTP." . PHP_EOL); // $log->appendLog("No se pudo conectar o iniciar sesión en el servidor FTP." . PHP_EOL); return ''; } } function UploadVoucherToWordpressByImageFileName($imageFileName): string { $log = PluginLogManager::create(); //Initialize Logger $configManager = PluginConfigManager::create(); $config = $configManager->loadConfig(); // Configuración de conexión FTP $ftp_server = $config['hostServerFTP']; $ftp_username = $config['usernameServerFTP']; $ftp_password = $config['passServerFTP']; $remote_folder = "/public_html/wp/wp-content/uploads/vouchers_oxxo/"; $log->appendLog("Subiendo voucher a worpdpress " . PHP_EOL); // Configuración de conexión FTP $remote_file = "/public_html/wp/wp-content/uploads/vouchers_oxxo/" . $imageFileName; $file_to_upload = __DIR__ . '/../../vouchers_oxxo/' . $imageFileName; $url = 'https://siip.mx/wp/wp-content/uploads/vouchers_oxxo/' . $imageFileName; $log->appendLog("file_to_upload: " . $file_to_upload . PHP_EOL); // Conexión FTP $ftp_conn = ftp_connect($ftp_server) or die("No se pudo conectar al servidor FTP"); $login = ftp_login($ftp_conn, $ftp_username, $ftp_password); ftp_pasv($ftp_conn, true); // Verificar conexión y login if ($ftp_conn && $login) { $log->appendLog("Conexión FTP exitosa" . PHP_EOL); // Cargar archivo if (ftp_put($ftp_conn, $remote_file, $file_to_upload, FTP_BINARY)) { $log->appendLog("El archivo ha sido cargado exitosamente." . PHP_EOL); $log->appendLog("La URL es: " . $url . PHP_EOL); // try { // $this->deleteFilesWordpressExceptLastHundred($log, $ftp_conn, $remote_folder); // } catch (\Exception $e) { // $log->appendLog("Error en limpieza de archivos antiguos: " . $e->getMessage() . PHP_EOL); // } // Cerrar conexión FTP (o dejar abierta si se prefiere, pero mejor cerrar al final si es posible, aunque aquí retornamos directo) // ftp_close($ftp_conn); // Cerrar conexión FTP //ftp_close($ftp_conn); //COMENTAR AQUÍ SI SE BORRAN LOS ARCHIVOS DE WORDPRESS DESCOMENTANDO EL CÓDIGO DE MÁS ABAJO return $url; //COMENTAR AQUÍ SI SE BORRAN LOS ARCHIVOS DE WORDPRESS DESCOMENTANDO EL CÓDIGO DE MÁS ABAJO } else { $log->appendLog("Error al cargar el archivo " . PHP_EOL); ftp_close($ftp_conn); return ''; } //SI SE DECIDE VOLVER A ELIMINAR LOS COMPROBANTES ENTONCES DESCOMENTAR ESTA PARTE DE ABAJO Y COMENTAR LA SECCIÓN DE ARRIBA // Obtener lista de archivos en la carpeta // $files = ftp_nlist($ftp_conn, $remote_folder); // if (is_array($files)) { // // Eliminar la ruta del directorio de los archivos // $files = array_map(function ($file) use ($remote_folder) { // return str_replace($remote_folder, '', $file); // }, $files); // // Obtener fechas de modificación // $filesWithTime = []; // foreach ($files as $file) { // $modifiedTime = ftp_mdtm($ftp_conn, $remote_folder . $file); // if ($modifiedTime != -1) { // $filesWithTime[$file] = $modifiedTime; // } // } // // Ordenar archivos por fecha de modificación, más recientes primero // arsort($filesWithTime); // // Obtener los archivos a eliminar (todos menos los 50 más recientes) // $filesToDelete = array_slice(array_keys($filesWithTime), 50); // // Eliminar archivos antiguos // foreach ($filesToDelete as $file) { // if (ftp_delete($ftp_conn, $remote_folder . $file)) { // $log->appendLog("Comprobante eliminado de Wordpress: " . $file . PHP_EOL); // } else { // $log->appendLog('Error al borrar comprobante' . $file . PHP_EOL); // } // } // $log->appendLog("Archivos eliminados" . PHP_EOL); // ftp_close($ftp_conn); // return $url; // } else { // $log->appendLog("No se pudo obtener la lista de archivos de la carpeta FTP" . PHP_EOL); // ftp_close($ftp_conn); // return $url; // } } else { $log->appendLog("No se pudo conectar o iniciar sesión en el servidor FTP." . PHP_EOL); return ''; } } function deleteFilesWordpressExceptLastHundred($log, $ftp_conn, $remote_folder): bool { // Obtener lista de archivos en la carpeta $files = ftp_nlist($ftp_conn, $remote_folder); if (is_array($files)) { // Eliminar la ruta del directorio de los archivos $files = array_map(function ($file) use ($remote_folder) { return str_replace($remote_folder, '', $file); }, $files); // Obtener fechas de modificación $filesWithTime = []; foreach ($files as $file) { $modifiedTime = ftp_mdtm($ftp_conn, $remote_folder . $file); if ($modifiedTime != -1) { $filesWithTime[$file] = $modifiedTime; } } // Ordenar archivos por fecha de modificación, más recientes primero arsort($filesWithTime); // Obtener los archivos a eliminar (todos menos los 50 más recientes) $filesToDelete = array_slice(array_keys($filesWithTime), 50); // Eliminar archivos antiguos foreach ($filesToDelete as $file) { if (ftp_delete($ftp_conn, $remote_folder . $file)) { $log->appendLog("Voucher eliminado de Wordpress: " . $file . PHP_EOL); } else { $log->appendLog('Error al borrar voucher' . $file . PHP_EOL); } } $log->appendLog("Archivos eliminados" . PHP_EOL); ftp_close($ftp_conn); return true; } else { $log->appendLog("No se pudo obtener la lista de archivos de la carpeta FTP" . PHP_EOL); ftp_close($ftp_conn); return false; } } }