Qoyod
الأسعار

 دليل المعرفة

المواصفة التقنية للختم التشفيري

هذه وثيقة تقنية تشرح الموضع الدقيق للختم التشفيري داخل ملف الفاتورة الإلكترونية بصيغة UBL 2.1، والعناصر التي يتكوّن منها على مستوى وسم XML. الهدف منها مساعدة المطوّرين ومسؤولي التكامل على فهم كيف يُبنى الختم، وأين يُدرَج، وكيف تتحقق منه منصة فاتورة. إذا كنت تبحث عن شرح مفاهيمي لمعنى الختم التشفيري وأسباب فرضه، فالأفضل أن تبدأ من مقال الختم التشفيري في الفاتورة الإلكترونية. هنا ننزل إلى مستوى الحقل والوسم.

الختم التشفيري في الفوترة الإلكترونية السعودية ليس حقلاً واحداً تكتبه في الفاتورة. هو بنية XML مركّبة تعيش داخل امتداد قياسي اسمه ext:UBLExtensions، وتربط محتوى الفاتورة بهوية المُصدِّر وبختم زمني وبسلسلة من القيم المختصرة. فهم هذه البنية على مستوى الوسم هو الفرق بين تكامل ينجح من أول محاولة وتكامل يرفضه نظام الهيئة بأخطاء غامضة.

تتوافق هذه الوثيقة مع متطلبات هيئة الزكاة والضريبة والجمارك (ZATCA) للمرحلة الثانية من الفوترة الإلكترونية، وتعتمد على معيار UBL 2.1 مع ملف XAdES للتوقيع. كل المقتطفات أدناه مبسّطة لأغراض الشرح، وليست بديلاً عن الوثائق الرسمية للهيئة. لمدخل أوسع على بنية الفوترة الإلكترونية ومستنداتها التقنية، راجع قسم تعلّم الفوترة الإلكترونية.

لمن هذه الوثيقة وما الذي تغطّيه

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

نركّز هنا على أربعة أسئلة: أين يقع الختم داخل ملف XML بالضبط، ما العناصر التي يتكوّن منها على مستوى الوسم، كيف تُحسب كل قيمة فيه، وكيف تتحقق منه منصة فاتورة. لا نكرّر شرح ما هو الختم أو لماذا فُرض، فذلك موضوع المقال المفاهيمي. هنا المستوى تقني بحت.

اصطلاحاً، نستخدم «بصمة» للترجمة العربية لـ digest و hash، و«الختم التشفيري» للإشارة إلى قيمة التوقيع وما يحيط بها من بنية. حيثما ظهر مصطلح إنجليزي مثل SignedInfo أو SignatureValue فهو اسم عنصر XML فعلي يجب كتابته حرفياً كما هو، لا ترجمته.

أين يعيش الختم التشفيري داخل ملف XML؟

الفاتورة الإلكترونية ملف XML مبني على معيار UBL 2.1. الجسم الأساسي للفاتورة يحوي بيانات البائع والمشتري والبنود والضريبة. الختم التشفيري لا يُكتب وسط هذه البيانات، بل يُدرَج في منطقة مخصّصة في أعلى الملف اسمها UBLExtensions.

هذه المنطقة هي الطريقة القياسية في UBL لإضافة بيانات لا ينص عليها المعيار الأساسي. التوقيع الرقمي والختم التشفيري بيانات إضافية من هذا النوع، لذلك يستقران داخل امتداد واحد. الهيكل العام يبدو هكذا:

