شرح XML UBL 2.1 وربط ZATCA API: الدليل التقني الكامل للمطورين في السعودية

يواجه المطورون في السعودية تحديًا تقنيًا متزايد التعقيد مع تطبيق المرحلة الثانية من الفوترة الإلكترونية. لم يعد الأمر مجرد إنشاء ملف PDF وإضافة QR Code بسيط، بل أصبح يتطلب فهمًا عميقًا لمعايير UBL 2.1 وآليات التوقيع الرقمي والتكامل مع ZATCA API. هذا الدليل موجه للمهندسين الذين يبنون أنظمة فوترة من الصفر أو يدمجون منصات التجارة الإلكترونية مع متطلبات هيئة الزكاة والضريبة والجمارك.

لفهم الصورة الكاملة لنظام الفوترة الإلكترونية في السعودية، بما في ذلك المتطلبات النظامية، مراحل التطبيق، والفرق بين الفواتير القياسية والمبسطة، يمكن الرجوع إلى دليل الفوترة الإلكترونية في السعودية 2026: المتطلبات والربط مع ZATCA . هذا الدليل يوفر الأساس المفاهيمي قبل الدخول في التفاصيل التقنية لـ XML UBL و ZATCA API.

شرح بنية ملف XML UBL 2.1 والربط البرمجي مع ZATCA API
مخطط تقني يوضح بنية مستندات UBL 2.1 وآلية التكامل مع واجهات برمجة التطبيقات (API) الخاصة بنظام فاتورة.

ما هو UBL 2.1 ولماذا تعتمد ZATCA عليه؟

Universal Business Language (UBL) هو معيار دولي تم تطويره من قبل منظمة OASIS لتوحيد تبادل المستندات التجارية إلكترونيًا. النسخة 2.1 تحديدًا توفر هيكلية محددة وقابلة للتحقق آليًا لتمثيل الفواتير والإشعارات الدائنة والمدينة.

الفرق الجوهري بين ملف XML تقليدي وملف UBL يكمن في التوحيد المعياري. في XML العادي، يمكن للمطور تسمية العناصر كما يشاء، لكن UBL يفرض أسماء محددة، تسلسل هرمي معرّف مسبقًا، وقواعد تحقق صارمة. هذا يضمن أن أي نظام يدعم UBL 2.1 يمكنه قراءة ومعالجة الفاتورة بدون الحاجة لاتفاقيات خاصة بين الأطراف.

اختارت ZATCA هذا المعيار لثلاثة أسباب رئيسية: قابلية التشغيل البيني بين الأنظمة المختلفة، إمكانية التحقق الآلي من صحة البيانات، والتوافق مع معايير الفوترة الإلكترونية المعتمدة عالميًا مثل Peppol وe-Invoicing الأوروبي.

بنية ملف XML UBL في الفوترة الإلكترونية

كل فاتورة UBL تبدأ بعنصر جذري يحدد نوع المستند. في حالة الفواتير الضريبية نستخدم Invoice، بينما الإشعارات الدائنة تستخدم CreditNote والمدينة تستخدم DebitNote. كل عنصر جذري يتطلب تعريف مجموعة من Namespaces الإلزامية.

Namespaces الأساسية

هناك ثلاثة Namespaces أساسية يجب تضمينها:

  • xmlns: الـ namespace الافتراضي لـ UBL Invoice
  • xmlns:cac: Common Aggregate Components (العناصر المركبة)
  • xmlns:cbc: Common Basic Components (العناصر الأساسية)

أي خطأ في كتابة هذه الـ Namespaces سيؤدي لرفض الفاتورة فورًا من ZATCA API. يجب نسخها بدقة تامة من المواصفات الرسمية.

مثال الهيكل الأساسي

