Supported public API¶
The public API covers invoice construction, XML import, signing, validation, QR generation, credential creation, and submission. Implementation helpers stay private. Operation inputs and outputs use crate-owned or standard-library types; Serde integration remains supported.
Generate the full signature reference from the checkout with
cargo doc -p fatoora-core --no-deps. This page records the supported workflows
and their contracts. The generated reference is authoritative for the complete
method and field inventory.
Entry points¶
| Module | Supported surface |
|---|---|
| crate root | Decimal, DecimalError, Error, ErrorKind, Diagnostic, DiagnosticSeverity |
config |
Config, EnvironmentType |
csr |
CsrProperties, SigningKey, Csr, CsrError |
invoice |
Builder, finalized/signed invoices, invoice data and totals, parties, lines, validated value types, flags, QR payloads, and field-validation errors |
invoice::sign |
InvoiceSigner, read-only SignedProperties, SigningError, invoice_hash_base64_from_xml_str |
invoice::xml |
XmlFormat, InvoiceXmlError, and parse |
invoice::xml::parse |
Finalized/signed XML import, corresponding file helpers, ParseError |
invoice::validation |
Bundled-XSD validation, validation reports and findings, XmlValidationError |
api |
ZatcaClient, CsidCredentials<Compliance> / <Production>, response data, InvoiceOutcome, HttpResponseError, ZatcaError |
Domain enums and result types remain public where callers construct inputs,
inspect results, or handle errors. InvoiceData, InvoiceTotalsData,
VatBreakdown, and SignedProperties expose read-only accessors. Public structs
such as Address and validation findings support ordinary value construction.
Visibility and extension points¶
InvoiceViewis internal. Both invoice types expose inherentdata()andtotals()methods.InvoiceXmlis a private serialization wrapper.- XML serialization uses inherent
FinalizedInvoicemethods. There is no publicToXmltrait. PartyRoleis sealed: callers can use it as a generic bound, but onlySellerRoleandBuyerRoleimplement it.SellerandBuyerare the usual construction types.- The unused
TokenScopetrait is removed. API methods select the requiredCsidCredentials<Compliance>orCsidCredentials<Production>type directly. - XML/backend nodes, serializers, signing keys from dependencies, and HTTP clients are implementation details. No public backend injection or custom XSD API is supported.
Construction and errors¶
InvoiceBuilder::new(InvoiceType) -> InvoiceBuilder creates a builder.
Configuration methods consume and return it; build(self) returns
Result<FinalizedInvoice, InvoiceError> and consumes the builder on failure too.
Conditional configuration therefore reassigns the returned builder.
Amounts use Decimal, including LineItem constructors and builder adjustments.
Parse exact values from strings with Decimal::parse; bindings pass decimal
strings. Validated country, currency, date, timestamp, and VAT ID wrappers reject
invalid values through constructors and deserialization.
Module operations return their module errors (CsrError, InvoiceError,
SigningError, InvoiceXmlError, ParseError, XmlValidationError,
QrCodeError, ZatcaError, or DecimalError). Callers can convert these into
fatoora_core::Error; ErrorKind provides shared classification. Diagnostic text
may change. Callers should use structured variants, fields, and codes.
XML and signing¶
The principal signatures are:
// FinalizedInvoice
pub fn to_xml(&self) -> Result<String, InvoiceXmlError>;
pub fn to_xml_with_format(&self, format: XmlFormat) -> Result<String, InvoiceXmlError>;
pub fn hash_base64(&self) -> Result<String, SigningError>;
pub fn sign(self, signer: &InvoiceSigner) -> Result<SignedInvoice, SigningError>;
// InvoiceSigner
pub fn from_pem(cert_pem: &str, private_key_pem: &str) -> Result<Self, SigningError>;
pub fn from_der(cert_der: &[u8], private_key_der: &[u8]) -> Result<Self, SigningError>;
pub fn sign_xml(&self, xml: &str) -> Result<String, SigningError>;
// SignedInvoice
pub fn xml(&self) -> &str;
pub fn into_xml(self) -> String;
pub fn to_xml_base64(&self) -> String;
to_xml() uses two-space indentation. to_xml_with_format() accepts compact
output or explicit indentation. to_xml_pretty() has been removed because it
was identical to to_xml().
parse_finalized_invoice_xml(&str) returns
Result<FinalizedInvoice, ParseError>; parse_signed_invoice_xml(&str) returns
Result<SignedInvoice, ParseError>. Seller address import preserves additional street and additional number.
district maps to XML CitySubdivisionName. The redundant subdivision field
has been removed. Import does not promise a lossless model round trip. Their _file variants accept
impl AsRef<Path> and return the same result types.
Signed invoices preserve the supplied signed XML on import, or the output of
signing. They expose no formatting methods. Raw sign_xml() may parse and
serialize its input; preserve its returned output when storing or submitting it.
Signing metadata remains readable through signed_properties() and invoice
accessors.
Validation guarantees¶
| Operation | Guarantee |
|---|---|
Builder build() |
Implemented field/range checks and computed totals |
| XML import | Supported parser/model checks, including supplied amounts |
| Signing | Signature material and signed XML generated |
| Signed XML import | Signature material extracted; authenticity is unverified |
validate_xml_invoice_from_str(&str, &Config) |
Bundled XSD check; returns Result<(), XmlValidationError> |
validate_xml_invoice_report_from_str(&str, &Config) |
Same XSD check; returns Result<ValidationReport, XmlValidationError> |
Reports list only completed layers. Schema violations become findings in a report; malformed XML returns an error. These operations do not establish full business-rule compliance or signature trust.
Submission¶
ZatcaClient::new(Config) returns Result<ZatcaClient, ZatcaError>.
check_invoice_compliance() takes &SignedInvoice and
&CsidCredentials<Compliance>. Reporting and clearance take &SignedInvoice,
&CsidCredentials<Production>, clearance_status: bool, and
accept_language: Option<&str>. These async operations return
Result<ValidationResponse, ZatcaError>.
A decoded 2xx response is Ok. Call ensure_accepted() to require endpoint
acceptance, or inspect outcome() for Accepted, Rejected, or Unknown.
http_status() preserves the received status. Clearance responses expose
cleared_invoice_base64() and fallible cleared_invoice_xml(); decoding the
returned invoice does not verify authenticity.
Non-2xx responses return ZatcaError::Response with status, raw body, and an
optional typed validation response. A malformed 2xx body returns
ZatcaError::ResponseDecode. A failed ensure_accepted() retains the response
in ZatcaError::NotAccepted.
Contract coverage¶
- Public consumer workflows: construction, direct serialization, import/signing, validation, and structured failures.
- Compile-fail consumers: private helpers, removed traits/aliases, sealed roles, and signed XML formatting.
- Public submission: local HTTP requests, credentials, payloads, acceptance, business rejection, HTTP rejection, compliance, and cleared XML. Child processes isolate the endpoint override.
- Builder ownership, signed XML ownership, and numeric contracts cover values and ownership in more detail.
- Examples compile as external consumers. The live API example test is ignored by default and requires explicit opt-in.
Run the offline workspace checks with:
SKIP_ZATCA_LIVE_API=1 CARGO_INCREMENTAL=0 cargo test --workspace --locked --offline
The live API tests are skipped by that environment variable. SDK compatibility coverage remains tracked separately in issue #2. Binding migration remains in issue #5. The FFI uses these core methods; its authoritative declarations are the generated headers and exports, rather than a duplicate signature list here.
Migration¶
Remove imports of ToXml and call FinalizedInvoice::to_xml() directly. Replace
to_xml_pretty() with to_xml(). Replace public InvoiceView bounds with the
concrete invoice type or a consumer-owned abstraction. Custom PartyRole
implementations and direct use of InvoiceXml are no longer supported.
Address callers must replace subdivision with district. The C address and
inline seller/buyer constructors now take one district argument after postal
code; rebuild callers against the regenerated header and matching library.
Python constructors likewise accept only district.