### 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);
}
```