لماذا UBL 2.1؟ ولماذا اختارته ZATCA؟
UBL (Universal Business Language) هو معيار XML مفتوح من OASIS لمستندات الأعمال (فاتورة، طلب شراء، بيان شحن…). الإصدار 2.1 هو الذي تبنّته هيئة الزكاة والضريبة والجمارك (ZATCA) في الفوترة الإلكترونية المرحلة الثانية لأنه: (أ) معياري عالمي مدعوم في الاتحاد الأوروبي وآسيا، (ب) قابل للتحقق آلياً عبر XSD وSchematron، (ج) يدعم التواقيع الرقمية XAdES، (د) ينقل البيانات الدلالية وليس فقط النصية.
النتيجة: ملف XML واحد يفهمه نظام البائع، نظام المشتري، ومنصة FATOORA لدى ZATCA — بدون اعتماد على PDF أو معالجة بصرية.
Phase 1 (Generation) vs Phase 2 (Integration)
Phase 1 — منذ 4 ديسمبر 2021
يكفي إصدار الفاتورة إلكترونياً بحقول إلزامية وQR للفاتورة المبسّطة (B2C). لا تكامل لحظي مع ZATCA. هذا ما تغطّيه أداة مولّد الفاتورة الضريبية لدينا.
Phase 2 — متدرّجة منذ 1 يناير 2023
إلزام التكامل مع منصة FATOORA: كل فاتورة قياسية (B2B) ترسل قبل تسليمها للمشتري (Clearance)، وكل فاتورة مبسّطة (B2C) ترسل خلال 24 ساعة (Reporting). الإلزام يصل المنشآت على موجات حسب إيراداتها السنوية.
من يستخدم هذه الأداة؟
المطوّرون الذين يبنون تكاملاً مع FATOORA، المحاسبون الذين يفحصون مخرجات ERP، أو أصحاب المنشآت الذين يريدون فهم الـ XML الذي يصدره مزوّد الحلول لديهم.
بنية ملف UBL 2.1 لفاتورة ZATCA
الملف يبدأ بعنصر جذر <Invoice> ضمن مساحات الأسماء (namespaces) المعتمدة من OASIS. ثم ينقسم إلى أربع كتل رئيسية:
- Header — معرّفات الفاتورة، التاريخ، نوع الفاتورة (388 = قياسية، 381 = إشعار دائن).
- AccountingSupplierParty — بيانات البائع (الاسم، الرقم الضريبي، السجل التجاري، العنوان).
- AccountingCustomerParty — بيانات المشتري.
- InvoiceLine[] — بنود الفاتورة، كل بند به الكمية، السعر، النسبة الضريبية، والإجمالي.
- TaxTotal & LegalMonetaryTotal — مجاميع الضريبة والمبالغ.
الحقول الإلزامية في UBL 2.1 لزاتكا
- ProfileID =
reporting:1.0للمبسّطة أوstandard:1.0للقياسية. - ID — رقم الفاتورة الفريد.
- UUID — معرّف عالمي فريد (يفضّل UUID v4).
- IssueDate و IssueTime بتوقيت السعودية.
- InvoiceTypeCode مع السمة
nameبرمز رباعي يحدّد طبيعة الفاتورة. - DocumentCurrencyCode = SAR.
- TaxCurrencyCode = SAR (الضريبة دائماً بالريال حتى لو كانت الفاتورة بعملة أخرى).
- PartyTaxScheme/CompanyID — الرقم الضريبي 15 رقم يبدأ وينتهي بـ 3.
- PartyIdentification للسجل التجاري CRN.
- InvoiceLine لكل بند مع
TaxCategory/Percent.
TLV QR Code — الحقول الخمسة
QR إجباري على المبسّطة، ومفضّل على القياسية. يُبنى بترميز TLV (Tag-Length-Value) بـ 5 حقول بهذا الترتيب:
- اسم البائع (UTF-8)
- الرقم الضريبي للبائع
- التاريخ والوقت بصيغة ISO 8601
- الإجمالي شامل الضريبة
- مبلغ ضريبة القيمة المضافة
كل حقل: بايت Tag (1–5)، بايت Length، ثم Value بطول Length بايت. النتيجة سلسلة بايتات تُحوّل إلى Base64 وتُرمَّز في QR.
Base64 وكيف يُولَّد عملياً
أداتنا تستخدم دالّة zatcaTLVBase64() من مكتبة @/lib/zatca. هي تأخذ كائن InvoiceData وتعيد سلسلة Base64 جاهزة لتمريرها لأي مولّد QR (نحن نستخدم qrcode NPM). المولّد ينتج صورة PNG قابلة للتنزيل والإلصاق في إيصال نقطة البيع.
نقطة دقيقة
في Phase 2 الكاملة، يجب أن تضاف 3 حقول إضافية للـ TLV: Hash الفاتورة، التوقيع الرقمي، والمفتاح العام للشهادة. أداتنا تولّد الحقول الخمسة الأساسية — الثلاثة المتقدّمة تضيفها عادةً بعد التوقيع بشهادة ZATCA.
Cryptographic Stamp — التوقيع الرقمي
كل فاتورة قياسية في Phase 2 يجب أن تحمل ختماً تشفيرياً (Cryptographic Stamp) باستخدام شهادة من ZATCA: تطلب CSR (Certificate Signing Request)، تحصل على شهادة Compliance، تختبر في الـ Sandbox، ثم تحصل على شهادة Production. الختم يُبنى بمعيار XAdES-B-B على الـ XML نفسه.
هذه الخطوة خارج نطاق هذه الأداة (لأنها تتطلّب مفاتيح خاصة محليّة)، لكن الـ XML الذي تنتجه أداتنا هو المدخل الذي توقّعه مكتبة التوقيع لاحقاً.
سلسلة الـ Hash بين الفواتير
ZATCA تشترط أن كل فاتورة Phase 2 تحمل PIH (Previous Invoice Hash) — هاش الفاتورة السابقة في تسلسل البائع. هذا يبني سلسلة Blockchain-like تكشف أي محاولة لإدراج فاتورة مفقودة أو حذف فاتورة لاحقاً.
أول فاتورة تستخدم هاش ابتدائي قياسي. كل فاتورة بعدها تأخذ SHA-256 من الـ XML الموقّع للفاتورة السابقة وتدرجه في حقل PIH.
FATOORA: Sandbox vs Production
Sandbox
بيئة اختبار مفتوحة بدون رسوم أو إجراءات. تحصل على شهادة Compliance، ترسل مئات الفواتير التجريبية، تتأكّد من أن XML الخاص بك يمر بدون أخطاء، ثم تنتقل للإنتاج.
Production
يتطلّب شهادة Production من ZATCA. الفواتير الحقيقية تُرسَل عبر API لحظياً: B2B = Clearance قبل التسليم، B2C = Reporting خلال 24 ساعة.
سير العمل من JSON إلى FATOORA
- نظامك يبني كائن
InvoiceData(نفس الشكل الذي تستخدمه أداتنا). - تحوّل الكائن إلى UBL 2.1 XML — هذه هي خطوة هذه الأداة.
- تحسب SHA-256 للـ XML، وتضع PIH.
- توقّع الـ XML بشهادتك (XAdES).
- تبني TLV QR موسّع (5 + 3 حقول) وتحقنه في الـ XML.
- ترسل الـ XML المشفّر إلى FATOORA API.
- تحفظ ردّ ZATCA (UUID، حالة Clearance، أي ملاحظات).
مثال عملي: من JSON إلى UBL 2.1 XML
لنرى التحويل في تطبيقه الفعلي. تخيّل أنك بنّاء حلول POS وتستقبل من نقطة البيع كائن JSON بسيط لفاتورة قهوة وكرواسون:
{
"id": "INV-2026-00142",
"uuid": "f81d4fae-7dec-11d0-a765-00a0c91e6bf6",
"issueDate": "2026-05-26",
"issueTime": "14:32:08",
"seller": {
"name": "مقهى الندى",
"vatNumber": "300012345600003",
"crn": "1010234567"
},
"buyer": { "name": "عميل نقدي" },
"lines": [
{ "id": "1", "name": "قهوة سادة", "qty": 1, "unitPrice": 10, "vatRate": 15 },
{ "id": "2", "name": "كرواسون", "qty": 2, "unitPrice": 8, "vatRate": 15 }
]
}تمرّر هذا الكائن للأداة عبر زرّ «تحويل». خلف الكواليس، تنفّذ ثلاث خطوات: التحقق من الحقول، بناء شجرة UBL 2.1، ثم تسلسل (serialize) إلى XML بترميز UTF-8 مع إعلان <?xml version="1.0"?>.
الناتج (مقطع مختصر) يبدو كالتالي — لاحظ أن كل بند يصبح cac:InvoiceLine منفصلاً بحساب الضريبة الخاص به، والمجموع الكلي يُجمع في cac:LegalMonetaryTotal:
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
xmlns:cac="urn:...:CommonAggregateComponents-2"
xmlns:cbc="urn:...:CommonBasicComponents-2">
<cbc:ProfileID>reporting:1.0</cbc:ProfileID>
<cbc:ID>INV-2026-00142</cbc:ID>
<cbc:UUID>f81d4fae-7dec-11d0-a765-00a0c91e6bf6</cbc:UUID>
<cbc:IssueDate>2026-05-26</cbc:IssueDate>
<cbc:IssueTime>14:32:08</cbc:IssueTime>
<cbc:InvoiceTypeCode name="0200000">388</cbc:InvoiceTypeCode>
<cbc:DocumentCurrencyCode>SAR</cbc:DocumentCurrencyCode>
<cbc:TaxCurrencyCode>SAR</cbc:TaxCurrencyCode>
<!-- ... seller, buyer, lines, totals ... -->
</Invoice>نفس الـ XML يمكنك حفظه بصيغة .xml ورفعه يدوياً للـ Sandbox، أو أتمتته داخل خطّ أنابيب CI/CD يبني الفاتورة، يوقّعها، ويرسلها للـ API الإنتاجي.
تلميح للمطوّرين
احتفظ بنسخة من الـ XML قبل التوقيع وبعده. عند تتبّع المشاكل، الفرق بين الاثنين يكشف ما إذا كانت المشكلة في بناء الـ XML أم في عملية التوقيع نفسها.
قواعد Schematron الـ150: لماذا الـ XSD وحده لا يكفي
ZATCA لا تكتفي بـ XSD للتحقق البنيوي — تطبّق فوقه أكثر من 150 قاعدة Schematron تتحقّق من المنطق الدلالي: هل المجاميع متطابقة؟ هل الرقم الضريبي يبدأ وينتهي بـ 3؟ هل سعر البند موجب؟ هل التاريخ ليس في المستقبل؟
هذه القواعد منشورة علناً في Developer Portal الخاص بـ FATOORA، مرتّبة بأكواد مثل BR-KSA-01 إلى BR-KSA-150+. كل قاعدة تحمل وصفاً ومستوى خطورة: Error (يرفض الفاتورة)، أو Warning (يقبل ويُسجّل ملاحظة).
أمثلة على القواعد الحرجة
- BR-KSA-02 — رقم ضريبي البائع يجب أن يكون 15 رقماً ويبدأ وينتهي بـ
3. - BR-KSA-15 — مجموع
LegalMonetaryTotal/TaxInclusiveAmountيجب أن يساوي مجموع كل بنود الفاتورة بفرق ≤ 0.01 ريال. - BR-KSA-26 — تاريخ الإصدار يجب ألا يسبق 1 يناير 2022 ولا يتجاوز اليوم بأكثر من يوم واحد.
- BR-KSA-44 — في الفاتورة القياسية B2B، المشتري يجب أن يحمل رقم ضريبي صالح في
PartyTaxScheme. - BR-KSA-67 — حقل
cbc:Noteيجب ألا يتجاوز 1,000 حرف. - BR-KSA-89 — في الإشعارات الدائنة (381)، يجب الإشارة لرقم الفاتورة الأصلية في
BillingReference/InvoiceDocumentReference/ID.
أداتنا تبني XML يتوافق مع الـ XSD الأساسي، لكن مسؤوليتك أن تختبره مقابل Schematron الكامل في الـ Sandbox قبل الإنتاج. ZATCA توفّر «SDK Validator» مجاني بـ Java يشغّل القواعد الـ150 محلياً ويعرض الأخطاء بنفس صيغة الـ Production API.
أداة التحقق المحلية: ZATCA SDK
نزّل الـ SDK من بوّابة FATOORA، شغّل fatoora -validate invoice.xml في الطرفية، وستحصل على تقرير مفصّل بكل قاعدة فاشلة. مفيد جداً قبل إرسال أي فاتورة للـ API الفعلي — يوفّر عليك دورات تطوير كاملة من ردود الخادم.
الأداء والحجم العالي: من 10 فواتير إلى 10,000 يومياً
المنشآت الصغيرة قد تصدر 20–50 فاتورة يومياً، لكن سلاسل التجزئة الكبرى والشركات السحابية تصدر عشرات الآلاف. متطلبات الأداء في Phase 2 صارمة: B2B Clearance يجب أن يحصل قبل تسليم الفاتورة للمشتري، ما يعني أن زمن الاستجابة للمستخدم يشمل زمن رحلة API كاملة لـ FATOORA.
تصميم معماري للحجم العالي
- Worker pool للتوقيع — توقيع XAdES عملية CPU مكلفة (~50–200ms للفاتورة). استخدم تجمّع عمّال (Node Workers أو Goroutines) بدلاً من توقيع متسلسل.
- Connection pooling مع FATOORA — كل استدعاء HTTPS جديد يكلّف ~300ms TLS handshake. استخدم Keep-Alive وأبقِ 5–10 اتصالات حيّة.
- طابور رسائل (Queue) للفواتير المبسّطة — B2C Reporting يتحمّل تأخير 24 ساعة. ادفعها لـ Redis/RabbitMQ ومعالج مستقل يرسل دفعات كل دقيقة.
- Hash chaining ذرّي — PIH يتطلّب قراءة هاش الفاتورة السابقة. استخدم قفل توافقي (Optimistic Locking) في قاعدة البيانات لمنع التعارض عند الإصدار المتزامن.
- Cache للشهادات — لا تحمّل شهادة ZATCA من القرص في كل توقيع. حمّلها مرة في الذاكرة وأعد استخدامها.
رقم واقعي
منصّة POS كبيرة في الرياض تصدر ~12,000 فاتورة يومياً عبر معمارية من ثلاث طبقات: Edge node يبني الـ XML، Worker pool يوقّعه، و Reporter يرسل دفعات لـ FATOORA. متوسط زمن استجابة الـ Clearance ~1.4 ثانية، 99th percentile ~3.2 ثانية.
مراقبة وملاحظة (Observability)
راقب هذه المقاييس في لوحة Grafana أو ما يعادلها: (أ) معدّل نجاح Clearance، (ب) زمن الاستجابة P50/P95/P99، (ج) أكثر قواعد Schematron تكراراً في الفشل، (د) عمر أقدم رسالة في طابور الـ Reporting (إنذار إذا تجاوز ساعتين). هذا يحوّل تكامل ZATCA من «صندوق أسود» إلى نظام قابل للتشغيل بثقة.
موجات الإلزام: متى يصلك دورك وكيف تستعد
ZATCA لا تطبّق Phase 2 على الجميع دفعة واحدة. الإلزام يصل المنشآت على موجات حسب إيراداتها السنوية الخاضعة للضريبة. الموجة الأولى (يناير 2023) شملت المنشآت بإيرادات تتجاوز 3 مليار ريال، والموجات اللاحقة نزلت تدريجياً حتى وصلت في 2025–2026 إلى المنشآت بإيرادات أقل من 7 مليون ريال.
كل منشأة تتلقّى إشعاراً رسمياً من ZATCA قبل 6 أشهر من تاريخ الإلزام، عبر البريد الإلكتروني المسجّل في حسابها الضريبي ومن خلال بوّابة FATOORA. تجاهل الإشعار = غرامات تبدأ من 5,000 ريال وقد تصل إلى 50,000 لكل فاتورة غير متوافقة.
قائمة استعداد عملية للموجات القادمة
- تحقّق من حالتك في FATOORA — سجّل دخول لبوّابة ZATCA وراجع تبويب «الفوترة الإلكترونية». ستجد إما «غير مُلزم بعد»، «مُلزم — Phase 1»، أو «مُلزم — Phase 2 ابتداءً من [تاريخ]».
- اختر مزوّد حلول معتمد (Solution Provider) — قائمة المزوّدين المعتمدين منشورة في موقع ZATCA. أو ابنِ تكاملاً مخصّصاً إذا كان لديك فريق تقني — أداتنا تقدّم نقطة بداية للـ XML.
- اطلب CSR وشهادة Compliance — هذه أوّل خطوة تقنية. ستحتاج رقم تسجيلك في FATOORA و OTP من البوّابة.
- اختبر 6+ سيناريوهات في الـ Sandbox — فاتورة B2C عادية، B2B بمشتري داخل المملكة، إشعار دائن، فاتورة بسلع صفرية، فاتورة بأكثر من 50 بنداً، وفاتورة بعملة أجنبية مع ضريبة بالريال.
- اطلب شهادة Production بعد نجاح الاختبارات. صلاحيتها سنة — جدّد قبل الانتهاء بشهر على الأقل.
- راقب أوّل أسبوع إنتاج بدقة — وفّر تنبيهات على أي فشل Clearance، وكن جاهزاً لـ Rollback لإصدار يدوي مؤقت إذا تعطّل التكامل.
مزوّدو الحلول مقابل البناء الداخلي
المنشآت الصغيرة والمتوسطة عادةً تختار مزوّد حلول معتمد (مثل Foodics للمطاعم، Zoho Books وWafeq للمحاسبة، أو حلول POS متخصّصة). التكلفة الشهرية تتراوح بين 100–800 ريال حسب حجم الفواتير. الميزة: لا تتعامل مع XSD أو Schematron أو تجديد الشهادات.
المنشآت الكبيرة وشركات SaaS التي تخدم تجّاراً متعدّدين يفضّل لها البناء الداخلي: تحكّم كامل في معمارية الفوترة، تكامل عميق مع ERP وأنظمة المخزون، وقدرة على خدمة العملاء بدون رسوم لكل فاتورة. هذه هي البيئة التي صُمّمت لها هذه الأداة بالأساس.
القاعدة العملية
إذا كنت تصدر أقل من 500 فاتورة شهرياً، استخدم مزوّد معتمد — التوفير في الوقت يفوق التكلفة. فوق 5,000 فاتورة شهرياً، احسب: هل رسوم المزوّد سنوياً تكفي لتمويل مطوّر يبني تكاملاً مخصّصاً؟ غالباً نعم.
XML Namespaces: مرجع المطوّر السريع
أكثر سؤال يتلقّاه فريق دعم زاتكا من المطوّرين الجدد: «لماذا يرفض الـ XSD الملف رغم أن البنية تبدو صحيحة؟». الإجابة في 90% من الحالات: namespaces خاطئة أو مفقودة. UBL 2.1 يستخدم خمس namespaces رئيسية، وكل واحدة يجب أن تكون مُعرَّفة في عنصر Invoice الجذر بالضبط كما تتوقّعها OASIS.
الـ Namespaces الخمسة الإلزامية
الـ namespace الافتراضي للوثيقة: urn:oasis:names:specification:ubl:schema:xsd:Invoice-2. الـ cac (Common Aggregate Components): urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2. الـ cbc (Common Basic Components): urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2. الـ ext (UBL Extensions): urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2. الـ sig للتوقيع: urn:oasis:names:specification:ubl:schema:xsd:CommonSignatureComponents-2.
خطأ شائع: استخدام xmlns:cac="...CommonAggregate..." بدلاً من ...CommonAggregateComponents-2 (لاحظ -2 في النهاية). نسيان الرقم 2 يعني أنك تستخدم UBL 2.0 بدل 2.1، والـ XSD يرفض الملف بدون رسالة واضحة. تأكد دائماً من نسخ الـ namespaces من ملف عيّنة معتمَد من ZATCA SDK، لا من Stack Overflow.
ترتيب العناصر داخل Invoice
UBL 2.1 صارم في ترتيب العناصر: UBLExtensions أولاً، ثم ProfileID، ID، UUID، IssueDate، IssueTime، InvoiceTypeCode، Note، DocumentCurrencyCode، TaxCurrencyCode، ثم البيانات. تبديل ترتيب أي عنصرين يؤدّي إلى رفض الـ XSD برسالة عامة مثل "Invalid element in content". استخدم XSD viewer مثل Oxygen أو XMLSpy للتحقّق من الترتيب قبل الإرسال.
التعامل مع الأحرف العربية والترميز
ملف XML يجب أن يكون UTF-8 بدون BOM (Byte Order Mark). كثير من محرّرات Windows (مثل Notepad) تُضيف BOM تلقائياً، وهذا يُفسد parser الـ ZATCA. استخدم محرّراً قادراً على الحفظ بدون BOM (VS Code: "UTF-8" بدلاً من "UTF-8 with BOM"). الأحرف العربية الخاصة مثل الهمزات والتنوين يجب أن تُحفَظ كما هي بدون escape، لكن الرموز الأربعة & < > ' يجب escape بـ entities (&، إلخ).
تشخيص أخطاء FATOORA: رسائل غامضة وحلولها
عند رفض الفاتورة، ترسل FATOORA رسالة JSON تحتوي حقول category، code، message. لكن الرسائل في الغالب مختصرة وغير واضحة. هذا مرجع لأكثر الأخطاء شيوعاً وكيفية حلّها.
BR-S-08 / BR-S-09: عدم تطابق الضريبة
يحدث عندما تكون قيمة cbc:TaxAmount في سطر الفاتورة لا تساوي بالضبط (LineExtensionAmount × VATRate) / 100. الفرق المسموح به 0.01 فقط. السبب الأكثر شيوعاً: الـ ERP يحسب الضريبة بتقريب مختلف عن FATOORA. الحل: استخدم banker's rounding (HALF_EVEN) وتأكد أنك تحفظ القيم بدقّة 5 أرقام عشرية داخلياً ثم تقرّب إلى 2 عند الإخراج.
BR-KSA-26: PIH مفقود أو خاطئ
Previous Invoice Hash إلزامي في كل فاتورة. أول فاتورة بعد التسجيل تستخدم hash ثابت: NWZlY2ViNjZmZmM4NmYzOGQ5NTI3ODZjNmQ2OTZjNzljMmRiYzIzOWRkNGU5MWI0NjcyOWQ3M2EyN2ZiNTdlOQ== (وهو Base64 لـ SHA-256 لسلسلة فارغة). نسيان هذا في أول فاتورة يؤدّي إلى رفض كامل لكامل سلسلة الفواتير التالية.
BR-KSA-EN16931-07: التوقيع غير صالح
يعني أن XAdES signature لا يطابق محتوى الفاتورة. الأسباب: تعديل الـ XML بعد التوقيع، أو استخدام canonicalization مختلف (يجب C14N 1.1 لا 1.0)، أو شهادة منتهية. تأكد من أن CSID نشط من خلال GET /compliance/csids/{csid} قبل التوقيع.
سجلّ زمني للأخطاء
احتفظ بسجلّ كامل لكل استجابة من FATOORA لمدّة 6 سنوات على الأقل (متطلّب نظامي). يجب أن يحوي السجلّ: timestamp، نص الفاتورة، الـ hash، نص الاستجابة الكامل، و reportingId إن وُجد. هذا السجلّ مفيد جداً عند المراجعات الضريبية أو عند فتح ticket مع دعم ZATCA. ولتجهيز الفواتير قبل التحويل تحقّق من بياناتك عبر مولّد الفاتورة الضريبيّة، أداة فاتورة ZATCA، و أداة التحقّق من الرقم الضريبي.
أفضل ممارسات التكامل مع FATOORA
بناء تكامل موثوق مع FATOORA لا يتوقّف عند توليد XML صحيح، بل يمتدّ إلى كيفية إدارة دورة حياة الفاتورة بالكامل. الممارسات السليمة توفّر عليك ساعات تصحيح وغرامات محتملة.
أولاً، افصل طبقة توليد الـ XML عن طبقة التوقيع والإرسال، حتى تختبر كل مرحلة مستقلةً. ثانياً، تحقّق من الـ XML محلياً بـ XSD و Schematron قبل إرساله، لتكتشف الأخطاء قبل أن ترفضها الهيئة. ثالثاً، سجّل كل استجابة من FATOORA بالكامل (الـ UUID والـ hash ورسالة الحالة) لأغراض التدقيق وحل المشكلات.
رابعاً، صمّم آلية إعادة محاولة (retry) ذكية للتعامل مع انقطاع الاتصال، مع طابور محلي يحفظ الفواتير غير المُرسلة. هذه الممارسات الأربع تحوّل تكاملك من هشّ إلى متين قادر على التعافي من الأعطال.
تأمين الشهادات والمفاتيح
الشهادة الرقمية (CSID) والمفتاح الخاص هما قلب الثقة في تكامل FATOORA. تسرّب المفتاح الخاص يعني أن طرفاً ثالثاً يستطيع توقيع فواتير باسم منشأتك، لذلك حمايته أولوية قصوى.
لا تخزّن المفاتيح الخاصة في الشيفرة المصدرية أو في ملفات إعداد غير مشفّرة، بل استخدم خزائن مفاتيح آمنة (مثل HSM أو خدمات إدارة الأسرار)، وافصل تماماً بين شهادات الـ sandbox وشهادات الإنتاج. خلطهما في نفس الـ keystore خطأ شائع خطير.
طبّق مبدأ أقل صلاحية (least privilege) على الوصول للمفاتيح، وسجّل كل عملية توقيع، وخطّط لتدوير الشهادات قبل انتهائها. الأمن ليس خطوة واحدة بل ممارسة مستمرة تحمي منشأتك من المخاطر القانونية والمالية.
الفوترة من منظور المحاسب
ليس كل من يتعامل مع الفوترة الإلكترونية مطوّراً. كثير من المحاسبين وأصحاب المنشآت يحتاجون فهم الصورة العامة دون الغوص في تفاصيل UBL 2.1، ليتخذوا القرارات الصحيحة.
النقطة الجوهرية للمحاسب: الفوترة الإلكترونية ليست مجرد تغيير شكل الفاتورة، بل تغيير جوهري في توقيت وآلية إصدارها. في فواتير B2B لا يجوز تسليم الفاتورة للعميل قبل اعتمادها من الهيئة (Clearance)، بينما فواتير B2C تُبلّغ خلال 24 ساعة.
للمحاسب دور مهم في التحقّق من صحة البيانات الضريبية قبل إرسالها: الرقم الضريبي الصحيح، ونسبة الضريبة المطبّقة، وتطابق المجاميع. التعاون بين المحاسب والمطوّر يضمن فواتير سليمة شكلاً ومضموناً.
خلاصة عملية
تحويل بيانات الفاتورة إلى UBL 2.1 XML متوافق مع ZATCA Phase 2 يمرّ بخطوات واضحة: بناء الـ XML بالحقول الإلزامية، ثم توليد TLV QR وتحويله إلى Base64، ثم التوقيع بـ XAdES والختم الرقمي، ثم الإرسال إلى FATOORA.
تذكّر الفروق الجوهرية: Clearance لفواتير B2B القياسية (لا تُسلّم قبل الاعتماد)، و Reporting لفواتير B2C المبسّطة (خلال 24 ساعة). والتعديل بعد الاعتماد يتم عبر إشعار دائن لا بتعديل الفاتورة الأصلية.
تحقّق من الـ XML محلياً بـ XSD و Schematron قبل الإرسال، وأمّن شهاداتك ومفاتيحك، وافصل بيئة الاختبار عن الإنتاج. التكامل السليم أرخص بكثير من تصحيح الأخطاء والغرامات لاحقاً.
أخطاء شائعة في الـ XML
- InvoiceTypeCode بدون السمة name — يرفضه الـ Schematron.
- الرقم الضريبي 15 رقم لا يبدأ أو لا ينتهي بـ 3 — البائع مرفوض.
- عدم تطابق المجاميع بين
InvoiceLineوLegalMonetaryTotal(الفرق > 0.01). - التاريخ بدون توقيت أو بمنطقة زمنية غير +03:00.
- عملة الضريبة ليست SAR رغم أن العملة الأساسية SAR.
- UUID مكرّر بين فاتورتين — رفض فوري.
أسئلة شائعة
هل أحتاج آلية إعادة محاولة عند فشل الإرسال؟
نعم، آلية إعادة المحاولة الذكية مع طابور محلي للفواتير غير المُرسلة ضرورية للتعامل مع انقطاع الاتصال. لفواتير B2C لديك مهلة 24 ساعة، أما B2B فلا تُسلّم قبل الاعتماد، لذا صمّم آلية تأجيل أو موفّراً بديلاً عند الانقطاع الطويل حتى لا تتعطّل عملياتك.
لماذا أفصل بين طبقة توليد XML وطبقة التوقيع؟
الفصل بين الطبقات يتيح لك اختبار كل مرحلة على حدة، فتكتشف أخطاء البنية بمعزل عن أخطاء التوقيع والإرسال. هذا التصميم يجعل تكاملك أمتن وأسهل في الصيانة، ويسرّع تشخيص أي خلل لأنك تعرف بالضبط في أي طبقة وقع.
هل أفصل قواعد بيانات الاختبار عن الإنتاج؟
نعم، استخدم قواعد بيانات منفصلة فعلياً لبيئتي الاختبار والإنتاج، ولا تخلط شهادات الـ sandbox مع شهادات الإنتاج في نفس الخزينة. أضف علامة مرئية للفواتير المُولّدة في الاختبار حتى لا تُسلّم للعملاء بالخطأ، فالخلط بين البيئتين مصدر شائع للأخطاء الخطيرة.
هل أوثّق استجابات FATOORA كاملة؟
نعم، سجّل كل استجابة بالكامل: الـ UUID والـ hash ورسالة الحالة وأي رمز خطأ. هذا السجل ضروري للتدقيق وحل المشكلات لاحقاً، ويثبت التزامك عند أي مراجعة من الهيئة. الاعتماد على الذاكرة أو السجلات الناقصة يصعّب تتبّع أي خلل في الفواتير المُرسلة.
ما الفرق العملي بين فواتير B2B و B2C في التكامل؟
فواتير B2B القياسية تمرّ بمسار Clearance، ولا يجوز تسليمها للعميل قبل اعتمادها من الهيئة. أما فواتير B2C المبسّطة فتمرّ بمسار Reporting، وتُبلَّغ خلال 24 ساعة دون انتظار ردّ قبل تسليمها للمستهلك. الخلط بين المسارين خطأ قد يعرّضك لغرامة.
كيف أتعامل مع تعديل فاتورة بعد اعتمادها؟
لا يمكن تعديل فاتورة معتمدة. الحل إصدار إشعار دائن يلغي القيم الأصلية ثم إصدار فاتورة جديدة بالقيم الصحيحة، مع الإشارة للفاتورة الأصلية في BillingReference. الإشعار يمرّ بنفس مسار الاعتماد، ويحافظ على سلامة السجل الضريبي.
هل التعاون بين المحاسب والمطوّر ضروري؟
نعم. المطوّر يضمن سلامة بنية الـ XML والتوقيع، والمحاسب يتحقّق من صحة البيانات الضريبية: الرقم الضريبي ونسبة الضريبة وتطابق المجاميع. هذا التعاون يضمن فواتير سليمة شكلاً ومضموناً ويقلّل الرفض والغرامات.
ما أفضل طريقة لاختبار التكامل قبل الإنتاج؟
ابدأ دائماً في بيئة الـ Sandbox، وافصل طبقة توليد الـ XML عن طبقة التوقيع والإرسال لتختبر كل مرحلة على حدة. تحقّق من الـ XML محلياً بـ XSD و Schematron قبل إرساله للهيئة، لتكتشف الأخطاء مبكراً دون استهلاك محاولات الإنتاج أو التعرّض لرفض رسمي.
كيف أحمي الشهادة الرقمية والمفتاح الخاص؟
لا تخزّن المفتاح الخاص في الشيفرة المصدرية أو في ملفات إعداد غير مشفّرة. استخدم خزائن مفاتيح آمنة، وافصل تماماً بين شهادات الـ sandbox وشهادات الإنتاج، وطبّق مبدأ أقل صلاحية على الوصول. تسرّب المفتاح يعني أن طرفاً ثالثاً يستطيع توقيع فواتير باسم منشأتك.
لماذا يجب التحقّق محلياً قبل الإرسال لـ FATOORA؟
التحقّق المحلي بـ XSD و Schematron يكشف أخطاء البنية والحقول قبل أن ترفضها الهيئة، ما يوفّر وقتاً ويقلّل المحاولات الفاشلة. إرسال XML غير صالح مراراً يبطئ سير عملك وقد يثير تنبيهات، بينما التحقّق المسبق يجعل الإرسال أنظف وأسرع.
هل أحتاج هذه الأداة إذا كنت أستخدم ERP موافَق عليه من ZATCA؟
لا — ERP يبني الـ XML ويرسله. الأداة مفيدة عندما تكتب تكاملاً مخصّصاً، تختبر مخرجات ERP، أو تتعلّم البنية.
هل الـ XML الناتج صالح للإرسال مباشرة لـ FATOORA؟
الـ XML يحوي البنية والحقول الأساسية، لكن FATOORA تتطلّب توقيع XAdES و TLV موسّع. الأداة تنتج الـ XML قبل خطوة التوقيع.
كيف يحسب الـ TLV QR رياضياً؟
لكل حقل: 0x[Tag] 0x[Length] [Value-Bytes]. نضمّ كل البايتات ونحوّلها إلى Base64. الأداة تستخدم نفس الدالّة الموجودة في كل أدوات ZATCA لدينا.
هل يدعم UBL 2.1 الإشعارات الدائنة والمدينة؟
نعم، عبر InvoiceTypeCode = 381 (Credit Note) أو 383 (Debit Note)، مع الإشارة للفاتورة الأصلية في BillingReference.
ما الفرق بين هذه الأداة وأداة «فاتورة زاتكا الكاملة»؟
هذه الأداة JSON-in / XML-out — مخصّصة للمطوّرين. أداة فاتورة زاتكا الكاملة توفّر واجهة كاملة لإدخال البائع والمشتري والبنود، مع PDF و QR و XML معاً.
هل يعمل المتصفّح بدون اتصال بالإنترنت؟
نعم — التحويل والـ QR كلها محليّة في المتصفح. لا ترسل بيانات لأي خادم.
هل أستطيع إدخال أكثر من بند؟
نعم، فقط أضف عناصر إلى مصفوفة lines في JSON. كل بند يحتوي id, name, qty, unitPrice, vatRate.
أين أجد توثيق FATOORA الرسمي؟
منصة ZATCA: portal.fatoora.zatca.gov.sa، قسم Developer Portal يحتوي XSD وSchematron وأمثلة XML معتمدة.
ما الفرق بين Clearance و Reporting في الاستجابة؟
Clearance للفواتير B2B القياسية: ZATCA تردّ بـ clearedInvoice يحتوي الـ XML بعد إضافة QR موسّع وختم رسمي. لا يجوز إرسال الفاتورة للمشتري قبل استلام هذه الاستجابة. Reporting للفواتير B2C المبسّطة: ترسلها خلال 24 ساعة وتستلم reportingStatus: REPORTED. لا تنتظر الردّ لتسليم الفاتورة للمستهلك.
كيف أتعامل مع انقطاع الاتصال بـ FATOORA؟
صمّم طابوراً (queue) محليّاً يحفظ الفواتير غير المُرسَلة. لـ B2C لديك 24 ساعة، لذا انقطاع 30 دقيقة لا يُسبب مشكلة. لـ B2B تحتاج آلية fallback: إما تأجيل تسليم الفاتورة للعميل، أو استخدام موفّر بديل ثانوي. أبداً لا تُرسل فاتورة B2B للعميل قبل Clearance — هذا يُعرّضك لغرامة كاملة.
هل أحتاج خادمَين منفصلَين لـ Sandbox و Production؟
لا — يكفي مفتاح بيئة (ENV) يبدّل بين الـ endpoints والـ CSID. لكن استخدم قواعد بيانات منفصلة فعلياً، ولا تخلط شهادات الـ sandbox مع شهادات production في نفس keystore. أيضاً: تأكد من أن الفواتير المُولَّدة في sandbox تحوي علامة مرئية (مثل watermark) حتى لا تُسلَّم للعملاء بالخطأ.
ماذا لو احتجت تعديل فاتورة بعد Clearance؟
لا يمكن تعديل فاتورة معتمدة. الحل: إصدار إشعار دائن (Credit Note، InvoiceTypeCode 381) يلغي القيم الأصلية، ثم إصدار فاتورة جديدة بالقيم الصحيحة. الإشعار يجب أن يُشير للفاتورة الأصلية في BillingReference، ويمرّ بنفس مسار Clearance.
هل أستطيع توليد UUID على مستوى تطبيقي بدلاً من قاعدة البيانات؟
نعم — UUID v4 (عشوائي) كافٍ تماماً ومقبول من ZATCA. توليده على مستوى التطبيق يقلّل round-trips لقاعدة البيانات ويحسّن الأداء. التحدّي الوحيد: ضمان عدم التكرار في حالة sharding أو multi-instance. UUID v4 يوفّر 122 bit من العشوائية، لذا احتمال التصادم في مليار فاتورة شهرياً يقارب الصفر عملياً. سجّل الـ UUID في قاعدة البيانات مع unique constraint كطبقة أمان إضافية.
أدوات ذات صلة
أدوات أخرى مجانية على ArabToolBox، كلها تعمل في متصفّحك بدون تسجيل.
- فحص جاهزية ZATCA المرحلة الثانيةقائمة فحص شاملة لجاهزية منشأتك للفوترة الإلكترونية المرحلة الثانية
- فاكّ ترميز ZATCA TLV QRألصق QR Base64 واستخرج الحقول الخمسة وفق ZATCA
- التحقق من الرقم الضريبيتحقق من صيغة الرقم الضريبي قبل إصدار الفاتورة
- فاتورة زاتكاإصدار فواتير متوافقة مع المرحلة الثانية — UBL 2.1 + QR + ختم
- مولّد QR Codeأنشئ QR Code فوراً — 8 أنواع بما فيها فاتورة زاتكا TLV
- Base64 ترميز وفكترميز وفك Base64 — نصوص، صور، ملفات — محلياً 100%