<?xml version="1.0" encoding="UTF-8"?>
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
         xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
         xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">
  
  <cbc:UBLVersionID>2.1</cbc:UBLVersionID>
  <cbc:ProfileID>reporting:1.0</cbc:ProfileID>
  <cbc:ID>INV-2026-001</cbc:ID>
  <cbc:UUID>123e4567-e89b-12d3-a456-426614174000</cbc:UUID>
  <cbc:IssueDate>2026-02-16</cbc:IssueDate>
  <cbc:IssueTime>14:30:00</cbc:IssueTime>
  <cbc:InvoiceTypeCode name="0200000">388</cbc:InvoiceTypeCode>
  
  <cac:AccountingSupplierParty>
    <!-- بيانات البائع -->
  </cac:AccountingSupplierParty>
  
  <cac:AccountingCustomerParty>
    <!-- بيانات المشتري -->
  </cac:AccountingCustomerParty>
  
  <cac:InvoiceLine>
    <!-- سطور الفاتورة -->
  </cac:InvoiceLine>
  
  <cac:LegalMonetaryTotal>
    <!-- المجاميع -->
  </cac:LegalMonetaryTotal>
  
  <cac:TaxTotal>
    <!-- الضريبة -->
  </cac:TaxTotal>
  
</Invoice>

هذا الهيكل يمثل الحد الأدنى المطلوب. أي عنصر مفقود من العناصر الإلزامية سيسبب فشل التحقق.

العناصر الإلزامية في فاتورة ZATCA

معرفات الفاتورة

كل فاتورة تحتاج ثلاثة معرفات مختلفة:

UBLVersionID: يجب أن يكون دائمًا "2.1" للتوافق مع المعيار الحالي.

ID: رقم تسلسلي فريد للفاتورة ضمن نظامك. يمكن أن يكون "INV-001" أو أي نمط تسلسلي تستخدمه.

UUID: معرف فريد عالميًا يجب توليده باستخدام UUID v4. هذا المعرف لا يمكن أن يتكرر أبدًا، حتى عبر أنظمة مختلفة. استخدم مكتبات UUID المعيارية في لغة البرمجة التي تستخدمها، ولا تحاول توليده يدويًا.

ProfileID وأنواع الفواتير

هذا العنصر يحدد نوع المعالجة المطلوبة من ZATCA. القيمتان الأساسيتان هما:

  • reporting:1.0: للفواتير المبسطة (B2C) التي تُرسل للعميل فورًا ويتم إبلاغ ZATCA خلال 24 ساعة
  • clearance:1.0: للفواتير الضريبية (B2B) التي تحتاج موافقة من ZATCA قبل إرسالها للعميل

الاختيار الخاطئ هنا يؤدي لرفض الفاتورة أو معالجتها بطريقة غير صحيحة. إذا كنت تبني نظامًا يدعم كلا النوعين، يجب أن تكون هذه القيمة ديناميكية بناءً على نوع العميل (منشأة أم فرد).

InvoiceTypeCode

الأكواد الأساسية المستخدمة:

  • 388: فاتورة ضريبية (Tax Invoice)
  • 381: إشعار دائن (Credit Note)
  • 383: إشعار مدين (Debit Note)

يجب إضافة attribute اسمه "name" يحتوي على كود فرعي يحدد نوع الفاتورة حسب تصنيف ZATCA. للفواتير المبسطة: "0200000"، وللفواتير الضريبية: "0100000".

التاريخ والوقت

عنصر IssueDate يجب أن يكون بصيغة ISO 8601: YYYY-MM-DD. أي صيغة أخرى سترفض. عنصر IssueTime يجب أن يكون بصيغة HH:MM:SS. هذان العنصران يحددان لحظة إصدار الفاتورة وليس لحظة البيع أو التسليم.

بيانات الأطراف

عنصر AccountingSupplierParty يحتوي على بيانات البائع (منشأتك)، ويجب أن يتضمن: الرقم الضريبي، اسم المنشأة، العنوان. عنصر AccountingCustomerParty يحتوي على بيانات المشتري. في الفواتير الضريبية (B2B)، الرقم الضريبي للمشتري إلزامي. في الفواتير المبسطة (B2C)، يمكن الاكتفاء بالاسم.

المجاميع والضريبة

عنصر LegalMonetaryTotal يحتوي على أربعة قيم أساسية: LineExtensionAmount (مجموع السطور قبل الضريبة)، TaxExclusiveAmount (الإجمالي بدون ضريبة)، TaxInclusiveAmount (الإجمالي مع الضريبة)، وPayableAmount (المبلغ المستحق الدفع).

