phpcfdi/sat-estado-cfdi

Consulta el estado de un cfdi en el webservice del SAT

v2.0.0 2024-03-07 16:48 UTC

This package is auto-updated.

Last update: 2024-09-07 18:11:35 UTC


README

Source Code Packagist PHP Version Support Discord Latest Version Software License Build Status Reliability Maintainability Code Coverage Violations Total Downloads

Consulta el estado de un CFDI en el webservice del SAT

馃嚭馃嚫 This library contains helpers to consume the Servicio de Consulta de CFDI from SAT. The documentation of this project is in spanish as this is the natural language for intended audience.

馃嚥馃嚱 Esta librer铆a se utiliza para consumir el Servicio de Consulta de CFDI del SAT. La documentaci贸n del proyecto est谩 en espa帽ol porque ese es el lenguaje de los usuarios que la utilizar谩n.

Esta librer铆a solo permite verificar el estado de un CFDI Regular y no de CFDI de Retenciones e informaci贸n de pagos. Para estos 煤ltimos, use la librer铆a phpcfdi/sat-estado-retenciones.

Servicio de Consulta de CFDI del SAT:

Cambios recientes en el servicio:

  • Por motivo del cambio en el proceso de cancelaci贸n, en 2018 agregaron nuevos estados.
  • Por una raz贸n desconocida e inexplicable, el WSDL no estuvo disponible de 2018 a 2020. Esta librer铆a usa una estrategia en donde no depende del WSDL para consumir el servicio.
  • A finales de 2020 agregaron el campo de respuesta VerificacionEFOS.

Instalaci贸n

Usa composer

composer require phpcfdi/sat-estado-cfdi

Ejemplo b谩sico de uso

Los pasos b谩sicos son:

  • Tener un cliente que implemente ConsumerClientInterface.
  • Crear un consumidor del servicio Consumer
  • Realizar la solicitud con una expresi贸n definida.
  • Usar el resultado
<?php
use PhpCfdi\SatEstadoCfdi\Consumer;
use PhpCfdi\SatEstadoCfdi\Contracts\ConsumerClientInterface;

/** @var ConsumerClientInterface $client */
$consumer = new Consumer($client);

$cfdiStatus = $consumer->execute('...expression');

if ($cfdiStatus->cancellable->isNotCancellable()) {
    echo 'CFDI no es cancelable';
}

Clientes de consumo ConsumerClientInterface

Esta librer铆a incluye dos diferentes clientes de consumo: SoapConsumerClient y HttpConsumerClient.

Adem谩s, puedes usar tu propio cliente de consumo implementando la interface ConsumerClientInterface.

Cliente SOAP SoapConsumerClient

El cliente SoapConsumerClient permite hacer el consumo usando la estrategia SOAP.

Requerimientos:

  • ext-soap: Extensi贸n SOAP de PHP.

Ejemplo:

<?php
use PhpCfdi\SatEstadoCfdi\Clients\Soap\SoapConsumerClient;
use PhpCfdi\SatEstadoCfdi\Consumer;

function createConsumerUsingSoap(): Consumer
{
    $client = new SoapConsumerClient();
    return new Consumer($client);
}

Cliente HTTP PSR HttpConsumerClient

El cliente HttpConsumerClient permite hacer el consumo usando la estrategia HTTP con base en los est谩ndares PSR.

Est谩ndares utilizados:

Las librer铆as de Guzzle guzzlehttp/guzzle, y guzzlehttp/psr7 proveen los est谩ndares necesarios.

O puedes ver en Packagist los que te agraden:

Requerimientos:

  • ext-dom: Extensi贸n DOM de PHP.
  • psr/http-client: ^1.0: Est谩ndar PSR-18 (Cliente HTTP).
  • psr/http-factory: ^1.0: Est谩ndar PSR-17 (F谩bricas de mensajes HTTP).
  • Algunas librer铆as que implementen PSR-18 y PSR-17, por ejemplo:
    • Guzzle: guzzlehttp/guzzle y guzzlehttp/psr7.
    • Symfony: symfony/http-client y nyholm/psr7 o laminas/laminas-diactoros.

Ejemplo:

<?php
use PhpCfdi\SatEstadoCfdi\Clients\Http\HttpConsumerClient;
use PhpCfdi\SatEstadoCfdi\Clients\Http\HttpConsumerFactory;
use PhpCfdi\SatEstadoCfdi\Consumer;

function createConsumerUsingGuzzle(): Consumer
{
    // Implements PSR-18 \Psr\Http\Client\ClientInterface
    $guzzleClient = new \GuzzleHttp\Client();
    // Implements PSR-17 \Psr\Http\Message\RequestFactoryInterface and PSR-17 \Psr\Http\Message\StreamFactoryInterface
    $guzzleFactory = new \GuzzleHttp\Psr7\HttpFactory();

    $factory = new HttpConsumerFactory($guzzleClient, $guzzleFactory, $guzzleFactory);
    $client = new HttpConsumerClient($factory);
    return new Consumer($client);
}

El siguiente es un ejemplo usando symfony/http-client y nyholm/psr7:

use PhpCfdi\SatEstadoCfdi\Consumer;
use PhpCfdi\SatEstadoCfdi\Clients\Http\HttpConsumerClient;
use PhpCfdi\SatEstadoCfdi\Clients\Http\HttpConsumerFactory;

function createConsumerUsingSymfonyNyholm(): Consumer
{
    $httpClient = new \Symfony\Component\HttpClient\Psr18Client();
    $messageFactory = new \Nyholm\Psr7\Factory\Psr17Factory();
    
    $factory = new HttpConsumerFactory($httpClient, $messageFactory, $messageFactory);
    $client = new HttpConsumerClient($factory);
    return new Consumer($client);
}