<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
  <ext:UBLExtensions>
    <ext:UBLExtension>
      <ext:ExtensionURI>urn:oasis:names:specification:ubl:dsig:enveloped:xades</ext:ExtensionURI>
      <ext:ExtensionContent>
        <sig:UBLDocumentSignatures>
          <sac:SignatureInformation>
            <ds:Signature Id="signature">
              <!-- التوقيع الرقمي والختم التشفيري يعيشان هنا -->
            </ds:Signature>
          </sac:SignatureInformation>
        </sig:UBLDocumentSignatures>
      </ext:ExtensionContent>
    </ext:UBLExtension>
  </ext:UBLExtensions>

  <cbc:ProfileID>reporting:1.0</cbc:ProfileID>
  <cbc:ID>INV-2026-000128</cbc:ID>
  <cbc:UUID>3cf5ee18-ee25-4ea1-bf7e-3df0d5a5e1f2</cbc:UUID>
  <!-- بقية بيانات الفاتورة: البائع، المشتري، البنود، الضريبة -->
</Invoice>

لاحظ ترتيب المساحات الاسمية (namespaces):

  • ext: امتداد UBL، الحاوية الخارجية.
  • sig: وsac: عناصر توقيع المستندات في UBL.
  • ds: عناصر XMLDSig القياسية، وهي قلب التوقيع.
  • xades: عناصر XAdES التي تضيف الخصائص الموقّعة مثل وقت التوقيع وبصمة الشهادة.

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

الفرق بين الجسم والامتداد

تذكّر هذه القاعدة: بيانات الفاتورة تُكتب مرة واحدة في الجسم. الختم التشفيري يُحسب فوق هذه البيانات ثم يُدرَج في الامتداد. هذا يعني أن أي تعديل على البنود أو على المبالغ بعد توليد الختم يجعل الختم غير مطابق، وهو بالضبط ما يكشفه التحقق.

السبب التقني وراء هذا الترتيب أن منطقة الامتداد تُستثنى من حساب بصمة الفاتورة. لو دخلت قيمة التوقيع في البصمة التي يوقّعها التوقيع نفسه لنشأت حلقة مستحيلة: تحتاج قيمة التوقيع كي تحسب البصمة، وتحتاج البصمة كي تحسب التوقيع. لذلك يُحذف عنصر التوقيع من المحتوى قبل حساب بصمته، عبر تحويل (Transform) معلَن داخل المرجع. هذه نقطة دقيقة يخطئ فيها كثير من المطوّرين في أول تكامل.

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

لماذا UBLExtensions تحديداً

معيار UBL 2.1 يعرّف بنية الفاتورة بدقة، لكنه لا ينص على التوقيع الرقمي ضمن العناصر الأساسية. بدلاً من تعديل المعيار، يوفّر UBL آلية رسمية للتوسعة عبر ext:UBLExtensions. الهيئة استغلّت هذه الآلية لإدراج التوقيع وفق ملف XAdES المعروف عالمياً، بدلاً من اختراع صيغة خاصة. هذا القرار يجعل فواتير المرحلة الثانية متوافقة مع أدوات تحقق دولية، ويسهّل على المطوّرين الاعتماد على مكتبات XMLDSig و XAdES الجاهزة بدلاً من بنائها من الصفر.

قيمة ext:ExtensionURI المعلنة في المقتطف السابق ليست اختيارية. هي تخبر أي قارئ أن محتوى هذا الامتداد توقيع من نوع enveloped XAdES، أي توقيع يعيش داخل المستند نفسه لا منفصلاً عنه. أي قيمة أخرى أو غيابها يجعل أدوات التحقق غير قادرة على تحديد كيفية معالجة المحتوى.

بنية عنصر التوقيع: ds:Signature

داخل ds:Signature ثلاثة أقسام رئيسية. كل قسم له دور محدد، ومعرفة أدوارها يوضّح أين تعيش بصمة الفاتورة وأين تعيش بصمة الخصائص الموقّعة.

