Zatca.Net
1.0.2
dotnet add package Zatca.Net --version 1.0.2
NuGet\Install-Package Zatca.Net -Version 1.0.2
<PackageReference Include="Zatca.Net" Version="1.0.2" />
<PackageVersion Include="Zatca.Net" Version="1.0.2" />
<PackageReference Include="Zatca.Net" />
paket add Zatca.Net --version 1.0.2
#r "nuget: Zatca.Net, 1.0.2"
#:package Zatca.Net@1.0.2
#addin nuget:?package=Zatca.Net&version=1.0.2
#tool nuget:?package=Zatca.Net&version=1.0.2
Zatca.Net
A clean, modern .NET SDK for Saudi Arabia's ZATCA e-invoicing (Fatoora) Phase 2: TLV QR code generation, SHA-256 invoice hashing, ECDSA (secp256k1) signing primitives, EGS onboarding (compliance → production CSIDs), and the clearance / reporting APIs.
Targets netstandard2.0 and net8.0. Only dependencies: System.Text.Json (+ DI abstractions). Bring your own HttpClient.
Disclaimer: community SDK, not affiliated with or endorsed by ZATCA (Zakat, Tax and Customs Authority). Always verify behavior against ZATCA's official documentation before going live — tax compliance is your responsibility.
Install
dotnet add package Zatca.Net
Or reference the project directly until the NuGet package is published.
Quickstart — QR code
using Zatca.Qr;
// Phase 1 style: tags 1-5
string qrPayload = QrCodeGenerator.GeneratePhase1(
sellerName: "شركة الاختبار",
vatNumber: "399999999900003",
timestamp: "2026-10-08T12:00:00Z", // ISO-8601
invoiceTotalWithVat: "115.00",
vatTotal: "15.00");
// Render qrPayload as a QR image with your favorite QR library.
Phase 2 adds the cryptographic tags (6–9):
using Zatca.Crypto;
using Zatca.Qr;
byte[] xmlBytes = GetSignedInvoiceXml(); // your signed UBL 2.1 XML
byte[] hash = InvoiceHasher.ComputeHash(xmlBytes);
using (var signer = EcdsaInvoiceSigner.CreateSecp256k1())
{
string qr = QrCodeGenerator.Generate(new QrData
{
SellerName = "شركة الاختبار",
VatNumber = "399999999900003",
Timestamp = "2026-10-08T12:00:00Z",
InvoiceTotalWithVat = "115.00",
VatTotal = "15.00",
InvoiceHash = hash, // tag 6
EcdsaSignature = signer.SignHash(hash), // tag 7 (raw 64-byte r‖s)
PublicKey = signer.ExportPublicKeyUncompressed(), // tag 8
StampSignature = stampSignatureBytes // tag 9 (simplified invoices)
});
}
Quickstart — onboarding & submission
using Zatca;
using Zatca.Models;
var client = new ZatcaClient(new ZatcaClientOptions
{
// defaults to the sandbox; switch BaseUrl for simulation/production
});
// 1. Compliance CSID (OTP from the Fatoora portal; "123345" on sandbox)
CsIdResponse compliance = await client.RequestComplianceCsIdAsync(csrBase64, otp);
// 2. Compliance checks with the compliance credentials
var complianceClient = client.WithCredentials(compliance.BinarySecurityToken, compliance.Secret);
await complianceClient.SubmitComplianceInvoiceAsync(new InvoiceSubmission
{
InvoiceHash = InvoiceHasher.ComputeHashHex(signedXmlBytes),
Uuid = invoiceUuid,
InvoiceBase64 = Convert.ToBase64String(signedXmlBytes)
});
// ... repeat for each invoice type declared in your CSR ...
// 3. Production CSID
CsIdResponse production = await complianceClient.RequestProductionCsIdAsync(compliance.RequestId);
// 4. Report (simplified/B2C) or clear (standard/B2B) with production credentials
var prod = client.WithCredentials(production.BinarySecurityToken, production.Secret);
ZatcaSubmissionResult reported = await prod.ReportInvoiceAsync(submission); // simplified
ZatcaSubmissionResult cleared = await prod.ClearInvoiceAsync(submission); // standard
if (cleared.IsAccepted) { /* persist, then share with buyer */ }
ASP.NET Core DI
services.AddZatca(options =>
{
options.BaseUrl = ZatcaClientOptions.SimulationBaseUrl;
options.BinarySecurityToken = config["Zatca:Token"];
options.Secret = config["Zatca:Secret"];
});
API reference
ZatcaClient
| Method | Endpoint | Notes |
|---|---|---|
RequestComplianceCsIdAsync(csrBase64, otp) |
POST /compliance |
Headers OTP, Accept-Version: V2. No auth. |
SubmitComplianceInvoiceAsync(submission) |
POST /compliance/invoices |
Basic auth with compliance CSID. |
RequestProductionCsIdAsync(complianceRequestId) |
POST /production/csids |
Basic auth with compliance CSID. |
RenewProductionCsIdAsync(csrBase64, otp) |
PATCH /production/csids |
Basic auth with production CSID + OTP header. |
ReportInvoiceAsync(submission) |
POST /invoices/reporting/single |
Clearance-Status: 0. Simplified invoices, report within 24h. |
ClearInvoiceAsync(submission) |
POST /invoices/clearance/single |
Clearance-Status: 1. Standard invoices, clear before sharing with buyer. |
WithCredentials(token, secret) |
— | New client sharing the HttpClient, different CSID set. |
Errors surface as ZatcaApiException (HTTP status + raw body).
Crypto & QR
QrCodeGenerator.Generate(QrData)/GeneratePhase1(...)→ base64 TLV payload;Decode(base64)→ fields (for verification).InvoiceHasher.ComputeHash / ComputeHashHex / ComputeHashBase64— SHA-256.IInvoiceSigner+EcdsaInvoiceSigner(ECDsa)— bring your own key;SignHashreturns raw 64-byte r‖s;CreateSecp256k1()factory for supported platforms.TaxInvoice(+Party,InvoiceLine) — domain model withValidate()(totals consistency, buyer required for standard, Saudi VAT format).SaudiVatValidator.IsValid— 15 digits, starts and ends with 3.
Environments
| Environment | Base URL | OTP | CSR certificate template |
|---|---|---|---|
| Sandbox (developer portal) | https://gw-fatoora.zatca.gov.sa/e-invoicing/developer-portal |
fixed 123345 |
TSTZATCA-Code-Signing |
| Simulation | https://gw-fatoora.zatca.gov.sa/e-invoicing/simulation |
from Fatoora portal | PREZATCA-Code-Signing |
| Production | https://gw-fatoora.zatca.gov.sa/e-invoicing/core |
from Fatoora portal (single-use, 1h) | ZATCA-Code-Signing |
Notes from the official flow:
- The sandbox only validates invoices for the test VAT
399999999900003. - Simulation enforces the compliance gate (up to 6 sample documents: standard/simplified × invoice/credit/debit) before issuing a production CSID.
- Always branch on ZATCA's response body (
reportingStatus/clearanceStatus,validationResults.status), not just the HTTP status.
Honest scope
What this SDK does:
- TLV QR payloads (tags 1–9) per ZATCA's Security Features Implementation Standards, with decode support.
- SHA-256 hashing and ECDSA/secp256k1 signing primitives with raw r‖s output for QR tag 7.
- Typed clients for onboarding (compliance/production CSIDs, renewal) and clearance/reporting, with correct endpoints, headers and Basic auth.
- Invoice domain models with consistency validation, Saudi VAT validation.
What it does not do (by design):
- UBL 2.1 XML generation — you build the invoice XML (fields, UBLExtensions, cac:Signature skeleton) in your ERP; the SDK hashes, signs and submits it.
- XAdES-BES enveloped signing inside the XML — the
IInvoiceSignersigns hash bytes; embedding the<ds:Signature>block is integrator work. - CSR generation — generate the secp256k1 key and CSR (with ZATCA's required subject/extension fields) via OpenSSL or your CA tooling, then pass the base64 CSR in.
- Private-key storage — keys never enter this SDK except as the
ECDsainstance you hand it; encrypt CSIDs and keys at rest.
Development
dotnet build Zatca.sln
dotnet test Zatca.sln
Tests are fully offline (mocked HTTP, fixed vectors, independent golden QR bytes).
License
MIT — see LICENSE.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.2)
- System.Text.Json (>= 8.0.5)
-
net8.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.2)
- System.Text.Json (>= 8.0.5)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
NuGet polish release: branded package icon, embedded README, and full repository metadata.