الانتقال إلى المحتوى الرئيسي
يُوثَّق نشاط المصادقة وتسليم webhooks المرتبط بالإصدار في أحداث المصادقة وwebhooks. لا تسجّل أنواع أحداث المصادقة غير المدرجة مع واجهة برمجة التطبيقات الحالية للوجهات.

نظرة عامة

تُعد webhooks الطريقة التي تُرسل بها Didit تحديثات فورية إلى الخادم الخلفي لديك كلما تغيّرت حالة جلسة تحقق، أو جلسة شركة، أو مستخدم مورّد، أو شركة مورّد، أو معاملة. يتيح لك الاشتراك بوجهة واحدة أو أكثر في الأحداث التي تهمك الاستجابةَ بمجرد جاهزية النتيجة، وعادةً خلال ثوانٍ من إكمال المستخدم الخاضع للتحقق لسير عمله. تُعد webhooks نمط التكامل الموصى به. الاستطلاع عبر GET /v3/session/{id}/decision/ مدعوم كخيار احتياطي، لكنه أبطأ، ويستهلك طلبات أكثر، ويتجاوز أحداثاً مثل data.updated وtransaction.status.updated والتغييرات على مستوى الكيان، وهي أمور لا تُرسل إلا عبر webhooks. اعتبر تسليم webhook مصدر الحقيقة، ولا تستطلع إلا عند بدء التشغيل البارد أو لأغراض المطابقة. يبدأ هذا الفصل من درس التكامل في أكاديمية Didit من نظرة عامة على webhooks. يغطي درس الأكاديمية الخاص بالتكامل موضوع webhooks من الدقيقة 3:12: إضافة وجهة، واختيار الأحداث، والسر المشترك الذي ستستخدمه للتحقق أدناه.
مستخدمو Cloudflare / WAF. تُرسل Didit webhooks من عنوان IP الثابت 18.203.201.92 مع User-Agent: DiditWebhook/2.0 +https://didit.me. إذا كانت الحافة لديك تحجب العملاء غير المعروفين، فاسمح بهذا العنوان في Security > WAF > Tools > IP Access Rules، واختر سماح لاسم المضيف المستقبِل.

أنواع الأحداث

استخدم القيمة الدقيقة في مصفوفة subscribed_events الخاصة بالوجهة. لا يوجد حرف بدل للاختيار الشامل، لذا اذكر كل فئة تريد استلامها، ويمكنك توزيع الأحداث على وجهات متعددة. القيم الواردة في هذا الجدول هي المجموعة الكاملة من الأحداث القابلة للاشتراك، ولا يوجد فيها session.status.updated ولا kyc.completed أو ما شابه ذلك. أما الإرسال الوحيد الآخر الذي تُجريه Didit فهو الاستدعاء غير الموقّع kyb.registry_search.resolved، ويُرسل إلى webhook_url لكل طلب على حدة تمرّره إلى POST /v3/kyb/search/، وليس إلى وجهة webhook.
يُرسل الخيار Try Webhook من قائمة Business Console > API & Webhooks payloads مكتملة التكوين لكل حدث من هذه الأحداث (مقبولة، ومرفوضة، وقيد المراجعة، وأسماء بترميز Unicode، وKYC/KYB، والكيان، والنشاط، والمعاملة). استخدمه للتحقق من نقطة الاستقبال (endpoint) لديك قبل الانتقال إلى الإنتاج.

بنية الحمولة

الهيكل العام

يشترك كل webhook في الهيكل العام نفسه. تُسلسَل الحقول دائمًا باستخدام sort_keys=True وبفواصل مضغوطة (, / :)، حتى تنتج الحمولة نفسها التوقيع نفسه على الطرفين. بالنسبة إلى أحداث الجلسة (status.updated وdata.updated للجلسات العادية وجلسات الشركات)، يتضمن الهيكل العام الحقول التالية أيضًا:

مثال على الهيكل العام للجلسة

كائن decision هو المخطط نفسه الذي تعيده GET /v3/session/{id}/decision/. للاطلاع على المرجع القياسي لحقول كل ميزة (كل خاصية في id_verifications[] وaml_screenings[] وغيرها)، راجع نماذج البيانات. لم نكرر جداول الحقول لكل ميزة هنا عن قصد، حتى يبقى المخطط في مكان واحد فقط.

مصفوفات V3 المتعددة (مهم)