<ds:Signature Id="signature" xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
  <ds:SignedInfo>
    <ds:CanonicalizationMethod Algorithm=".../xml-exc-c14n#"/>
    <ds:SignatureMethod Algorithm=".../xmldsig-more#ecdsa-sha256"/>

    <!-- المرجع الأول: بصمة الفاتورة كاملة -->
    <ds:Reference URI="">
      <ds:Transforms> ... </ds:Transforms>
      <ds:DigestMethod Algorithm=".../xmlenc#sha256"/>
      <ds:DigestValue>k3Pn...بصمة-الفاتورة...=</ds:DigestValue>
    </ds:Reference>

    <!-- المرجع الثاني: بصمة الخصائص الموقّعة -->
    <ds:Reference URI="#xadesSignedProperties" Type=".../XAdES#SignedProperties">
      <ds:DigestMethod Algorithm=".../xmlenc#sha256"/>
      <ds:DigestValue>9Qa2...بصمة-الخصائص...=</ds:DigestValue>
    </ds:Reference>
  </ds:SignedInfo>

  <ds:SignatureValue>MEUCIQ...قيمة-التوقيع-ECDSA...</ds:SignatureValue>

  <ds:KeyInfo>
    <ds:X509Data>
      <ds:X509Certificate>MIID...شهادة-CSID...</ds:X509Certificate>
    </ds:X509Data>
  </ds:KeyInfo>

  <ds:Object>
    <xades:QualifyingProperties> ... </xades:QualifyingProperties>
  </ds:Object>
</ds:Signature>

ds:SignedInfo: ما الذي يُوقَّع فعلاً

التوقيع لا يُطبَّق على الفاتورة مباشرة. يُطبَّق على عنصر ds:SignedInfo. هذا العنصر يحوي مرجعين، كل مرجع يخزّن بصمة (digest) لجزء معيّن:

  • المرجع الأول بـ URI="" يشير إلى الفاتورة كاملة. قيمة ds:DigestValue هنا هي بصمة الفاتورة المعروفة باسم Invoice Hash.
  • المرجع الثاني بـ URI="#xadesSignedProperties" يشير إلى الخصائص الموقّعة، أي بصمة الشهادة ووقت التوقيع.

بهذا الربط يصبح كل من محتوى الفاتورة وهوية المُصدِّر ووقت التوقيع جزءاً من القيمة الموقّعة. أي تغيير في أيٍّ منها يكسر التحقق.

لكل مرجع داخل SignedInfo ثلاثة عناصر فرعية يجب أن تُفهم معاً. ds:Transforms يصف التحويلات المطبَّقة على الجزء المرجَعي قبل حساب بصمته، وأهمها حذف عنصر التوقيع نفسه وتطبيق التقنين. ds:DigestMethod يعلن خوارزمية الاختزال، وهي SHA-256 في فواتير المرحلة الثانية. ds:DigestValue يحمل الناتج بترميز Base64. غياب أي من هذه الثلاثة أو اختلاف خوارزمية عن المعتمد يجعل التحقق يحسب قيمة مختلفة عن المخزّنة فيرفض الفاتورة.

التقنين (Canonicalization) ودوره الحاسم

قبل حساب أي بصمة، يمرّ الجزء المرجَعي من XML بعملية تقنين. السبب أن نفس المحتوى المنطقي يمكن كتابته بصور نصية مختلفة: مسافات بادئة، ترتيب سمات، أسطر فارغة، طريقة إعلان المساحات الاسمية. هذه الفروق لا تغيّر المعنى، لكنها تغيّر النص حرفاً بحرف، وبالتالي تغيّر بصمة SHA-256 تغييراً جذرياً.

التقنين يحوّل XML إلى صيغة معيارية واحدة قبل الحساب، فيضمن أن المُصدِّر والهيئة يحسبان البصمة على نفس البايتات بالضبط. الخوارزمية المعتمدة هي Exclusive Canonical XML المعلنة في ds:CanonicalizationMethod. لو استخدم نظام المُصدِّر تقنيناً مختلفاً، سيحسب بصمة سليمة محلياً لكنها لن تطابق ما تحسبه الهيئة، فتُرفَض الفاتورة برسالة خطأ في البصمة رغم أن المحتوى صحيح. هذا من أكثر مصادر الإحباط في التكامل الأول، ويتطلب الالتزام الحرفي بخوارزمية التقنين المعتمدة.

