آخر مراجعة: فبراير 2026 — راجع إشعار منشأتك دائماً من بوابة ZATCA الرسمية للتأكد من موجتك.
إذا كنت تدير متجراً على Shopify وتبيع في السعودية، فأنت أمام قرار عملي لا يحتمل التأجيل: اختيار الطريقة الصحيحة لربط Shopify مع ZATCA في المرحلة الثانية. الغرامات تصل إلى 50,000 ريال، وإرسال الفاتورة بصيغة خاطئة يعادل عدم الإرسال من الناحية القانونية.
في هذا الدليل نجيب على سؤال واحد محدد: ما الخطوات والمتطلبات التقنية لربط Shopify مع ZATCA فعلياً؟ المحتوى موجه للتجار الذين يريدون فهم خياراتهم، وللمطورين الذين يريدون التطبيق من اليوم الأول بلا أخطاء.
📌 هذا الدليل موجه لـ:
- ✅ أصحاب متاجر Shopify في السعودية
- ✅ مطوري تطبيقات Shopify
- ✅ فرق التقنية في شركات التجارة الإلكترونية
- ✅ مدراء المنتجات الرقمية
- ✅ مزودي حلول الدفع والأنظمة المحاسبية
- ✅ استشاريي ERP والتكامل
مستوى المحتوى: متوسط إلى متقدم — يحتوي أمثلة برمجية وتفاصيل تقنية قابلة للتطبيق.
⚡ للمطورين — النقاط الحرجة قبل البداية
- ✅ UTF-8 encoding إلزامي في كل XML
- ✅ UUID فريد (RFC 4122) لكل فاتورة
- ✅ QR Code بصيغة TLV + Base64 حصراً
- ✅ Compliance CSID أولاً ← ثم Production CSID
- ✅ مراقبة تلقائية لانتهاء صلاحية الشهادة
- ✅ SHA-256 Hash Chain (أول فاتورة = "0")
- ✅ Webhook: orders/paid أو fulfillments/create
- ✅ إعادة حساب VAT قبل الإرسال
- ✅ Retry Logic مع Exponential Backoff
- ✅ حفظ invoice_hash لكل فاتورة في Metafields
أي حل تختار؟ — اتخذ قرارك في دقيقة
قبل أي تفاصيل تقنية، هذا السؤال الأهم: ما الحل المناسب لحجم عملك ومواردك؟
متى يسري عليك الالتزام؟ — الجدول الزمني للموجات
هيئة الزكاة تطبق المرحلة الثانية تدريجياً حسب الإيرادات. الجدول التالي يعكس الاتجاه العام — راجع الإشعار الرسمي المرسل لمنشأتك دائماً:
| نطاق الإيرادات السنوية | حالة الالتزام |
|---|---|
| أكثر من 3 مليون ريال | ✅ تم التطبيق (موجات سابقة) |
| 750,000 – 3 مليون ريال | ⚠️ موجات متوقعة خلال 2026 |
| أقل من 750,000 ريال | ⏳ موجات قادمة — جهّز نفسك مبكراً |
📌 تنويه: نطاقات الإيرادات وتواريخ الموجات تُعلن رسمياً من الهيئة بشكل دوري. راجع دائماً بوابة الهيئة للإشعار الرسمي لمنشأتك.
الخطأ الأول الذي يقع فيه التجار — فهم نوعَي الفاتورة
كثير من التجار يعتقدون أن كل الفواتير تُعالَج بنفس الطريقة. هذا خطأ يكلّف الرفض. في ZATCA المرحلة الثانية هناك نوعان مختلفان كلياً:
الفاتورة الضريبية B2B — للمنشآت (Clearance)
- تُرسل إلى ZATCA أولاً للموافقة قبل إرسالها للعميل
- إلزامي وجود الرقم الضريبي للمشتري
- الإرسال يجب أن يكون فورياً Real-time
- لا يمكن إصدارها بدون موافقة النظام
الفاتورة المبسطة B2C — للأفراد (Reporting)
- تُرسل للعميل مباشرة، ثم تُبلَّغ الهيئة خلال 24 ساعة
- لا تحتاج رقماً ضريبياً من المشتري
- QR Code إلزامي بصيغة TLV + Base64
لماذا هذا مهم عملياً؟ لأن طريقة ربط Shopify مع ZATCA تختلف تماماً بين النوعين. اختيار Webhook خاطئ = فواتير ترفضها الهيئة أو تأخر في الإبلاغ.
أرسل الفاتورة عند "التوريد أو الدفع أيهما أسبق":
- orders/paid ← للدفع الإلكتروني (بطاقة، Apple Pay، إلخ)
- fulfillments/create ← للدفع عند الاستلام (COD)
⚠️ استخدام Webhook خاطئ = إرسال مبكر قبل التوريد الفعلي أو تأخر في الإبلاغ — كلاهما يُعدّ عدم امتثال.
المتطلبات التقنية — تفصيل لكل نقطة
1. الشهادة الرقمية CSID — أهم خطوة في الربط
كل متجر Shopify يُعدّ EGS (Electronic Generation Solution) ويحتاج شهادة رقمية خاصة من ZATCA. 3 متاجر = 3 شهادات مستقلة.
نوعان لا بد منهما بالترتيب:
- Compliance CSID — للاختبار في بيئة Sandbox فقط
- Production CSID — للتشغيل الفعلي وإصدار فواتير حقيقية
الحصول عليها يتم عبر عملية Onboarding في بوابة الهيئة: تولّد CSR وترسله وتستلم الشهادة.
إذا انتهت الشهادة:
- ❌ تُرفض جميع الفواتير فوراً
- ❌ يتوقف Clearance و Reporting تماماً
- ❌ خطر الغرامات بسبب عدم الامتثال
أفضل ممارسة: راقب تاريخ الانتهاء تلقائياً، جدّد قبل 30 يوم على الأقل، واحفظ الشهادة في Key Vault لا داخل الكود مباشرة.
2. ترميز XML — UTF-8 إلزامي بلا استثناء
<?xml version="1.0" encoding="UTF-8"?>
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
...
</Invoice>
3. سلسلة Hash — كيف تمنع ZATCA التلاعب في الفواتير
كل فاتورة جديدة تحتوي على Hash الفاتورة السابقة، مما يُنشئ سلسلة متصلة تمنع تعديل أي فاتورة سابقة.
Hash الفاتورة الحالية = SHA-256(محتوى XML + Hash الفاتورة السابقة)
Previous Invoice Hash = "0"
هذا يسمح ببداية السلسلة بدون الاعتماد على فاتورة سابقة. بعد ذلك كل فاتورة تستخدم hash السابقة. إذا فشلت فاتورة في المنتصف، ينكسر كل ما بعدها.
الحل الاحترافي: استخدم نظام Queue (مثل Redis أو RabbitMQ) لضمان الترتيب وعدم فقدان أي فاتورة.
4. UUID الفريد — مرجع كل فاتورة في ZATCA
كل فاتورة تحتاج UUID (Universally Unique Identifier) فريداً يُولَّد قبل الإرسال.
// JavaScript / Node.js
const uuid = crypto.randomUUID();
// مثال: "123e4567-e89b-12d3-a456-426614174000"
// Python
import uuid
invoice_uuid = str(uuid.uuid4())
// PHP
$uuid = \Ramsey\Uuid\Uuid::uuid4()->toString();
// C#
string uuid = Guid.NewGuid().ToString();
- ❌ لا تستخدم أرقاماً تسلسلية (1, 2, 3…)
- ❌ لا تستخدم Timestamp كـ UUID
- ❌ لا تستخدم رقم الطلب من Shopify
- ✅ يجب أن يكون عشوائياً مطابقاً لمعيار RFC 4122
5. مشكلة Rounding Issues — الخطأ الخفي الذي يرفض الفاتورة
Shopify يحسب الضريبة بطريقته الخاصة، والفرق البسيط (هللات) كافٍ لرفض الفاتورة من الهيئة:
المبلغ الأصلي: 57.50 ريال
الضريبة حسب Shopify: 8.63 ريال (تقريب)
الضريبة حسب ZATCA: 8.625 ريال (دقيق)
الفرق: 0.005 ريال → رفض الفاتورة ❌
الحل الصحيح — أعد حساب VAT قبل إرسال XML:
VAT Amount = (Taxable Amount × 15%) مقرّب لأقرب هللتين
Total Amount = Taxable Amount + VAT Amount
ملاحظة تقنية: التقريب يجب أن يتم على مستوى إجمالي الفاتورة (Invoice Level Total) لا على مستوى كل عنصر (Line Item) لتجنب تراكم الفروقات الصغيرة.
6. QR Code للفواتير المبسطة — TLV وليس نصاً عادياً
الحقول الإلزامية الخمسة في QR Code:
| Tag | الحقل | مثال على القيمة |
|---|---|---|
| 1 | اسم البائع | شركة المثال التجارية |
| 2 | الرقم الضريبي | 300000000000003 |
| 3 | تاريخ ووقت الفاتورة | 2026-02-15T14:30:00Z |
| 4 | إجمالي الفاتورة (مع الضريبة) | 115.00 |
| 5 | قيمة الضريبة | 15.00 |
خطوات توليد QR Code الصحيح:
الخطوة 1: بناء مصفوفة TLV من البيانات
Tag (1 byte) + Length (1 byte) + Value (variable)
الخطوة 2: دمج الحقول الخمسة في مصفوفة واحدة
الخطوة 3: تحويل المصفوفة إلى Bytes
الخطوة 4: ترميز Bytes إلى Base64
الخطوة 5: توليد صورة QR من Base64 String النهائي
💡 نصيحة للمطورين: لا تبني TLV encoder يدوياً — احتمال الخطأ عالٍ جداً. استخدم مكتبات جاهزة:
Python: pytlv — Node.js: node-tlv — Java/C#: ابحث عن TLV encoder libraries.
مثال تطبيقي — كيف يشتغل الربط تقنياً
// استقبال Webhook من Shopify عند دفع الطلب
app.post('/webhooks/orders/paid', async (req, res) => {
const order = req.body;
// 1. توليد UUID فريد للفاتورة (RFC 4122)
const invoiceUUID = crypto.randomUUID();
// 2. تحويل البيانات إلى XML UBL 2.1 مع UTF-8
const xmlInvoice = await convertToUBL(order, {
uuid: invoiceUUID,
encoding: 'UTF-8'
});
// 3. إعادة حساب VAT — تجنب Rounding Issues
const correctedXML = await recalculateVAT(xmlInvoice);
// 4. توقيع الفاتورة بـ CSID
const signedInvoice = await signWithCSID(correctedXML);
// 5. إرسال إلى ZATCA (Clearance أو Reporting حسب نوع الفاتورة)
const response = await submitToZATCA(signedInvoice);
// 6. حفظ البيانات في Shopify Metafields
await updateOrderMetafields(order.id, {
zatca_uuid: invoiceUUID,
zatca_clearance_id: response.clearanceId,
zatca_qr: response.qrCode, // TLV + Base64
zatca_hash: response.invoiceHash // حرج! للفاتورة التالية
});
res.sendStatus(200);
});
📌 هذا مثال تبسيطي. التطبيق الفعلي يحتاج: معالجة أخطاء، Retry Logic مع Exponential Backoff، تشفير آمن، Queue management، وإدارة Hash Chain.
تخزين بيانات ZATCA في Shopify Metafields
| البيانات | Namespace | Key | لماذا مهم؟ |
|---|---|---|---|
| UUID | zatca | invoice_uuid | المعرف الفريد للفاتورة |
| Clearance ID | zatca | clearance_id | إثبات الموافقة من الهيئة |
| Invoice Hash | zatca | invoice_hash | حرج! تحتاجه للفاتورة التالية |
| QR Code | zatca | qr_code_base64 | TLV مشفر — للطباعة على PDF |
| تاريخ الختم | zatca | clearance_timestamp | وقت الموافقة (للمراجعة) |
أشهر أخطاء ربط Shopify مع ZATCA وحلولها الفورية
| الخطأ | السبب | الحل |
|---|---|---|
| XML Validation Error | بنية XML غير متوافقة مع UBL 2.1 | استخدم مكتبة معتمدة واختبر في Sandbox |
| Encoding Error | encoding غير UTF-8 | تأكد من <?xml encoding="UTF-8"?> |
| Invalid UUID | رقم تسلسلي بدلاً من UUID صحيح | crypto.randomUUID() أو uuid.uuid4() |
| QR Code Rejected | نص عادي بدلاً من TLV | TLV encoder ← Base64 ← QR |
| Hash Mismatch | Hash الفاتورة السابقة خاطئ | احفظ hash كل فاتورة في Metafields |
| Rounding Error | فرق في حساب VAT من Shopify | أعد حساب VAT بمعادلة ZATCA |
| Certificate Expired | انتهت صلاحية CSID | جدّد قبل 30 يوم + أضف تنبيهاً تلقائياً |
| API Timeout | بطء في استجابة خوادم الهيئة | Retry Logic مع Exponential Backoff |
Checklist قبل التشغيل الفعلي
الشهادات والأمان:
- ☐ حصلت على Compliance CSID واختبرت في Sandbox
- ☐ حصلت على Production CSID للتشغيل الفعلي
- ☐ الشهادة غير منتهية الصلاحية (ضبطت تنبيهاً تلقائياً)
- ☐ المفاتيح الخاصة مخزنة في Key Vault لا داخل الكود
التقنيات:
- ☐ XML بصيغة UBL 2.1 و UTF-8
- ☐ توليد UUID فريد (RFC 4122) لكل فاتورة
- ☐ QR Code بصيغة TLV + Base64 (ليس نصاً)
- ☐ تم حل مشكلة Rounding Issues
- ☐ Hash Chain يشتغل بشكل صحيح (أول فاتورة = "0")
- ☐ Webhook صحيح حسب طريقة الدفع
- ☐ في Retry Logic عند فشل الإرسال
- ☐ تحفظ invoice_hash لكل فاتورة
الجاهزية:
- ☐ اختبرت في بيئة Sandbox بالكامل
- ☐ جربت B2B (Clearance) و B2C (Reporting)
- ☐ تحققت أن QR Code يُقرأ بشكل صحيح
- ☐ فعّلت Monitoring وتنبيهات على الأخطاء
- ☐ عندك خطة نسخ احتياطي للشهادة والبيانات
الخلاصة — هل Shopify يدعم ZATCA المرحلة الثانية؟
نعم، ربط Shopify مع ZATCA المرحلة الثانية ممكن وفعّال، لكنه يختلف عن المنصات المحلية التي توفر تكاملاً مدمجاً. Shopify يتطلب حلاً خارجياً — سواء تطبيقاً جاهزاً أو Middleware مخصص — يتولى Clearance و Reporting بشكل صحيح.
الميزة الأساسية لـ Shopify هي المرونة والتوسع الدولي. لكن هذا يأتي مع مسؤولية إعداد الربط التقني بدقة. المتطلبات الأساسية التي لا تقبل التساهل:
- ✅ فهم الفرق بين B2B (Clearance) و B2C (Reporting)
- ✅ توليد UUID فريد لكل فاتورة
- ✅ QR Code بصيغة TLV + Base64 حصراً
- ✅ الشهادة الرقمية CSID ومراقبة تاريخ انتهائها
- ✅ استخدام UTF-8 مع UBL 2.1
- ✅ معالجة Rounding Issues قبل الإرسال
- ✅ بناء نظام Hash Chain سليم (أول فاتورة = "0")
- ✅ اختيار الـ Webhook الصحيح
نفّذ هذه النقاط بشكل صحيح، وسيصير Shopify منصة قوية مع امتثال كامل للمرحلة الثانية.
أسئلة شائعة — ربط Shopify مع ZATCA
هل يمكنني استخدام تطبيق واحد لعدة متاجر Shopify مع ZATCA؟
لا. كل متجر Shopify يُعدّ EGS مستقلاً ويحتاج شهادة رقمية (CSID) منفصلة من ZATCA. 3 متاجر = 3 شهادات مستقلة، ولا يمكن مشاركة الشهادة بين كيانات قانونية مختلفة.
ماذا يحدث بالضبط إذا انتهت صلاحية الشهادة الرقمية؟
يتوقف إصدار الفواتير فوراً. كل محاولة إرسال لـ ZATCA ستُرفض. لذلك راقب تاريخ الانتهاء وجدّد قبل 30 يوم على الأقل مع تنبيهات تلقائية.
هل أحتاج مطوراً لربط Shopify مع ZATCA؟
مع التطبيقات الجاهزة (QuiXcel، Marmin) لا تحتاج مطوراً في الغالب — الإعداد بسيط. مع Middleware مخصص تحتاج خبرة تقنية في Node.js أو Python وفهم كامل لـ API الهيئة.
كيف أعرف إذا كان الحل يعالج Rounding Issues؟
اسأل المزود مباشرة: "هل تعيدون حساب VAT قبل إرسال XML؟" — إذا كان الجواب غير واضح أو لا، ابحث عن حل آخر. الفرق حتى 0.005 ريال كافٍ لرفض الفاتورة.
لماذا QR Code يُرفض رغم أنه يعمل عندي؟
السبب الأشهر: أنت تستخدم نصاً عادياً بدلاً من ترميز TLV. ZATCA تتطلب QR Code بصيغة TLV محوّل إلى Base64. استخدم مكتبة TLV encoder متخصصة لتجنب هذا الخطأ.
📚 اقرأ أيضاً من Trelyoon:
فريق Trelyoon التقني
فريق متخصص في حلول التجارة الإلكترونية والفوترة الإلكترونية في منطقة الخليج. نغطي التحديات التقنية والتشريعية للمطورين وأصحاب المتاجر الرقمية. المحتوى مبني على التطبيق الفعلي وتوثيق ZATCA الرسمي.