عنصر TaxTotal يجب أن يحتوي على TaxAmount (إجمالي الضريبة) وعلى الأقل TaxSubtotal واحد يحدد نسبة الضريبة المطبقة (عادة 15% للسعودية).

آلية التوقيع الرقمي في XML (XMLDSec)

التوقيع الرقمي في UBL يتم باستخدام معيار XML Digital Signature. العملية تتكون من ثلاث خطوات حرجة: Canonicalization، Hash Generation، ثم التوقيع بالمفتاح الخاص.

Canonicalization (التوحيد القياسي)

قبل حساب الـ Hash، يجب تحويل XML لصيغة قياسية موحدة. هذه العملية تزيل الاختلافات غير الجوهرية مثل المسافات الزائدة، ترتيب Attributes، وطريقة إغلاق العناصر الفارغة. المعيار المستخدم عادة هو Exclusive XML Canonicalization.

السبب وراء هذه الخطوة: نفس محتوى XML يمكن كتابته بطرق مختلفة تقنيًا. بدون Canonicalization، مسافة واحدة زائدة أو تغيير بسيط في الترتيب سيؤدي لـ Hash مختلف تماماً، وبالتالي فشل التحقق من التوقيع.

حساب SHA-256 Hash

بعد Canonicalization، يتم حساب SHA-256 hash للمحتوى. هذا الـ Hash يمثل "بصمة" الفاتورة. أي تغيير ولو بحرف واحد سينتج Hash مختلف تماماً.

التوقيع بـ ECDSA

يتم توقيع الـ Hash باستخدام المفتاح الخاص من الشهادة الرقمية (CSID) التي حصلت عليها من ZATCA. الخوارزمية المستخدمة هي ECDSA مع منحنى secp256r1 (المعروف أيضًا بـ P-256). التوقيع الناتج يُشفّر بـ Base64 ويوضع داخل عنصر UBLExtensions في بداية الفاتورة.

مكان إدراج التوقيع

<Invoice>
  <ext:UBLExtensions>
    <ext:UBLExtension>
      <ext:ExtensionURI>urn:oasis:names:specification:ubl:dsig:enveloped:xades</ext:ExtensionURI>
      <ext:ExtensionContent>
        <sig:UBLDocumentSignatures>
          <sac:SignatureInformation>
            <cbc:ID>urn:oasis:names:specification:ubl:signature:1</cbc:ID>
            <!-- التوقيع الرقمي هنا -->
          </sac:SignatureInformation>
        </sig:UBLDocumentSignatures>
      </ext:ExtensionContent>
    </ext:UBLExtension>
  </ext:UBLExtensions>
  
  <!-- باقي عناصر الفاتورة -->
</Invoice>

الأخطاء الشائعة في التوقيع

أكثر خطأ يواجه المطورين: عدم إجراء Canonicalization بشكل صحيح قبل الـ Hash. إذا قمت بحساب Hash للملف مباشرة كما هو، ثم أرسلته لـ ZATCA، سيقوم السيرفر بإجراء Canonicalization على نسخته ويحصل على Hash مختلف، مما يؤدي لرفض التوقيع.

الخطأ الثاني: استخدام شهادة منتهية أو شهادة Compliance في البيئة الإنتاجية. شهادة Compliance صالحة فقط في Sandbox، أما الإنتاج فيتطلب Production CSID.

Hash Chain و Previous Invoice Hash (PIH)

من المتطلبات الفريدة في ZATCA: كل فاتورة يجب أن تحتوي على Hash الفاتورة السابقة. هذا ينشئ "سلسلة" (Chain) من الفواتير يصعب التلاعب بها. إذا حاول أحد تعديل فاتورة قديمة، سيتغير Hash الخاص بها، وبالتالي ستصبح جميع الفواتير اللاحقة غير صالحة.

الفاتورة الأولى

المشكلة: الفاتورة الأولى في النظام لا يوجد لها فاتورة سابقة. الحل: القيمة المتفق عليها هي "0" كـ Previous Invoice Hash للفاتورة الأولى فقط.

