المقارنات

reCAPTCHA v3 Enterprise مقابل الإصدار القياسي

إذا نجح سكربت الحل نفسه على موقع ورُفض على آخر رغم أن كليهما يشغّل reCAPTCHA v3، فالسبب غالباً معلمة واحدة: الموقع الثاني يستخدم نسخة Enterprise وطلبك لا يحمل enterprise=1. الإصداران متطابقان أمام الزائر ويعيدان درجة مخاطرة بين 0.0 و1.0، لكن Enterprise يضيف عتبات لكل إجراء، ورموز أسباب، وإدارة على مستوى المشروع.

وما يهمّ المطوّر هنا ثلاث نقاط:

  • كيف تميّز الإصدارين من كود الصفحة قبل إرسال أي طلب.
  • ما الذي تضيفه Enterprise فعلياً، وما الذي يبقى كما هو.
  • كيف تبني مساراً واحداً يتعرّف على الإصدار ويتصرّف تلقائياً.

كيف تعرف أي إصدار يشغّله الموقع

الفارق ظاهر في وسم البرنامج النصي وحده: الإصدار القياسي يحمّل api.js?render=KEY ويستدعي grecaptcha.execute()، بينما تحمّل Enterprise ملف enterprise.js?render=KEY وتستدعي grecaptcha.enterprise.execute(). لا فرق بصري بينهما إطلاقاً. الدالة التالية تقرأ HTML مرة واحدة وتعيد الإصدار ومفتاح الموقع وقيمة action:

import requests
import re

def detect_v3_version(url):
    html = requests.get(url).text

    if "enterprise.js" in html:
        version = "enterprise"
    elif "recaptcha/api.js" in html and "render=" in html:
        version = "standard"
    else:
        return None

    # Extract sitekey
    key_match = re.search(r'render[=:]\s*["\']?([A-Za-z0-9_-]{40})', html)
    sitekey = key_match.group(1) if key_match else None

    # Extract action
    action_match = re.search(r'action["\']?\s*[:=]\s*["\'](\w+)', html)
    action = action_match.group(1) if action_match else None

    return {"version": version, "sitekey": sitekey, "action": action}

ونفس المنطق في Node.js عبر Axios:

const axios = require("axios");

