### Install CFDI SAT Scraper and dependencies Source: https://github.com/phpcfdi/cfdi-sat-scraper/blob/main/docs/EjemploConsumo.md Installs the base packages and the BoxFacturaAI captcha resolver. Ensure libonnxruntime.so is installed separately if needed. ```shell composer require phpcfdi/cfdi-sat-scraper phpcfdi/image-captcha-resolver-boxfactura-ai ``` ```shell composer run-script post-update-cmd -d vendor/ankane/onnxruntime/ ``` ```shell bash vendor/phpcfdi/image-captcha-resolver-boxfactura-ai/bin/download-model storage/boxfactura-model ``` -------------------------------- ### Initial Page Setup and State Configuration - JavaScript Source: https://github.com/phpcfdi/cfdi-sat-scraper/blob/main/tests/_files/sample-response-receiver-form-page.html Executes initial setup functions for the page, including character validation, defining initial position, setting component states, and configuring combos. ```javascript validarCaracteres(); DefinePosicionInicial(); Estado_Comprobante('-1'); Estado_Comprobante('-1'); ConfiguraCombosMac(); ``` -------------------------------- ### Install BoxFactura AI Captcha Resolver Source: https://github.com/phpcfdi/cfdi-sat-scraper/blob/main/README.md Install the AI captcha resolver using Composer. This step is required before configuring the resolver. ```shell composer require phpcfdi/image-captcha-resolver-boxfactura-ai ``` -------------------------------- ### Repository JSON Structure Example Source: https://github.com/phpcfdi/cfdi-sat-scraper/blob/main/develop/TestIntegracion.md Defines the expected structure for the repository JSON file, which lists CFDI details for testing. Ensure all fields are correctly populated. ```json [ { "uuid": "", "issuer": "", "receiver": "", "date": "", "state": "", "type": "" } ] ``` -------------------------------- ### Custom CFDI Download Handler Source: https://github.com/phpcfdi/cfdi-sat-scraper/blob/main/README.md Implement ResourceDownloadHandlerInterface to define custom logic for successful downloads and errors. This example saves XML files and logs errors. ```php listByPeriod($query); $myHandler = new class implements ResourceDownloadHandlerInterface { public function onSuccess(string $uuid, string $content, ResponseInterface $response): void { $filename = '/storage/' . $uuid . '.xml'; echo 'Saving ', $uuid, PHP_EOL; file_put_contents($filename, (string) $response->getBody()); } public function onError(ResourceDownloadError $error) : void { if ($error instanceof ResourceDownloadRequestExceptionError) { echo "Error getting {$error->getUuid()} from {$error->getReason()->getRequest()->getUri()} "; } elseif ($error instanceof ResourceDownloadResponseError) { echo "Error getting {$error->getUuid()}, invalid response: {$error->getMessage()} "; $response = $error->getReason(); // reason is a ResponseInterface print_r(['headers' => $response->getHeaders(), 'body' => $response->getBody()]); } else { // ResourceDownloadError echo "Error getting {$error->getUuid()}, reason: {$error->getMessage()} "; print_r(['reason' => $error->getReason()]); } } }; // $downloadedUuids contiene un listado de UUID que fueron procesados correctamente $downloadedUuids = $satScraper->resourceDownloader(ResourceType::xml(), $list)->download($myHandler); echo json_encode($downloadedUuids); ``` -------------------------------- ### Project Build and Test Commands Source: https://github.com/phpcfdi/cfdi-sat-scraper/blob/main/CONTRIBUTING.md These shell commands are used to manage project dependencies, check and fix code style, and run tests. Ensure you have Composer and Phive installed. ```shell # Actualiza tus dependencias composer update phive update ``` ```shell # Verificación de estilo de código composer dev:check-style ``` ```shell # Corrección de estilo de código composer dev:fix-style ``` ```shell # Ejecución de pruebas composer dev:test ``` ```shell # Ejecución todo en uno: corregir estilo, verificar estilo y correr pruebas composer dev:build ``` -------------------------------- ### Install phpcfdi/cfdi-sat-scraper with Composer Source: https://github.com/phpcfdi/cfdi-sat-scraper/blob/main/README.md Use Composer to add the cfdi-sat-scraper package to your PHP project. This is the standard method for managing dependencies in PHP. ```shell composer require phpcfdi/cfdi-sat-scraper ``` -------------------------------- ### Running GitHub Actions Locally with act Source: https://github.com/phpcfdi/cfdi-sat-scraper/blob/main/CONTRIBUTING.md This command uses the 'act' tool to execute GitHub Actions workflows locally. It's useful for testing CI/CD configurations before pushing changes. Ensure 'act' is installed. ```shell act -P ubuntu-latest=shivammathur/node:latest ``` -------------------------------- ### Instalar dependencias del proyecto Source: https://github.com/phpcfdi/cfdi-sat-scraper/blob/main/develop/EntornoDesarrollo.md Clona el repositorio, navega a la carpeta del proyecto e instala las dependencias de la librería y de desarrollo usando Composer. ```shell # clonación del proyecto git clone https://github.com/phpcfdi/cfdi-sat-scraper.git # posicionarse en la carpeta del proyecto cd cfdi-sat-scraper # instalar dependencias de la librería composer install # instalar dependencias de desarrollo composer dev:install ``` -------------------------------- ### Validate Time Range - JavaScript Source: https://github.com/phpcfdi/cfdi-sat-scraper/blob/main/tests/_files/sample-to-extract-metadata-one-cfdi.html Validates that the selected end time is not earlier than the start time. If it is, the end time is adjusted to be one minute after the start time. ```javascript function validaHora() { var horaJsObj = document.getElementById('ctl00_MainContent_CldFecha_DdlHora'); var minutoJsObj = document.getElementById('ctl00_MainContent_CldFecha_DdlMinuto'); var segundoJsObj = document.getElementById('ctl00_MainContent_CldFecha_DdlSegundo'); var horaFinJsObj = document.getElementById('ctl00_MainContent_CldFecha_DdlHoraFin'); var minutoFinJsObj = document.getElementById('ctl00_MainContent_CldFecha_DdlMinutoFin'); var segundoFinJsObj = document.getElementById('ctl00_MainContent_CldFecha_DdlSegundoFin'); var horaJs = horaJsObj.options[horaJsObj.selectedIndex].value; var minutoJs = minutoJsObj.options[minutoJsObj.selectedIndex].value; var segundoJs = segundoJsObj.options[segundoJsObj.selectedIndex].value; var horaFinJs = horaFinJsObj.options[horaFinJsObj.selectedIndex].value; var minutoFinJs = minutoFinJsObj.options[minutoFinJsObj.selectedIndex].value; var segundoFinJs = segundoFinJsObj.options[segundoFinJsObj.selectedIndex].value; var segIni = timeToSeconds(horaJs, minutoJs, segundoJs); var segFin = timeToSeconds(horaFinJs, minutoFinJs, segundoFinJs); if (segIni > segFin) { horaFinJsObj.selectedIndex = horaJsObj.selectedIndex; minutoFinJsObj.selectedIndex = minutoJsObj.selectedIndex+1; segundoFinJsObj.selectedIndex = segundoJsObj.selectedIndex; } } ``` -------------------------------- ### Create necessary directories Source: https://github.com/phpcfdi/cfdi-sat-scraper/blob/main/docs/EjemploConsumo.md Creates the build directories for cookies and downloaded CFDI files before running the script. ```shell mkdir -p build/cookies build/cfdis ``` -------------------------------- ### Validate Time Range Source: https://github.com/phpcfdi/cfdi-sat-scraper/blob/main/tests/_files/sample-response-receiver-using-filters-initial.html Validates that the selected end time is not earlier than the start time. If it is, it adjusts the end time to match the start time or the next minute. ```javascript function validaHora() { var horaJsObj = document.getElementById('ctl00_MainContent_CldFecha_DdlHora'); var minutoJsObj = document.getElementById('ctl00_MainContent_CldFecha_DdlMinuto'); var segundoJsObj = document.getElementById('ctl00_MainContent_CldFecha_DdlSegundo'); var horaFinJsObj = document.getElementById('ctl00_MainContent_CldFecha_DdlHoraFin'); var minutoFinJsObj = document.getElementById('ctl00_MainContent_CldFecha_DdlMinutoFin'); var segundoFinJsObj = document.getElementById('ctl00_MainContent_CldFecha_DdlSegundoFin'); var horaJs = horaJsObj.options[horaJsObj.selectedIndex].value; var minutoJs = minutoJsObj.options[minutoJsObj.selectedIndex].value; var segundoJs = segundoJsObj.options[segundoJsObj.selectedIndex].value; var horaFinJs = horaFinJsObj.options[horaFinJsObj.selectedIndex].value; var minutoFinJs = minutoFinJsObj.options[minutoFinJsObj.selectedIndex].value; var segundoFinJs = segundoFinJsObj.options[segundoFinJsObj.selectedIndex].value; var segIni = timeToSeconds(horaJs, minutoJs, segundoJs); var segFin = timeToSeconds(horaFinJs, minutoFinJs, segundoFinJs); if (segIni > segFin) { horaFinJsObj.selectedIndex = horaJsObj.selectedIndex; minutoFinJsObj.selectedIndex = minutoJsObj.selectedIndex+1; segundoFinJsObj.selectedIndex = segundoJsObj.selectedIndex; } } ``` -------------------------------- ### Initialize UI Elements and Event Handlers in JavaScript Source: https://github.com/phpcfdi/cfdi-sat-scraper/blob/main/tests/_files/sample-response-receiver-form-page.html This JavaScript code runs when the document is ready. It checks for download quotas, displays warning messages, and attaches live event handlers for various UI elements like buttons and radio buttons. ```javascript var rfcetiqueta; var rfcvalido; $(document).ready(function () { var hfDescarga = document.getElementById('hfDescarga'); if (hfDescarga.value == "CuotaParcial") { document.getElementById('dvAlert').innerHTML = ''; var newDivSuccess = document.createElement('div'); newDivSuccess.className = "alert alert-warning alert-dismissible"; newDivSuccess.innerHTML = "Atencin!
Ests intentando descargas ms documentos de los permitidos."; document.getElementById('dvAlert').appendChild(newDivSuccess); $('html, body').animate({ scrollTop: $('#dvAlert').offset().top }, 'slow'); } if (hfDescarga.value == "CuotaCompleta") { document.getElementById('dvAlert').innerHTML = ''; var newDivSuccess = document.createElement('div'); newDivSuccess.className = "alert alert-warning alert-dismissible"; newDivSuccess.innerHTML = "Atencin!
Ya no puedes descargar ms documentos, espera hasta maana."; document.getElementById('dvAlert').appendChild(newDivSuccess); $('html, body').animate({ scrollTop: $('#dvAlert').offset().top }, 'slow'); } $('#ctl00\_MainContent\_BtnBusqueda').live('click', function () { $("#ctl00\_MainContent\_PnlErrores").empty(); $("#ctl00\_MainContent\_PnlErrores").css('display', 'none'); }); $("#ctl00\_MainContent\_RdoFolioFiscal").live('click', function () { validaRfcAutenticado(); }); $("#ctl00\_MainContent\_BtnBusqueda").live('mouseover', function (event) { validaRfcAutenticado(); }); $("#BtnImprimirVisible").live('click', function (event) { validaRfcAutenticado(); }); $("#ctl00\_MainContent\_BtnImprimir").live('mouseover', function (event) { validaRfcAutenticado(); }); $("#ctl00\_MainContent\_BtnMetadata").live('mouseover', function (event) { validaRfcAutenticado(); }); $("#ctl00\_MainContent\_RdoFechas").live('click', function () { validaRfcAutenticado(); }); }); ``` -------------------------------- ### Adjust End Time Based on Start Time in JavaScript Source: https://github.com/phpcfdi/cfdi-sat-scraper/blob/main/tests/_files/sample-response-receiver-form-page.html Adjusts the end time selection if the start time is later than the end time. This function relies on the `timeToSeconds` helper function. ```javascript function AjustarHoraFin() { var horaJsObj = document.getElementById('ctl00_MainContent_CldFecha_DdlHora'); var minutoJsObj = document.getElementById('ctl00_MainContent_CldFecha_DdlMinuto'); var segundoJsObj = document.getElementById('ctl00_MainContent_CldFecha_DdlSegundo'); var horaFinJsObj = document.getElementById('ctl00_MainContent_CldFecha_DdlHoraFin'); var minutoFinJsObj = document.getElementById('ctl00_MainContent_CldFecha_DdlMinutoFin'); var segundoFinJsObj = document.getElementById('ctl00_MainContent_CldFecha_DdlSegundoFin'); var horaJs = horaJsObj.options[horaJsObj.selectedIndex].value; var minutoJs = minutoJsObj.options[minutoJsObj.selectedIndex].value; var segundoJs = segundoJsObj.options[segundoJsObj.selectedIndex].value; var horaFinJs = horaFinJsObj.options[horaFinJsObj.selectedIndex].value; var minutoFinJs = minutoFinJsObj.options[minutoFinJsObj.selectedIndex].value; var segundoFinJs = segundoFinJsObj.options[segundoFinJsObj.selectedIndex].value; var segIni = timeToSeconds(horaJs, minutoJs, segundoJs); var segFin = timeToSeconds(horaFinJs, minutoFinJs, segundoFinJs); if (segIni > segFin) { horaFinJsObj.selectedIndex = horaJsObj.selectedIndex; minutoFinJsObj.selectedIndex = minutoJsObj.selectedIndex+1; segundoFinJsObj.selectedIndex = segundoJsObj.selectedIndex; } } ``` -------------------------------- ### Validate Time Range Source: https://github.com/phpcfdi/cfdi-sat-scraper/blob/main/tests/_files/sample-to-extract-metadata-zero-cfdi.html Validates that the selected start time does not exceed the selected end time. If it does, it adjusts the end time to be one minute after the start time to maintain a valid range. ```javascript function validaHora() { var horaJsObj = document.getElementById('ctl00_MainContent_CldFecha_DdlHora'); var minutoJsObj = document.getElementById('ctl00_MainContent_CldFecha_DdlMinuto'); var segundoJsObj = document.getElementById('ctl00_MainContent_CldFecha_DdlSegundo'); var horaFinJsObj = document.getElementById('ctl00_MainContent_CldFecha_DdlHoraFin'); var minutoFinJsObj = document.getElementById('ctl00_MainContent_CldFecha_DdlMinutoFin'); var segundoFinJsObj = document.getElementById('ctl00_MainContent_CldFecha_DdlSegundoFin'); var horaJs = horaJsObj.options[horaJsObj.selectedIndex].value; var minutoJs = minutoJsObj.options[minutoJsObj.selectedIndex].value; var segundoJs = segundoJsObj.options[segundoJsObj.selectedIndex].value; var horaFinJs = horaFinJsObj.options[horaFinJsObj.selectedIndex].value; var minutoFinJs = minutoFinJsObj.options[minutoFinJsObj.selectedIndex].value; var segundoFinJs = segundoFinJsObj.options[segundoFinJsObj.selectedIndex].value; var segIni = timeToSeconds(horaJs, minutoJs, segundoJs); var segFin = timeToSeconds(horaFinJs, minutoFinJs, segundoFinJs); if (segIni > segFin) { horaFinJsObj.selectedIndex = horaJsObj.selectedIndex; minutoFinJsObj.selectedIndex = minutoJsObj.selectedIndex + 1; segundoFinJsObj.selectedIndex = segundoJsObj.selectedIndex; } } ``` -------------------------------- ### Initialize SAT Scraper with AI Captcha Resolver Source: https://github.com/phpcfdi/cfdi-sat-scraper/blob/main/README.md Instantiate the SatScraper using the BoxFacturaAIResolver. Ensure the model configuration file path is correct. ```php isFiel()) { throw new Exception('The certificate and private key is not a FIEL'); } if (! $credential->certificate()->validOn()) { throw new Exception('The certificate and private key is not valid at this moment'); } // crear el objeto scraper usando la FIEL $satScraper = new SatScraper(FielSessionManager::create($credential)); ``` -------------------------------- ### Initialize SAT Scraper Source: https://github.com/phpcfdi/cfdi-sat-scraper/blob/main/tests/_files/sample-response-receiver-form-page.html Initializes the SAT PageRequestManager. This is a common setup for web forms in ASP.NET applications, often used to manage asynchronous postbacks and partial page updates. ```javascript Sys.WebForms.PageRequestManager._initialize('ctl00$ScriptManager1', 'aspnetForm', ['fctl00$UpnlNombre','','tctl00$MainContent$UpnlBusqueda','','tctl00$MainContent$CldFecha$UpnlSeleccionFecha','','tctl00$MainContent$UpnlResultados','' ], [], ['ctl00$MainContent$BtnDescargar','','ctl00$MainContent$BtnMetadata','','ctl00$MainContent$BtnImprimir','' ], 18000000, 'ctl00'); ``` -------------------------------- ### Execute Metadata Download via AJAX Source: https://github.com/phpcfdi/cfdi-sat-scraper/blob/main/tests/_files/sample-response-receiver-form-page.html Displays the progress indicator and then makes an AJAX POST request to 'ConsultaReceptor.aspx/DescargaMetadatos' to download metadata. It handles success and error responses, displaying appropriate messages to the user. ```javascript function DescargaMetadata() { document.getElementById('dvAlert').innerHTML = ''; ShowProgress(); Metadatos(); return false; } function Metadatos() { var urlValidaDescarga = "ConsultaReceptor.aspx/DescargaMetadatos"; var Parametros = document.getElementById('hfParametrosMetadata').value; var data = encodeURIComponent($.base64.encode(Parametros)); $.ajax({ url: urlValidaDescarga, type: "POST", dataType: "json", contentType: 'application/json; charset=utf-8', data: "{'Parametros':'" + data + "'}", cache: false, async: true, success: function (respuestaValidacion) { $get('ctl00_MainContent_UpdateProgress1').style.display = "none"; document.getElementById('dvAlert').innerHTML = ''; var newDivSuccess = document.createElement('div'); if (respuestaValidacion.d.startsWith("Error:")) { newDivSuccess.className = "alert alert-danger alert-dismissible"; newDivSuccess.innerHTML = "Error!
" + respuestaValidacion.d.replace("Error:", ""); } else { newDivSuccess.className = "alert alert-success alert-dismissible"; newDivSuccess.innerHTML = "Descarga realizada con xito!
" + respuestaValidacion.d; } document.getElementById('dvAlert').appendChild(newDivSuccess); $('html, body').animate({ scrollTop: $('#dvAlert').offset().top }, 'slow'); }, error: function (errorDetail) { $get('ctl00_MainContent_UpdateProgress1').style.display = "none"; document.getElementById('dvAlert').innerHTML = ''; var newDivSuccess = document.createElement('div'); newDivSuccess.className = "alert alert-danger alert-dismissible"; newDivSuccess.innerHTML = "Error!
Ocurrio un error al procesar su solicitud. Por favor intentelo ms tarde"; document.getElementById('dvAlert').appendChild(newDivSuccess); $('html, body').animate({ scrollTop: $('#dvAlert').offset().top }, 'slow'); }, }); } ``` -------------------------------- ### Execute CFDI SAT Scraper script Source: https://github.com/phpcfdi/cfdi-sat-scraper/blob/main/docs/EjemploConsumo.md Runs the demo script using environment variables for authentication. Replace '******' with your actual CIEC key. ```shell env SAT_AUTH_RFC="COSC8001137NA" SAT_AUTH_CIEC="******" php demo-ciec.php ``` -------------------------------- ### Execute Integration Tests with PHPUnit Source: https://github.com/phpcfdi/cfdi-sat-scraper/blob/main/develop/TestIntegracion.md Run integration tests using PHPUnit. The first command uses the default `.env` configuration. The second overrides the `SAT_AUTH_MODE` to use FIEL credentials. ```shell # using .env config php vendor/bin/phpunit --testsuite integration ``` ```shell # overriding SAT_AUTH_MODE (use CIEC or FIEL) SAT_AUTH_MODE="FIEL" php vendor/bin/phpunit --testsuite integration ``` -------------------------------- ### Configure Composer for ONNX Runtime Source: https://github.com/phpcfdi/cfdi-sat-scraper/blob/main/README.md Set up Composer scripts to automatically manage the ONNX Runtime library. This ensures the AI model can be loaded. ```json { "scripts": { "post-install-cmd": "OnnxRuntime\\Vendor::check", "post-update-cmd": "OnnxRuntime\\Vendor::check" } } ``` -------------------------------- ### Initiate Metadata Download Source: https://github.com/phpcfdi/cfdi-sat-scraper/blob/main/tests/_files/sample-response-receiver-form-page.html Sets the action to 'DescargaMetadata' and calls AccionCaptcha to proceed with the metadata download process. This function is typically triggered by a user action. ```javascript function MetadataDescarga() { $('#hdnValAccion').attr('value', 'DescargaMetadata'); //ValidaCaptcha(); // Se habilita cuando este captcha AccionCaptcha(); // Se quita cuando este captcha return false; } ``` -------------------------------- ### Initialize FIEL Session Manager Source: https://github.com/phpcfdi/cfdi-sat-scraper/blob/main/README.md Use `FielSessionManager` for authentication with FIEL credentials, including the certificate, private key, and password. This method does not require a captcha solver. ```php new FielSessionManager(certificate: $certificate, privateKey: $privateKey, password: $password) ``` -------------------------------- ### CFDI Querying and Downloading Source: https://github.com/phpcfdi/cfdi-sat-scraper/blob/main/README.md This section details how to use the SatScraper class to query CFDI metadata by UUIDs or filters, and how to download the related files. ```APIDOC ## Querying CFDI Metadata ### Description The `SatScraper` object is the main interface for interacting with the SAT to retrieve CFDI metadata. It supports querying by specific UUIDs or by various filters. ### Methods - **`listByUuids(string[] $uuids, DownloadType $type): MetadataList`**: Retrieves metadata for one or more specific CFDI UUIDs. - **`listByPeriod(Query $query): MetadataList`**: Retrieves metadata for a given period using filter criteria. - **`listByDateTime(Query $query): MetadataList`**: Retrieves metadata for a specific date and time using filter criteria. ### MetadataList Processing The `MetadataList` returned by queries can be further processed. The library employs a strategy to subdivide large date ranges into smaller periods (down to 1 second) to manage performance and then merges the results. You can filter the `MetadataList` to find specific `Metadata` objects by UUID or by downloadable resource type. ### Downloading CFDI Files Once you have a `MetadataList`, you can initiate downloads for various file types: - CFDI files (XML) - Printed representation of CFDI (PDF) - Cancellation request (PDF) - Cancellation acknowledgment (PDF) ### Resource Downloader To download files, you first create a `ResourceDownloader` object and then instruct it to perform the downloads. - **`SatScraper::resourceDownloader(ResourceType $resourceType, MetadataList $list = null, int $concurrency = 10): ResourceDownloader`**: Creates a `ResourceDownloader` instance. - **`ResourceDownloader::saveTo(string $destination): void`**: Saves downloaded files to a specified directory. - **`ResourceDownloader::download(ResourceDownloadHandlerInterface $handler): void`**: Downloads files using a custom handler. ### Query Configuration When creating a query, a date range is mandatory. By default, the search is for issued CFDI with any complement and any status (valid or canceled). These defaults can be modified before processing the query. ### Guzzle Integration The library is built upon Guzzle, allowing for custom client configurations such as proxy settings or HTTP call debugging. This enables efficient simultaneous downloads and faster communication compared to browser-based methods. ``` -------------------------------- ### Show Progress Indicator and Session Management Source: https://github.com/phpcfdi/cfdi-sat-scraper/blob/main/tests/_files/sample-response-receiver-form-page.html Initializes intervals to manage UI progress and maintain user sessions. It displays a progress indicator and checks for download completion status. ```javascript /// Variables var validCookieInterval; var validSesionInterval; var downloadCookie = 'DownloadedFile' /** * Funcin que muestra el update progress y crea 2 Intervals uno para validar que la descarga del archivo * se haya realizado y el otro mantiene la sesin. */ function ShowProgress() { validSesionInterval = setInterval(function () { MantieneSesion(); }, 60000); $get('ctl00_MainContent_UpdateProgress1').style.display = 'block'; validCookieInterval = setInterval(function () { if ($.cookie(downloadCookie) != null && $.cookie(downloadCookie) == "OK") { $.cookie(downloadCookie, null); $get('ctl00_MainContent_UpdateProgress1').style.display = "none"; clearInterval(validCookieInterval);//delete interval clearInterval(validSesionInterval); } }, 2000); } ``` -------------------------------- ### Generate Repository Script Source: https://github.com/phpcfdi/cfdi-sat-scraper/blob/main/develop/TestIntegracion.md Use this PHP script to generate the `tests/repository.json` file by specifying a date range. The output is redirected to the repository file. ```shell php tests/generate-repository.php "2020-01-01 00:00:00" "2020-01-31 29:59:59" > tests/repository.json ``` -------------------------------- ### Ejecutar tests de integración (no por defecto) Source: https://github.com/phpcfdi/cfdi-sat-scraper/blob/main/develop/EntornoDesarrollo.md Ejecuta específicamente los tests de integración del proyecto. Asegúrate de que las dependencias estén actualizadas. ```shell # run integration tests (not included by default) vendor/bin/phpunit tests/Integration --testdox ``` -------------------------------- ### Initialize SatScraper with Anti-Captcha Source: https://github.com/phpcfdi/cfdi-sat-scraper/blob/main/README.md Configure the SatScraper to use Anti-Captcha for resolving image CAPTCHAs during authentication. Ensure you have the 'anticaptcha-client-key' set. ```php '...']) ``` -------------------------------- ### Global Variables and Initialization Source: https://github.com/phpcfdi/cfdi-sat-scraper/blob/main/tests/_files/sample-response-fiel-login-form.html These global variables define base URLs and paths for SAT resources. The `showEncabezado` variable is determined by checking if the parent document is accessible, indicating whether to display the header. ```javascript //Variables globales var root = 'https://cfdiau.sat.gob.mx/'; var path = root + 'nidp/gobmx/assets/'; var imagesPath = path + 'images/'; var scriptsPath = path + 'scripts/'; var stylesPath = path + 'styles/'; var myVar; var jsurlciec = 'https://cfdiau.sat.gob.mx/nidp/wsfed/ep?id=SATUPCFDiCon&sid=0&option=credential&sid=0'; var showEncabezado = false; try { window.parent.document if (window.parent.document == undefined) { showEncabezado = false; } else { showEncabezado = true; } } catch (e) { showEncabezado = false; } ``` -------------------------------- ### Initialize Comprobante State Source: https://github.com/phpcfdi/cfdi-sat-scraper/blob/main/tests/_files/sample-to-extract-metadata-one-cfdi.html Sets the initial state for the Comprobante (certificate) dropdown. This is likely used to set a default or initial value. ```javascript Estado_Comprobante('-1'); ``` -------------------------------- ### Initialize CIEC Session Manager Source: https://github.com/phpcfdi/cfdi-sat-scraper/blob/main/README.md Use `CiecSessionManager` for authentication with CIEC credentials, requiring RFC, CIEC password, and a captcha resolver. This method avoids the need for FIEL. ```php new CiecSessionManager(rfc: $rfc, ciec: $ciec, captchaResolver: $captchaResolver) ``` -------------------------------- ### Custom Download Handler for XML Files Source: https://context7.com/phpcfdi/cfdi-sat-scraper/llms.txt Implement a custom resource download handler to manage individual download successes and errors. This allows for custom storage logic and detailed error reporting. ```php getUuid(); if ($error instanceof ResourceDownloadRequestExceptionError) { // Error de conexión HTTP $this->errors[$uuid] = "Error de conexión: " . $error->getMessage(); } elseif ($error instanceof ResourceDownloadResponseError) { // Respuesta HTTP inválida $this->errors[$uuid] = "Respuesta inválida: " . $error->getReason()->getStatusCode(); } else { // Otro tipo de error $this->errors[$uuid] = "Error: " . $error->getMessage(); } error_log("Error descargando $uuid: " . $this->errors[$uuid]); } public function getErrors(): array { return $this->errors; } }; /** @var CiecSessionManager $sessionManager */ $satScraper = new SatScraper($sessionManager); $query = new QueryByFilters( new DateTimeImmutable('2024-01-01'), new DateTimeImmutable('2024-01-31') ); $metadataList = $satScraper->listByPeriod($query); // Ejecutar descarga con handler personalizado $downloadedUuids = $satScraper->resourceDownloader(ResourceType::xml(), $metadataList) ->download($customHandler); // Reportar resultados echo "Descargados: " . count($downloadedUuids) . "\n"; echo "Errores: " . count($customHandler->getErrors()) . "\n"; foreach ($customHandler->getErrors() as $uuid => $errorMsg) { echo " - $uuid: $errorMsg\n"; } ``` -------------------------------- ### Authentication Methods Source: https://github.com/phpcfdi/cfdi-sat-scraper/blob/main/README.md Details on how to authenticate with the SAT using either FIEL or CIEC credentials. ```APIDOC ## SAT Authentication ### Description The library supports two primary mechanisms for authenticating with the SAT: FIEL (Electronic Signature) and CIEC (Electronic Tax Account Key). ### FIEL Authentication - **Manager**: `FielSessionManager` - **Requirements**: Certificate, private key, and private key password. - **Advantages**: Does not require a captcha solver. - **Disadvantages**: Working with FIEL can be risky. It is strongly advised not to use the FIEL of third parties due to legal regulations. ### CIEC Authentication - **Manager**: `CiecSessionManager` - **Requirements**: RFC, CIEC password, and a captcha solver. - **Advantages**: Does not require FIEL. - **Disadvantages**: Requires a captcha solver. ### Captcha Resolution Captcha resolution is handled by the `phpcfdi/image-captcha-resolver` library. For development and production environments, the recommended solution is `phpcfdi/image-captcha-resolver-boxfactura-ai`. ``` -------------------------------- ### Download CFDI by Date Range Source: https://github.com/phpcfdi/cfdi-sat-scraper/blob/main/README.md Lists and downloads CFDI documents within a specified date range. It demonstrates iterating through the results and initiating bulk downloads. ```php listByPeriod($query); // impresión de cada uno de los metadata foreach ($list as $cfdi) { echo 'UUID: ', $cfdi->uuid(), PHP_EOL; echo 'Emisor: ', $cfdi->get('rfcEmisor'), ' - ', $cfdi->get('nombreEmisor'), PHP_EOL; echo 'Receptor: ', $cfdi->get('rfcReceptor'), ' - ', $cfdi->get('nombreReceptor'), PHP_EOL; echo 'Fecha: ', $cfdi->get('fechaEmision'), PHP_EOL; echo 'Tipo: ', $cfdi->get('efectoComprobante'), PHP_EOL; echo 'Estado: ', $cfdi->get('estadoComprobante'), PHP_EOL; } // descarga de cada uno de los CFDI, reporta los descargados en $downloadedUuids $downloadedUuids = $satScraper->resourceDownloader(ResourceType::xml(), $list) ->setConcurrency(50) // cambiar a 50 descargas simultáneas ->saveTo('/storage/downloads'); // ejecutar la instrucción de descarga echo json_encode($downloadedUuids); ``` -------------------------------- ### Download PDF Representations and Cancellation Files Source: https://context7.com/phpcfdi/cfdi-sat-scraper/llms.txt Use this snippet to download PDF representations of CFDI, cancellation requests, and cancellation vouchers. Configure the download directory and concurrency for optimal performance. ```php listByPeriod($query); // Descargar representación impresa (PDF del CFDI) $satScraper->resourceDownloader(ResourceType::pdf(), $metadataList) ->setConcurrency(20) ->saveTo('/storage/cfdi-pdf'); // Archivos: uuid.pdf // Descargar solicitudes de cancelación $satScraper->resourceDownloader(ResourceType::cancelRequest(), $metadataList) ->saveTo('/storage/cancelaciones'); // Archivos: uuid-cancel-request.pdf // Descargar acuses de cancelación $satScraper->resourceDownloader(ResourceType::cancelVoucher(), $metadataList) ->saveTo('/storage/acuses'); // Archivos: uuid-cancel-voucher.pdf ``` -------------------------------- ### Download CFDI XML Files Source: https://context7.com/phpcfdi/cfdi-sat-scraper/llms.txt Use `ResourceDownloader` to download CFDI XML files concurrently. The `saveTo` method saves files to a specified folder, creating it if necessary with given permissions. ```php listByPeriod($query); // Configurar descargador de XML con 50 descargas concurrentes $downloader = $satScraper->resourceDownloader(ResourceType::xml(), $metadataList, 50); // Descargar a carpeta (crear si no existe, con permisos 0775) $downloadedUuids = $downloader->saveTo( '/storage/cfdi-xml', // Carpeta destino true, // Crear carpeta si no existe 0775 // Permisos para la carpeta ); echo "Descargados " . count($downloadedUuids) . " archivos XML\n"; // Los archivos se guardan como: uuid.xml // Ejemplo: 5cc88a1a-8672-11e6-ae22-56b6b6499611.xml ``` -------------------------------- ### Download CFDI XMLs to a Folder Source: https://github.com/phpcfdi/cfdi-sat-scraper/blob/main/README.md This snippet demonstrates how to download CFDI XML files to a specified directory. It returns an array of UUIDs that were successfully downloaded. Errors during individual downloads are ignored. ```php listByPeriod($query); // $downloadedUuids contiene un listado de UUID que fueron procesados correctamente, 50 descargas simultáneas $downloadedUuids = $satScraper->resourceDownloader(ResourceType::xml(), $list, 50) ->saveTo('/storage/downloads', true, 0777); echo json_encode($downloadedUuids); ``` -------------------------------- ### PHP script for downloading CFDI XMLs Source: https://github.com/phpcfdi/cfdi-sat-scraper/blob/main/docs/EjemploConsumo.md This script automates the download of CFDI XMLs from the SAT portal. It uses CIEC authentication, a BoxFacturaAI captcha resolver, and handles cookie persistence and download retries. ```php [CURLOPT_SSL_CIPHER_LIST => 'DEFAULT@SECLEVEL=1'], ]); $gateway = new SatHttpGateway($client, new FileCookieJar($cookieJarPath, true)); $configsFile = __DIR__ . '/storage/boxfactura-model/configs.yaml'; $captchaResolver = BoxFacturaAIResolver::createFromConfigs($configsFile); $ciecSessionManager = CiecSessionManager::create($rfc, $claveCiec, $captchaResolver); $satScraper = new SatScraper($ciecSessionManager, $gateway); $resourceDownloader = $satScraper->resourceDownloader(ResourceType::xml()) ->setConcurrency(20); $query = new QueryByFilters(new DateTimeImmutable('2019-12-01'), new DateTimeImmutable('2019-12-31')); $query->setDownloadType(DownloadType::recibidos()) // default: emitidos ->setStateVoucher(StatesVoucherOption::vigentes()); // default: todos $list = $satScraper->listByPeriod($query); printf("\nSe encontraron %d registros", $list->count()); $list = $list->filterWithResourceLink(ResourceType::xml()); printf("\nPero solamente %d registros contienen archivos XML", $list->count()); while ($list->count() > 0) { // perform download printf("\nIntentando descargar %d archivos: ", $list->count()); $downloadedUuids = $resourceDownloader->setMetadataList($list) ->saveTo($downloadsPath, true); printf('%d descargados.', count($downloadedUuids)); // check that at least one uuid were downloaded if ([] === $downloadedUuids) { printf("\nNo se pudieron descargar %d registros", $list->count()); break; // exit loop since no records were downloaded } // reduce list $list = $list->filterWithOutUuids($downloadedUuids); } ```