حفظ الـ Hash

بعد توليد فاتورة وإرسالها لـ ZATCA، يجب حفظ الـ Hash الخاص بها في قاعدة البيانات. هذا الـ Hash سيستخدم كـ PIH في الفاتورة التالية. إذا فقدت هذا الـ Hash، ستنكسر السلسلة ولن تستطيع إصدار فواتير جديدة إلا بإعادة Onboarding.

مثال كود Python لحساب Hash

import hashlib
from lxml import etree

def calculate_invoice_hash(xml_content, previous_hash):
    # Canonicalization باستخدام lxml
    tree = etree.fromstring(xml_content.encode('utf-8'))
    canonical_xml = etree.tostring(
        tree, 
        method='c14n', 
        exclusive=True,
        with_comments=False
    )
    
    # دمج المحتوى مع PIH
    data_to_hash = canonical_xml + previous_hash.encode('utf-8')
    
    # حساب SHA-256
    sha256_hash = hashlib.sha256(data_to_hash).hexdigest()
    
    return sha256_hash

# مثال الاستخدام
xml_invoice = "<Invoice>...</Invoice>"
previous_invoice_hash = "a1b2c3d4e5f6..."  # من قاعدة البيانات

current_hash = calculate_invoice_hash(xml_invoice, previous_invoice_hash)
print(f"Hash: {current_hash}")

# احفظ current_hash في قاعدة البيانات للفاتورة التالية

استخدام مكتبة lxml يضمن Canonicalization صحيح ومطابق لمعيار W3C. مكتبة xml.etree.ElementTree القياسية لا تدعم Canonicalization الكامل في جميع إصدارات Python.

Clearance vs Reporting في ZATCA API

أحد الجوانب المحيرة للمطورين الجدد: الفرق بين Clearance و Reporting. الأمر ليس مجرد اختلاف في API endpoint، بل اختلاف جذري في سير العمل.

إذا كنت بحاجة لفهم الفرق التنظيمي والتشغيلي بين الفواتير الضريبية والمبسطة ومتطلبات كل منها حسب هيئة الزكاة والضريبة والجمارك، راجع الدليل الكامل للفوترة الإلكترونية في السعودية الذي يشرح متى يتم استخدام Clearance ومتى يتم استخدام Reporting من منظور النظام والامتثال.

Clearance (الفواتير الضريبية - B2B)

في هذا النمط، يجب إرسال الفاتورة لـ ZATCA والحصول على موافقة (Clearance) قبل إرسالها للعميل. السير العملي:

  1. العميل يقوم بالشراء
  2. النظام ينشئ فاتورة XML UBL
  3. يتم إرسالها لـ ZATCA عبر /invoices/clearance/single
  4. ZATCA يتحقق ويعيد Clearance ID وتوقيع رقمي
  5. فقط بعد ذلك يمكن إرسال الفاتورة للعميل

إذا رفضت ZATCA الفاتورة، لا يمكن إصدارها للعميل. يجب تصحيح الأخطاء وإعادة المحاولة.

Reporting (الفواتير المبسطة - B2C)

هنا الأمر أبسط. الفاتورة ترسل للعميل فورًا، ثم يتم إبلاغ ZATCA خلال 24 ساعة:

  1. العميل يقوم بالشراء
  2. النظام ينشئ فاتورة وترسل للعميل فوراً
  3. في الخلفية، ترسل نسخة لـ ZATCA عبر /invoices/reporting/single
  4. ZATCA يتحقق ويرسل تأكيد الاستلام

الفرق الزمني كبير. Clearance يجب أن يكون متزامنًا (Synchronous)، بينما Reporting يمكن أن يكون غير متزامن (Asynchronous) طالما تم خلال 24 ساعة.

جدول المقارنة

الجانب Clearance (B2B) Reporting (B2C)
ProfileID clearance:1.0 reporting:1.0
الرقم الضريبي للمشتري إلزامي اختياري
التوقيت قبل الإرسال للعميل خلال 24 ساعة
API Endpoint /invoices/clearance/single /invoices/reporting/single
الموافقة تحتاج موافقة صريحة تأكيد استلام فقط
QR Code اختياري إلزامي