async function detectV3Version(url) {
  const { data: html } = await axios.get(url);

  const version = html.includes("enterprise.js")
    ? "enterprise"
    : html.includes("recaptcha/api.js") && html.includes("render=")
      ? "standard"
      : null;

  const keyMatch = html.match(/render[=:]\s*['"]?([A-Za-z0-9_-]{40})/);
  const actionMatch = html.match(/action['"]?\s*[:=]\s*['"](\w+)/);

  return {
    version,
    sitekey: keyMatch?.[1],
    action: actionMatch?.[1],
  };
}

وانتبه: مفتاح موقع v3 يأتي من معلمة render لا من سمة data-sitekey كما في v2 — وهذا أشهر أسباب ERROR_WRONG_GOOGLEKEY.


ما الذي تضيفه Enterprise فوق الإصدار القياسي

معظم إضافات Enterprise موجّهة إلى مالك الموقع لا إليك، لكن العتبات المختلفة لكل إجراء تنعكس مباشرة على تكاملك:

العنصر v3 القياسي v3 Enterprise
تشغيل غير مرئي نعم نعم
درجة المخاطرة 0.0–1.0 نعم نعم
معلمة action مطلوبة مطلوبة
رموز الأسباب لا نعم
عتبات مخصصة لكل إجراء لا نعم — عبر Cloud Console
كشف تسرّب كلمات المرور لا نعم
Account Defender لا نعم
وسوم منع الاحتيال لا نعم
تكامل المصادقة متعددة العوامل لا نعم
نقطة نهاية التحقق siteverify — مجانية recaptchaenterprise.googleapis.com
الحصة الشهرية مليون تقييم مجاناً دفع لكل تقييم
ملف JavaScript api.js?render=KEY enterprise.js?render=KEY
معلمات CaptchaAI version=v3 version=v3 + enterprise=1

كيف يتغيّر احتساب الدرجة

يعتمد الإصدار القياسي على نموذج تسجيل عالمي واحد، أما Enterprise فيبني فوقه طبقات إضافية:

جانب الاحتساب القياسي Enterprise
نموذج الدرجة الأساسي نموذج Google العام نموذج Google العام + إشارات مخصصة
العتبات عتبة واحدة لكل الإجراءات عتبة لكل إجراء — تسجيل الدخول 0.7، إتمام الشراء 0.9
التعلّم التكيفي أساسي يتعلّم من أنماط حركة الموقع نفسه
دقة الدرجة 0.0–1.0 برقمين عشريين النطاق نفسه بإشارات أدق
معالجة الإنذارات الكاذبة ضبط يدوي للعتبة تحليل آلي عبر رموز الأسباب

الخلاصة: إرسال قيمة action الصحيحة يصبح أهم مع Enterprise، لأن كل إجراء قد يُقاس بعتبة مختلفة.


معلمة واحدة تفصل بين المسارين

الفرق على جانب CaptchaAI ليس نقطة نهاية مختلفة ولا استجابة مختلفة، بل معلمة واحدة في طلب in.php.

المسار القياسي

import requests
import time

resp = requests.get("https://ocr.captchaai.com/in.php", params={
    "key": "YOUR_API_KEY",
    "method": "userrecaptcha",
    "version": "v3",
    "googlekey": sitekey,
    "action": "login",
    "pageurl": page_url
})
task_id = resp.text.split("|")[1]

for _ in range(60):
    time.sleep(5)
    result = requests.get("https://ocr.captchaai.com/res.php", params={
        "key": "YOUR_API_KEY", "action": "get", "id": task_id
    })
    if result.text.startswith("OK|"):
        token = result.text.split("|")[1]
        break

مسار Enterprise

import requests
import time

# Only difference: enterprise=1
resp = requests.get("https://ocr.captchaai.com/in.php", params={
    "key": "YOUR_API_KEY",
    "method": "userrecaptcha",
    "version": "v3",
    "enterprise": 1,
    "googlekey": sitekey,
    "action": "login",
    "pageurl": page_url
})
task_id = resp.text.split("|")[1]

for _ in range(60):
    time.sleep(5)
    result = requests.get("https://ocr.captchaai.com/res.php", params={
        "key": "YOUR_API_KEY", "action": "get", "id": task_id
    })
    if result.text.startswith("OK|"):
        token = result.text.split("|")[1]
        break

حلّ يتعرّف على الإصدار تلقائياً

بدل تثبيت الإصدار في الكود، اجعل الكشف جزءاً من دورة الحل — الصنف التالي يستنتج الإصدار والمفتاح والإجراء ثم يستطلع النتيجة حتى يعود الرمز:

class V3AutoSolver:
    def __init__(self, api_key):
        self.api_key = api_key

    def solve(self, page_url, action=None):
        import re
        html = requests.get(page_url).text

        is_enterprise = "enterprise.js" in html
        key_match = re.search(r'render[=:]\s*["\']?([A-Za-z0-9_-]{40})', html)
        if not key_match:
            raise Exception("No v3 sitekey found")

        if not action:
            act_match = re.search(r'action["\']?\s*[:=]\s*["\'](\w+)', html)
            action = act_match.group(1) if act_match else "verify"

        params = {
            "key": self.api_key,
            "method": "userrecaptcha",
            "version": "v3",
            "googlekey": key_match.group(1),
            "action": action,
            "pageurl": page_url
        }
        if is_enterprise:
            params["enterprise"] = 1

        resp = requests.get("https://ocr.captchaai.com/in.php", params=params)
        if not resp.text.startswith("OK|"):
            raise Exception(f"Submit failed: {resp.text}")

        task_id = resp.text.split("|")[1]
        for _ in range(60):
            time.sleep(5)
            result = requests.get("https://ocr.captchaai.com/res.php", params={
                "key": self.api_key, "action": "get", "id": task_id
            })
            if result.text.startswith("OK|"):
                return result.text.split("|")[1]
            if result.text != "CAPCHA_NOT_READY":
                raise Exception(f"Solve error: {result.text}")
        raise Exception("Timed out")

رموز الأسباب: ما يراه مالك الموقع ولا تراه أنت

تعيد Enterprise رموز أسباب تشرح سبب الدرجة. لا تصلك عند الحل عبر CaptchaAI، لكن معرفتها تختصر تشخيص الرفض المتكرر:

الرمز الدلالة الأثر على الأتمتة
AUTOMATION رُصد سلوك آلي استخدم بصمة متصفح واقعية
UNEXPECTED_ENVIRONMENT بيئة متصفح غير معتادة راجع كشف المتصفح بدون واجهة رسومية
TOO_MUCH_TRAFFIC حجم طلبات مرتفع من المصدر نفسه حدّد معدل الطلبات ودوّر الخادم الوسيط
UNEXPECTED_USAGE_PATTERNS أنماط تفاعل غير طبيعية أضف تأخيرات واقعية بين الإجراءات
LOW_CONFIDENCE_SCORE بيانات غير كافية لدرجة موثوقة أرسل إشارات سلوكية أكثر
SUSPECTED_CARDING أنماط احتيال ببطاقات الدفع لا تخصّ معظم حالات الأتمتة
SUSPECTED_CHARGEBACK أنماط احتيال بردّ المبالغ لا تخصّ معظم حالات الأتمتة

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


سيناريو من السوق العربي: متجر إقليمي ينتقل إلى Enterprise

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

والتشخيص هنا يبدأ من الصفحة لا من الخدمة:

  1. تأكد من وجود enterprise.js في وسوم الصفحة الجديدة.
  2. اقرأ قيمة action لكل مسار على حدة — login وcheckout عتبتان مختلفتان.
  3. أعد الإرسال مع enterprise=1 وقيمة action المطابقة للمسار.

ولا يغيّر هذا الانتقال فاتورة CaptchaAI: التسعير قائم على عدد الـ Threads المتزامنة لا على نوع الـ CAPTCHA.


أخطاء شائعة أثناء التكامل

العَرَض السبب الأرجح المعالجة
رفض الرمز على موقع Enterprise enterprise=1 غير مُرسلة ابحث عن enterprise.js وأضف المعلمة
درجة منخفضة على Enterprise قيمة action خاطئة استخرجها من استدعاء grecaptcha.enterprise.execute()
ينجح في الاختبار ويفشل في الإنتاج التعلّم التكيفي رصد نمط طلباتك دوّر عناوين IP، وأضف تأخيرات، ونوّع الأنماط
ERROR_WRONG_GOOGLEKEY المفتاح مأخوذ من data-sitekey بدل render ابحث عن render=KEY في رابط البرنامج النصي

أسئلة شائعة

متى أرسل enterprise=1 ومتى أتركها؟

تعامل معها كوصف لحالة الموقع لا كخيار جودة: أرسلها فقط عندما يؤكد الكشف وجود enterprise.js أو استدعاء grecaptcha.enterprise.execute(). إرسالها عشوائياً يجعل تشخيص أي رفض لاحق أصعب لأنك غيّرت متغيرين معاً؛ اترك القرار للكشف التلقائي.

هل تختلف تكلفة الحل بين v3 القياسي وEnterprise على CaptchaAI؟

لا. التسعير قائم على عدد الـ Threads المتزامنة، لا على عدد عمليات الحل أو نوعها. تبدأ الخطط من BASIC بسعر 15 دولاراً شهرياً مع 5 Threads وتصل إلى VIP-3 بسعر 7,500 دولار مع 5,000 Thread، وراجع صفحة الأسعار الرسمية قبل الاعتماد.

كيف أستخرج قيمة action إذا كان JavaScript مُصغّراً؟

ابحث عن الاستدعاء نفسه لا عن اسم المتغير: execute( متبوعاً بكائن يحتوي المفتاح action. إن لم تجده في الملف المُصغّر، راقب طلبات الشبكة أثناء تنفيذ الإجراء يدوياً واقرأ القيمة من حمولة الطلب. واستخدام قيمة عامة مثل verify احتياط أخير.

هل تعني الدرجة المرتفعة قبول الطلب دائماً؟

لا. الدرجة إشارة واحدة يقرأها خادم الموقع عبر siteverify أو واجهة Enterprise، ثم يقرر وفق العتبة المضبوطة لهذا الإجراء تحديداً، وقد يضيف فحوصاً أخرى مثل سمعة عنوان IP. اختبر على المسار الحقيقي بدل الاكتفاء برقم الدرجة.


أدلة ذات صلة

التعليقات غير مفعّلة لهذا المقال.