π Homepage Β β’Β β‘ Features Β β’Β π₯ Installation Β β’Β π‘ Examples Β β’Β ποΈ Documentation
fatoority is an unofficial open-source toolkit for developers building e-invoicing integrations
in Saudi Arabia. Sign invoice XML, validate it locally, generate QR payloads, and
connect to ZATCA APIs for Phase 1 and Phase 2 workflows.
With bindings and support for many programming languages:
-
π C/C++ (through the C FFI see releases)
Disclaimer:
fatoorityis not affiliated, associated, authorized, endorsed by, or in any way officially connected with ZATCA (Zakat, Tax and Customs Authority), or any of its subsidiaries or its affiliates. The official ZATCA website can be found at https://zatca.gov.sa.
Checkout the homepage, also the Rust API at docs.rs may prove useful.
Tools for your ZATCA integration:
- π© CSR Generation
- βοΈ Invoice Signing (Standard and simplified invoices, credit notes, and debit notes)
- β Validation (UBL and local ZATCA rules)
- π§Ύ QR Generation
- π¨ API Requests
π But we do it faster and better e.g. ~190x faster invoice hashing than ZATCA's SDK (see bench/)
βοΈ Rigorously tested with fuzzing and ~97% coverage
See the benchmarks for measured performance and methodology, and SDK compatibility notes for the scope of comparison with the official SDK. Local validation does not establish certificate issuer trust or ZATCA gateway acceptance.
Rust
cargo add fatoora-corePython
pip install fatoorityJavaScript / TypeScript
npm install fatoorityC# / .NET 8
dotnet add package FatoorityKotlin / Java / Android
Install a published release from Maven Central.
Gradle (build.gradle.kts):
repositories { mavenCentral() }
dependencies {
implementation("com.fatoority:fatoority:0.4.3")
}Maven (pom.xml):
<dependency>
<groupId>com.fatoority</groupId>
<artifactId>fatoority</artifactId>
<version>0.4.3</version>
</dependency>For Android, use com.fatoority:fatoority-android:0.4.3 instead of the desktop
artifact. Java/Kotlin imports remain com.fatoora.
C/C++
Download the fatoora-ffi-* archive for your platform from
releases and extract it. The shared
library is at the archive root. The C API header is include/fatoora.h,
with its adapter in src/fatoora.c and generated headers in include/generated; C++ headers
are in include/diplomat-cpp and expose types in namespace fatoora.
For example, on Linux, compile your application from the extracted directory:
cc -std=c11 -Iinclude app.c src/fatoora.c -L. -lfatoora_ffi -Wl,-rpath,'$ORIGIN' -o app
c++ -std=c++17 -Iinclude/diplomat-cpp app.cpp -L. -lfatoora_ffi -Wl,-rpath,'$ORIGIN' -o app-cppInclude fatoora.h in C or fatoora/Config.hpp in C++. Keep the shared library
beside these executables. On Windows, link the supplied import library and keep
the DLLs from the archive beside your executable.
To build the library from a source checkout:
cargo build -p fatoora-ffi --release --lockedThe C API
provides one fatoora.h, plain strings, shared errors, and named-field options.
Compile bindings/c/fatoora.c once with your application, add bindings/c to
your include path, and link fatoora_ffi. Release archives also include the C
examples under examples/c.
The library is written to target/release/. The generated headers are checked in
under bindings/c/generated and bindings/cpp; raw ABI callers use those include paths and
-Ltarget/release when compiling against a source build. Cargo does not regenerate
headers. After changing bridge declarations, follow the
Diplomat generation workflow.
CLI
The cli tool can also be installed with cargo:
cargo install fatoora-cli
Alternatively, you can grab the precompiled binary for your platform on the repo's releases (fatoora-cli-*). Extract the archive and run fatoora.
CSR Generation
Rust
use fatoora_core::config::EnvironmentType;
use fatoora_core::csr::{CsrProperties, SigningKey};
let props_text = std::fs::read_to_string("csr.properties")?;
let props = CsrProperties::from_properties_str(&props_text)?;
let key = SigningKey::generate();
let csr = props.build(&key, EnvironmentType::NonProduction)?;
let csr_pem = csr.to_pem()?;
let key_pem = key.to_pem()?;Python
from fatoora.config import Environment
from fatoora.csr import CsrProperties, SigningKey
props = CsrProperties.parse_csr_config_file("csr.properties")
key = SigningKey.generate()
csr = props.build(key, Environment.NON_PRODUCTION)
csr_pem = csr.to_pem_base64()
key_pem = key.to_pem()JavaScript
import { CsrProperties, SigningKey } from 'fatoority';
const key = SigningKey.generate();
const csr = CsrProperties.fromPropertiesStr(propertiesText).build(key, 0);
const csrPem = csr.toPem();CLI
fatoora csr --csr-config csr.properties --generated-csr csr.pem --private-key key.pem --pemInvoice Signing
Rust
use fatoora_core::invoice::sign::InvoiceSigner;
let cert_pem = std::fs::read_to_string("cert.pem")?;
let key_pem = std::fs::read_to_string("key.pem")?;
let signer = InvoiceSigner::from_pem(cert_pem.trim(), key_pem.trim())?;
let xml = std::fs::read_to_string("invoice.xml")?;
let signed_xml = signer.sign_xml(&xml)?;CLI
fatoora sign --invoice invoice.xml --cert cert.pem --key key.pem --signed-invoice signed.xmlValidation
Rust
use fatoora_core::config::Config;
use fatoora_core::invoice::validation::{
validate_zatca_invoice_from_str, ZatcaValidationOptions,
};
let config = Config::new(fatoora_core::config::EnvironmentType::NonProduction);
let xml = std::fs::read_to_string("invoice.xml")?;
let options = ZatcaValidationOptions {
previous_invoice_hash: Some(previous_hash_from_history),
..Default::default()
};
let report = validate_zatca_invoice_from_str(&xml, &config, &options)?;
if !report.is_valid() {
// Inspect stages and findings; missing predecessor context is incomplete.
}CLI
fatoora validate --invoice invoice.xml --profile zatca --format json \
--previous-invoice-hash "$PREVIOUS_INVOICE_HASH"The local profile implements all 257 business-rule assertions from SDK
238-R3.4.8, plus applicable signature, QR and predecessor checks. It preserves
warnings and partial reports. Standard invoices skip signature/QR checks under
this SDK profile. Local validation does not establish issuer trust or remote
acceptance. Existing validate_xml_invoice_from_str and the default CLI profile
continue to check XSD only.
QR Generation & Reading
Rust
use fatoora_core::invoice::xml::parse::parse_signed_invoice_xml;
let xml = std::fs::read_to_string("signed.xml")?;
let signed = parse_signed_invoice_xml(&xml)?;
let qr = signed.qr_code();CLI
fatoora qr --invoice invoice.xml
fatoora qr --invoice signed.xml
fatoora qr --invoice signed.xml --fail-on-signed
fatoora qr-read --invoice signed.xmlInvoice Hash
Rust
use fatoora_core::invoice::xml::parse::parse_finalized_invoice_xml;
let xml = std::fs::read_to_string("invoice.xml")?;
let invoice = parse_finalized_invoice_xml(&xml)?;
let hash = invoice.hash_base64()?;CLI
fatoora generate-hash --invoice invoice.xmlInvoice Request Payload
Rust
use fatoora_core::invoice::xml::parse::parse_signed_invoice_xml;
let xml = std::fs::read_to_string("signed.xml")?;
let signed = parse_signed_invoice_xml(&xml)?;
let payload = serde_json::json!({
"invoiceHash": signed.invoice_hash(),
"uuid": signed.uuid(),
"invoice": signed.to_xml_base64(),
});CLI
fatoora invoice-request --invoice signed.xml --api-request request.jsonAnd much more... see the guides in the homepage for more info.
Contributions are always welcome! See CONTRIBUTING.md for more details.
- Increase test coverage to 100% (Inshallah)
- Expand bindings to other languages (subject to demand)
- PDF invoice generation
This project began as a personal undertaking, and continues to be developed and maintained by me @mqqz.
While this means development happens alongside other commitments, I do my best to keep the project maintained and responsive to issues. Contributions, feedback, and suggestions are always welcome.
If you or your team are working on fintech or ZATCA integrations and need help building reliable scalable infrastructure, feel free to get in touch.
This project's code is licensed under either the MIT License or the Apache License, Version 2.0, at your option.
Third-party schemas, fixtures, templates, and dependencies retain their applicable upstream licenses and notices. The project license does not relicense those materials.
See third-party notices for attribution, applicable terms, and outstanding permission questions.