Para Laravel puedes usar alg煤n paquete adicional como wimski/laravel-psr-http, que gracias al uso del propio framework y php-http/discovery, facilita la creaci贸n de los objetos, ya sea que los fabrique directamente usando el contenedor, o bien los inyecte como dependencias.

<?php
use PhpCfdi\SatEstadoCfdi\Consumer;
use PhpCfdi\SatEstadoCfdi\Clients\Http\HttpConsumerClient;
use PhpCfdi\SatEstadoCfdi\Clients\Http\HttpConsumerFactory;

function createConsumerUsingLaravel(): Consumer
{
    $httpClient = app(\Psr\Http\Client\ClientInterface::class);
    $requestFactory = app(Psr\Http\Message\RequestFactoryInterface::class);
    $streamFactory = app(Psr\Http\Message\StreamFactoryInterface::class);
    
    $factory = new HttpConsumerFactory($httpClient, $requestFactory, $streamFactory);
    $client = new HttpConsumerClient($factory);
    return new Consumer($client);
}

Tambi茅n te recomiendo hacer tu propio Service Provider o configurar el Service Container y solo requerir la clase Consumer como cualquier otra dependencia y permitir que sea inyectada.

Expresiones (input)

El consumidor requiere una expresi贸n para poder consultar. La expresi贸n es el texto que viene en el c贸digo QR de la representaci贸n impresa de un CFDI.

Las expresiones son diferentes para CFDI 3.2, CFDI 3.3, CFDI 4.0, RET 1.0 y RET 2.0. Tienen reglas espec铆ficas de formato y de la informaci贸n que debe contener.

Si no cuentas con la expresi贸n, te recomiendo usar la librer铆a phpcfdi/cfdi-expresiones que puedes instalar usando composer require phpcfdi/cfdi-expresiones.

<?php
use PhpCfdi\CfdiExpresiones\DiscoverExtractor;
use PhpCfdi\SatEstadoCfdi\Consumer;

// lectura del contenido del CFDI
$document = new DOMDocument();
$document->load('archivo-cfdi.xml');

// creaci贸n de la expresi贸n
$expressionExtractor = new DiscoverExtractor();
$expression = $expressionExtractor->extract($document);

// realizar la consulta con la expresi贸n obtenida
/** @var Consumer $consumer */
$cfdiStatus = $consumer->execute($expression);

// usar el estado
if ($cfdiStatus->document->isActive()) {
    echo 'El CFDI se encuentra vigente';
}

Estados (salida)

Despu茅s de consumir el servicio, se responder谩 con un objeto CfdiStatus que agrupa de los cuatro estados.

Los estados son enumeradores, puedes compararlos r谩pidamente usando m茅todos de ayuda is*, por ejemplo: $response->document->isCancelled().

Posibles estados:

  • CodigoEstatus: query: QueryStatus.

    • Found: Si el estado inicia con S - .
    • NotFound: en cualquier otro caso.
  • Estado: document: DocumentStatus.

    • Active: Si el estado report贸 Vigente.
    • Cancelled: Si el estado report贸 Cancelado.
    • NotFound: en cualquier otro caso.
  • EsCancelable: cancellable: CancellableStatus.

    • CancellableByDirectCall: Si el estado report贸 Cancelable sin aceptaci贸n.
    • CancellableByApproval: Si el estado report贸 Cancelable con aceptaci贸n.
    • NotCancellable: en cualquier otro caso.
  • EstatusCancelacion: cancellation: CancellationStatus.

    • CancelledByDirectCall: Si el estado report贸 Cancelado sin aceptaci贸n.
    • CancelledByApproval: Si el estado report贸 Cancelado con aceptaci贸n.
    • CancelledByExpiration: Si el estado report贸 Plazo vencido.
    • Pending: Si el estado report贸 En proceso.
    • Disapproved: Si el estado report贸 Solicitud rechazada.
    • Undefined: en cualquier otro caso.
  • ValidacionEFOS: efos: EfosStatus.

    • Included: Si el estado no report贸 200 o 201.
    • Excluded: Si el estado report贸 200 o 201.

Estados mutuamente excluyentes

Cuando tienes un CFDI en estado Cancelable con aceptaci贸n y mandas a hacer la cancelaci贸n entonces su estado de cancelaci贸n cambiar铆a a En proceso.

El receptor puede aceptar la cancelaci贸n (Cancelado con aceptaci贸n) o rechazarla (Solicitud rechazada).

Si es la primera vez que se hace la solicitud, el receptor tiene 72 horas para aceptarla o rechazarla, si no lo hace entonces autom谩ticamente ser谩 cancelada (Plazo vencido).

Podr铆as volver a enviar la solicitud de cancelaci贸n por segunda vez aun cuando la solicitud fue previamente rechazada.

En ese caso, el receptor puede aceptar o rechazar la cancelaci贸n, pero ya no aplicar谩 un lapso de 72 horas. Por lo anterior entonces podr铆as tener el CFDI en estado de cancelaci贸n en proceso indefinidamente. Incluso, que la cancelaci贸n suceda meses despu茅s de lo esperado.

Compatibilidad

Esta librer铆a se mantendr谩 compatible con al menos la versi贸n con soporte activo de PHP m谩s reciente.

Tambi茅n utilizamos Versionado Sem谩ntico 2.0.0 por lo que puedes usar esta librer铆a sin temor a romper tu aplicaci贸n.

Contribuciones

Las contribuciones con bienvenidas. Por favor lee CONTRIBUTING para m谩s detalles y recuerda revisar el archivo de tareas pendientes TODO y el archivo CHANGELOG.

Copyright and License

The phpcfdi/sat-estado-cfdi library is copyright 漏 PhpCfdi and licensed for use under the MIT License (MIT). Please see LICENSE for more information.