إنشاء QR Code لفواتير ZATCA في Flutter باستخدام zatca_2_invoice_generator
إذا كنت تعمل على تطبيق نقاط بيع أو نظام فواتير داخل المملكة العربية السعودية، فمن المتطلبات المهمة التعامل مع الفوترة الإلكترونية ومتطلبات هيئة الزكاة والضريبة والجمارك ZATCA.
مكتبة zatca_2_invoice_generator توفر مجموعة من الأدوات التي تساعد مطوري Flutter على إنشاء بيانات الفاتورة وإنشاء QR Code الخاص بالفاتورة الإلكترونية وفق آلية المرحلة الثانية.
كما تدعم المكتبة بيانات الفواتير من نوع B2C وB2B، وتتعامل مع عناصر مثل رقم الفاتورة وUUID والضريبة وبيانات البائع والمشتري والأسطر الموجودة داخل الفاتورة.
ما هي مكتبة zatca_2_invoice_generator؟
zatca_2_invoice_generator هي مكتبة Flutter مخصصة لإنشاء QR Code لفواتير ZATCA Phase 2.
توفر المكتبة Manager باسم:
ZatcaManager
ومن خلاله يمكن تهيئة بيانات المنشأة، وإنشاء بيانات الفاتورة، ثم الحصول على محتوى QR Code أو عرضه مباشرة داخل واجهة Flutter.
تثبيت المكتبة
الإصدار المنشور حاليًا على pub.dev هو:
zatca_2_invoice_generator: ^9.0.0
قم بإضافته داخل:
pubspec.yaml
بهذا الشكل:
dependencies:
flutter:
sdk: flutter
zatca_2_invoice_generator: ^9.0.0
ثم نفذ:
flutter pub get
ماذا توفر المكتبة؟
- إنشاء QR Code لفاتورة ZATCA.
- إنشاء محتوى QR كـ String.
- دعم بيانات الفواتير B2C.
- دعم بيانات الفواتير B2B.
- إضافة بيانات المورد Supplier.
- إضافة بيانات العميل Customer.
- إضافة أسطر المنتجات Invoice Lines.
- التعامل مع Invoice UUID.
- التعامل مع Invoice Number.
- إنشاء Invoice Hash.
- التعامل مع Digital Signature.
- استخدام Private Key وCertificate.
- توليد TLV المستخدم داخل QR Code.
تهيئة ZatcaManager
قبل إنشاء أي فاتورة نحتاج أولًا إلى تهيئة ZatcaManager ببيانات المنشأة.
void main() {
initZatca();
runApp(
const MyApp(),
);
}
ثم نقوم بإنشاء:
void initZatca() {
ZatcaManager.instance.initializeZacta(
sellerName: 'Geecoders Store',
sellerTRN: '310123456700003',
privateKeyBase64: privateKeyBase64,
certificateBase64: certificateBase64,
supplier: Supplier(
companyID: '1010123456',
registrationName:
'310123456700003',
address: Address(
streetName:
'King Fahd Road',
buildingNumber:
'1234',
citySubdivisionName:
'Al Olaya',
cityName:
'Riyadh',
postalZone:
'12345',
),
),
);
}
لاحظ أن اسم الدالة داخل الإصدار الحالي من المكتبة هو:
initializeZacta()
وليس:
initializeZatca()
وهو الاسم المستخدم فعليًا في API الخاصة بالمكتبة.
هل يجب استدعاء initializeZacta مع كل فاتورة؟
لا تحتاج عادة إلى تنفيذ:
initializeZacta()
مع كل فاتورة.
يمكن تهيئة ZatcaManager مرة واحدة بعد تشغيل التطبيق طالما أن بيانات المنشأة والشهادة والمفتاح لم تتغير.
بعد ذلك يمكن استخدام:
ZatcaManager.instance
لإنشاء أي عدد من الفواتير أثناء جلسة تشغيل التطبيق.
لكن إذا تم إغلاق التطبيق بالكامل ثم تشغيله مرة أخرى، فيجب إعادة التهيئة لأن البيانات الموجودة داخل Singleton تكون داخل ذاكرة التطبيق.
إنشاء فاتورة B2C
فاتورة B2C هي الفاتورة التي تكون عادة بين المنشأة والمستهلك النهائي.
يمكن إنشاء بيانات QR لها باستخدام:
final qrDataModel =
ZatcaManager.instance
.generateZatcaQrInit(
totalVat: '15.00',
totalWithVat: '115.00',
issueDate: '2026-10-05',
issueTime: '12:30:00',
invoiceUUid:
'8e6000cf-1a98-4174-b3e7-b5d5954bc10d',
invoiceNumber:
'INV-000001',
invoiceLines: [
InvoiceLine(
id: '1',
quantity: '1',
unitCode: 'PCE',
lineExtensionAmount:
'100.00',
itemName:
'Product 1',
taxPercent:
'15',
),
],
invoiceType:
InvoiceType.invoice,
);
بعد تنفيذ الدالة تحصل على:
QrDataModel
والذي يحتوي على البيانات التي تحتاجها المكتبة لإنشاء محتوى QR Code.
إضافة أكثر من منتج إلى الفاتورة
يمكن إضافة أكثر من InvoiceLine بسهولة.
invoiceLines: [
InvoiceLine(
id: '1',
quantity: '2',
unitCode: 'PCE',
lineExtensionAmount:
'100.00',
itemName:
'Coffee',
taxPercent:
'15',
),
InvoiceLine(
id: '2',
quantity: '1',
unitCode: 'PCE',
lineExtensionAmount:
'50.00',
itemName:
'Dessert',
taxPercent:
'15',
),
]
ما هو lineExtensionAmount؟
القيمة:
lineExtensionAmount
تمثل قيمة سطر المنتج التي يتم استخدامها أثناء بناء بيانات الفاتورة.
ومن المهم أن تكون القيم المالية التي ترسلها متوافقة مع العمليات الحسابية الموجودة داخل نظام نقاط البيع لديك.
لا تعتمد على المكتبة وحدها للتحقق من صحة إجمالي الفاتورة، بل احسب:
- السعر قبل الضريبة.
- الخصومات.
- الكمية.
- إجمالي الضريبة.
- إجمالي الفاتورة شامل الضريبة.
داخل نظامك أولًا، ثم أرسل القيم النهائية الصحيحة إلى المكتبة.
إنشاء فاتورة B2B
إذا كانت الفاتورة بين منشأتين، يمكن تحديد نوع العلاقة:
invoiceRelationType:
InvoiceRelationType.b2b
مع إرسال بيانات العميل:
final qrDataModel =
ZatcaManager.instance
.generateZatcaQrInit(
totalVat:
'150.00',
totalWithVat:
'1150.00',
issueDate:
'2026-10-05',
issueTime:
'13:00:00',
invoiceUUid:
'8e6000cf-1a98-4174-b3e7-b5d5954bc10d',
invoiceNumber:
'INV-000002',
invoiceLines: [
InvoiceLine(
id: '1',
quantity:
'1',
unitCode:
'PCE',
lineExtensionAmount:
'1000.00',
itemName:
'Software Service',
taxPercent:
'15',
),
],
invoiceType:
InvoiceType.invoice,
invoiceRelationType:
InvoiceRelationType.b2b,
customer: Customer(
companyID:
'310000000000003',
registrationName:
'Customer Company',
address: Address(
streetName:
'Olaya Street',
buildingNumber:
'100',
citySubdivisionName:
'Olaya',
cityName:
'Riyadh',
postalZone:
'12211',
countryCode:
'SA',
),
),
);
الفرق بين B2B و B2C
عند إنشاء B2C يمكن ترك القيمة الافتراضية المستخدمة داخل المكتبة:
InvoiceRelationType.b2c
أما عند إنشاء B2B فيتم تحديد:
InvoiceRelationType.b2b
وإرسال بيانات:
Customer
الخاصة بالعميل أو المنشأة المشترية.
ما الفرق بين invoiceNumber و invoiceUUid؟
من الأشياء التي تسبب بعض الالتباس عند تنفيذ ZATCA وجود:
invoiceNumber
و:
invoiceUUid
لكن كل قيمة لها وظيفة مختلفة.
Invoice Number
هو الرقم التجاري المتسلسل للفاتورة داخل نظامك.
مثل:
INV-000001
INV-000002
INV-000003
ويتم توليده وإدارته بواسطة نظام الفواتير أو POS الخاص بك.
Invoice UUID
أما:
invoiceUUid
فهو معرف فريد للفاتورة.
مثال:
8e6000cf-1a98-4174-b3e7-b5d5954bc10d
ومن الأفضل إنشاء UUID جديد لكل فاتورة.
يمكن استخدام مكتبة:
uuid
مثلًا:
import 'package:uuid/uuid.dart';
const uuid = Uuid();
final invoiceUuid =
uuid.v4();
هل يمكن استخدام نفس UUID لأكثر من فاتورة؟
لا.
الـ UUID يجب أن يميز كل فاتورة عن الأخرى، لذلك يجب إنشاء UUID جديد عند إنشاء فاتورة جديدة.
final uuid =
const Uuid().v4();
هل invoiceNumber يجب أن يكون UUID؟
لا.
يمكن أن يكون رقم الفاتورة رقمًا تسلسليًا مفهومًا داخل نظامك:
INV-2026-000001
بينما UUID يكون منفصلًا:
550e8400-e29b-41d4-a716-446655440000
إدارة أرقام الفواتير في أكثر من فرع
إذا كان لديك أكثر من Warehouse أو Branch أو POS Terminal فمن الأفضل أن تكون لديك استراتيجية واضحة لإنشاء أرقام الفواتير.
مثلًا:
R01-INV-000001
R02-INV-000001
J01-INV-000001
أو استخدام Sequence منفصل لكل فرع أو جهاز.
المهم ألا تعتمد على:
DateTime.now().millisecondsSinceEpoch
كبديل كامل لإدارة تسلسل الفواتير إذا كان نظامك المحاسبي يحتاج إلى Sequence واضح وقابل للتتبع.
عرض QR Code مباشرة
توفر المكتبة Widget جاهزة:
Zatca2InvoiceQrGenerator
ويمكن استخدامها بهذا الشكل:
Center(
child:
Zatca2InvoiceQrGenerator(
qrDataModel:
qrDataModel,
),
)
وبذلك يتم إنشاء وعرض QR Code مباشرة داخل Flutter.
الحصول على محتوى QR Code كـ String
في بعض الحالات قد لا تريد استخدام Widget المكتبة.
مثلًا إذا كنت تريد:
- طباعة QR باستخدام طابعة POS.
- إنشاء QR باستخدام مكتبة أخرى.
- تحويله إلى صورة.
- إرساله إلى Native Printer SDK.
هنا يمكن استخدام:
final content =
ZatcaManager.instance
.getQrCodeContent(
qrDataModel,
);
ثم استخدام الـ String مع مكتبة مثل:
pretty_qr_code
مثال:
SizedBox(
width: 200,
height: 200,
child:
PrettyQrView.data(
data:
content,
decoration:
const PrettyQrDecoration(),
),
)
طباعة QR Code في تطبيق POS
إذا كان لديك تطبيق POS وتتعامل مع طابعات مثل Sunmi أو iMin أو أجهزة أخرى، فمن الأفضل أحيانًا تحويل QR إلى صورة بدل إرسال النص مباشرة إلى أمر طباعة QR الخاص بالطابعة.
السبب أن بعض SDKs الخاصة بالطابعات تضع حدًا لطول البيانات التي يستطيع أمر QR الداخلي استقبالها.
في هذه الحالة يمكن تنفيذ المسار التالي:
ZATCA Data
↓
getQrCodeContent()
↓
Generate QR Widget
↓
Convert Widget to Image
↓
Print Image
وهذه الطريقة تمنحك أيضًا تحكمًا أفضل في حجم وجودة QR Code عند الطباعة.
ما هو QrDataModel؟
عند تنفيذ:
generateZatcaQrInit()
ترجع المكتبة Object من نوع:
QrDataModel
ويتضمن مجموعة من البيانات المرتبطة بالفاتورة والتوقيع، ومنها:
- Seller Name.
- Seller TRN.
- Issue Date.
- Invoice Hash.
- Digital Signature.
- Public Key.
- Certificate Signature.
- Invoice Data.
Invoice Hash
من البيانات التي تقوم المكتبة بإنشائها:
qrDataModel.invoiceHash
والذي يمثل Hash مرتبطًا ببيانات الفاتورة.
يمكن الوصول إليه مثلًا:
final hash =
qrDataModel.invoiceHash;
debugPrint(hash);
Digital Signature
كما يحتوي QrDataModel على:
qrDataModel.digitalSignature
بالإضافة إلى:
qrDataModel.publicKey
و:
qrDataModel.certificateSignature
وهي من المعلومات المستخدمة في تكوين البيانات التي تدخل ضمن QR الخاص بالمرحلة الثانية.
هل المكتبة تتصل مباشرة بـ ZATCA؟
هذه نقطة مهمة جدًا.
بحسب الـ API العامة والتوثيق الحالي لمكتبة zatca_2_invoice_generator، فإن دورها الأساسي هو تجهيز بيانات الفاتورة وإنشاء QR Code وما يرتبط به من Hash وتوقيع.
المكتبة لا توفر في واجهتها الموثقة نظامًا كاملًا لإرسال الفواتير إلى ZATCA من خلال:
Reporting API
أو:
Clearance API
لذلك وجود QR Code تم إنشاؤه بنجاح لا يعني تلقائيًا أن الفاتورة تم إرسالها أو قبولها في منصة فاتورة.
الفرق بين إنشاء QR والربط مع ZATCA
يمكن تصور العملية بهذا الشكل:
Invoice Data
↓
Generate XML / Hash
↓
Digital Signature
↓
Generate QR
↓
━━━━━━━━━━━━━━━━━━
↓
Reporting / Clearance
↓
ZATCA Fatoora Platform
المكتبة تساعد في الجزء الأول من العملية، لكن مرحلة الربط الفعلي مع منصة فاتورة تحتاج إلى تنفيذ متطلبات الـ API الخاصة بالهيئة.
هل نجاح قراءة QR يعني أن الفاتورة مقبولة؟
لا.
إمكانية مسح QR وعرض البيانات تعني أن محتوى QR يمكن قراءته، لكنها ليست بديلًا عن عمليات التحقق أو Reporting أو Clearance المطلوبة للمنشأة الخاضعة لمرحلة الربط والتكامل.
هل يجب وضع Private Key داخل تطبيق Flutter؟
يجب التعامل مع هذه النقطة بحذر شديد.
لا يفضل وضع:
privateKeyBase64
كنص ثابت داخل Source Code مثل:
const privateKey =
'MY_PRIVATE_KEY';
لأن التطبيق النهائي يمكن تحليله واستخراج البيانات منه.
يفضل تصميم آلية آمنة لحماية المفاتيح والشهادات بما يتناسب مع متطلبات الحل الخاص بك ومتطلبات ZATCA، وعدم التعامل معها كقيم Config عادية داخل التطبيق.
هل يمكن إنشاء QR بدون Widget المكتبة؟
نعم.
كل ما تحتاج إليه هو:
final content =
ZatcaManager.instance
.getQrCodeContent(
qrDataModel,
);
ثم استخدام أي QR Generator يدعم النصوص.
متى أستخدم getQrCodeContent؟
هذه الطريقة مفيدة جدًا إذا كنت تعمل على:
- POS Application.
- Thermal Printer.
- PDF Invoice.
- Custom Receipt Design.
- Sunmi Printer.
- iMin Printer.
- Bluetooth Printer.
- Network Printer.
لأنك تحصل على محتوى QR وتقرر بنفسك طريقة عرضه أو طباعته.
مثال كامل مبسط
final qrData =
ZatcaManager.instance
.generateZatcaQrInit(
totalVat:
'15.00',
totalWithVat:
'115.00',
issueDate:
'2026-10-05',
issueTime:
'14:30:00',
invoiceUUid:
const Uuid().v4(),
invoiceNumber:
'INV-000123',
invoiceLines: [
InvoiceLine(
id: '1',
quantity:
'1',
unitCode:
'PCE',
lineExtensionAmount:
'100.00',
itemName:
'Product',
taxPercent:
'15',
),
],
invoiceType:
InvoiceType.invoice,
);
final qrContent =
ZatcaManager.instance
.getQrCodeContent(
qrData,
);
ثم يمكن عرض QR:
PrettyQrView.data(
data: qrContent,
)
ملاحظات مهمة قبل استخدام المكتبة في Production
الفوترة الإلكترونية ليست مجرد QR Code، خصوصًا بالنسبة للمنشآت التي دخلت في المرحلة الثانية من الربط والتكامل.
لذلك قبل الاعتماد على أي مكتبة خارجية في بيئة Production يجب التأكد من:
- مطابقة XML لأحدث متطلبات ZATCA.
- صحة طريقة إنشاء Invoice Hash.
- صحة Digital Signature.
- صحة Certificate المستخدم.
- صحة UUID لكل فاتورة.
- صحة ICV وتسلسل الفواتير إذا كان مطلوبًا في بنية نظامك.
- إدارة Previous Invoice Hash بالشكل المطلوب في الحل الكامل.
- تنفيذ Reporting أو Clearance حسب نوع الفاتورة ومتطلبات المنشأة.
- اختبار الحل باستخدام بيئة ZATCA المناسبة قبل Production.
ملاحظة حول تحديث المكتبة
الإصدار الحالي 9.0.0 منشور منذ فترة، لذلك لا يُنصح بالاعتماد فقط على وصف المكتبة باعتباره ضمانًا دائمًا للتوافق.
متطلبات الفوترة الإلكترونية قد يتم تحديثها، ولهذا يجب دائمًا مقارنة النتائج مع أحدث المستندات المنشورة من هيئة الزكاة والضريبة والجمارك.
ZATCA Phase 1 و Phase 2
بشكل مبسط توجد مرحلتان رئيسيتان للفوترة الإلكترونية.
Phase 1 – Generation
تركز على إنشاء وحفظ الفواتير إلكترونيًا وفق المتطلبات المحددة.
Phase 2 – Integration
تضيف متطلبات تقنية إضافية، وأهمها ربط نظام الفوترة الإلكتروني بمنصة فاتورة التابعة لـ ZATCA.
ويتم تطبيق المرحلة الثانية تدريجيًا على مجموعات من المنشآت.
هل zatca_2_invoice_generator كافية لعمل نظام ZATCA كامل؟
إذا كان هدفك فقط إنشاء بيانات QR والاستفادة من الـ Models والـ Hash والتوقيع الذي توفره المكتبة، فهي توفر أدوات تساعدك على ذلك.
أما إذا كنت تريد بناء نظام Phase 2 متكامل في Production، فلا يكفي الاعتماد على QR Generator فقط.
ستحتاج إلى التعامل أيضًا مع متطلبات مثل:
Onboarding
CSR
CSID
Compliance
UBL XML
Invoice Hash
Cryptographic Stamp
PIH
ICV
Reporting
Clearance
ZATCA APIs
وذلك حسب نوع الفاتورة ومتطلبات المنشأة.
الخلاصة
مكتبة zatca_2_invoice_generator توفر طريقة سهلة نسبيًا لإضافة إنشاء QR Code الخاص بفواتير ZATCA داخل تطبيقات Flutter، مع دعم بيانات B2C وB2B والتعامل مع بيانات المورد والعميل والفاتورة.
كما توفر ZatcaManager لإنشاء QrDataModel واستخراج محتوى QR كـ String، مما يجعل دمجها مع تطبيقات POS والطابعات الحرارية وتصميمات الفواتير المخصصة أمرًا بسيطًا.
لكن من المهم التفرقة بين إنشاء QR Code وبين بناء حل ZATCA Phase 2 كامل يقوم بالربط والإرسال إلى منصة فاتورة.
لذلك استخدم المكتبة كجزء من الحل، مع التأكد دائمًا من أحدث متطلبات الهيئة قبل تشغيل النظام في بيئة Production.
مكتبة zatca_2_invoice_generator على pub.dev
الموقع الرسمي للفوترة الإلكترونية ZATCA
لمزيد من المقالات : شرح SourceTree للمبتدئين | ربط مشروعك مع GitHub ورفع الكود خطوة بخطوة