ds:SignatureValue: الختم الفعلي

عنصر ds:SignatureValue هو الختم التشفيري بالمعنى الدقيق. قيمته ناتج توقيع SignedInfo بالمفتاح الخاص لشهادة CSID باستخدام خوارزمية ECDSA مع SHA-256. هذه القيمة بترميز Base64، ولا يمكن إنتاجها إلا بحيازة المفتاح الخاص، ولا يمكن تزويرها دون كسر التشفير.

بنية عنصر التوقيع ds:Signature
كيف يتركّب عنصر الختم التشفيري داخل XML.
بنية الختم

يقع داخل ext:UBLExtensions

ds:SignedInfo يحوي مرجعين (Reference) للتجزئة

ds:SignatureValue = قيمة الختم الفعلية

ds:KeyInfo/X509Certificate = شهادة CSID

xades:SignedProperties (وقت التوقيع وبصمة الشهادة)

الختم الفعلي يكمن في SignatureValue داخل بنية التوقيع.

ds:KeyInfo: الشهادة العامة

يحمل ds:KeyInfo داخل ds:X509Certificate الشهادة العامة لـ CSID بترميز Base64. هذه الشهادة تتيح لمنصة فاتورة استخراج المفتاح العام والتحقق من قيمة التوقيع دون حاجة للمفتاح الخاص. تفاصيل شهادة الترميز والامتثال موضّحة في مرجع معرّف الختم التشفيري CSID.

نقطة دقيقة في ترميز الشهادة: القيمة داخل X509Certificate هي محتوى DER بترميز Base64 دون رؤوس PEM وذيولها، ودون فواصل أسطر. كثير من المطوّرين ينسخون الشهادة كما تظهر في ملف PEM مع سطري البداية والنهاية، فتفشل قراءتها لأن المُحلِّل يتوقّع Base64 نقياً. القاعدة: جرّد الرؤوس والذيول والأسطر الفارغة، وأبقِ سلسلة Base64 متصلة.

شهادة الامتثال مقابل شهادة الإنتاج

على مستوى التكامل يجب التمييز بين نوعين من شهادات CSID، لأنهما يظهران في نفس الموضع داخل X509Certificate لكنهما يخدمان مرحلتين مختلفتين. شهادة الامتثال (Compliance CSID) تُصدَر في بيئة المحاكاة لاختبار تكاملك قبل التشغيل. شهادة الإنتاج (Production CSID) تُصدَر بعد اجتياز الاختبار، وهي التي توقّع الفواتير الحقيقية المُبلَّغة للهيئة.

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

الخصائص الموقّعة: xades:SignedProperties

القسم الذي يضيف بُعد XAdES فوق XMLDSig هو xades:SignedProperties. هنا تعيش بصمة الشهادة ووقت التوقيع، وهما العنصران اللذان يشير إليهما المرجع الثاني في SignedInfo.

<xades:SignedProperties Id="xadesSignedProperties">
  <xades:SignedSignatureProperties>

    <!-- وقت التوقيع -->
    <xades:SigningTime>2026-06-24T11:42:18Z</xades:SigningTime>

    <!-- بصمة شهادة التوقيع ومُصدِرها -->
    <xades:SigningCertificate>
      <xades:Cert>
        <xades:CertDigest>
          <ds:DigestMethod Algorithm=".../xmlenc#sha256"/>
          <ds:DigestValue>ZmI3...بصمة-الشهادة...=</ds:DigestValue>
        </xades:CertDigest>
        <xades:IssuerSerial>
          <ds:X509IssuerName>CN=ZATCA-CA, ...</ds:X509IssuerName>
          <ds:X509SerialNumber>379...</ds:X509SerialNumber>
        </xades:IssuerSerial>
      </xades:Cert>
    </xades:SigningCertificate>

  </xades:SignedSignatureProperties>
</xades:SignedProperties>

