Please feel free to contribute if you are missing features or tags
View Examples
·
Report a bug
·
Request a feature
·
Ask questions
PHP-ZATCA-XML is an unofficial PHP library for generating ZATCA Fatoora e-invoices. It simplifies the process of creating compliant e-invoices, generating QR codes, handling certificates, and submitting invoices to ZATCA’s servers.
Designed for easy integration, this library provides developers with a customizable, robust, and efficient toolkit to automate the ZATCA e-invoicing process in PHP applications.
We are actively working on expanding the capabilities of this library. If you're a developer and would like to contribute, your help is highly appreciated! 💡
- XML to JSON Conversion – Support for converting invoices from XML to JSON format.
- JSON/Array to Invoice – Ability to generate invoices directly from JSON or array structures.
- Simplified Invoice Creation – Streamlined generation of Simplified Invoices, Debit, and Credit compliant with ZATCA simplified.
- Standard Invoice Creation – Streamlined generation Standard Invoices, Debit, and Credit compliant with ZATCA standards.
- Invoice to PDF Conversion – Generate PDF versions of invoices for easy sharing and record-keeping.
💡 Got an idea? Feel free to suggest it or contribute!
Let's build something great together! 🚀
- 🚀 ZATCA-Compliant – Easily generate valid e-invoices for ZATCA regulations
- 📜 Invoice Creation – Generate standard and simplified invoices in XML format
- 🔐 Digital Signing – Sign invoices securely to ensure compliance
- 🏷 QR Code Generation – Automatically generate QR codes for invoices
- 📡 Direct Submission to ZATCA – Send invoices directly to ZATCA’s servers
- ⚡ Lightweight & Fast – Optimized for performance and easy integration in PHP projects
- 🔄 Customizable & Extensible – Easily adapt the library to your needs
- PHP 8.1 or higher
Ensure the following PHP extensions are installed and enabled:
ext-dom
ext-libxml
ext-openssl
ext-hash
ext-mbstring
composer require saleh7/php-zatca-xml
This library simplifies the process of generating ZATCA-compliant e-invoices, handling certificates, signing invoices, and submitting them to ZATCA’s API. Below are the main usage examples:
First, generate a certificate signing request (CSR) and private key:
use Saleh7\Zatca\CertificateBuilder;
use Saleh7\Zatca\CertificateBuilderException;
try {
(new CertificateBuilder())
->setOrganizationIdentifier('312345678901233') // The Organization Identifier must be 15 digits, starting andending with 3
// string $solutionName .. The solution provider name
// string $model .. The model of the unit the stamp is being generated for
// string $serialNumber .. # If you have multiple devices each should have a unique serial number
->setSerialNumber('Saleh', '1n', 'SME00023')
->setCommonName('My Organization') // The common name to be used in the certificate
->setCountryName('SA') // The Country name must be Two chars only
->setOrganizationName('My Company') // The name of your organization
->setOrganizationalUnitName('IT Department') // A subunit in your organizatio
->setAddress('Riyadh 1234 Street') // like Riyadh 1234 Street
->setInvoiceType(1100)// # Four digits, each digit acting as a bool. The order is as follows: Standard Invoice, Simplified, future use, future use
->setProduction(false)// true = Production | false = Testing
->setBusinessCategory('Technology') // Your business category like food, real estate, etc
->generateAndSave('output/certificate.csr', 'output/private.pem');
echo "Certificate and private key saved.\n";
} catch (CertificateBuilderException $e) {
echo "Error: " . $e->getMessage() . "\n";
exit(1);
}
Once the CSR is generated, you need to request a compliance certificate from ZATCA's API.
use Saleh7\Zatca\ZatcaAPI;
use Saleh7\Zatca\Exceptions\ZatcaApiException;
$zatcaClient = new ZatcaAPI('sandbox');
try {
$otp = "123123"; // The OTP received from ZATCA
$certificatePath = __DIR__ . '/output/certificate.csr';
// Load the generated CSR
$csr = $zatcaClient->loadCSRFromFile($certificatePath);
// Request the compliance certificate from ZATCA
$complianceResult = $zatcaClient->requestComplianceCertificate($csr, $otp);
// Display the returned certificate and API secret
echo "Compliance Certificate:\n" . $complianceResult->getCertificate() . "\n";
echo "API Secret: " . $complianceResult->getSecret() . "\n";
echo "Request ID: " . $complianceResult->getRequestId() . "\n";
// Save the certificate details to a JSON file
$outputFile = __DIR__ . '/output/ZATCA_certificate_data.json';
$zatcaClient->saveToJson(
$complianceResult->getCertificate(),
$complianceResult->getSecret(),
$complianceResult->getRequestId(),
$outputFile
);
echo "Certificate data saved to {$outputFile}\n";
} catch (ZatcaApiException $e) {
echo "API Error: " . $e->getMessage();
} catch (\Exception $e) {
echo "Error: " . $e->getMessage();
}
Now that we have the compliance certificate, we can generate a ZATCA-compliant e-invoice in XML format.
use Saleh7\Zatca\{
SignatureInformation, UBLDocumentSignatures, ExtensionContent, UBLExtension, UBLExtensions, Signature,
InvoiceType, AdditionalDocumentReference, TaxScheme, PartyTaxScheme, Address, LegalEntity, Delivery,
Party, PaymentMeans, TaxCategory, AllowanceCharge, TaxSubTotal, TaxTotal, LegalMonetaryTotal,
ClassifiedTaxCategory, Item, Price, InvoiceLine, GeneratorInvoice, Invoice, UnitCode,
OrderReference, BillingReference, Contract, Attachment
};
// --- Invoice Type ---
$invoiceType = (new InvoiceType())
->setInvoice('standard') // 'standard' or 'simplified'
->setInvoiceType('invoice') // 'invoice', 'debit', or 'credit', 'prepayment'
->setIsThirdParty(false) // Third-party transaction
->setIsNominal(false) // Nominal transaction
->setIsExportInvoice(false) // Export invoice
->setIsSummary(false) // Summary invoice
->setIsSelfBilled(false); // Self-billed invoice
// --- Supplier & Customer Information ---
$taxScheme = (new TaxScheme())->setId("VAT");
$partyTaxSchemeSupplier = (new PartyTaxScheme())->setTaxScheme($taxScheme)->setCompanyId('311111111101113');
$partyTaxSchemeCustomer = (new PartyTaxScheme())->setTaxScheme($taxScheme);
$address = (new Address())
->setStreetName('Prince Sultan Street')
->setBuildingNumber("2322")
->setPlotIdentification("2223")
->setCitySubdivisionName('Riyadh')
->setCityName('Riyadh')
->setPostalZone('23333')
->setCountry('SA');
// --- Delivery ---
$delivery = (new Delivery())->setActualDeliveryDate(date('Y-m-d'));
// --- Additional Document References ---
$additionalDocs = [];
$additionalDocs[] = (new AdditionalDocumentReference())
->setId('ICV')
->setUUID("23"); //Invoice counter value
$additionalDocs[] = (new AdditionalDocumentReference())
->setId('PIH')
->setAttachment($attachment); // Previous Invoice Hash
// ->setPreviousInvoiceHash('NWZlY2ViNjZmZmM4NmYzOGQ5NTI3ODZjNmQ2OTZjNzljMmRiYzIzOWRkNGU5MWI0NjcyOWQ3M2EyN2ZiNTdlOQ=='); // Previous Invoice Hash
$additionalDocs[] = (new AdditionalDocumentReference())
->setId('QR');
$legalEntity = (new LegalEntity())->setRegistrationName('Acme Widget’s LTD');
$supplierCompany = (new Party())
->setPartyIdentification("311111111111113")
->setPartyIdentificationId("CRN")
->setLegalEntity($legalEntity)
->setPartyTaxScheme($partyTaxSchemeSupplier)
->setPostalAddress($address);
$supplierCustomer = (new Party())
->setPartyIdentification("311111111111113")
->setPartyIdentificationId("NAT")
->setLegalEntity($legalEntity)
->setPartyTaxScheme($partyTaxSchemeCustomer)
->setPostalAddress($address);
// --- Invoice Items & Pricing ---
$classifiedTax = (new ClassifiedTaxCategory())->setPercent(15)->setTaxScheme($taxScheme);
$productItem = (new Item())->setName('Pencil')->setClassifiedTaxCategory($classifiedTax);
$price = (new Price())->setUnitCode(UnitCode::UNIT)->setPriceAmount(2);
$lineTaxTotal = (new TaxTotal())->setTaxAmount(0.60)->setRoundingAmount(4.60);
$invoiceLine = (new InvoiceLine())
->setUnitCode("PCE")
->setId(1)
->setItem($productItem)
->setLineExtensionAmount(4)
->setPrice($price)
->setTaxTotal($lineTaxTotal)
->setInvoicedQuantity(2);
$invoiceLines = [$invoiceLine];
// --- Tax Totals ---
$taxCategory = (new TaxCategory)
->setPercent(15)
->setTaxScheme($taxScheme);
$taxSubTotal = (new TaxSubTotal)
->setTaxableAmount(4)
->setTaxAmount(0.6)
->setTaxCategory($taxCategory);
$taxTotal = (new TaxTotal)
->addTaxSubTotal($taxSubTotal)
->setTaxAmount(0.6);
// --- Legal Monetary Total ---
$legalMonetaryTotal = (new LegalMonetaryTotal())
->setLineExtensionAmount(4)
->setTaxExclusiveAmount(4)
->setTaxInclusiveAmount(4.60)
->setPrepaidAmount(0)
->setPayableAmount(4.60)
->setAllowanceTotalAmount(0);
// --- Build the Invoice ---
$invoice = (new Invoice())
->setUUID('3cf5ee18-ee25-44ea-a444-2c37ba7f28be')
->setId('SME00023')
->setIssueDate(new DateTime())
->setIssueTime(new DateTime())
->setInvoiceType($invoiceType)
->setInvoiceCurrencyCode('SAR')
->setTaxCurrencyCode('SAR')
->setDelivery($delivery)
->setAccountingSupplierParty($supplierCompany)
->setAccountingCustomerParty($supplierCustomer)
->setAdditionalDocumentReferences($additionalDocs)
->setTaxTotal($taxTotal)
->setLegalMonetaryTotal($legalMonetaryTotal)
->setInvoiceLines($invoiceLines);
// ......
// --- Generate XML ---
try {
$generatorXml = GeneratorInvoice::invoice($invoice);
$outputXML = $generatorXml->getXML();
// Save the XML to a file
$filePath = __DIR__ . '/output/unsigned_invoice.xml';
file_put_contents($filePath, $outputXML);
echo "Invoice XML saved to: " . $filePath . "\n";
} catch (\Exception $e) {
echo "An error occurred: " . $e->getMessage() . "\n";
exit(1);
}
Before submitting the invoice to ZATCA, we need to digitally sign it using the compliance certificate obtained earlier.
use Saleh7\Zatca\Helpers\Certificate;
use Saleh7\Zatca\InvoiceSigner;
// Load the unsigned invoice XML
$xmlInvoice = file_get_contents(__DIR__ . '/output/unsigned_invoice.xml');
// Load the compliance certificate data from the JSON file
$json_certificate = file_get_contents(__DIR__ . '/output/ZATCA_certificate_data.json');
// Decode the JSON data
$json_data = json_decode($json_certificate, true, 512, JSON_THROW_ON_ERROR);
// Extract certificate details
$certificate = $json_data[0]['certificate'];
$secret = $json_data[0]['secret'];
// Load the private key
$privateKey = file_get_contents(__DIR__ . '/output/private.pem');
$cleanPrivateKey = trim(str_replace(["-----BEGIN PRIVATE KEY-----", "-----END PRIVATE KEY-----"], "", $privateKey));
// Create a Certificate instance
$certificate = new Certificate(
$certificate,
$cleanPrivateKey,
$secret
);
// Sign the invoice
$signedInvoice = InvoiceSigner::signInvoice($xmlInvoice, $certificate);
// Save the signed invoice
InvoiceSigner::signInvoice($xmlInvoice, $certificate)->saveXMLFile('/output/signed_invoice.xml');
Once the invoice is digitally signed, it can be submitted to ZATCA’s API for compliance validation and clearance.
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
Please make sure to update tests as appropriate.
Thank you all for your continuous support and contributions!
This project has also benefited from some code snippets and ideas from the SallaApp/ZATCA repository. We appreciate their contribution to the community.
This project is licensed under the MIT License.