داخل decision، تكون كل نتيجة لكل ميزة مصفوفة تحتوي على عدة عناصر، بحيث يمكن لسير عمل واحد أن يتضمن عدة نسخ من الميزة نفسها (على سبيل المثال، عدة فحوصات للهوية عبر مستندات متعددة):
يحمل كل عنصر node_id يطابق رسم سير العمل، إضافةً إلى status خاص بالميزة. لا تقرأ الحقول المفردة القديمة في V2 (id_verification وnfc وliveness وface_match وphone وemail وpoa وaml وip_analysis وdatabase_validation)؛ فهي تظهر فقط إذا كانت الوجهة مثبتة صراحةً على webhook_version: "v2". تُخزَّن نتائج التوقيع الإلكتروني في signatures[]، ولا تظهر إلا عندما يتضمن سير العمل خطوة التوقيع الإلكتروني: عنصر واحد لكل موقّع، يتضمن الموقّع، وتأكيد الرمز، والشهادة، والطابع الزمني، وتجزئات كل مستند ومسارات تنزيله، وملف PDF الخاص بالأدلة. لا يوجد حدث توقيع منفصل: يخبرك status.updated بوقت قراءتها. راجع تقرير التوقيع الإلكتروني. تُخزَّن نتائج التحقق المصرفي في bank_verifications[]، عنصر واحد لكل خطوة من خطوات التحقق المصرفي. يوضح method في كل عنصر الطريقة التي تم بها التحقق من الحساب: open_banking (تسجيل الدخول إلى البنك)، وaccount_lookup (فحص الحساب)، أو بديل statement أو microdeposits، ويكون null حتى يتم تقييم حساب. عندما يختار الشخص المتابعة من دون استخدام بنكك، تكون قيمة continued_without_bank هي true، ويوضح continuation السبب. تكون قيمة method عندئذٍ none، إلا إذا جرت محاولة تقييم سابقة: راجع متى لا يتمكن الشخص من التحقق من حسابه المصرفي. لا تتم قراءة أي بيانات مالية عند التحقق من الحساب عبر فحص الحساب، لذلك يسرد financial_data.not_collected الأرصدة أو المعاملات أو الدخل التي طلبتها الخطوة. راجع طرق التحقق المصرفي.

قسم Investigator

يحمل decision من الإصدار V3 لجلسة مستخدم أو شركة أيضاً الحقل investigator: وهو آخر مراجعة Investigator للجلسة، أو null إذا لم تكن لها أي مراجعة قط. ميزات جمع المستندات، وهي مستندات KYB وDocument AI، ليس لها مصفوفة مخصصة بصيغة الجمع، بل تظهر في قائمة features[] ضمن القرار على شكل { "feature": "DOCUMENT_AI", "node_id": "…" } (عنصر واحد لكل عقدة). راجع Document AI. تحمل قرارات التحقق من المستخدمين أيضاً كائن biometric_consent واحداً يحدد ما إذا كانت الجلسة تتطلب موافقة بيومترية صريحة، وtrigger، وحالة الإجابة status، وأدلتها. تعني الحالة status: "pending" مع reason: "BIOMETRIC_CONSENT_UNSUPPORTED_SDK" أن حزمة SDK أصلية أقل من 4.10.0 التقطت وجهاً دون خطوة الموافقة. راجع الموافقة البيومترية.

مفاتيح طريقة التحقق من الهوية

يحدد كل عنصر في id_verifications[] طريقة التحقق من الهوية التي أنتجته، حتى يتمكن المعالج من التفرع بحسب الدليل الذي حصل عليه فعلاً: تستمر المعالجات الحالية في العمل دون أي تعديل: تضبط جلسة المستند القيمة verification_method على document وتترك باقي الحقول null. توجد أنواع الحقول تفصيلاً في نماذج البيانات.

جلسة المحفظة المقبولة (status.updated)

حمولات أحداث الكيانات

حالات الكيانات هي ACTIVE أو FLAGGED أو BLOCKED. يحتفظ الحدثان user.data.updated وbusiness.data.updated بالحقل status، ويضيفان مصفوفة changed_fields بالإضافة إلى خريطة changes بالصيغة { field: { previous, current } }. تستخدم أحداث business.* الحقل vendor_business_id بدلاً من vendor_user_id.

حمولات أحداث المعاملات

تعمل webhook المعاملات على البنية التحتية نفسها للتوزيع المتعدد وتستخدم الغلاف نفسه، لكنها تعرّف المعاملة بدلاً من الجلسة:
القيمة action_required هي null، أو الإجراء المعلّق للمستخدم (type بقيمة wallet_ownership أو verification_session، مع url الخاص به) كما هو موضح في الإجراءات المطلوبة من المستخدم. تُعيَّن المعاملات الجديدة افتراضياً إلى APPROVED. إذا غيّرت قاعدة أو قائمة حظر أو سير عمل حالة المعاملة أثناء إنشائها، فإن الحمولة الأولية transaction.created تعكس الحالة النهائية. يحمل transaction.status.updated الشكل نفسه مع قيمة status المحدّثة. حالات المعاملة هي APPROVED وIN_REVIEW وDECLINED أو AWAITING_USER؛ وseverity هي UNKNOWN / LOW / MEDIUM / HIGH / CRITICAL وdirection هي INBOUND / OUTBOUND. لا يُرسل transaction.created وtransaction.status.updated إلا للتطبيقات في بيئة الإنتاج فقط. أما تبادلات Travel Rule في البيئة التجريبية فتُرسل فعلاً travel_rule.status.updated مع environment: sandbox.