هنا ثلاث قيم تُربَط بالتوقيع:

  • xades:SigningTime: الطابع الزمني للتوقيع بصيغة UTC. يثبت لحظة ختم الفاتورة، وهو جزء من القيمة الموقّعة فلا يمكن تغييره لاحقاً.
  • xades:CertDigest: بصمة SHA-256 للشهادة المستخدمة، تربط التوقيع بشهادة CSID محددة.
  • xades:IssuerSerial: اسم الجهة المُصدِرة للشهادة ورقمها التسلسلي، يثبت أن الشهادة صادرة عن سلطة معتمدة.

الأهمية العملية: المرجع الثاني في SignedInfo يحسب بصمة لهذا العنصر كاملاً. بهذا تصبح هوية الشهادة ووقت التوقيع جزءاً لا يتجزأ من الختم، لا مجرد بيانات وصفية مرفقة.

الطابع الزمني هنا يختلف عن طابع الفاتورة في الجسم. طابع الجسم (cbc:IssueDate وcbc:IssueTime) هو تاريخ الفاتورة التجاري. أما xades:SigningTime فهو لحظة توليد الختم تحديداً. في الغالب يتقاربان، لكن الفرق مهم تقنياً: الأول بيانات عمل، والثاني دليل أمني على متى ختم النظام الفاتورة. يجب أن يكون SigningTime بصيغة UTC مع لاحقة Z، فاستخدام توقيت محلي دون منطقة زمنية يسبب التباساً في التحقق.

كيف تُحسب كل بصمة خطوة بخطوة

لتثبيت الصورة على مستوى التنفيذ، إليك التسلسل الذي يبنيه أي نظام توقيع لكل بصمة من البصمتين:

  1. بصمة الفاتورة (المرجع الأول): يأخذ النظام مستند الفاتورة، يحذف منه عنصر ds:Signature عبر التحويل المعلَن، يطبّق التقنين، ثم يحسب SHA-256 على الناتج. القيمة الناتجة تُوضَع في DigestValue الأول.
  2. بصمة الخصائص (المرجع الثاني): يأخذ النظام عنصر xades:SignedProperties وحده، يطبّق التقنين، ثم يحسب SHA-256. القيمة الناتجة تُوضَع في DigestValue الثاني.
  3. قيمة التوقيع: بعد أن تكتمل البصمتان داخل SignedInfo، يطبّق النظام التقنين على SignedInfo كاملاً، ثم يوقّعه بـ ECDSA باستخدام المفتاح الخاص. الناتج بترميز Base64 يُوضَع في SignatureValue.

هذا الترتيب غير قابل للعكس: لا يمكن حساب قيمة التوقيع قبل اكتمال البصمتين، لأن SignedInfo الذي يُوقَّع يحويهما. أي نظام يحاول توقيع SignedInfo قبل ملء البصمات ينتج توقيعاً لا معنى له، ويفشل التحقق فوراً.

القيم الثلاث في الختم التشفيري
ما تغطيه كل قيمة من قيم التوقيع الثلاث.
القيمة ما تغطيه
DigestValue الأول تجزئة محتوى الفاتورة
DigestValue الثاني تجزئة الخصائص الموقّعة
SignatureValue الختم التشفيري الفعلي
تجزئة الفاتورة + تجزئة الخصائص + الختم تثبت الأصالة والسلامة.

ربط بصمة الفاتورة السابقة: سلسلة السلامة

بصمة الفاتورة السابقة (Previous Invoice Hash) لا تعيش داخل ds:Signature، بل في جسم الفاتورة ضمن خصائص إضافية للمستند. لكنها مشمولة في بصمة الفاتورة، لذلك يحميها الختم.

<cac:AdditionalDocumentReference>
  <cbc:ID>PIH</cbc:ID>
  <cac:Attachment>
    <cbc:EmbeddedDocumentBinaryObject mimeCode="text/plain">
      NWZh...بصمة-الفاتورة-السابقة...=
    </cbc:EmbeddedDocumentBinaryObject>
  </cac:Attachment>
