sevaske / zatca-api
Installs: 2 503
Dependents: 1
Suggesters: 0
Security: 0
Stars: 4
Watchers: 2
Forks: 3
Open Issues: 0
pkg:composer/sevaske/zatca-api
Requires
- php: ^7.4||^8.0
- ext-json: *
- psr/http-client: ^1.0
- psr/http-message: ^1.0||^2.0
- sevaske/support: ^1.0
Requires (Dev)
- guzzlehttp/guzzle: ^7.9
- laravel/pint: ^1.0
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^9.6
README
ZATCA API PHP Client
This is a simple PHP library to work with the ZATCA API. You can send invoice data and manage certificates easily.
⚠️ Note: This is an unofficial library and not maintained by ZATCA. I do not provide personal support or consulting.
If you’re looking for a library to generate XML invoices, you can use this one: https://github.com/sevaske/php-zatca-xml
Features
- Full coverage of ZATCA API endpoints (reporting, clearance, compliance)
- Authentication via certificate and secret or auth token
- Supports middleware for request/response processing
- Typed response objects for easy validation and error handling
- Supports multiple environments: sandbox, simulation, production
- Follows PSR standards (PSR-4, PSR-7, PSR-17, PSR-18)
- Works with any PSR-18 compatible HTTP client (e.g., Guzzle)
Environments
| Environment | Purpose | Certificates |
|---|---|---|
| sandbox | CSR + basic testing | Sandbox certificate |
| simulation | Compliance invoices (6 required) | Compliance cert |
| production | Real invoices | Production cert |
Invoice types
-
Reporting (B2P)
Used for invoices issued to consumers. -
Clearance (B2B)
Used for invoices issued to VAT-registered businesses.
Authentication flow
- Generate CSR (outside this library)
- Request compliance certificate
- Create
ZatcaAuthfrom certificate + secret - Submit simulation invoices
- Request production certificate
- Switch client to
productionenvironment
Immutability
Methods like withEnvironment() and withMiddleware() return a new client instance.
The original client is not modified.
Installation
composer require sevaske/zatca-api:^2.0
Usage
Client Initialization
Create HTTP client and factories for PSR-17 / PSR-18. For example, GuzzleHttp
use GuzzleHttp\Client; use GuzzleHttp\Psr7\HttpFactory; use Sevaske\ZatcaApi\ZatcaClient; $httpClient = new Client(); $factory = new HttpFactory(); // Initialize ZatcaClient with sandbox environment $client = new ZatcaClient( $httpClient, $factory, // RequestFactoryInterface $factory, // StreamFactoryInterface 'sandbox' // environment: sandbox | simulation | production );
Compliance Certificate Request
use Sevaske\ZatcaApi\Exceptions\ZatcaRequestException; use Sevaske\ZatcaApi\Exceptions\ZatcaResponseException; /** * @var Sevaske\ZatcaApi\ZatcaClient $client */ try { $certificateResponse = $client->complianceCertificate('your .csr file content', '112233'); } catch (ZatcaRequestException|ZatcaResponseException $e) { // handle } $credentials = [ 'requestId' => $certificateResponse->requestId(), 'certificate' => $certificateResponse->certificate(), 'secret' => $certificateResponse->secret(), ]; print_r($credentials); file_put_contents('output/credentials.json', json_encode($credentials, JSON_PRETTY_PRINT));
Authorized requests
Create AuthToken from compliance certificate to make authorized requests.
use Sevaske\ZatcaApi\ZatcaAuth; /** * @var Sevaske\ZatcaApi\Responses\CertificateResponse $certificateResponse * @var Sevaske\ZatcaApi\ZatcaClient $client */ $authToken = new ZatcaAuth($certificateResponse->certificate(), $certificateResponse->secret()); $client->setAuthToken($authToken);
Submitting Invoices
Once you have a valid compliance certificate and auth token, you can submit invoices in the simulation environment.
Submitting 6 documents is required to switch to production mode.
use Sevaske\ZatcaApi\Exceptions\ZatcaRequestException; use Sevaske\ZatcaApi\Exceptions\ZatcaResponseException; /** * @var Sevaske\ZatcaApi\ZatcaClient $client */ try { // B2P $client->reportingInvoice('b2p invoice xml', 'hash', 'uuid'); $client->reportingInvoice('b2p debit note xml', 'hash', 'uuid'); $client->reportingInvoice('b2p credit note xml', 'hash', 'uuid'); // B2B $client->clearanceInvoice('b2b invoice xml', 'hash', 'uuid'); $client->clearanceInvoice('b2b debit note xml', 'hash', 'uuid'); $client->clearanceInvoice('b2b credit note xml', 'hash', 'uuid'); } catch (ZatcaRequestException|ZatcaResponseException $e) { // handle }
Production Onboarding
After submitting the required simulation invoices, you can request a production certificate.
This certificate allows you to submit real invoices in the production environment.
use Sevaske\ZatcaApi\Exceptions\ZatcaRequestException; use Sevaske\ZatcaApi\Exceptions\ZatcaResponseException; /** * @var \Sevaske\ZatcaApi\ZatcaClient $client */ try { $productionCertificateResponse = $client->productionCertificate($certificateResponse->requestId()); $credentials = [ 'requestId' => $productionCertificateResponse->requestId(), 'certificate' => $productionCertificateResponse->certificate(), 'secret' => $productionCertificateResponse->secret(), ]; // display print_r($credentials); // save file_put_contents('output/production-credentials.json', json_encode($credentials, JSON_PRETTY_PRINT)); } catch (ZatcaRequestException|ZatcaResponseException $e) { // handle }
Submitting Production Invoices
Once the client is configured with the production certificate and environment, you can submit real invoices to ZATCA.
use Sevaske\ZatcaApi\ZatcaAuth; use Sevaske\ZatcaApi\Exceptions\ZatcaRequestException; use Sevaske\ZatcaApi\Exceptions\ZatcaResponseException; /** * @var Sevaske\ZatcaApi\ZatcaClient $client * @var Sevaske\ZatcaApi\Responses\ProductionCertificateResponse $productionCertificateResponse */ $productionClient = $client->withEnvironment('production'); $productionAuth = new ZatcaAuth($productionCertificateResponse->certificate(), $productionCertificateResponse->secret()); $productionClient->setAuthToken($productionAuth); try { // submitting production invoices $responseReporting = $productionClient->reportingInvoice('my real B2P invoice xml', 'hash', 'uuid'); $responseClearance = $productionClient->clearanceInvoice('my real B2B invoice xml', 'hash', 'uuid'); } catch (ZatcaRequestException|ZatcaResponseException $e) { // handle } print_r($responseReporting->toArray()); print_r($responseClearance->toArray());
Middleware
Middleware in ZatcaClient allows you to inspect, modify, or wrap HTTP requests and responses. It works as a pipeline, meaning that multiple middleware can be chained together, each receiving the request and a $next callable that continues to the next middleware and ultimately to the HTTP client.
ZatcaClient provides four ways to manage middleware:
withMiddleware($middleware)– returns a new cloned instance with the provided middleware. Existing middleware in the original client is replaced in the clone.setMiddleware($middleware)– mutates the current instance, replacing its middleware with the given ones.attachMiddleware($middleware)– mutates the current instance, adding the given middleware to the end of the existing middleware stack.withoutMiddleware()- returns a new cloned instance with no middleware attached.
All middleware must implement the MiddlewareInterface:
use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; interface MiddlewareInterface { /** * @param RequestInterface $request The incoming request * @param callable $next Callable to forward the request to the next middleware or the HTTP client * @return ResponseInterface */ public function handle(RequestInterface $request, callable $next): ResponseInterface; }
Example
For example, implementation of "logging" requests and responses:
use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; use Sevaske\ZatcaApi\Interfaces\MiddlewareInterface; // Attach a custom middleware to inspect requests and responses $client = $client->withMiddleware(new class implements MiddlewareInterface { public function handle(RequestInterface $request, callable $next): ResponseInterface { // request $this->info('URL: '); $this->info((string) $request->getUri()); $this->info('Body: '); $this->info($this->safeStreamContents($request->getBody())); /** * @var $response \Psr\Http\Message\ResponseInterface */ $response = $next($request); // response $this->info('Response:'); $this->info($this->safeStreamContents($response->getBody())); return $response; } private function safeStreamContents(\Psr\Http\Message\StreamInterface $stream): string { if (! $stream->isSeekable()) { return '[unseekable stream]'; } // Save original cursor position $pos = $stream->tell(); // Read from beginning $stream->rewind(); $content = $stream->getContents(); // Restore original cursor $stream->seek($pos); return $content; } private function info(string $text): void { echo "\n\r".$text; } });
Exception handling
The library throws the following exceptions which you can catch and handle:
ZatcaException— general exception classZatcaRequestException— errors during the HTTP requestZatcaResponseException— errors processing the API response