استدعاء نتائج بحث سجل KYB

يُرسل مسار POST /v3/kyb/search/ غير المتزامن حمولة إضافية واحدة ليست حدث وجهة: عند تمرير webhook_url إلى طلب البحث، يرسل Didit طلب POST بالاستدعاء kyb.registry_search.resolved إلى ذلك الرابط بمجرد أن يُحلّ السجل. وتختلف عن webhook الوجهة في ثلاث نواحٍ:
  • هو غير موقّع ولا يحمل ترويسات X-Signature*؛ بدلاً من ذلك يضع Didit عليه العلامة X-Didit-Unsigned-Callback: true، ويُتحقق منه بمطابقة request_id مع المعرّف الذي أعاده طلب البحث.
  • يُخزَّن اسم الحدث في event_type (وليس webhook_type)، ولا يوجد application_id ولا environment.
  • timestamp وcreated_at أعداد صحيحة بصيغة epoch، مثل webhooks الأخرى، على خلاف created_at بصيغة ISO في نقطة نهاية البحث.
يتبع الاستدعاء سياسة إعادة المحاولة نفسها التي تتبعها webhook الوجهة (تعيد عمليات إعادة المحاولة ختم timestamp وتبقى غير موقّعة). راجع KYB Registry API للاطلاع على مخطط قائمة المرشحين الكامل.

التحقق من التوقيع

يُوقَّع كل webhook باستخدام HMAC-SHA256 والمفتاح secret_shared_key الخاص بالوجهة. تُرسَل ثلاثة رؤوس توقيع حتى تختار الرأس الذي يناسب بنيتك التقنية: الرؤوس التي يرسلها Didit دائمًا: لمدة 24 ساعة بعد تدوير سر التوقيع للوجهة، تحمل عمليات التسليم أيضًا الرؤوس X-Signature-Previous وX-Signature-V2-Previous وX-Signature-Simple-Previous (انظر تدوير سر التوقيع).

تدوير سر التوقيع

تُدوِّر سر التوقيع لوجهة webhook من داخل Business Console. لمدة 24 ساعة بعد التدوير، تحمل كل عملية تسليم إلى تلك الوجهة ثلاثة رؤوس إضافية، وهي موقّعة بالسر الذي تم استبداله: المعالج الذي يقبل التطابق مع رأس التوقيع العادي أو مع رأس -Previous الخاص به يستمر في التحقق أثناء نشر السر الجديد، إذ يطابق السر القديم رأس -Previous، ويطابق السر الجديد الرأس العادي. تطبّق نماذج الكود أدناه هذا السلوك. انشر السر الجديد خلال 24 ساعة، لأنه بعد ذلك لا يتحقق إلا السر الجديد. يخبرك الحقل previous_secret_expires_at في GET /v3/webhook/destinations/{destination_uuid}/ بموعد انتهاء الساعات الأربع والعشرين. تُوقَّع عمليات إعادة المحاولة وإعادة الإرسال من جديد بالسر الحالي للوجهة وقت إرسالها، وتحمل رؤوس -Previous فقط خلال الساعات الأربع والعشرين. إذا أُجري تدوير آخر خلال الساعات الأربع والعشرين، يُلغى أقدم سر فورًا، ولا يبقى صالحًا للتحقق عبر رؤوس -Previous إلا السر الذي كان حاليًا حتى التدوير الجديد، ولمدة 24 ساعة من التدوير الجديد. رؤوس -Previous موجودة فقط لوجهات webhook. أما secret_shared_key على مستوى التطبيق، الذي يوقّع webhook_url القديم، فليس له تداخل: إذ يؤدي تدويره عبر rotate_secret_key في PATCH /v3/webhook/ إلى إبطال السر القديم فورًا.

X-Signature-Simple مهمَل