</cac:AdditionalDocumentReference>

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

تمثيل الختم داخل رمز QR

رمز الاستجابة السريع المطبوع على الفاتورة المبسّطة (B2C) ليس صورة عشوائية. هو سلسلة بترميز TLV (Tag-Length-Value) بترميز Base64، تحوي حقولاً محددة. حقول الختم ضمنها تأتي في الوسوم الأخيرة:

Tag 1  اسم البائع
Tag 2  الرقم الضريبي للبائع
Tag 3  الطابع الزمني للفاتورة
Tag 4  إجمالي الفاتورة شامل الضريبة
Tag 5  مبلغ ضريبة القيمة المضافة
Tag 6  بصمة الفاتورة (Invoice Hash)
Tag 7  قيمة التوقيع الرقمي (ECDSA)
Tag 8  المفتاح العام لشهادة التوقيع
Tag 9  ختم الهيئة على الشهادة (للفواتير المبسّطة)

الوسوم من 6 إلى 9 هي امتداد المرحلة الثانية. تنقل جوهر الختم التشفيري إلى رمز QR كي يتمكّن أي تطبيق تحقق معتمد من قراءة الفاتورة المبسّطة والتأكد من صحة ختمها دون الرجوع إلى ملف XML الكامل.

ترميز TLV يعني أن كل حقل يُكتب بثلاثة أجزاء متتابعة: رقم الوسم (Tag)، طول القيمة (Length) بالبايت، ثم القيمة نفسها (Value). بهذا يستطيع القارئ معرفة أين ينتهي كل حقل ويبدأ التالي دون فواصل. السلسلة الناتجة تُرمَّز بـ Base64 ثم تُحوَّل إلى صورة QR. أي خلل في حساب الطول يزحزح بقية الحقول فيقرأ التطبيق قيماً خاطئة، لذلك يجب حساب الطول بالبايت لا بالحرف، وهو فرق يظهر بوضوح مع النص العربي متعدّد البايتات.

الفرق بين الفاتورة الضريبية (B2B) والفاتورة المبسّطة (B2C) ينعكس على رمز QR. الفاتورة المبسّطة تُولَّد وتُسلَّم للعميل فوراً وتُبلَّغ للهيئة خلال 24 ساعة، فيحمل رمزها الوسوم التسعة كاملة بما فيها ختم الهيئة على الشهادة في الوسم التاسع. الفاتورة الضريبية تمرّ على الهيئة للاعتماد (Clearance) قبل تسليمها للمشتري، فتختلف بعض تفاصيل توليد الرمز تبعاً لمسار الاعتماد.

دورة حياة الختم: من التوليد إلى الأرشفة

لإكمال الصورة على مستوى التكامل، إليك دورة حياة الختم داخل فاتورة واحدة من لحظة الإنشاء حتى الأرشفة:

  1. بناء الجسم: يجمع النظام بيانات الفاتورة في عناصر UBL ويحسب الإجماليات والضريبة، ويُدرِج بصمة الفاتورة السابقة في عنصر PIH.
  2. حساب بصمة الفاتورة: بعد اكتمال الجسم، يقنّن النظام المستند ويحسب SHA-256 ليحصل على بصمة الفاتورة.
  3. بناء الخصائص الموقّعة: يملأ النظام SigningTime وبصمة الشهادة ومرجعها، ثم يحسب بصمة هذا العنصر.
  4. التوقيع: يبني النظام SignedInfo بالبصمتين، يقنّنه، ثم يوقّعه بـ ECDSA ليملأ SignatureValue.
  5. التجميع: يُدرِج النظام ds:Signature كاملاً داخل ext:UBLExtensions، ويولّد رمز QR من القيم المطلوبة.
  6. الإرسال: يُرسَل الملف لمنصة فاتورة للاعتماد (B2B) أو للإبلاغ (B2C).
  7. الأرشفة: يحفظ النظام الفاتورة الموقّعة وبصمتها، لتصبح بصمتها هي قيمة PIH للفاتورة التالية، فتستمر السلسلة.

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