اختيار النوع الخطأ يؤدي لرفض الفاتورة أو معالجتها بشكل غير صحيح. في الأنظمة التي تدعم كلا النوعين، يجب أن يكون التحديد تلقائيًا بناءً على نوع العميل.

مثال API Response من ZATCA

عند نجاح عملية Clearance، ترجع ZATCA استجابة JSON تحتوي على البيانات الحرجة:

{
  "clearanceStatus": "CLEARED",
  "clearedInvoice": "base64_signed_xml_here",
  "clearanceId": "c1a2b3c4d5e6f7g8h9i0",
  "invoiceHash": "a1b2c3...xyz",
  "qrCode": "base64_qr_code_here",
  "reportingStatus": "NOT_REPORTED",
  "validationResults": {
    "status": "PASS",
    "warningMessages": [],
    "errorMessages": []
  }
}

بينما في Reporting، الاستجابة أبسط:

{
  "reportingStatus": "REPORTED",
  "invoiceHash": "a1b2c3...xyz",
  "validationResults": {
    "status": "PASS",
    "warningMessages": [],
    "infoMessages": ["Invoice reported successfully"]
  }
}

يجب حفظ clearanceId و invoiceHash في قاعدة البيانات للمراجعة والتدقيق. هذه القيم ضرورية للتحقق من صحة الفواتير لاحقًا.

إدراج QR Code داخل UBL

في الفواتير المبسطة (B2C)، QR Code إلزامي. ليس QR عاديًا، بل يحتوي على بيانات مشفرة بصيغة TLV (Tag-Length-Value) محولة لـ Base64.

محتوى QR Code

المرحلة الثانية تتطلب 5 حقول أساسية كحد أدنى:

  1. اسم البائع
  2. الرقم الضريبي للبائع
  3. تاريخ ووقت الفاتورة
  4. إجمالي الفاتورة (مع الضريبة)
  5. قيمة الضريبة

كل حقل يُشفّر بصيغة TLV: رقم Tag (بايت واحد)، طول البيانات (بايت واحد)، ثم البيانات نفسها. جميع الحقول تُدمج في مصفوفة بايتات واحدة، ثم تحول لـ Base64.

إدراجه في XML

الـ QR Code يوضع في عنصر AdditionalDocumentReference:

<cac:AdditionalDocumentReference>
  <cbc:ID>QR</cbc:ID>
  <cac:Attachment>
    <cbc:EmbeddedDocumentBinaryObject mimeCode="text/plain">
      VGVzdCBCYXNlNjQgUVIgQ29kZQ==
    </cbc:EmbeddedDocumentBinaryObject>
  </cac:Attachment>
</cac:AdditionalDocumentReference>

القيمة داخل EmbeddedDocumentBinaryObject هي Base64 الناتج من TLV encoding. استخدام نص عادي بدلاً من TLV هو خطأ شائع جداً يؤدي لرفض الفاتورة.

التحقق والاختبار قبل الإرسال إلى ZATCA

قبل إرسال أي فاتورة لبيئة الإنتاج، يجب المرور بثلاث مراحل تحقق.

XML Schema Validation

أول خطوة: التحقق من أن الملف يطابق UBL 2.1 schema. يمكن استخدام أدوات مثل xmllint من سطر الأوامر:

xmllint --noout --schema UBL-Invoice-2.1.xsd invoice.xml

إذا ظهرت أخطاء، يجب تصحيحها قبل المتابعة. الأخطاء الشائعة: عنصر في مكان خاطئ، نوع بيانات خاطئ، أو Namespace مفقود.

ZATCA Sandbox

بعد التحقق من Schema، استخدم بيئة Sandbox للاختبار. هذه البيئة تحاكي الإنتاج تماماً لكن بدون تأثير على البيانات الفعلية. يمكنك اختبار:

  • عملية Onboarding كاملة
  • إصدار فواتير تجريبية
  • اختبار Clearance و Reporting
  • محاكاة سيناريوهات الفشل

الخطأ الأكبر: تجاوز Sandbox والاختبار مباشرة في الإنتاج. هذا يؤدي لمشاكل يصعب تتبعها.