X-Signature-Simple يصادق على الغلاف، وليس على payload. تغطي سلسلته القياسية الحقول timestamp وsession_id وstatus وwebhook_type فقط. وهي لا تغطي decision، ولا resubmit_info أو metadata أو vendor_data أو event_id. يمكن لطلب أُعيدت كتابة decision فيه مع ترك هذه الحقول الأربعة دون تغيير أن يجتاز التحقق عبر X-Signature-Simple.وهو أضعف في الأحداث التي لا تحمل أي session_id على الإطلاق. فأحداث المعاملات (transaction_id وtxn_id وamount وscore) وأحداث الكيانات (vendor_user_id وvendor_business_id) توقّع "{timestamp}::{status}:{webhook_type}"، وهذه السلسلة لا تحدد أي معاملة أو كيان يتعلق به الحدث. فمعاملتان مختلفتان أُبلغ عنهما بالحالة نفسها في الثانية نفسها تحملان X-Signature-Simple نفسه. كذلك لا تحمل أحداث travel_rule.status.updated وworkflow.compliance_changed وtransaction_rule_suggestions.created أي status، لذلك توقّع "{timestamp}:::{webhook_type}"، مثل "{timestamp}:::travel_rule.status.updated".إذا كان X-Signature-Simple هو الرأس الوحيد الذي تتحقق منه، فاعتبر كل ما عدا تلك الحقول الأربعة غير موثوق، وأعد جلب السجل من الواجهة البرمجية: GET /v3/session/{session_id}/decision/ للجلسة، وGET /v3/transactions/{transaction_id}/ للمعاملة، لأنها المصدر الرسمي للسجل.
هذا قيد للتوافق، ولا يمكن إصلاحه في مكانه. فالسلسلة القياسية المكونة من أربعة حقول عقد على مستوى البروتوكول: كل مستقبِل يتحقق من X-Signature-Simple يعيد حساب هذه الحقول الأربعة بالضبط، ولذلك فإن تغيير ما يوقّعه الرأس سيكسر كل هؤلاء المستقبِلين بصمت في مسار التحقق من التوقيع. كما أن تغطية الجسم تتطلب شكلًا قياسيًا محددًا لـ JSON يعيد الطرفان إنتاجه بايتًا بايتًا، وقد وفّرت Didit هذا الشكل بالفعل عبر X-Signature-V2. بدلًا من إضافة متغير ثانٍ يغطي الجسم مع قواعد تطبيع خاصة به، يبقى الرأس كما هو ويُهمَل لصالح المتغيرات الموجودة التي تغطي payload كاملة. وما يعنيه ذلك لك:
  • لا يتغير شيء على الشبكة. يستمر إرسال X-Signature-Simple مع كل عملية تسليم، وبالطريقة نفسها. تستمر أدوات التحقق القائمة في العمل، وسنعلن أي إزالة مسبقًا بوقت كافٍ.
  • يجب أن تستخدم التكاملات الجديدة X-Signature-V2. فهو آمن مع الوسيط البرمجي ويغطي payload كاملة.
  • إذا لم تستطع الانتقال عن X-Signature-Simple، فتحقق منه، ثم اجلب السجل من الواجهة البرمجية بدلًا من قراءته من جسم الـ webhook.

لماذا ثلاثة متغيرات

تُعيد بعض أطر الويب (محللات الجسم في Express، والوسيط البرمجي في Django، وبوابات الواجهات البرمجية) ترميز JSON بصمت قبل أن يقرأه المعالج الخاص بك. فإذا احتوى الجسم الأصلي على "José" وأعاد الوسيط كتابته إلى "José"، تتغير البايتات مع أن البيانات واحدة. عندئذٍ يفشل X-Signature. أما X-Signature-V2 فيُحسب من شكل JSON قياسي (ترتيب المفاتيح، وفواصل بلا مسافات، ويونيكود غير مُهرَّب) يعيد معظم الوسطاء إنتاجه بشكل حتمي. وعندما يفشل ذلك أيضًا، يعود X-Signature-Simple المهمَل إلى توقيع سلسلة قصيرة لا تعتمد على المحلل، ولا يوقّع الجسم أبدًا، لذلك لا يستطيع أن يضمن سلامة الجسم.

ترتيب التحقق الموصى به

  1. جرّب X-Signature-V2 أولاً. أعد ترميز JSON.parse(body) مع فرز المفاتيح والحفاظ على Unicode، ثم احسب HMAC-SHA256(secret, canonical) وقارن الناتج بالرأس باستخدام timingSafeEqual.
  2. إذا فشل V2 ويمكنك قراءة البايتات الخام، جرّب X-Signature مقابل HMAC-SHA256(secret, rawBody).
  3. فقط إذا فشلت الطريقتان، ارجع إلى X-Signature-Simple المتقادمة مقابل HMAC-SHA256(secret, "{timestamp}:{session_id}:{status}:{webhook_type}"). هذه الطريقة تصادق الغلاف وحده: عامل decision في النص على أنه غير موثوق، وأعد جلبه من GET /v3/session/{session_id}/decision/ قبل أن تتصرف بناءً عليه.
عند كل خطوة، إذا وُجد الرأس -Previous المقابل، فاقبل التطابق مع أيٍّ من الرأسين (راجع تدوير السر المشترك للتوقيع). في جميع الحالات، ارفض أي طلب أقدم من 5 دقائق (abs(now - X-Timestamp) > 300) للحماية من إعادة التشغيل.

سياسة إعادة المحاولة