حقول رمز QR بترميز TLV
الحقول التسعة التي يحملها رمز QR في المرحلة الثانية.
حقول QR (TLV)

1-5: اسم البائع، الرقم الضريبي، الوقت، الإجمالي، الضريبة

6: تجزئة الفاتورة

7: الختم التشفيري (التوقيع)

8: المفتاح العام

9: ختم الهيئة

الحقول 6–9 هي إضافات المرحلة الثانية المرتبطة بالختم.

دورة التحقق على مستوى الحقل

عندما تستقبل منصة فاتورة الملف، تنفّذ سلسلة تحقق على العناصر التي وصفناها. فهم هذا التسلسل يساعد على تشخيص سبب رفض أي فاتورة:

  1. إعادة حساب بصمة الفاتورة ومقارنتها بقيمة DigestValue في المرجع الأول. عدم التطابق يعني أن المحتوى تغيّر بعد التوقيع.
  2. إعادة حساب بصمة الخصائص الموقّعة ومقارنتها بالمرجع الثاني. عدم التطابق يعني عبثاً بوقت التوقيع أو بمرجع الشهادة.
  3. التحقق من قيمة التوقيع في SignatureValue باستخدام المفتاح العام المستخرج من الشهادة. الفشل هنا يعني أن المفتاح لا يطابق التوقيع.
  4. التحقق من صلاحية الشهادة: هل صادرة عن سلطة معتمدة؟ هل ما زالت سارية؟ شهادة CSID منتهية تُفشل هذه الخطوة.
  5. التحقق من سلسلة البصمات: هل تشير بصمة الفاتورة السابقة إلى آخر فاتورة فعلية؟

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

أخطاء التكامل الأكثر شيوعاً على مستوى XML

  • ترتيب العناصر: UBL حساس لترتيب الأبناء داخل العنصر الواحد. عنصر صحيح في موضع خطأ يُرفَض.
  • التقنين (Canonicalization): استخدام خوارزمية تقنين مختلفة عن المعتمدة يغيّر البصمة المحسوبة فيفشل التطابق.
  • المساحات الاسمية: إعلان ds: أو xades: في الموضع الخطأ يكسر مراجع البصمة.
  • ترميز الشهادة: إدراج الشهادة مع فواصل أسطر أو رؤوس PEM داخل X509Certificate يُفشل قراءتها.

ربط رسالة الخطأ بالعنصر المسؤول

عند رفض فاتورة، تعيد منصة فاتورة رسالة تشير إلى نوع الخلل. ربط نوع الرسالة بالعنصر المسؤول يختصر زمن التشخيص. إليك خريطة عملية:

  • خطأ في بصمة المستند: راجع DigestValue الأول وخوارزمية التقنين والتحويلات. غالباً السبب تقنين مختلف أو تعديل طرأ على الجسم بعد الحساب.
  • خطأ في بصمة الخصائص: راجع DigestValue الثاني ومحتوى xades:SignedProperties. غالباً تغيّر SigningTime أو مرجع الشهادة بعد الحساب.
  • خطأ في قيمة التوقيع: راجع تطابق المفتاح الخاص الموقِّع مع الشهادة المُدرَجة في KeyInfo. مفتاح لا يطابق الشهادة ينتج توقيعاً لا يُتحقَّق منه.
  • خطأ في الشهادة: راجع صلاحية CSID ونوعها (امتثال مقابل إنتاج) ومُصدِرها في IssuerSerial.
  • خطأ في سلسلة البصمة: راجع قيمة PIH في الجسم ومطابقتها لآخر فاتورة فعلية.

الميزة في هذه الخريطة أنها تحوّل رسالة خطأ غامضة إلى موضع محدد في XML تبدأ منه. بدلاً من مراجعة الملف كاملاً، تذهب مباشرة إلى العنصر المسؤول.

