ملخص الإجابة: الرمز الذي سيقرأه الإنسان من الصورة ويكتبه يجب أن يكتشف الخطأ بطبيعته. الرمز المكوَّن من 6 أرقام (5 أرقام بيانات ورقم تحقق لون Luhn) يلتقط 100٪ من أخطاء الرقم الواحد ومعظم تبديلات الرقمين المتجاورين؛ وله سعة 100,000 رمز لكل متجر، وهو أقصر من أن يكون كتابته عبئاً على العميل.
لنحدد المسألة بدقة
لدينا قناة اتصال مُرسِلها صورة، ومستقبِلها عين الإنسان، ووسيطها لوحة مفاتيح الهاتف. ومثل أي قناة أخرى، فيها تشويش: رقم يُقرأ خطأً، إصبع يضغط الرقم المجاور، أو رقمان يُكتبان بترتيب معكوس. هناك ثلاثة أنواع شائعة من الأخطاء في الإدخال اليدوي للأرقام معروفة في أدبيات اكتشاف الأخطاء:
- خطأ برقم واحد (single-digit error): رقم خاطئ واحد — الأكثر شيوعاً.
- التبديل المتجاور (adjacent transposition): «47» بدلاً من «74».
- خطأ التكرار والتخطي: تكرار رقم أو حذفه.
دون رقم تحقق، أي خطأ في رقم واحد يتحول على الأرجح إلى كود صالح آخر. أي أن العميل يدخل صفحة منتج خاطئة دون أي تحذير — أسوأ سيناريو ممكن، لأن النظام يبدو واثقاً وهو مخطئ.
لماذا لون (Luhn) وليس فيرهوف (Verhoeff) أو دام (Damm)؟
توجد ثلاث خوارزميات كلاسيكية لرقم التحقق العشري:
| الخوارزمية | خطأ برقم واحد | التبديل المجاور | تعقيد التنفيذ |
|---|---|---|---|
| لوهن (Luhn) | ۱۰۰٪ | معظم الحالات (باستثناء 09/90) | بضعة أسطر، دون جدول |
| فيرهوف (Verhoeff) | ۱۰۰٪ | ۱۰۰٪ | ثلاثة جداول 10×10 (مجموعة ثنائية الوجه) |
| خوارزمية Damm | ۱۰۰٪ | ۱۰۰٪ | جدول شبه مجموعة 10×10 |
من حيث قوة الكشف، يتفوّق Verhoeff وDamm. لكن اخترنا خوارزمية Luhn، والسبب هندسي لا رياضي: يُتحقق من رمز المنتج في متصفح العميل أيضاً إلى جانب الخادم، قبل إرسال النموذج، لتُكتشف الأخطاء فوراً ودون رحلة ذهاب وإياب عبر الشبكة. تُنفَّذ Luhn دون أي جداول في بضعة أسطر من جافاسكربت، وتقلّل خطر عدم التطابق بين تنفيذ الخادم والمتصفح، وهي نفس الخوارزمية التي تُتحقق بها مليارات أرقام البطاقات المصرفية — أي أن مستوى اختبارها في العالم الحقيقي لا يُضاهى. خطأ Luhn غير المغطّى (تبديل 09↔90) في فضاء من 5 أرقام يمثّل حصة ضئيلة من الأخطاء، وقُبِل مقابل بساطة التنفيذ.
اختبار رقمي، لا ادعاء
قسنا التصميم باختبار آلي: وُلّد 200 كود عشوائي، وعُدّل في كل كود كل موضع من المواضع الرقمية الستة بكل بديل ممكن من الأرقام التسعة، أي 200×6×9 = 10,800 طفرة برقم واحد. وكُشف كل الـ 10,800 حالة بوصفها كودًا غير صالح. وهذا هو الضمان النظري لخوارزمية Luhn نفسه، لكن هذا الاختبار هو ما يصنع الفرق بين «يجب أن يعمل» و«يعمل على كودنا».
لماذا خمسة أرقام للبيانات؟ حساب السعة والتخمين
5 أرقام تعني 100,000 رمز ممكن. بالنسبة لمتجر يضم آلاف المنتجات، تكون المساحة المستهلكة جزءًا صغيرًا من المساحة الكلية، ولأن الرموز تُخصَّص عشوائيًا (لا تسلسليًا)، يستحيل تخمين رمز المنتج التالي من الرمز الحالي. هذه نقطة أمنية مهمة: الرمز التسلسلي يكشف للمنافس قائمة منتجاتك بل ومعدل نمو كتالوجك.
ومع ذلك يجب القول بصراحة: هذا الكود معرّف وليس سرًّا. الكود لا يوصل إلا إلى الصفحة العامة للمنتج، ولا توجد خلفه أي عملية حساسة. وحيثما يرتبط الكود بسجل خاص (مثل العودة إلى سلة غير مكتملة) يُحمى المسار برمز مستقل لا بالكود نفسه.
مسألة لا وجود لها إطلاقاً في النص الإنجليزي: الأرقام الفارسية والنص ثنائي الاتجاه
عندما تضع الشيفرة داخل تعليق فارسي أو تكتب على صورة من اليمين إلى اليسار، تواجهك مشكلتان حقيقيتان.
1) تطبيع أرقام الإدخال
قد يكتب العميل الرمز بلوحة مفاتيح فارسية (۰۱۲۳) أو عربية (٠١٢٣) أو لاتينية (0123). وإذا قارنتم المُدخل كما هو، سيُرفض الرمز الصحيح أيضاً. الحل: توحيد جميع الأرقام إلى اللاتينية قبل أي تحقق — في المتصفح وعلى الخادم معاً، بدالة مشتركة واحدة.
PERSIAN = "۰۱۲۳۴۵۶۷۸۹"
ARABIC = "٠١٢٣٤٥٦٧٨٩"
def normalize(raw):
out = []
for ch in raw:
if ch in PERSIAN:
out.append(str(PERSIAN.index(ch)))
elif ch in ARABIC:
out.append(str(ARABIC.index(ch)))
elif ch.isdigit():
out.append(ch)
return "".join(out)
2) ترتيب العرض في النص من اليمين إلى اليسار
خوارزمية يونيكود ثنائية الاتجاه (UAX #9) يحدد كيفية إعادة ترتيب الأرقام داخل النص من اليمين إلى اليسار. الأثر العملي أنه إذا جمّعت الرمز للقراءة — مثلاً «482 917» — فقد يظهر في النص العربي بالشكل «917 482» ويقرأ العميل الرمز معكوساً. رأينا هذا الخلل فعلياً على صورة حقيقية لا نظرياً.
حلّه قراران: تجميع الأرقام معطّل افتراضيًا، ويُطبع الكود دائمًا داخل فواصل عزل ثنائية الاتجاه (LRI وPDI) حتى لا يستطيع أي نص محيط تغيير ترتيبه.
3) تشكيل الأحرف عند النقش على الصورة
لكتابة نص فارسي على الصورة مع Pillow هناك عالمان: إذا كانت المكتبة تدعم Raqm فإنها تتولى بنفسها تشكيل الحروف مع HarfBuzz ويتولى ترتيب النص ثنائي الاتجاه عبر FriBidi، ويجب أن تمرّر إليه النص بترتيبه المنطقي (لا بعد إعادة ترتيبه مسبقًا). وإن لم يتوافر Raqm فعليك أنت تشكيل النص وإعادة ترتيبه. والخطأ الشائع — وهو خلل وقعنا فيه نحن أيضًا — هو القيام بالأمرين معًا: فالنتيجة نص شُكِّل مرتين فتختلط حروفه. الطريقة الصحيحة: اكتشف وجود Raqm وقت التشغيل واسلك أحد المسارين فقط.
أين يُوضع الرمز على الصورة؟
وضوح الرمز مسألة تصميم بصري وتؤثر مباشرة في معدل إدخال الرمز. بعض القواعد العملية المضبوطة مسبقًا في متجر إنستغرام من Abrak تم تنفيذها:
- شريط خلفية شبه شفاف أسفل الرمز، لضمان التباين على أي صورة.
- الزاوية السفلية، بعيداً عن الحافة: جزء من الصورة يبقى ظاهراً في عرض الشبكة بالملف الشخصي وفي جميع أبعاد الصورة.
- حجم خط متناسب مع عرض الصورة، لا حجم ثابت — يجب أن يُقرأ الرمز حتى في المعاينة الصغيرة للمنشور.
- تكرار الرمز في التعليق ويُفضَّل في السطر الأخير: بحسب مكان اقتطاع التعليق، يظهر على الأقل أحد التكرارين.
الخلاصة
يبدو الرمز المكوّن من 6 أرقام بسيطًا، لكن خلفه أربعة قرارات مستقلة: الطول والسعة، خوارزمية كشف الأخطاء، التعامل مع الأرقام غير اللاتينية والنص ثنائي الاتجاه، والتصميم البصري على الصورة. أي خطأ في أحدها يظهر على هيئة «لم يصل العميل إلى المنتج» — لا كخطأ برمجي. لمعرفة موقع هذا الرمز في مسار البيع بأكمله، مقال فجوة متجر إنستغرام ولنشر منشور يحتوي على هذا الرمز تلقائيًا، مقال النشر التلقائي للاطلاع عليه.