zatca 0.8.2
zatca: ^0.8.2 copied to clipboard
A Flutter package for generating ZATCA (Saudi Arabia's Zakat, Tax and Customs Authority) compliant invoice XML and QR codes.
Changelog #
0.8.2 #
Critical fix — invoices signed by 0.8.0 and 0.8.1 are rejected by ZATCA production reporting. 0.8.1 fixed only half of the 0.8.0 regression.
Fixed #
-
xmlns:dsleaking into the embedded<xades:SignedProperties>. 0.8.0 addedxmlns:ds="http://www.w3.org/2000/09/xmldsig#"to theds:DigestMethod,ds:DigestValue,ds:X509IssuerNameandds:X509SerialNumberelements written into the invoice. Those elements inherit the prefix from the enclosing<ds:Signature>and must not redeclare it: the copy of the element being hashed never had the declarations, so the digest stopped describing the element that was actually submitted and ZATCA rejected every invoice with:signed-properties-hashing: Invalid signed properties hashing0.8.1 corrected the digest encoding but left this in place, so the error survived the upgrade unchanged. The embedded element is now byte-identical to 0.6.6 and to the ZATCA reference implementation.
Changed #
-
One SignedProperties template, not two. The signer used to build the element twice - once to embed, once to hash - and the two copies drifted, which is what produced both the 0.8.0 and the 0.8.1 defect. The element is now built once, and the string that gets hashed is derived from the embedded element by
XmlUtil.signedPropertiesHashInput, which applies the only two transformations ZATCA expects (renderxmlns:dson eachds:*element, self-close empty elements) and throws if the element it is handed already declaresxmlns:ds. ThedefaultUBLExtensionsSignedPropertiesForSigningtemplate is gone. -
CertificateManager.checkInvoiceCompliancenow returns the gateway response (it wasFuture<void>) so callers can fail onvalidationResults.errorMessagesinstead of discarding them. Existing callers are unaffected.
Testing #
-
test/digest_encoding_test.dartnow pins the exact bytes of the<xades:SignedProperties>element as submitted and as hashed, against goldens taken from the ZATCA reference implementation, and checks that the digest inds:Referenceis the digest of the submitted element. Encoding assertions alone did not catch this - they were green for the whole of 0.8.1. -
test/zatca_test.dartnow asserts onerrorMessagesinstead of printing the response, and documents that the sandbox compliance endpoint does not validate the XAdES digests at all: an invoice submitted there with a deliberately corrupted signed-properties digest still returnsreportingStatus: REPORTEDwith no errors. A green sandbox run is not evidence that the signer works.
0.8.1 #
Critical fix — every signed invoice produced by 0.7.0 and 0.8.0 is rejected by ZATCA. Upgrade immediately if you are on either version.
Fixed #
-
Certificate hash (
xades:CertDigest) encoding. ZATCA hashes the certificate's Base64 text and expects Base64 of the SHA-256 hex string (88 characters). 0.8.0 hashed the decoded DER bytes and Base64-encoded the raw digest (44 characters), so the gateway rejected every invoice with:certificate-hashing: Invalid certificate hashing -
SignedProperties hash (
ds:Reference URI="#xadesSignedProperties") encoding. Same rule — Base64 of the SHA-256 hex string, not of the raw digest. 0.7.0 and 0.8.0 emitted the raw form, producing:signed-properties-hashing: Invalid signed properties hashing, SignedProperties with id='xadesSignedProperties'The invoice hash (
ds:Reference Id="invoiceSignedData") is unaffected — it is Base64 of the raw digest, and was already correct.
Both digests match 0.6.6 again. No public API changed, so upgrading from 0.7.x/0.8.0 needs no code changes.
If you shipped 0.7.0 or 0.8.0: invoices already signed by those builds cannot be reported as-is — the stored XML carries the bad digests. They have to be re-signed after upgrading.
Added #
test/digest_encoding_test.dart— offline regression tests pinning all three digest encodings (88 / 88 / 44 characters) against a fixture certificate, so this cannot silently regress again.
0.8.0 #
Adds ZATCA Phase-1 (Generation) support via a new dedicated class,
so a single integration of this package can serve both onboarded
(Phase-2) and not-yet-onboarded (Phase-1) merchants. Fully backward
compatible — existing ZatcaManager Phase-2 code is unchanged.
Added #
-
SimpleZatcaManager— a new singleton for ZATCA Phase-1 (Generation). Takes onlysellerNameandsellerTRN— no private key, no certificate, no supplier info, no ZATCA API. Produces a compliant basic TLV QR (tags 1–5) that is identical for both simplified (B2C) and standard (B2B) invoices.import 'package:zatca/simple_zatca_manager.dart'; SimpleZatcaManager.instance.initialize( sellerName: 'My Shop', sellerTRN: '300000000000003', ); final qr = SimpleZatcaManager.instance.generateQrString( issueDateTime: DateTime.now(), totalWithVat: 115.00, vatTotal: 15.00, );A convenience wrapper
generateQrStringFromInvoice(BaseInvoice)is also provided for integrators who already have aBaseInvoice. -
Input validation.
sellerTRNmust be 15 digits starting and ending with3. Amounts must be finite and non-negative, and VAT cannot exceed the invoice total. -
Example app has a dedicated Phase-1 QR screen that demonstrates
SimpleZatcaManagerend-to-end (form → TLV QR → tag-by-tag breakdown). -
Unit tests (
test/phase1_qr_test.dart) covering TLV tag order and lengths, two-decimal amount formatting, UTF-8 (Arabic) seller names, VAT-format validation, negative-amount rejection, and B2B/B2C QR equivalence.
Architecture #
Phase-1 and Phase-2 are now exposed as two purpose-built singletons rather than one manager with a runtime phase flag. Pick the class that matches the merchant — the type system enforces phase separation.
| Phase | Class | Use when |
|---|---|---|
| Phase-1 (Generation) | SimpleZatcaManager |
Merchant not yet onboarded to FATOORA |
| Phase-2 (Integration) | ZatcaManager |
Merchant onboarded with compliance + production CSID |
Unchanged #
ZatcaManager— public API and behavior identical to 0.7.0.CertificateManager— public API and behavior identical to 0.7.0 (it remains a Phase-2 helper; Phase-1 callers simply don't use it).
0.7.0 #
A major correctness release that fixes several bugs that were silently producing invoices ZATCA would reject. Also ships a rewritten example app and cleans up a number of typos in public identifiers. Minor breaking changes are listed below — most users only need to rename one method call.
Fixed (critical — these affected signature & QR validation) #
- ECDSA curve mismatch. Signing used
secp256r1while keys were generated onsecp256k1. Every signature produced before this release was invalid and would be rejected by ZATCA. Now both paths usesecp256k1as required by the ZATCA specification. - SignedProperties hash double-encoding. The
xades:SignedPropertiesdigest was hex-stringified, then UTF-8-encoded, then Base64'd — producing a garbage value. Now correctly Base64-encodes the raw SHA-256 bytes. - Certificate digest double-encoding. The
xades:CertDigestvalue was hashed from the PEM text rather than the DER bytes, with the same hex→UTF-8→Base64 problem. Now hashes the DER-encoded certificate and Base64-encodes the raw digest. - Missing
xmlns:dson signed-properties children. The post-signing<ds:DigestMethod>,<ds:DigestValue>,<ds:X509IssuerName>, and<ds:X509SerialNumber>elements are now correctly namespaced.
Fixed (moderate) #
- Inconsistent decimal formatting.
NumberFormat("#.##")was dropping trailing zeros ("100.00"became"100") on some monetary elements while others correctly usedtoStringAsFixed(2). All amounts now use 2 fixed decimals. - Per-line
PriceAmountoverflow.toStringAsFixed(14)has been reduced totoStringAsFixed(2)for both line unit prices and discount amounts. - Customer
PlotIdentificationfield. Was usingaddress.buildingas a stand-in.Addressnow exposes a dedicatedplotIdentificationfield which falls back to the building number when not provided. - Fragile
<ds:Object-1/>placeholder. Replaced with a normal<ds:Object>element whose content is substituted after signing. - Missing
Content-Type: application/jsonheader inreportInvoiceandclearanceInvoice.
Added #
paymentMethodon regular invoices.Invoice,SimplifiedInvoiceandStandardInvoicenow accept an optionalpaymentMethodof typeZATCAPaymentMethods, which emits acac:PaymentMeansblock in the generated XML. Previously only credit/debit notes emitted payment means.- Dedicated
Address.plotIdentification. Optional; defaults to thebuildingvalue for backwards compatibility. - Rewritten example app (
example/) demonstrating the full ZATCA lifecycle: onboarding (keypair → CSR → compliance cert → optional production cert), invoice composition (all 6 invoice variants, dynamic line items, live totals), and a result screen (scannable QR, TLV breakdown, signed UBL XML, copyable hashes and signatures). Usesflutter_blocfor state, adapts to desktop/tablet/mobile viaNavigationRail/NavigationBar, and persists onboarding state across launches.
Changed #
-
BREAKING:
ZatcaManager.initializeZactarenamed toZatcaManager.initializeZatca. Rename the single call at the start of your integration. -
BREAKING (internal paths): directory and file typos corrected. If you were importing internal paths directly, update them:
package:zatca/resources/cirtificate/…→package:zatca/resources/certificate/……/cirtificate/certficate_util.dart→…/certificate/certificate_util.dartpackage:zatca/models/cirtificate_info.dart→package:zatca/models/certificate_info.dartpackage:zatca/extesions/discount_list_extesions.dart→package:zatca/extensions/discount_list_extensions.dart
Most integrators only import the top-level APIs (
ZatcaManager,CertificateManager, models) and are unaffected.
0.6.6 #
- Updates and improvements.
0.6.5 #
- Updates and improvements.
0.6.4 #
- Fixed missing type annotations in functions.
0.6.2 #
- Bug fixes.
0.6.0 #
- Added simulation environment.
- Bug fixes.
0.5.0 #
- Added support for item discounts.
0.4.0+3 #
- Updated documentation for better clarity and consistency.
0.4.0 #
- Bug fixed: OpenSSL certificate generation now supported on Linux, Windows, and macOS.
0.3.2 #
- Adding a custom path for the certificate.
0.3.0 #
- Resolved an issue with certificate generation.
0.2.4 #
- Updated dependencies to the latest package versions for improved functionality and compatibility.