phpcfdi / sat-estado-cfdi
查询 SAT 官方 web 服务中的 CFDI 状态
Requires
- php: >=8.2
Requires (Dev)
- ext-curl: *
- ext-dom: *
- ext-soap: *
- guzzlehttp/guzzle: ^7.8
- guzzlehttp/psr7: ^2.6
- phpcfdi/cfdi-expresiones: ^3.0
- phpunit/phpunit: ^11.0.3
- psr/http-client: ^1.0|^2.0
- psr/http-factory: ^1.0|^2.0
- psr/http-message: ^1.0|^2.0
Suggests
- ext-dom: Extensión para poder usar el cliente HTTP PSR
- ext-soap: Extensión para poder usar el cliente SOAP
- phpcfdi/cfdi-expresiones: Genera expresiones de CFDI 4.0, CFDI 3.3, CFDI 3.2, RET 1.0 y RET 2.0
- psr/http-client: Requerimiento del cliente HTTP PSR
- psr/http-factory: Requerimiento del cliente HTTP PSR
- psr/http-message: Requerimiento del cliente HTTP PSR
README
查询 SAT 官方 web 服务中的 CFDI 状态
🇺🇸 此库包含用于消费 SAT 的 CFDI 查询服务 的辅助函数。由于这是目标受众的自然语言,因此此项目的文档是西班牙语。
🇲🇽 此库用于消费 SAT 的 CFDI 查询服务。项目的文档是西班牙语,因为这是将要使用此库的用户使用的语言。
此库仅允许验证 常规 CFDI 的状态,而不是 滞纳金 CFDI 和支付信息 的状态。对于后者,请使用 phpcfdi/sat-estado-retenciones 库。
SAT CFDI 查询服务:
- 生产服务:https://consultaqr.facturaelectronica.sat.gob.mx/ConsultaCFDIService.svc
- 测试服务:https://pruebacfdiconsultaqr.cloudapp.net/ConsultaCFDIService.svc
- SAT:https://www.sat.gob.mx/consultas/20585/conoce-los-servicios-especializados-de-validacion
- CFDI 查询服务文档版本 1.4(2022 年 11 月):https://www.sat.gob.mx/cs/Satellite?blobcol=urldata&blobkey=id&blobtable=MungoBlobs&blobwhere=1461175223997&ssbinary=true
服务近期变更:
- 由于取消流程的改变,2018 年增加了新的状态。
- 由于一个无法解释的原因,从 2018 年到 2020 年,WSDL 不可用。此库使用一种策略,不依赖于 WSDL 来消费服务。
- 2020 年底增加了响应字段
VerificacionEFOS
。
安装
使用 composer
composer require phpcfdi/sat-estado-cfdi
基本使用示例
基本步骤如下
- 拥有一个实现
ConsumerClientInterface
的客户端。 - 创建服务消费者
Consumer
- 使用定义的表达式进行请求。
- 使用结果
<?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'; }
消费客户端 ConsumerClientInterface
此库包括两个不同的消费客户端:SoapConsumerClient
和 HttpConsumerClient
。
此外,您可以自己实现消费客户端,实现 ConsumerClientInterface
接口。
SOAP 客户端 SoapConsumerClient
SoapConsumerClient
客户端允许使用 SOAP 策略进行消费。
要求
ext-soap
:PHP SOAP 扩展。
示例
<?php use PhpCfdi\SatEstadoCfdi\Clients\Soap\SoapConsumerClient; use PhpCfdi\SatEstadoCfdi\Consumer; function createConsumerUsingSoap(): Consumer { $client = new SoapConsumerClient(); return new Consumer($client); }
HTTP PSR 客户端 HttpConsumerClient
HttpConsumerClient
客户端允许使用基于 PSR 的 HTTP 策略进行消费。
使用标准
- PSR-18:HTTP Client:HTTP 客户端接口(进行 POST 调用的客户端)。https://www.php-fig.org/psr/psr-18/
- PSR-17:HTTP Factories:HTTP 请求和响应工厂接口(用于 PSR-7)。https://www.php-fig.org/psr/psr-17/
Guzzle 库 guzzlehttp/guzzle
和 guzzlehttp/psr7
提供了必需的标准。
你可以在 Packagist 上查看你喜欢的库
- PSR-18: https://packagist.org.cn/providers/psr/http-client-implementation
- PSR-17: https://packagist.org.cn/providers/psr/http-factory-implementation
要求
ext-dom
: PHP的DOM扩展。psr/http-client: ^1.0
: PSR-18标准(HTTP客户端)。psr/http-factory: ^1.0
: PSR-17标准(HTTP消息工厂)。- 一些实现了PSR-18和PSR-17的库,例如
- Guzzle:
guzzlehttp/guzzle
和guzzlehttp/psr7
。 - Symfony:
symfony/http-client
和nyholm/psr7
或laminas/laminas-diactoros
。
- Guzzle:
示例
<?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); }
以下是一个使用 symfony/http-client
和 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); }
对于Laravel,你可以使用一些额外的包,例如 wimski/laravel-psr-http
,它利用了自身的框架和 php-http/discovery
,使得创建对象更加方便,无论是直接通过容器创建,还是作为依赖项注入。
<?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); }
我还建议你创建自己的 Service Provider 或配置 Service Container,并且只要求类 Consumer
作为任何其他依赖项,并允许它被注入。
表达式(输入)
消费者需要一个表达式来查询。这个表达式是打印出来的CFDI表示中的二维码中的文本。
表达式对于CFDI 3.2、CFDI 3.3、CFDI 4.0、RET 1.0和RET 2.0是不同的。它们有特定的格式规则和包含的信息规则。
如果你没有表达式,我建议你使用库 phpcfdi/cfdi-expresiones
,你可以使用 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'; }
状态(输出)
在消费服务后,将返回一个包含四个状态的 CfdiStatus
对象。
状态是枚举,你可以使用辅助方法 is*
快速比较,例如:$response->document->isCancelled()
。
可能的状态
-
CodigoEstatus
:query: QueryStatus
。Found
: 如果状态以S -
开头。NotFound
: 在其他情况下。
-
Estado
:document: DocumentStatus
。Active
: 如果状态报告了Vigente
。Cancelled
: 如果状态报告了Cancelado
。NotFound
: 在其他情况下。
-
EsCancelable
:cancellable: CancellableStatus
。CancellableByDirectCall
: 如果状态报告了Cancelable sin aceptación
。CancellableByApproval
: 如果状态报告了Cancelable con aceptación
。NotCancellable
: 在其他情况下。
-
EstatusCancelacion
:cancellation: CancellationStatus
。CancelledByDirectCall
: 如果状态报告了Cancelado sin aceptación
。CancelledByApproval
: 如果状态报告了Cancelado con aceptación
。CancelledByExpiration
: 如果状态报告了Plazo vencido
。Pending
: 如果状态报告了En proceso
。Disapproved
: 如果状态报告了Solicitud rechazada
。Undefined
: 在其他情况下。
-
ValidacionEFOS
:efos: EfosStatus
。Included
: 如果状态没有报告200
或201
。Excluded
: 如果状态报告了200
或201
。
相互排斥的状态
当你有一个状态为 Cancelable with acceptance 的CFDI并提交取消请求时,其取消状态将变为 In process。
接收者可以接受取消请求(Cancelled with acceptance)或拒绝取消请求(Disapproved)。
如果是第一次提交申请,接收方有72小时的时间来接受或拒绝,如果未在规定时间内处理,则申请将自动取消(逾期)。
即使申请之前已被拒绝,你也可以再次提交取消申请的请求。
在这种情况下,接收方可以接受或拒绝取消申请,但不再适用72小时的期限。因此,CFDI的状态可能会无限期地保持在取消中,甚至可能是在预期时间之后的几个月。
兼容性
此库将保持与最新的至少具有PHP活跃支持的版本的兼容性。
我们还使用语义化版本2.0.0,因此你可以放心地使用此库而不用担心破坏你的应用程序。
贡献
欢迎贡献。请阅读CONTRIBUTING以获取更多详细信息,并请记住查看TODO文件和变更日志文件。
版权和许可
phpcfdi/sat-estado-cfdi
库版权归PhpCfdi所有,并许可在MIT许可(MIT)下使用。有关更多信息,请参阅LICENSE。