إذا ردّ endpoint الخاص بك بالرمز 5xx أو 404، يعيد Didit المحاولة حتى مرتين مع تأخير متزايد:
  • إعادة المحاولة الأولى: بعد نحو دقيقة واحدة من الفشل الأول.
  • إعادة المحاولة الثانية: بعد نحو 4 دقائق من إعادة المحاولة الأولى.
بعد إعادة المحاولة الثانية يُسقط التسليم. تُسجَّل كل محاولة تسليم (الأولى وإعادات المحاولة) كإدخال منفصل في Business Console ضمن تبويب التسليمات الخاص بالوجهة، حتى تتمكن من إعادة تشغيل أي محاولة أو فحصها. تحمل كل محاولة في سجل تسليم الوجهة رقم attempt: القيمة 1 لأول تسليم لحدث، وواحد زائد على رقم المحاولة التي تكرّرها في كل إعادة محاولة أو إعادة إرسال. تُرسل إعادات المحاولة التلقائية إلى العنوان الذي أُرسلت إليه المحاولة الأولى، حتى لو غيّرت url الوجهة بعد ذلك. تُرسل إعادة الإرسال اليدوية إلى عنوان URL الحالي للوجهة، لذلك فإن تصحيح العنوان يصحح إعادة الإرسال اليدوية أيضاً. قواعد أخرى للتسليم يجب معرفتها:
  • تنتهي مهلة طلب HTTP الصادر بعد 5 ثوانٍ.
  • يمنع Didit التسليم إلى العناوين الخاصة / localhost كحماية من SSRF، ولا يتبع إعادات التوجيه أبداً. استخدم endpoint عاماً عبر HTTPS يجيب مباشرة.
  • يُرفض عنوان url للوجهة الذي يحتوي على شرطة مائلة عكسية خام أو محرف تحكم أو أكثر من @ واحدة في جزء authority من عنوان URL، باعتباره غير صالح. لا يمكن إنشاء مثل هذا العنوان، ولا يمكن إعادة تسليم عنوان موجود أو إعادة تشغيله. استخدم عنوان https://host/path بسيطاً مع قسم معلومات مستخدم واحد على الأكثر (لعناوين webhook بأسلوب basic-auth).
  • تُعامَل الاستجابة 2xx على أنها نجاح. تُعدّ المهلات وفشل الاتصال قابلة لإعادة المحاولة (وتظهر بالرمزين 504 و503 في سجل التسليم)، أما الاستجابات 3xx و4xx باستثناء 404 فلا تُعاد محاولتها.
  • عند إعادة المحاولة يعيد Didit حساب التواقيع باستخدام X-Timestamp جديد، فيبقى زوج الطابع الزمني والتوقيع متوافقاً دائماً.
  • تُوقَّع إعادات المحاولة وإعادات الإرسال بسر التوقيع الحالي للوجهة وقت إرسالها.

ترتيب التسليم

لا يضمن Didit ترتيب وصول webhook إليك. يُضاف كل تسليم إلى قائمة الانتظار بشكل مستقل، وتصل إعادة المحاولة بعد دقائق من المحاولة الأولى، لذلك قد يصلك status.updated بالحالة Approved قبل حدث سابق بالحالة In Progress. ابنِ المستقبِل لديك وفق ذلك:
  • عامل webhook على أنه إشارة بأن شيئاً قد تغيّر. عندما يهم الترتيب، استدعِ GET /v3/session/{session_id}/decision/ وتصرّف بناءً على status الحالي.
  • لا تدع حدثاً أقدم يكتب فوق حالة أحدث: قارن created_at للحدث مع آخر حدث خزّنته لذلك السجل، وتجاهل الحدث الأقدم.
  • اعتمد event_id مفتاحًا للتكرارات، فهو يبقى كما هو عبر إعادات المحاولة.
يرتب Didit أحداث تحويل Travel Rule بترتيب محدد، موصوف ضمن تغيّر حالة تبادل Travel Rule، لكن إعادة المحاولة قد تغيّر ترتيب وصولها.

إعداد وجهة

يمكنك إدارة الوجهات من Business Console > API & Webhooks أو عبر Management API.
1

إنشاء الوجهة

استدعِ POST /v3/webhook/destinations/ (أو انقر إضافة وجهة في الكونسول) مع label وurl عام وwebhook_version ("v3") ومصفوفة subscribed_events تحتوي على كل عائلة أحداث يجب أن يستقبلها هذا endpoint. مطلوب حدث واحد على الأقل، ولا يوجد أي حرف بدل.
2

خزّن secret_shared_key