كيف يتولّى قيود بناء الختم التشفيري نيابة عنك

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

على مستوى الحقل، يتولّى قيود ما يلي:

  • توليد ملف XML متوافق مع UBL 2.1 بكل عناصره في الترتيب الصحيح.
  • إدارة شهادة CSID وحفظ مفتاحها الخاص بأمان، وتوقيع كل فاتورة بـ ECDSA دون تدخّل يدوي.
  • حساب بصمة الفاتورة وبصمة الخصائص الموقّعة وإدراجهما في عنصري المرجع الصحيحين.
  • بناء سلسلة بصمة الفاتورة السابقة (PIH) تلقائياً بين كل فاتورة وما قبلها.
  • توليد رمز QR بترميز TLV متضمّناً وسوم الختم من 6 إلى 9.
  • الربط الفوري مع منصة فاتورة: اعتماد فواتير B2B (Clearance) وإبلاغ فواتير B2C خلال 24 ساعة.
  • بيئة محاكاة لاختبار الفواتير قبل التشغيل الفعلي، فتتأكد من نجاح التحقق دون مخاطرة.

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

ابدأ اليوم

فواتير مختومة تشفيرياً دون كتابة سطر XML واحد

يولّد قيود بنية UBL 2.1 ويبني الختم التشفيري ويربطك بمنصة فاتورة تلقائياً، فتصدر فواتير متوافقة مع المرحلة الثانية من أول يوم.

ابدأ تجربتك المجانية وأصدر فواتير مختومة

أسئلة شائعة على مستوى التكامل

أين بالضبط تُكتب قيمة الختم داخل XML؟

في عنصر ds:SignatureValue داخل ds:Signature، والأخير يعيش داخل ext:UBLExtensions في أعلى ملف الفاتورة. هذه القيمة بترميز Base64 وهي ناتج توقيع ds:SignedInfo بمفتاح CSID الخاص.

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

بصمة الفاتورة (DigestValue في المرجع الأول) هي مجرد قيمة SHA-256 لمحتوى الفاتورة. قيمة التوقيع (SignatureValue) هي ناتج تشفير غير متماثل بـ ECDSA على SignedInfo كاملاً. البصمة تثبت السلامة، والتوقيع يثبت الهوية والسلامة معاً.

ما الخوارزمية المعتمدة للتوقيع؟

توقيع ECDSA على منحنى P-256 مع دالة اختزال SHA-256، أي ecdsa-sha256 في إعلان ds:SignatureMethod. البصمات تُحسب بـ SHA-256 ضمن ds:DigestMethod.

لماذا تُرفَض فاتورتي رغم أن العناصر موجودة؟

الأسباب الأكثر شيوعاً: خوارزمية تقنين (Canonicalization) مختلفة عن المعتمدة، أو ترتيب أبناء خاطئ داخل عنصر UBL، أو شهادة مُدرَجة برؤوس PEM. كل واحدة تغيّر البصمة المحسوبة أو تكسر قراءة الشهادة، فيفشل التحقق رغم اكتمال الوسوم ظاهرياً.

هل أحتاج لفهم هذه البنية كي أصدر فاتورة سليمة؟

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

أين أجد الشرح المفاهيمي لا التقني؟

في مقال الختم التشفيري في الفاتورة الإلكترونية الذي يشرح ما الختم ولماذا فرضته الهيئة وكيف يحمي فواتيرك، دون النزول إلى مستوى الوسم.

مركز المساعدة

لم تجد ما تبحث عنه؟

لا تقلق، لدينا المزيد من أدوات المساعدة.

ندوات مباشرة يقدمها فريق قيود لمساعدتك في استخدام البرنامج بسهولة والرد على أسئلتك.

تعرّف على أحدث تحديثات فيود والتحسينات المستمرة والخصائص الجديدة في مكان واحد.

فريقنا جاهز لمساعدتك وتقديم الدعم الفوري لأي مشكلة تواجهها على مدار الساعة