Appearance
ZATCA Package
Section: 16 — Packages
Last Updated: 2026-05-30
Scope:Microtec.Zatca— Saudi E-Invoicing Phase 1 and Phase 2
Overview
The Microtec.Zatca package implements Saudi Arabia's Zakat, Tax and Customs Authority (ZATCA) e-invoicing mandate. It handles both phases of compliance:
| Phase | Requirement | What the package does |
|---|---|---|
| Phase 1 | QR code on every invoice | TLV-encoded QR generation |
| Phase 2 | Cryptographic signing + ZATCA clearance/reporting | CSID onboarding, UBL 2.1 XML, clearance API |
Source Location
Platforms/Src/InfrastructureServices/Zatca/ — the package is also consumed by the Integration service as an external NuGet dependency.
Package Structure
Microtec.Zatca.Infrastructure ← Core implementation
Microtec.Zatca.Integration ← Integration events and public API clientMicrotec.Zatca.Infrastructure
| Category | What's Included |
|---|---|
| Main service | IZatcaService, ZatcaService |
| Phase 1 | QrCodeGenerator — TLV encoding per ZATCA spec |
| Phase 2 | ZatcaXmlBuilder — UBL 2.1 XML invoice |
| Phase 2 | ZatcaSigningService — X.509 certificate + digital signature |
| Onboarding | ZatcaComplianceClient — CSID onboarding API |
| Clearance | ZatcaClearanceClient — invoice clearance API |
| Reporting | ZatcaReportingClient — simplified invoice reporting API |
| Models | ZatcaInvoice, ZatcaLineItem, ZatcaConfig, ClearanceResult |
Microtec.Zatca.Integration
| What's Included |
|---|
InvoiceSubmittedToZatcaEvent — raised after invoice is submitted |
ZatcaClearedEvent — raised when ZATCA clears the invoice |
ZatcaRejectedEvent — raised when ZATCA rejects with validation errors |
IZatcaPublicApi — typed HTTP client for cross-service calls |
Phase 1 — QR Code Generation
Phase 1 requires a TLV-encoded QR code printed on every invoice. The QR encodes seller name, VAT number, timestamp, invoice total, and VAT amount.
Configuration
json
// appsettings.json
{
"Zatca": {
"Phase": 1,
"SellerName": "Microtec Technology",
"VatRegistrationNumber": "310XXXXXXXXX0003",
"Environment": "Production"
}
}Usage
csharp
// Program.cs
builder.Services.AddZatcaPhase1(builder.Configuration);
// In a command handler
public class AddInvoiceCommandHandler(IZatcaService zatca, IClockService clock)
{
public async Task<int> Handle(AddInvoiceCommand request, CancellationToken ct)
{
var qrCode = zatca.GenerateQrCode(new ZatcaInvoice
{
SellerName = "Microtec Technology",
VatNumber = "310XXXXXXXXX0003",
Timestamp = clock.Now,
InvoiceTotal = request.Total,
VatAmount = request.VatAmount
});
// qrCode is a Base64 string — embed directly in the PDF
var invoice = Invoice.Create(request, qrCode);
// ...
}
}TLV Field Reference
| Tag | Field | Max Length |
|---|---|---|
| 1 | Seller name | 255 |
| 2 | VAT registration number | 15 |
| 3 | Invoice timestamp (ISO 8601) | 20 |
| 4 | Invoice total (with VAT) | 20 |
| 5 | VAT amount | 20 |
Phase 2 — CSID Onboarding and Invoice Clearance
Phase 2 adds cryptographic signing and real-time clearance. Every standard tax invoice (B2B, above threshold) must be cleared by ZATCA before it is legally valid. Simplified invoices (B2C) go through the reporting API.
Prerequisites
Phase 2 requires:
- A CSID (Cryptographic Stamp Identifier) — issued by ZATCA during onboarding
- A private key + X.509 certificate — generated during CSR submission
- Access to ZATCA's compliance or production API
Onboarding is a one-time process per device/environment
The CSID and certificate pair are bound to the onboarding environment. Production CSIDs cannot be used against the sandbox API and vice versa. Each Azure environment (stage, preprod, production) needs its own onboarding.
Configuration
json
// appsettings.json (values in Key Vault in live environments)
{
"Zatca": {
"Phase": 2,
"SellerName": "Microtec Technology",
"VatRegistrationNumber": "310XXXXXXXXX0003",
"Environment": "Sandbox",
"Csid": "keyvaultref:zatca--csid",
"PrivateKey": "keyvaultref:zatca--private-key",
"Certificate": "keyvaultref:zatca--certificate",
"ApiBaseUrl": "https://gw-fatoora.zatca.gov.sa/e-invoicing/developer-portal"
}
}Environment value | API endpoint |
|---|---|
Sandbox | https://gw-fatoora.zatca.gov.sa/e-invoicing/developer-portal |
Simulation | https://gw-fatoora.zatca.gov.sa/e-invoicing/simulation |
Production | https://gw-fatoora.zatca.gov.sa/e-invoicing/core |
Service Registration
csharp
// Program.cs
builder.Services.AddZatcaPhase2(builder.Configuration);CSID Onboarding Flow
csharp
// Run once per environment during setup (not per invoice)
public class ZatcaOnboardingService(IZatcaService zatca)
{
public async Task OnboardAsync()
{
// 1. Generate CSR
var (privateKey, csr) = zatca.GenerateCsr(new CsrRequest
{
CommonName = "Microtec Technology",
SerialNumber = "1-Microtec|2-Integration|3-001",
VatNumber = "310XXXXXXXXX0003",
CountryCode = "SA",
InvoiceType = "1100" // Standard + Simplified
});
// 2. Submit CSR to compliance API → receive CSID
var onboardResult = await zatca.OnboardAsync(csr);
// 3. Store CSID, privateKey, and certificate in Key Vault
// These are then read back via appsettings keyvaultref entries
Console.WriteLine($"CSID: {onboardResult.BinarySecurityToken}");
Console.WriteLine($"Secret: {onboardResult.Secret}");
}
}Standard Invoice Clearance (B2B)
csharp
public class ClearInvoiceCommandHandler(IZatcaService zatca)
{
public async Task Handle(ClearInvoiceCommand request, CancellationToken ct)
{
var zatcaInvoice = BuildZatcaInvoice(request);
// Signs and submits the invoice for clearance
var result = await zatca.ClearInvoiceAsync(zatcaInvoice, ct);
if (result.IsCleared)
{
// result.ClearedXml contains the ZATCA-stamped XML
// Store result.InvoiceHash for later reference
}
else
{
throw new DomainException($"ZATCA rejected invoice: {result.ValidationResults}");
}
}
}Simplified Invoice Reporting (B2C)
csharp
var result = await zatca.ReportInvoiceAsync(zatcaInvoice, ct);
// No clearance required — ZATCA acknowledges asynchronouslySandbox vs Production
| Concern | Sandbox | Production |
|---|---|---|
| CSID validity | Test CSID from ZATCA portal | Issued via compliance onboarding |
| Invoice numbers | Any format | Must be sequential, no gaps |
| Rejection consequence | No legal impact | Invoice is invalid until cleared |
| API rate limits | Relaxed | Enforced (429 on burst) |
| Data retention | ZATCA may purge | Permanent |
Never mix environments
A production CSID submitted to the sandbox API returns 400 Bad Request. A sandbox CSID on the production API is rejected. Always confirm Zatca:Environment in appsettings matches the CSID origin environment before deploying.
Key Vault Secret Names
| Secret Key Vault Name | Maps to Config Key | Description |
|---|---|---|
Zatca--Csid | Zatca:Csid | Binary security token from onboarding |
Zatca--PrivateKey | Zatca:PrivateKey | PEM-encoded EC private key |
Zatca--Certificate | Zatca:Certificate | Base64-encoded X.509 certificate |
Key Vault naming convention
Azure Key Vault does not allow __ (double underscore) in secret names. Use -- (double dash) instead. The Microtec.Web.Hosting bootstrap automatically translates -- back to : when reading configuration.
Integration Events
Other services subscribe to ZATCA events via the Service Bus:
csharp
// Subscribe in your consumer registration
busConfig.AddConsumer<ZatcaClearedEventHandler>();
busConfig.AddConsumer<ZatcaRejectedEventHandler>();
// Handler example
public class ZatcaClearedEventHandler : ConsumerBase<ZatcaClearedEvent>
{
public override async Task Consume(ConsumeContext<ZatcaClearedEvent> context)
{
var ev = context.Message;
// ev.InvoiceId, ev.ClearedAt, ev.InvoiceHash
await _invoiceRepository.MarkClearedAsync(ev.InvoiceId, ev.ClearedAt);
}
}Troubleshooting
| Symptom | Likely Cause | Fix |
|---|---|---|
400 — Faulty invoice format | UBL XML does not match schema | Check invoice type code and mandatory fields |
401 — Invalid CSID | Wrong environment or expired CSID | Re-onboard; verify Zatca:Environment |
429 — Too many requests | Burst limit hit | Implement exponential backoff; use the reporting API for B2C |
| QR code not scanning | TLV encoding error | Validate with ZATCA's Fatoora mobile app |
Certificate chain validation failed | Production certificate not trusted | Ensure ZATCA root CA is included in the trust store |