تُعيد استجابة الإنشاء secret_shared_key الخاص بالوجهة. خزّنه مع أسرار خادمك الأخرى: فهو خاص بهذه الوجهة وحدها، وهو المدخل لجميع صيغ توقيع HMAC-SHA256 الثلاثة. تعيده GET /v3/webhook/destinations/{destination_uuid}/ مرة أخرى لمستدعي API الذين يستخدمون مفتاح API ولمستخدمي الكونسول الذين يمكنهم قراءة webhook.
3

نفّذ endpoint

أتِح مسار POST عاماً عبر HTTPS، واقرأ الجسم الخام، وتحقق من التوقيع (مع تفضيل X-Signature-V2)، وتحقق من نافذة الطابع الزمني، ثم وجّه الحدث حسب webhook_type. أعد 2xx بمجرد إضافة العمل إلى قائمة الانتظار، ونفّذ المعالجة الثقيلة بشكل غير متزامن.
4

أكّد الاستلام

راقب تبويب التسليمات الخاص بالوجهة. تُعيد الـ webhooks السليمة الاستجابة 200 خلال ثانية أو ثانيتين. يؤدي 5xx/404 إلى تفعيل سياسة إعادة المحاولة لدى Didit، وتُسقط الإخفاقات المستمرة بعد إعادة المحاولة الثانية.
5

كرّر الاختبار باستخدام Try Webhook

استخدم سيناريوهات Try Webhook في الكونسول لتوجيه endpoint الخاص بك عبر payload مقبولة (approved)، ومرفوضة (declined)، وقيد المراجعة (in-review)، وKYB، والكيان، والنشاط، والمعاملة، قبل تفعيل حركة الإنتاج.
ميزة اختبار webhook من Didit في Business Console

أرسل webhook اختباريًا مباشرة من Business Console للتحقق من endpoint الخاص بك قبل الإطلاق.

يمكنك إنشاء أي عدد من الوجهات تحتاجه. نمط شائع هو وجهة واحدة لكل مستهلك: على سبيل المثال، أحداث KYC إلى خدمة المصادقة لديك، وأحداث KYB إلى خدمة عمليات الامتثال، والمعاملات إلى طابور الاحتيال. لكل وجهة سرها وإصدارها وقائمة اشتراكاتها الخاصة، لذلك فإن تدوير السر أو نشراً معيباً على مستهلك واحد لا يؤثر على الآخرين.

نماذج الكود

لضمان أمان نقطة نهاية الـ webhook، تحقّق من أصالة الطلبات الواردة باستخدام secret_shared_key الخاص بالوجهة.
استخدم X-Signature-V2. هو المتغيّر الأكثر مقاومةً لإعادة الترميز الذي تجريه البرمجيات الوسيطة، وهو يصادق على نص الطلب بالكامل (بما في ذلك decision).

Node.js / Express (المُوصى به: X-Signature-V2)

إذا كان بإمكانك قراءة البايتات الخام قبل أن تصل إليها أي أداة تحليل، فإن متغيّر X-Signature الأصلي يعمل بالطريقة نفسها، لكنه يوقّع النص الخام. احتفظنا به في القسم القديم أدناه للتوثيق الكامل.

Python / FastAPI (المُوصى به: X-Signature-V2)

PHP / Laravel (المُوصى به: X-Signature-V2)

تخالف الإعدادات الافتراضية لـ JSON في PHP الشكلَ المعياري. يجب تجاوز أربعة منها، وأي إغفال لواحد منها يغيّر البايتات الموقّعة، فلا تتطابق البصمة أبدًا: فخ الكائن الفارغ هو الأشدّ إيلامًا. تحمل payloads الخاصة بـ Didit عادةً خرائط فارغة مثل risk_view.countries.risk_scores وrisk_view.crimes.risk_scores وrisk_view.custom_list، لذلك يكسر التحويل الترابطي ذهابًا وإيابًا التوقيعَ في الجلسات المقبولة العادية، لا في حالة نادرة استثنائية.
أمران آخران يستحقان الذكر:
  • الأعداد العشرية ذات القيم الصحيحة لا تحتاج إلى معالجة خاصة في PHP. تُسلسلها Didit أعدادًا صحيحة (100.0 → 100)، ويكتب json_encode في PHP العدد العشري ذي القيمة الصحيحة بالطريقة نفسها، لذا لا تحتاج إلى خطوة shortenFloats، على عكس نموذج Node.js أعلاه.
  • أبقِ serialize_precision على -1 (القيمة الافتراضية منذ PHP 7.1). إذا غيّر مضيفك القيمة إلى 17، فإن json_encode يكتب 87.42 بوصفه 87.420000000000002، وتفشل كل عملية تحقق من X-Signature-V2.
إذا تعذّر عليك جعل الشكل المعياري متطابقًا، فإن verifySignatureRaw أعلاه هو البديل الأبسط: تُرجع $request->getContent() في Laravel البايتات دون أي تعديل، فيعمل X-Signature طالما لم يُعِد أي شيء قبل PHP ترميزَ النص. وهذه بالضبط الحالة التي صُمّم X-Signature-V2 لتجاوزها، ولهذا يُجرَّب V2 أولًا.

