1. Go to this page and download the library: Download khaledhajsalem/zatca-php library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
khaledhajsalem / zatca-php example snippets
use KhaledHajSalem\Zatca\Support\CertificateBuilder;
$builder = new CertificateBuilder();
$builder->setOrganizationIdentifier('300000000000003') // 15 digits, starts & ends with 3
->setSerialNumber('MySolution', 'Model1', 'SN001')
->setCommonName('Your Company Name')
->setCountryName('SA')
->setOrganizationName('Your Company Name')
->setOrganizationalUnitName('IT Department')
->setAddress('123 Main Street, Riyadh, Saudi Arabia')
->setInvoiceType(1100) // 4 digits: [Standard][Simplified][future][future] — 1100 = both standard & simplified
->setProduction(false) // false = sandbox/simulation, true = production
->setBusinessCategory('Legal Entity');
$builder->generateAndSave('storage/certificate.csr', 'storage/private.pem');
use KhaledHajSalem\Zatca\Services\ZatcaAPIService;
// Initialize API service
$apiService = new ZatcaAPIService('sandbox'); // 'sandbox', 'simulation', or 'production'
// ── Step 2a: Request Compliance Certificate ──
$csr = file_get_contents('storage/certificate.csr');
$otp = '123456'; // OTP from ZATCA Fatoora Portal
$complianceResult = $apiService->requestComplianceCertificate($csr, $otp);
// Save the compliance certificate (keep it separate from the production one)
file_put_contents('storage/compliance_certificate.pem', $complianceResult->getCertificate());
$complianceSecret = $complianceResult->getSecret(); // Compliance API secret (keep it for step 2c)
$complianceRequestId = $complianceResult->getRequestId(); // Need this for production certificate
// ── Step 2b: Run Compliance Tests ──
// Submit test invoices with $apiService->validateInvoiceCompliance(...) using the
// compliance certificate and secret. In simulation/production, every invoice type
// declared in the CSR (e.g. 1100 → standard + simplified invoice, credit note and
// debit note) must pass before the production certificate is issued.
// Once all tests pass...
// ── Step 2c: Request Production Certificate ──
$complianceCert = file_get_contents('storage/compliance_certificate.pem');
$productionResult = $apiService->requestProductionCertificate(
$complianceCert, // Compliance certificate
$complianceSecret, // Compliance secret
$complianceRequestId // Request ID from step 2a
);
// Save the production certificate — use this for all real invoices
file_put_contents('storage/certificate.pem', $productionResult->getCertificate());
file_put_contents('storage/secret.txt', $productionResult->getSecret()); // Your API secret for invoices
use KhaledHajSalem\Zatca\ZatcaManager;
use KhaledHajSalem\Zatca\Data\InvoiceData;
use KhaledHajSalem\Zatca\Data\SellerData;
use KhaledHajSalem\Zatca\Data\BuyerData;
use KhaledHajSalem\Zatca\Data\InvoiceLineData;
// ── 1. Initialize ZatcaManager ──
$zatcaManager = new ZatcaManager([
'environment' => 'sandbox', // 'sandbox', 'simulation', or 'production'
'certificate_path' => __DIR__ . '/storage/certificate.pem', // Path to your certificate file
'private_key_path' => __DIR__ . '/storage/private.pem', // Path to your private key file
'secret' => trim(file_get_contents(__DIR__ . '/storage/secret.txt')), // API secret from step 2c
'validate' => true, // Check business rules before submitting (optional)
]);
// ── 2. Create Invoice Data ──
$invoiceData = new InvoiceData();
$invoiceData
->setInvoiceNumber('INV-001') // Your invoice number
->simplified() // B2C invoice (reporting) — or ->standard() for B2B (clearance)
->taxInvoice() // Tax Invoice (388) — or ->creditNote(), ->debitNote(), ->prepaymentInvoice()
->setIssueDate('2025-01-15') // Issue date in Y-m-d format
->setIssueTime('10:30:00') // Issue time in H:i:s format
->setDueDate('2025-02-15') // Due date in Y-m-d format
->setCurrencyCode('SAR') // Currency code (ISO 4217)
->setDocumentCurrencyCode('SAR') // Document currency (usually same as above)
->setTaxCurrencyCode('SAR') // Tax currency (usually same as above)
->setInvoiceCounter('1') // ICV: sequential counter, must increment per invoice
->setPreviousInvoiceHash(InvoiceData::FIRST_INVOICE_HASH); // PIH: first invoice, then use hash from previous invoice
// ── 3. Create Seller Data ──
$seller = new SellerData();
$seller->setRegistrationName('Your Company Name') // Company legal name
->setVatNumber('399999999900003') // VAT number (15 digits)
->setPartyIdentification('1010203020') // Identification value (e.g., CRN number)
->setPartyIdentificationId('CRN') // Identification type — see "Party Identification Schemes" above
->setStreetName('Main Street') // Street name
->setBuildingNumber('1234') // Building number
->setCityName('Riyadh') // City
->setPostalZone('12345') // Postal/ZIP code
->setCountryCode('SA') // 2-letter country code
->setPlotIdentification('PLOT-001') // Plot identification (optional)
->setCitySubdivisionName('District 1'); // District name (
$invoiceData = new InvoiceData();
$invoiceData
->setInvoiceNumber('INV-002')
->standard() // ← This is the only change (B2B, Code('SAR')
->setInvoiceCounter('2') // ICV: second invoice
->setPreviousInvoiceHash($previousInvoiceHash); // PIH: hash from INV-001
// ... seller, buyer, lines same as above ...
$result = $zatcaManager->processInvoice($invoiceData);
// For standard invoices, ZATCA returns a cleared XML:
if ($result['response']['clearanceStatus'] === 'CLEARED') {
$clearedXml = $result['xml']; // Use this XML (not your original)
}
use KhaledHajSalem\Zatca\Support\TaxCategory;
// Zero-rated export
$line->setTaxCategory(TaxCategory::ZERO_RATED, 'VATEX-SA-32');
// Exempt financial service
$line->setTaxCategory(TaxCategory::EXEMPT, 'VATEX-SA-29');
// Out of scope (reason text
// Invoice 1 (first invoice — no previous)
$invoice1->setPreviousInvoiceHash(InvoiceData::FIRST_INVOICE_HASH); // base64(hex SHA-256 of '0'), BR-KSA-26
$result1 = $zatcaManager->processInvoice($invoice1);
$hash1 = $result1['invoice_hash']; // Save this!
// Invoice 2 (references invoice 1)
$invoice2->setPreviousInvoiceHash($hash1);
$result2 = $zatcaManager->processInvoice($invoice2);
$hash2 = $result2['invoice_hash']; // Save this!
// Invoice 3 (references invoice 2)
$invoice3->setPreviousInvoiceHash($hash2);
// ... and so on
$issues = $invoiceData->validate();
foreach ($issues as $issue) {
echo "{$issue['severity']} [{$issue['rule']}] {$issue['field']}: {$issue['message']}\n";
}
// error [BR-KSA-40] seller.vat_number: Seller VAT number must be 15 digits, starting and ending with 3.
// error [BR-KSA-81] buyer.party_identification: Buyer ID is
use KhaledHajSalem\Zatca\Exceptions\ZatcaValidationException;
try {
$result = $zatcaManager->processInvoice($invoiceData);
} catch (ZatcaValidationException $e) {
$errors = $e->getIssues(); // same format as validate()
}