ZATCA المرحلة الثانية للمطورين: دليل الربط الفني الشامل
هذا الدليل موجَّه للمطورين الذين يبنون أنظمة فوترة في السوق السعودي. يغطي كل التفاصيل الفنية لربط نظامك مع منظومة ZATCA المرحلة الثانية، من توليد الـ CSR إلى إرسال أول فاتورة مُصفّاة (Cleared).
⚠️ متطلبات أساسية: فهم XML، X.509 certificates، ECDSA cryptography، REST APIs، وUBL 2.1 schema.
١. نظرة معمارية على المرحلة الثانية
المرحلة الثانية (Integration Phase) تتطلب اتصال نظامك مع ZATCA لحظياً عبر أحد مسارين:
- Clearance (تصفية): للفواتير الضريبية (Standard Invoice / B2B). الفاتورة تُرسَل لـ ZATCA وتنتظر استجابة قبل تسليمها للعميل.
- Reporting (إبلاغ): للفواتير المبسطة (Simplified Invoice / B2C). الفاتورة تُسلَّم للعميل أولاً ثم تُبلَّغ لـ ZATCA خلال 24 ساعة.
٢. الدورة الكاملة لربط النظام
- توليد CSR (Certificate Signing Request) بمعايير ZATCA
- الحصول على OTP من بوابة Fatoora
- إرسال CSR + OTP لـ Compliance API → الحصول على CSID (Compliance CSID)
- إصدار 6 فواتير اختبارية متنوعة (Standard + Simplified + Credit + Debit Notes)
- طلب Production CSID من Onboarding API
- توقيع الفواتير الفعلية + إرسالها لـ Clearance/Reporting API
٣. توليد الـ CSR بالمعايير الصحيحة
ZATCA يفرض حقولاً محددة في الـ CSR. أي خطأ هنا يرفض الطلب من البداية:
# OpenSSL config مطلوب: zatca.cnf
[ req ]
distinguished_name = req_distinguished_name
prompt = no
req_extensions = v3_req
[ req_distinguished_name ]
C = SA
OU = "Branch Name" # اسم الفرع
O = "Company Name" # اسم المنشأة
CN = "COMMON NAME" # اسم الجهاز/الكاشير
[ v3_req ]
basicConstraints = critical, CA:FALSE
keyUsage = critical, digitalSignature
extendedKeyUsage = critical, clientAuth
subjectAltName = dirName:san
[ san ]
SN = "1-Solution|2-Device|3-UUID" # بصمة فريدة
UID = "310000000000003" # الرقم الضريبي
title = "1100" # Invoice Type Code
registeredAddress = "Riyadh, ..."
businessCategory = "Restaurant"
# توليد المفتاح + CSR (لاحظ secp256k1)
openssl ecparam -name secp256k1 -genkey -noout -out privkey.pem
openssl req -new -sha256 -key privkey.pem -config zatca.cnf -out csr.pem
ملاحظة: ZATCA تُلزم باستخدام منحنى secp256k1 وليس secp256r1 (المنحنى الشهير في ECDSA).
٤. APIs الأساسية
| API | Endpoint | الغرض |
|---|---|---|
compliance | POST /compliance | طلب Compliance CSID |
compliance-invoice | POST /compliance/invoices | اختبار الفواتير الست |
production | POST /production/csids | طلب Production CSID |
clearance | POST /invoices/clearance/single | تصفية فاتورة B2B |
reporting | POST /invoices/reporting/single | إبلاغ فاتورة B2C |
Sandbox: https://gw-fatoora.zatca.gov.sa/e-invoicing/developer-portal
Production: https://gw-fatoora.zatca.gov.sa/e-invoicing/core
٥. توقيع XML بـ ECDSA secp256k1
كل فاتورة XML يجب توقيعها بمعيار XAdES-B-B. خطوات التوقيع:
- حساب
InvoiceHash= SHA-256 على XML بعد إزالة بعض الـ tags (UBLExtensions, Signature, AdditionalDocumentReference[QR]). - توليد
SignedPropertiesXML من القيم:SigningTime,CertDigest,IssuerName,SerialNumber. - حساب
SignedPropertiesHash= SHA-256(SignedProperties). - إنشاء
SignedInfoيحتوي الـ hashes. - توقيع
SignedInfoبـ ECDSA secp256k1 + المفتاح الخاص. - حقن النتيجة في
UBLExtensions/UBLExtension.
// PHP — توقيع SignedInfo
$key = openssl_pkey_get_private('file://privkey.pem');
$signature = '';
openssl_sign($signedInfoXml, $signature, $key, OPENSSL_ALGO_SHA256);
// تحويل DER → IEEE P1363 (ZATCA يطلب raw r||s)
$signature = derToP1363($signature);
$signatureB64 = base64_encode($signature);
٦. توليد QR Code (TLV - 9 tags)
QR في المرحلة الثانية يضم 9 حقول مشفّرة بـ Base64 على هيئة TLV:
Tag 1: Seller Name (UTF-8)
Tag 2: VAT Number
Tag 3: Timestamp (ISO 8601)
Tag 4: Invoice Total (with VAT)
Tag 5: VAT Total
Tag 6: Invoice Hash (SHA-256 base64)
Tag 7: Digital Signature (ECDSA)
Tag 8: Public Key (DER base64)
Tag 9: Signature of public key by ZATCA CA
# بنية كل tag: [tagNum:1byte][length:1byte][value:Nbytes]
// PHP — توليد QR base64
function tlv(int $tag, string $value): string {
return chr($tag) . chr(strlen($value)) . $value;
}
$qr = tlv(1, $sellerName) . tlv(2, $vatNumber) . tlv(3, $timestamp)
. tlv(4, $total) . tlv(5, $vat) . tlv(6, $invoiceHash)
. tlv(7, $signature) . tlv(8, $publicKey) . tlv(9, $caSignature);
$qrB64 = base64_encode($qr);
٧. تسلسل الفواتير (ICV / PIH)
- ICV (Invoice Counter Value): رقم تسلسلي يبدأ من 1 ويزيد بالترتيب لكل فاتورة (لكل CSID).
- PIH (Previous Invoice Hash): SHA-256 للفاتورة السابقة. الفاتورة الأولى تستخدم:
NWZlY2ViNjZmZmM4NmYzOGQ5NTI3ODZjNmQ2OTZjNzljMmRiYzIzOWRkNGU5MWI0NjcyOWQ3M2EyN2ZiNTdlOQ==
٨. التعامل مع الأخطاء الشائعة
❌ Error: BR-CO-15 / KSA-15
المجموع الإجمالي لا يطابق مجموع البنود + الضرائب.
الحل: تحقق من الـ rounding في حساب الإجماليات. ZATCA حساس لـ 2 decimals بالضبط.
❌ Error: HTTP 401 invalid certificate
الشهادة منتهية أو لا تطابق البيئة (Sandbox vs Production).
الحل: تأكد أنك لا تستخدم Compliance CSID مع Production endpoint أو العكس.
❌ Error: Hash mismatch
XML hash المحسوب لا يطابق الـ value في QR.
الحل: تأكد أن الـ canonicalization (C14N) صحيح، خاصة في handling الـ namespaces.
٩. أفضل الممارسات
- افصل ZATCA submit عن transaction: أرسل الفاتورة بعد COMMIT لتجنّب deadlocks.
- retry policy: 5 محاولات بفاصل دقيقة (exponential backoff). إن فشلت، خزّنها للمحاولة لاحقاً عبر cron.
- idempotency: ZATCA يقبل إرسال نفس الفاتورة مرتين — لكن النظام يعالجها مرة واحدة.
- logging: احفظ كل request/response لـ audit، خاصة في الـ first 100 invoices.
- certificate renewal: Production CSID صالح سنة. جدّد قبل الانتهاء بـ 30 يوماً تلقائياً.
١٠. POS SAAS API — اختصار الطريق
بناء كل الطبقة أعلاه يستغرق ٣-٦ أشهر لفريق متخصص. POS SAAS يوفر API جاهز يخفي كل التعقيدات:
POST /api/v1/sales
Authorization: Bearer YOUR_TOKEN
Content-Type: application/json
{
"items": [{"product_id": 123, "qty": 2}],
"customer_id": 45,
"invoice_type": "standard"
}
// Response
{
"sale_id": 1001,
"invoice_number": "SINV-001-1001",
"zatca_status": "cleared",
"qr_base64": "AQ5T2VsbGVy...",
"xml_hash": "..."
}
كل التعقيدات (CSR، CSID، توقيع XML، QR، ICV/PIH، retry) مُدارة تلقائياً. اقرأ توثيق API أو ابدأ تجربة مجانية.