الاختبار

لا تحتاج إلى تشغيل تحقق كامل لاختبار نقطة النهاية الخاصة بك.
  • من Business Console > API & Webhooks > Try Webhook، اختر سيناريو (مثل approved_full_features وdeclined_face_mismatch وin_review_aml_hit وbusiness_session_approved وtransaction_created وactivity_created وuser_status_updated)، ثم أرسل webhook مكتملة البنية إلى عنوان URL الخاص بالوجهة.
  • يُوقَّع كل سيناريو بالرؤوس الثلاثة نفسها للتوقيع وبالترميز المعياري نفسه المستخدم في حركة المرور في بيئة الإنتاج، بما في ذلك بيانات اختبار Unicode مثل approved_kyc_with_unicode، حتى تتأكد من أن X-Signature-V2 يعمل عبر البرمجيات الوسيطة لديك. تحمل عمليات التسليم التجريبية بيانات نموذجية، وتُعلَّم برأس إضافي هو X-Didit-Test-Webhook: true ولاحقة (Test) في User-Agent، حتى تستطيع استبعادها من الحالة الإنتاجية.
  • أعِد تشغيل أي تسليم سابق من تبويب التسليمات الخاص بالوجهة إذا احتجت إلى تصحيح تراجع.
لاختبارات الدخان من البداية إلى النهاية، شغّل تحققًا حقيقيًا عبر سير عمل تجريبي في تطبيقك، فالـ webhook الناتجة تستخدم الغلاف نفسه والرؤوس نفسها وسياسة إعادة المحاولة نفسها التي تستخدمها سيناريوهات Try Webhook.

أمثلة

جلسة KYC مقبولة (status.updated)

جلسة مرفوضة مع تحذيرات (status.updated)

جلسة شركة مقبولة (status.updated، KYB)

تم إنشاء معاملة (transaction.created)

تغيّرت حالة تبادل Travel Rule (travel_rule.status.updated)

يحمل travel_rule_status إحدى حالات التبادل الثلاث عشرة الموثّقة في Travel Rule. القيمة rail هي INTERNAL أو TRP أو GTR أو TRUST أو VERIFYVASP أو SYGNA أو EMAIL (قيمة فارغة إلى أن يُختار مسار، أو إذا تعذّر تحديد أي مسار، ودائماً في تسجيلات sunrise)، أو TEST_VASP لتحويل في البيئة التجريبية ردّ عليه Didit Test VASP، وقيمة direction هي INBOUND أو OUTBOUND. يحمل حدث البيئة التجريبية "environment": "sandbox"، ويحمل حدث TEST_VASP أيضاً test_vasp: true وtest_vasp_scenario (السيناريو الذي قدّم الاستجابة). عندما تُنشئ Didit معاملة لتحويل Travel Rule (استدعاء POST /v3/transactions/، أو تسجيل sunrise بعد الإيداع، أو استفسار TRP وارد، أو المرحلة الواردة على المسار INTERNAL)، فإنها ترسل حدث transaction.created الخاص بتلك المعاملة أولاً، ثم أحداث travel_rule.status.updated الخاصة بالتحويل بعدها. قد ترسل أيضاً قاعدة تغيّر حالة المعاملة خلال تلك الخطوة الحدث transaction.status.updated قبل transaction.created، وتسجيل sunrise الذي يرتبط بمعاملة إيداع سبق أن أنشأتها لا يرسل transaction.created. يُسلَّم كل webhook وتُجرى إعادة المحاولة له على حدة، لذا قد يصل حدث أُعيد إرساله بعد حدث لاحق له: اجعل معالجك يعتمد على transaction_id، وأعد قراءة المعاملة عندما تحتاج إلى حالتها الحالية. لا يحصل التحويل المُرسل عبر TRUST أبداً على حدث COMPLETED، لأن TRUST لا يعيد أي شيء تستطيع Didit قراءته: تبقى الحالة AWAITING_COUNTERPARTY حتى الموعد النهائي للتأكيد ثم تنتهي إلى EXPIRED. أما عبر GTR وVerifyVASP وSygna Bridge، فتقرأ Didit ردّ الطرف المقابل من الشبكة ويأتي الحدث بعده (راجع شبكات Travel Rule).

تغيّر امتثال سير العمل (workflow.compliance_changed)

تكون كل من transition.from وtransition.to إحدى القيم التالية: compliant أو gaps_detected أو pending_change أو unknown_coverage أو not_evaluated. تكون قيمة severity هي actionable عند رصد فجوات، وindicative في حالة تغيير معلّق، وinformative عند حل الفجوات. ترسل Didit حدثاً واحداً لكل سير عمل تغيّر.