Schematron Validation

ZATCA تستخدم Schematron rules إضافية فوق XML Schema. هذه القواعد تتحقق من منطق الأعمال، مثل: هل مجموع السطور يساوي الإجمالي؟ هل نسبة الضريبة صحيحة؟ استخدم ZATCA SDK للتحقق من هذه القواعد قبل الإرسال.

أخطاء شائعة يقع فيها المطورون

الخطأ السبب الحل
invalid-signature فشل التحقق من التوقيع الرقمي تأكد من Canonicalization قبل Hash، واستخدم الشهادة الصحيحة
missing-element عنصر إلزامي مفقود راجع قائمة العناصر الإلزامية وتأكد من وجودها جميعاً
invalid-namespace Namespace خاطئ أو مفقود انسخ Namespaces من المواصفات الرسمية بدقة
hash-mismatch Previous Invoice Hash غير صحيح تأكد من حفظ Hash كل فاتورة في قاعدة البيانات
invalid-qr QR Code بصيغة خاطئة استخدم TLV encoding ثم Base64، ليس نص عادي
date-format صيغة تاريخ غير صحيحة استخدم YYYY-MM-DD للتاريخ و HH:MM:SS للوقت
duplicate-uuid UUID مكرر استخدم UUID v4 الذي يضمن عدم التكرار
certificate-expired الشهادة الرقمية منتهية راقب تاريخ انتهاء CSID وجددها قبل 30 يوم

معظم هذه الأخطاء يمكن اكتشافها في Sandbox قبل الإنتاج. خصص وقتًا كافيًا للاختبار الشامل.

خطوات تنفيذ تكامل ZATCA من الصفر

للمطورين الذين يبدأون من الصفر، هذه خارطة الطريق الكاملة لتنفيذ تكامل ناجح مع ZATCA API:

الخطوة الأولى: التسجيل والحصول على بيانات الوصول

ابدأ بالتسجيل في بوابة ZATCA للحصول على: اسم مستخدم API، كلمة مرور، ومعرف الجهاز (Device ID). هذه البيانات ضرورية لعملية Onboarding والحصول على الشهادة الرقمية.

الخطوة الثانية: توليد CSR والحصول على CSID

استخدم OpenSSL لتوليد Certificate Signing Request (CSR)، ثم أرسله لـ ZATCA عبر API Onboarding. ستحصل على Compliance CSID للاختبار في Sandbox، ثم Production CSID بعد اجتياز الاختبارات.

الخطوة الثالثة: بناء XML Generator

اكتب وحدة برمجية تحول بيانات الفاتورة (من قاعدة البيانات أو API) إلى XML UBL 2.1 صالح. استخدم مكتبات XML موثوقة مثل lxml في Python أو xmlbuilder2 في Node.js. تأكد من Namespaces الصحيحة وترتيب العناصر.

الخطوة الرابعة: تطبيق التوقيع الرقمي

ابنِ وحدة منفصلة للتوقيع الرقمي تقوم بـ: Canonicalization، حساب SHA-256 Hash، التوقيع بـ ECDSA، وإدراج التوقيع في UBLExtensions. استخدم مكتبات مثل signxml بدلاً من كتابة الكود من الصفر.

الخطوة الخامسة: إدارة Hash Chain

أنشئ جدول في قاعدة البيانات لحفظ hash كل فاتورة. عند إنشاء فاتورة جديدة، اجلب hash الفاتورة السابقة وأدرجه في PIH. تأكد من آلية قفل (Lock) لمنع إصدار فاتورتين في نفس اللحظة.

الخطوة السادسة: التكامل مع ZATCA API

اكتب client لـ ZATCA API يدعم Clearance و Reporting endpoints. استخدم HTTPS مع Mutual TLS (mTLS) للأمان. تأكد من إرسال الشهادة الصحيحة (Compliance أو Production) حسب البيئة.

الخطوة السابعة: الاختبار الشامل في Sandbox

قبل الإطلاق، اختبر جميع السيناريوهات: فواتير B2B و B2C، إشعارات دائنة ومدينة، رفض الفواتير، انقطاع الشبكة، انتهاء الشهادة. سجل كل خطأ واحفظ طريقة حله.