أُنشئت اقتراحات قواعد المعاملات (transaction_rule_suggestions.created)

تكون قيمة kind هي ADD لقاعدة جديدة، وTWEAK لتعديل قاعدة موجودة، وDISABLE لإيقاف قاعدة. تكون قيمة rule_id هي null عند ADD. open_count هو عدد الاقتراحات التي ما زالت مفتوحة بعد التشغيل، بما فيها الجديدة. تتحكم إعدادات اقتراحات القواعد في تفعيل هذا webhook أو تعطيله؛ راجع webhooks المعاملات.

أحداث Investigator (investigator.*)

تستخدم الأحداث الاثني عشر investigator.* الغلاف نفسه (event_id، وwebhook_type، وtimestamp، وcreated_at، وapplication_id، وenvironment) ورؤوس التوقيع نفسها التي تستخدمها سائر الأحداث. وهي تحمل المعرّفات والأرقام فقط: لا تتضمن أبداً اسماً، ولا سبباً كتبه المراجِع، ولا سرداً، ولا أي شيء كتبه المستخدم النهائي. ولا تحتوي على session_id؛ والعنصر الذي يتعلق به الحدث مذكور في item.
تحدد provenance الإصدار الذي كتب به Investigator التقرير:
  • engine هو إصدار محرّك Investigator: معرّف مبهم مكوّن من ثمانية محارف. يتغيّر كلما تغيّر المحرّك، لذا فإن أي تقريرين يحملان القيمة نفسها لـ engine كتبهما المحرّك نفسه.
  • agent_version هو إصدار الوكيل.
  • policy_version هو إصدار سياستك الخاصة التي اتّبعها التقرير.
  • model_id اسم بديل مُهمَل يحمل القيمة نفسها التي يحملها engine. اقرأ engine بدلاً منه.
  • لم يعد يُرسل prompt_hash.
يحدّد item.kind نوع العنصر (جلسة، أو جلسة شركة، أو معاملة، أو حالة)، ويحدّد item.id معرّفه. الطوابع الزمنية التي تنتهي بـ _at هي سلاسل نصية بصيغة ISO 8601. اشترك في هذه الأحداث بإدراج السلاسل النصية الدقيقة في subscribed_events، كما في أي حدث آخر.

تغيّرت حالة كيان المستخدم (user.status.updated)

الجلسة المعاد تقديمها

القديم: X-Signature مع الجسم الخام

لا يزال X-Signature يُرسَل مع كل تسليم، وهو HMAC-SHA256 على البايتات الدقيقة التي يرسلها Didit (json.dumps(..., sort_keys=True, separators=(",", ":"))). استخدمه فقط عندما تستطيع قراءة الجسم الخام قبل أن يعدّله أي وسيط برمجي.

القديم: تنسيق webhook من V2

الحقول أدناه تنطبق فقط عندما تكون الوجهة مثبّتة على webhook_version: "v2". يوجد أيضاً تثبيت أقدم هو "v1" (وهو شكل ثالث للقرار بصيغة المفرد) للوجهات التي أُنشئت قبل V2، فتواصل مع الدعم قبل الاعتماد عليه. ينبغي أن تستخدم التكاملات الجديدة V3 (الافتراضي)، الموثّق أعلاه، والذي يستخدم المصفوفات بصيغة الجمع.
كائنات الميزات نفسها في الإصدارين متطابقة: تتلقى الوجهة المثبّتة على V2 بالضبط match_score وrisk_score وscore_breakdown وrisk_view وreview_status لكل تطابق، والدرجة الإجمالية score كما يتلقاها V3 (انظر تقرير AML). التثبيت على V2 يغيّر مكان تقرير AML داخل decision، لا محتواه. إذا كان مستهلك لاحق ما زال يحتاج إلى بنى V2، فأنشئ وجهة منفصلة مثبّتة على webhook_version: "v2"، وأبقِ مستهلكيك الجدد على V3.

ذات صلة

  • واجهة API لوجهات webhook: عرض الوجهات وإنشاؤها وتحديثها وحذفها وفحص سجلات التسليم.
  • إنشاء وجهة webhook: مخطط الطلب والاستجابة، ويشمل secret_shared_key الخاص بالوجهة.
  • نماذج البيانات: المخطط المرجعي لكل كائن خاص بميزة مضمَّن في decision.
  • مخطط القرار: قائمة الحقول الكاملة لكائن decision المضمَّن في webhooks الخاصة بالجلسات.
  • حالات التحقق: قيم status الدقيقة والانتقالات المشار إليها أعلاه.
  • تحديد المعدل: الحدود التي تنطبق عند الاستعلام الدوري عن GET /v3/session/{id}/decision/ كخيار احتياطي.
آخر تعديل في ٧ نوفمبر ٢٠٢٦