للمطورين الذين يعملون على تكامل منصات تجارة إلكترونية محددة، يمكن الاطلاع على ربط Shopify مع ZATCA لفهم كيفية تطبيق هذه المفاهيم عمليًا. بالنسبة للمطورين المهتمين بمعايير الفوترة الإلكترونية الإقليمية، يمكن مراجعة دليل الفوترة الإلكترونية Peppol في الإمارات لفهم الاختلافات بين الأنظمة.

أفضل الممارسات في تطوير تكامل ZATCA

لا تبنِ التوقيع الرقمي من الصفر

التوقيع الرقمي، Canonicalization، وإدارة الشهادات معقدة جداً. استخدم مكتبات مثبتة مثل signxml في Python، xml-crypto في Node.js، أو System.Security.Cryptography.Xml في .NET. محاولة بناء هذه الوظائف يدوياً ستؤدي لأخطاء أمنية وثغرات صعبة الاكتشاف.

افصل طبقة التوقيع عن طبقة إنشاء XML

في المعمار البرمجي، يجب أن تكون هناك طبقتان منفصلتان: واحدة لإنشاء محتوى الفاتورة، وأخرى للتوقيع والتشفير. هذا يسهل الاختبار، الصيانة، وإعادة الاستخدام. يمكنك اختبار XML generation بدون الحاجة لشهادات، واختبار التوقيع بملفات ثابتة.

تحقق قبل الإرسال

قبل استدعاء ZATCA API، قم بـ:

  1. XML Schema validation
  2. Schematron rules validation
  3. التحقق من وجود جميع العناصر الإلزامية
  4. التحقق من صحة المعادلات (مجموع السطور = الإجمالي)
  5. التحقق من صحة التوقيع محلياً

كل خطوة تتطلب وقتًا إضافيًا، لكنها توفر ساعات من التصحيح لاحقاً.

إدارة الشهادات بأمان

الشهادة الرقمية (CSID) والمفتاح الخاص يجب حمايتهما بعناية فائقة:

  • لا تخزنهما في الكود المصدري
  • استخدم Key Vault مثل AWS KMS أو Azure Key Vault
  • راقب تاريخ انتهاء الصلاحية وأتمت عملية التجديد
  • استخدم Compliance CSID فقط في Sandbox، و Production CSID في الإنتاج

معالجة الأخطاء بذكاء

عند فشل API call، لا ترسل نفس الفاتورة مباشرة. حلل رسالة الخطأ أولاً:

  • إذا كان خطأ في البنية (invalid XML)، صححه قبل إعادة المحاولة
  • إذا كان timeout، أعد المحاولة مع exponential backoff
  • إذا كان خطأ في الشهادة، تحقق من صلاحيتها
  • احفظ الأخطاء في logs للتحليل لاحقاً

اختبر Hash Chain بشكل منفصل

سلسلة Hash حرجة جداً. اكتب unit tests تتحقق من:

  • أول فاتورة تستخدم PIH = "0"
  • كل فاتورة لاحقة تستخدم hash الفاتورة السابقة
  • إذا فشلت فاتورة وسط السلسلة، كيف ستتعامل مع ذلك

استخدم قاعدة بيانات موثوقة لحفظ Hash كل فاتورة. إذا فقدت hash واحد، ستنكسر السلسلة.

استخدم Queue للمعالجة غير المتزامنة

في حالة Reporting (B2C)، لست مضطرًا للإرسال فورياً. استخدم نظام Queue مثل RabbitMQ أو Redis Queue لمعالجة الفواتير في الخلفية. هذا يحسن أداء التطبيق ويسمح بإعادة المحاولة التلقائية عند الفشل.

Checklist قبل الإطلاق إلى بيئة الإنتاج

  • ✔ XML Valid حسب UBL 2.1 Schema
  • ✔ XML Schema Validation ناجحة
  • ✔ Schematron Validation ناجحة
  • ✔ التوقيع الرقمي تم التحقق منه محليًا
  • ✔ Previous Invoice Hash محفوظ في قاعدة البيانات
  • ✔ تم اختبار API في Sandbox بالكامل
  • ✔ تم تثبيت Production CSID بشكل صحيح
  • ✔ تم اختبار سيناريوهات الفشل (Timeout / Invalid Data)

الأسئلة الشائعة للمطورين حول ZATCA و XML UBL 2.1

ما الفرق بين UUID و Invoice ID في فاتورة UBL؟

Invoice ID هو الرقم التسلسلي الداخلي للفاتورة داخل نظامك (مثل INV-2026-001)، بينما UUID هو معرف فريد عالميًا يتم توليده باستخدام UUID v4 ولا يمكن أن يتكرر أبدًا. ZATCA تعتمد على UUID لضمان عدم ازدواجية الفواتير حتى عبر أنظمة مختلفة.

ماذا يحدث إذا انقطعت سلسلة Hash (Previous Invoice Hash)؟

إذا فُقد أو تم تعديل Hash إحدى الفواتير، ستنكسر السلسلة بالكامل ولن تتمكن من إصدار فواتير جديدة بشكل صحيح. الحل غالبًا يتطلب إعادة Onboarding للنظام والحصول على CSID جديد. لذلك يجب حفظ Hash كل فاتورة في قاعدة البيانات بشكل آمن.

هل يمكن تعديل الفاتورة بعد الحصول على Clearance؟

لا يمكن تعديل الفاتورة بعد حصولها على Clearance من ZATCA. أي تعديل يتطلب إصدار إشعار دائن (Credit Note) ثم إصدار فاتورة جديدة. التعديل المباشر يؤدي إلى عدم تطابق Hash ورفض النظام.

هل يجب استخدام mTLS عند الربط مع ZATCA API؟

نعم، ZATCA تتطلب استخدام HTTPS مع Mutual TLS (mTLS) لضمان المصادقة الثنائية بين النظام وواجهة API. يجب تثبيت الشهادة الرقمية (CSID) في التطبيق وإرسالها مع كل طلب API.

الخلاصة

تطوير تكامل مع ZATCA API باستخدام XML UBL 2.1 ليس مجرد ترجمة بيانات لصيغة XML. يتطلب فهماً عميقاً للمعايير الدولية، آليات التوقيع الرقمي، وإدارة سلاسل Hash. الفوترة الإلكترونية في السعودية باستخدام XML UBL 2.1 تمثل نقلة نوعية في الامتثال الضريبي، وتتطلب من المطورين إتقان تقنيات متقدمة في معالجة XML، التشفير، وأمان البيانات.

النقاط الحرجة تشمل: استخدام Namespaces الصحيحة، إجراء Canonicalization قبل التوقيع، حفظ Previous Invoice Hash بدقة، والتفريق الواضح بين Clearance و Reporting. الاستثمار في فهم هذه التفاصيل التقنية واختبارها بدقة في Sandbox يوفر أسابيع من استكشاف الأخطاء لاحقاً.

استخدم المكتبات المثبتة، افصل المنطق التقني عن منطق الأعمال، واحفظ الشهادات بأمان. الأنظمة المبنية بهذه الطريقة تكون أكثر استقرارًا، أسهل في الصيانة، وأقل عرضة للأخطاء الأمنية. المطورون الناجحون في بناء تكاملات ZATCA هم من يجمعون بين الفهم العميق للمعايير التقنية والاهتمام بالتفاصيل الأمنية والتشغيلية.

لمزيد من الاستفسارات التقنية أو المساعدة في تطوير تكامل مخصص، يمكن التواصل عبر منصة Trelyoon.

TRELYOON
TRELYOON
أنا خالد، مدوّن تقني عربي شغوف بتبسيط المعرفة. أكتب عن الذكاء الاصطناعي، الأمن السيبراني، والمال و الأعمال، بهدف واحد: مساعدتك على فهم التقنية بوضوح، والاستفادة منها في حياتك وعملك. كل مقالة تمر بثلاث مراحل قبل النشر: بحث معمّق، تحليل دقيق، ثم صياغة مبسّطة تحافظ على الدقة. أرحب دائماً بأسئلتك واقتراحاتك.
تعليقات