نظرة عامة
تُلزم توصية FATF رقم 16 (Travel Rule)، التي تُطبَّق في الاتحاد الأوروبي عبر لائحة تحويل الأموال (Transfer of Funds Regulation)، مزودَ خدمات الأصول الافتراضية المُرسِل (originator VASP) بجمع معلومات المُرسِل والمستفيد وإرسالها إلى مزود خدمات الأصول الافتراضية المستقبِل (beneficiary VASP) قبل تسوية تحويل الأصل الافتراضي. على خلاف معظم الولايات القضائية، تُطبَّق لائحة TFR الأوروبية دون حد أدنى للمبلغ، أي أن كل تحويل للعملات المشفّرة يدخل في نطاقها، وليس فقط التحويلات التي تتجاوز عتبة الإبلاغ. منتج Travel Rule من Didit تبادلٌ مُدار، وليس مجرد حقل بيانات:- تُرسِل التحويل باستخدام
transaction_category: "travelRule". - تجرّب Didit المسارات بحسب ترتيب الأولوية للوصول إلى مزود الخدمات المقابل، وتُنفّذ التبادل عبر أول مسار يصل إليه، وإذا أعاد الطرف الآخر اسم المستفيد، تُطابقه مع الاسم الذي أرسلته.
- لا تبثّ التحويل على السلسلة إلا بعد أن يصل التبادل إلى
COMPLETED. - ثم ترفع تجزئة معاملة على السلسلة إلى Didit لإغلاق التبادل بحالة
FINISHED.
GTR وTRUST وVERIFYVASP وSYGNA) إلى عضوية شبكة لدى الجهة المُرسِلة، وحاليًا يجب أن تكون هذه العضوية خاصة بك.
اربط بيانات اعتماد عضويتك في وحدة التحكم ضمن Marketplace (فئة شبكات Travel Rule)، وستنتقل تحويلاتك بهوية الشبكة الخاصة بك.
لا تملك Didit حاليًا أي عضوية منصّة في أي من الشبكات الأربع، وهي قيد التسجيل كمزوّد فيها.
حتى يكتمل هذا الانضمام، تكون قيمة platform_available هي false للشبكات الأربع، ولا يكون وضع didit في الإعدادات (التوجيه عبر عضوية Didit في المنصة) متاحًا لأي شبكة، ولا تحتوي disabled_networks على شيء لإيقافه.
للعضوية التي تربطها بنفسك الأولوية دائمًا على عضوية المنصة.
لا تُضمَّن أي عضوية شبكة افتراضيًا، بما في ذلك GTR.
لا يؤدي تفعيل Travel Rule إلى تفعيل GTR أو TRUST أو VerifyVASP أو Sygna.
راجع Get Travel Rule Settings للاطلاع على أوضاع networks الفعّالة لتطبيقك، وTravel Rule networks لمعرفة ما تصل إليه كل شبكة اليوم.
إذا أبلغت الشبكات الأربع جميعها عن unavailable، فلا تستطيع أي منها توجيه تبادل: اربط عضويتك الخاصة في Marketplace، أو استخدم طرفًا مقابلًا يمكن الوصول إليه عبر INTERNAL أو TRP أو EMAIL.
عند حفظ عضوية، يجب أن يكون رمز المشارك خاصًا بك: يُرفض الحفظ بالرد 400 إذا كان الرمز هو رمز Didit الخاص على تلك الشبكة، أو رمزًا تحمله بالفعل عضوية تطبيق آخر.
تحتاج عضوية GTR إلى مفتاحها الخاص ed25519 (بترميز base64)، وهو المفتاح الذي يُسجَّل مفتاحه العام في بوابة GTR: يُشفّر البيانات الشخصية التي ترسلها، ويفكّ تشفير البيانات التي تستقبلها.
تشير عضوية VerifyVASP إلى Enclave الخاص بك: عنوان URL الخاص بها هو عنوان Enclave الخاص بك، وtoken الخاص بها هو الذي يقدّمه Enclave إلى Didit.
حتى مع عضويتك الخاصة، لا يوجّه مسار الشبكة تحويلًا إلا إذا كان beneficiary_data يحمل معرّف الطرف المقابل على تلك الشبكة (counterparty_gtr_vasp_code أو counterparty_trust_membership_id أو counterparty_verifyvasp_id أو counterparty_sygna_vasp_code)، أو إذا كان إدخال الطرف المقابل في الدليل يدرجه على تلك الشبكة (انظر Routes).
يُبلغ platform_available عمّا إذا كانت عضوية المنصة مُعدّة، وليس فحصًا حيًا لصحة الشبكة. وبالمثل، تعني connected أن بيانات اعتماد عضويتك محفوظة وقد اجتازت التحقق المحلي، أما التحقق الفعلي منها لدى الشبكة فيحدث مع أول تبادل حي. ووضع didit أو own لا يضمن إمكانية وصول الطرف المقابل ولا نجاح التسليم.
بفضل عضويتك الخاصة، يقرأ Didit ردّ الطرف المقابل عبر GTR وVERIFYVASP وSYGNA من استجابة الشبكة، أو عبر الاستعلام عن الشبكة كل 5 دقائق، وهو ما يُكمل التحويل أو يرفضه.
TRUST يقتصر على الإرسال: يبقى التحويل المُرسل عبره في حالة AWAITING_COUNTERPARTY حتى تنقضي مدة confirmation_timeout_hours، ثم ينتقل إلى حالة EXPIRED، ويُطبَّق عندها timeout_outcome الخاص بك.
استقبال تحويل يرسله إليك VASP آخر عبر شبكة غير مفعّل بعد، ولم يُستكمل أي تبادل عبر شبكة مع شبكة فعلية حتى الآن: راجع شبكات Travel Rule.
الشبكات الأخرى، وهي CODE (التحالف الكوري)، وTRISA، وNotabene، وVeriscope، مدرجة في Marketplace في وحدة التحكم كبطاقات لطلب الوصول.
Didit لا يملك تكاملاً معها بعد، لذلك لا يمكن توجيه أي تحويل عبرها اليوم.
عندما تعجز المسارات السبعة كلها عن تحديد الطرف المقابل، ينفّذ Didit خطوة تحديد إضافية واحدة قبل أن يستسلم: إسناد تحليلات البلوكتشين.
يُبحث عن محفظة الوجهة في بيانات تحليلات البلوكتشين؛ فإذا كانت المحفظة تابعة لبورصة أو حافظ أمانة معروف، يسجّل Didit VASP المُسنَد على التحويل، ويضيفه إلى الدليل، ويعيد تشغيل المسارات مرة واحدة بالاسم الجديد.
راجع إسناد تحليلات البلوكتشين للاطلاع على السلوك الكامل والأسعار.
التحويل الذي تكون وجهته معلنةً كمحفظة ذاتية الاستضافة يتجاوز المسارات السبعة كلها، إذ لا يوجد VASP مقابل للتبادل معه، وينتقل مباشرةً إلى إثبات ملكية المستخدم النهائي للمحفظة.
/v3/ الموضحة في هذه الصفحة.ما الذي ينفَّذ عند إرسال التحويل
تنفَّذ الفحوصات داخل استدعاء واحد هوPOST /v3/transactions/. عند استلام الاستجابة يكون الفحص قد اكتمل، وتكون بيانات التحويل مخزَّنة، وتكون القواعد قد قُيّمت.
يبدأ التبادل نفسه بعد الاستجابة: تختار Didit المسار وتتواصل مع الطرف المقابل خارج طلبك، لذلك لا يمكن لأي شبكة أو طرف مقابل أن يبطئ إنشاء التحويل أو يُفشله.
يصل كل تغيير في الحالة بعد ذلك في صورة حدث travel_rule.status.updated يذكر المسار، ويحمل كائن التحويل الذي تعيده نقاط نهاية تحويلات Travel Rule حقلَ delivery الذي يبيّن ما إذا وصلت الرسالة.
لا يوجد خط معالجة يجب الاستعلام عنه بشكل دوري؛ الأجزاء غير المتزامنة هي التبادل، وردّ الطرف المقابل، وأي إجراء يقوم به المستخدم النهائي.
خطوة بخطوة:
- التحقق وربط الجهة المعنية. يُتحقق من صحة الحمولة.
يُرفض
transaction_idسبق استخدامه في تطبيقك بالرمز400، وإذا تزامن طلبان بالمعرّف نفسه، يتلقى أحدهما الرمز409. تُربطsubjectبملفك الشخصي للمستخدم أو للشركة عبرvendor_data، فيظهر التحويل على ذلك الملف لأغراض المراقبة. تُرفض الجهة المعنية أو قيمةvendor_dataللطرف المقابل إذا كانت مدرجة في قائمة الحظر بالرمز400، ولا يُنشأ أي معاملة. تُفحص المحافظ وحسابات البنوك وعناوين IP والأجهزة المدرجة في قائمة الحظر في الخطوة 6، وترفض المعاملة هناك بعد تشغيل التوجيه. - إثراء الجهاز والشبكة. تُضاف بيانات الموقع الجغرافي إلى عناوين IP للأطراف، ويُوحَّد أي سياق للجهاز (يُجمع تلقائياً عند إرسال SDK) داخل مخطط الأجهزة نفسه الذي تغذّيه جلسات KYC، فتعمل خاصية “مرتبطة بالجهاز” عبر التسجيل والمعاملات معاً.
- فحص العملات الرقمية. بالنسبة إلى تحويلات العملات الرقمية مع تفعيل المراقبة، تُفحص المحفظة المعنية (أو التجزئة على السلسلة) أمام تحليلات البلوكتشين قبل تبادل أي شيء، لكشف التعرض للعقوبات، والقرب من خدمات الخلط، والأموال المسروقة. راجع فحص العملات الرقمية والمحافظ.
- تحويل Travel Rule. يتحقق محرك التبادل من الحد، ويعتمد إقرار المحفظة ذاتية الاستضافة، ثم يخزّن التحويل: الحالة
NOT_APPLICABLEإذا كان دون حدك، وUNCONFIRMED_OWNERSHIPلمحفظة ذاتية الاستضافة مُقرّ بها، وCOMPLETEDعندما تكون المحفظة الأخرى محفظتك الموثّقة، وAWAITING_COUNTERPARTYفي غير ذلك، مع بقاء مساره فارغاً. يضع كل تغيير في الحالة حدثtravel_rule.status.updatedفي الطابور، ويرسله Didit بعدtransaction.created(الخطوة 7). - إصدار جلسة ملكية المحفظة. إذا كان التحويل يتطلب إثبات سيطرة من المستخدم النهائي، وكان
auto_wallet_verificationمفعلاً، تُنشأ جلسة أداة ملكية المحفظة وتُعاد ضمنaction_required. - تقييم القواعد. تعمل كل قاعدة نشطة. تتفاعل حزمة EU TFR مع حالة التحويل المحددة في الخطوة 4، وترى قواعدك المخصصة نتائج الفحص في الخطوة 3، ويمكن لقاعدة أن تحتجز المعاملة (
IN_REVIEW)، أو ترفضها، أو تطلب تحققاً إضافياً (AWAITING_USER). - الفوترة والإشعار. تُحتسب المعاملة الصادرة في بند واحد: بند Travel Rule عندما تبدأ تبادلاً، وفحص المعاملات في غير ذلك. لا يُحتسب على التحويل الوارد أي رسم، ولا يُحاسَب تطبيق البيئة التجريبية أبداً.
تُعاد الاستجابة، ويُطلق
transaction.createdإلى وجهات webhook الخاصة بك، تليه أحداثtravel_rule.status.updatedمن الخطوة 4. - التوجيه، بعد الاستجابة. يمرّ Didit على المسارات السبعة بحسب الأولوية؛ فإذا لم يُحلّ أيٌّ منها، يحدّد إسناد تحليلات البلوكتشين الطرف المقابل، وتُعاد تشغيل المسارات مرة واحدة. يتولى أول مسار متاح التبادل: يبقى التحويل
AWAITING_COUNTERPARTYعلى ذلك المسار بينما يردّ الطرف المقابل، أو يُسوّى فوراً عندما يُطابَق طرف مقابل من نوعINTERNAL(COMPLETEDأوCOUNTERPARTY_MISMATCHED_DATA)، أو تصبح حالتهCOUNTERPARTY_VASP_NOT_FOUNDعندما لا يصل أي مسار إلى الطرف المقابل. يُطلق كل تغيير الحدثtravel_rule.status.updated، وتعمل قواعدك من جديد كلما تغيّرت الحالة أو المسار. أما التحويل الذي يتركه التوجيه في الحالةCOUNTERPARTY_VASP_NOT_FOUND، فتُنشأ له جلسة أداة ملكية المحفظة في تلك اللحظة (عندما يكونauto_wallet_verificationمفعلاً)، وتقرؤها منaction_requiredالخاص بالمعاملة باستخدام الحصول على معاملة.
كيف تُقابل كل خطوة التزاماتك
transaction.created أو transaction.status.updated عبر webhook. تبقى معاملات البيئة التجريبية في الحالة APPROVED ما لم يطبّق تحويل Travel Rule منتهي الصلاحية قيمة timeout_outcome الخاصة بك، ولا يُحتسب أي رسم.
يعمل محرك تبادل Travel Rule نفسه على تطبيقات البيئة التجريبية عند تفعيل Travel Rule، مع طرف مقابل واحد فقط: تذهب كل عملية تبادل صادرة إلى Didit Test VASP على المسار TEST_VASP، الذي يجيب داخل Didit بالسيناريو الذي تختاره، فيكتمل التحويل أو يُرفض أو يحدث عدم تطابق فيه أو يصبح غير قابل للوصول أو تنتهي صلاحيته أو ينتظر ملكية المحفظة.
لا تصل أي رسالة امتثال إلى مزوّد خدمات أصول افتراضية حقيقي، وتُسلَّم أحداث travel_rule.status.updated مع environment: sandbox.
أما الوارد، فيمكن لمزوّد الاختبار أن يرسل إليك تحويلاً (تشغيل تحويل وارد من VASP الاختباري)، ويُسوّى التسجيل (POST /v3/travel-rule/inbound/) مقابل دفتر عناوين محافظك كما في التطبيق الحي: ON_HOLD أو UNCONFIRMED_OWNERSHIP أو COUNTERPARTY_MISMATCHED_DATA أو COMPLETED ثم FINISHED.
راجع اختبار Travel Rule في البيئة التجريبية.دورة الحياة
لكل تحويل حالةstatus تُختار من 13 قيمة.
يبدأ التبادل بالحالة AWAITING_COUNTERPARTY في الحالة المعتادة، أو بالحالة NOT_APPLICABLE عندما يكون المبلغ أقل من threshold_amount الخاص بك، أو عندما يخلو طلب دفع Lightning من اسم المستلم (راجع Lightning)، أو بالحالة UNCONFIRMED_OWNERSHIP عندما تكون المحفظة المستلِمة قد أُعلن عنها كمحفظة ذاتية الاستضافة. وينتهي التبادل بإحدى الحالات النهائية: FINISHED أو CANCELLED أو EXPIRED.
يعمل التحويل بآلة حالات خاصة به، منفصلة عن حالة مراجعة المعاملة الأساسية.
لا تغيّر حالة التبادل حالة المعاملة وحدها أبداً، باستثناء حالة واحدة: عند انتهاء الصلاحية يُطبَّق timeout_outcome المُعدّ لديك (أو سياسة sunrise أو سياسة عدم الاستجابة عند انطباقها).
في المقابل، تُعاد تشغيل قواعدك عند كل انتقال، فيمكن لـقواعد EU TFR أو لأي قاعدة تعتمد على travel_rule_status أن تنقل المعاملة، مثلاً إلى IN_REVIEW.
يُضيف التحويل الذي يحتاج إلى إثبات من المستخدم النهائي على سيطرته على المحفظة كتلة action_required إلى المعاملة، لكن حالته status تبقى كما تركتها قواعدك.
اقرأ حالة التبادل من travel_rule.status، واقرأ قرار السماح / التعليق / الرفض من status الخاصة بالمعاملة نفسها.
يُنسخ كل انتقال (وكل حالة تُبلَّغ دون طلب مباشر) أيضاً إلى القيم المخصصة للمعاملة، حتى تتمكن القواعد من الاعتماد عليها: custom_values.travel_rule_status (الحالة المعيارية)، وcustom_values.travel_rule_rail (القناة التي حملت التبادل، وتكون فارغة إلى أن تُختار قناة)، وcustom_values.travel_rule_regime (المشتق من jurisdiction في الإعدادات لديك: SG → singapore، وGB → uk، وأي رمز لعضو في الاتحاد الأوروبي أو المنطقة الاقتصادية الأوروبية مثل DE أو FR → eu؛ والرموز غير المُدرجة تُكتب بأحرف صغيرة، مثلاً US → us. وقيمة props.travel_rule_regime التي ترسلها بنفسك تتقدم دائماً على القيمة المشتقة).
CANCELLED إضافةً من كل حالة غير نهائية باستخدام action: "cancel"):
التسليم وإعادة المحاولة
يرسل Didit كل رسالة يتعيّن على التحويل إرسالها بصورة منفصلة عن الطلب الذي تسبّب فيها، ويعيد محاولة إرسالها حتى يستلمها الطرف الآخر. يعيد الكائن الذي تُرجعه إنهاء التحويل أو إلغاؤه أو إعادة إرساله، وتأكيد الملكية، وتسجيل sunrise آخرَ رسالة فيdelivery، ويحملها أيضاً كائن travel_rule المضمّن في المعاملة. وتكون قيمته null إذا لم يكن يتعيّن على التحويل إرسال أي رسالة قط.
408 أو 429 أو 5xx) بعد 30 ثانية، ثم دقيقتين، ثم 10 دقائق، ثم 30 دقيقة، ثم ساعة واحدة.
عندما تفشل أول رسالة نهائياً، أي ترفضها الشبكة أو تفشل المحاولة السادسة أيضاً، ينتقل التحويل إلى COUNTERPARTY_VASP_NOT_REACHABLE مع last_error في timeline، ويمكنك إعادة المحاولة باستخدام action: "resend".
وتُدوَّن في timeline أي رسالة أخرى تفشل نهائياً.
لا يحل delivery محل status الخاص بالتحويل أبداً: اقرأ حالة التبادل من status، واستخدم delivery لمعرفة سبب بقاء التحويل في حالة انتظار.
قيم الحالة القديمة
إذا كنت ترسلtravel_rule_details.status قبل هذا الإصدار، فلا تزال تلك القيم تعمل وتُحوَّل تلقائياً. كما تُقبل صيغ camelCase لأي حالة (مثل counterpartyVaspNotFound)، سواء عند الإرسال أو في قيم شروط القاعدة، وتُطبَّع إلى الصيغة المعتمدة:
الإعداد
اضبط ملف VASP الخاص بك وسياسة التفاوض مرة واحدة لكل تطبيق باستخدامGET/PUT /v3/travel-rule/settings/.
PUT هو تحديث جزئي، لذا أرسل الحقول التي تريد تغييرها فقط.
صرامة مطابقة الأسماء
أوضاع التحقق من الملكية
يحددownership_validation_mode مقدار الإثبات الذي تتطلبه ملكية المحفظة قبل أن تعتمد عليها المعاملة.
FULL وSIMPLIFIED، لا تعيد المعاملة اللاحقة استخدامها إلا إذا دعمها دليل يقبله الوضع لتلك المعاملة.
عندما لا يوجد في دفتر العناوين محفظة تطابق inbound_auto_accept، تحدد المحفظة الرقمية التي سجّلتها في معاملات مستخدم نهائي واحد فقط صاحبَها.
تضيف Didit هذه المحفظة إلى دفتر عناوين المستخدم النهائي بوصفها تصريحاً ذاتياً منك، ومُتحقَّقاً منها ما لم يكن الوضع FULL. ولا تضيف أبداً محفظة يسردها دفتر العناوين بالفعل تحت اسم آخر على السلسلة نفسها (eth وethereum)، بما في ذلك المحفظة المسجّلة باسم VASP آخر.
تتبع أيضاً فحوصات العناوين التي تجريها VASPs الأخرى قبل الإرسال (عبر GTR وSygna وVerifyVASP) الوضع نفسه: في FULL، لا تُبلَّغ المحفظة التي صرّح بها المستخدم النهائي وحده على أنها محفظتك.
مشاركة البيانات
يحددdata_sharing_fields الحقول التي ترسلها Didit إلى الطرف المقابل عن مستخدميك، بشكل منفصل للأفراد والشركات:
- يمكن للأفراد مشاركة
nameوwallet_addressوaddressوnational_idوcustomer_identificationوdate_of_birthوplace_of_birth. وتشارك الشركات الحقول الخمسة الأولى فقط. - يُرسل
nameوwallet_addressدائماً. - نوع الكيان الذي لم تختره أبداً يشارك كل الحقول. والقائمة الفارغة اختيار أيضاً: فهي تشارك الاسم والمحفظة فقط.
- يدمج
PUTالإعدادات لكل نوع كيان، فإرسالindividualلا يغيّر اختيارcompanyالمخزَّن. - يُرفض الاختيار الذي لا يستوفي التزامات المُرسِل في نظام
jurisdictionلديك (EU TFR أو توصية FATF 16)، بما في ذلك القائمة الفارغة، بخطأ400. تظهر الأخطاء تحتdata_sharing_fields، مفهرسة حسب نوع الكيان.
مطابقة الحقول
يحددmatching_rules مدى صرامة مقارنة كل حقل من بيانات الطرف المقابل بما لديك:
NONE أو STRICT أو CONTAINS أو DEFAULT أو FUZZY.
القيم الافتراضية: يتبع name القيمة name_matching_strictness، وdate_of_birth وaddress قيمتهما NONE، وaccount قيمته STRICT.
يحتفظ الحقل الذي لم يُدرج بقيمته الافتراضية، ويُرفض بخطأ 400 أي حقل لا يقارنه نوع الكيان (فللشركة لا يوجد date_of_birth).
الحدود حسب الولاية القضائية
يوجدthreshold_amount لأن نطاق قاعدة Travel Rule يختلف من جهة تنظيمية إلى أخرى.
تستخدم Didit القيمة الافتراضية 0.00، أي موقف الاتحاد الأوروبي، فتُتبادل كل معاملة ما لم ترفع الحد عمداً:
threshold_amount على الرقم الذي تحدده جهتك التنظيمية؛ فإن المعاملات دونه تُصنَّف على أنها NOT_APPLICABLE دون تبادل أي بيانات هوية.
تتغير الحدود بمرور الوقت، فتأكد من الرقم الحالي مع مستشارك في الامتثال، ولا تعتبر هذا الجدول نصيحة قانونية.
نموذج بيانات IVMS 101
المعيار الصناعي للبيانات المتبادلة بموجب Travel Rule هو IVMS 101 (interVASP Messaging Standard).travel_rule_details.originator_data وtravel_rule_details.beneficiary_data هما حمولتا شخص بنمط IVMS 101. يغلّف Didit كلًّا منهما داخل الغلاف الأصلي لكل قناة: تتبادل قناة TRP كتل IVMS101 (ivms101.2023، ويُذكر في حقل ivms_version الخاص بالتحويل)، وتتبادل قناة Sygna نسختها من IVMS المشفّرة بـ ECIES، وتستخدم القنوات GTR وTRUST وVERIFYVASP أغلفتها المشفّرة الخاصة. وبذلك يصل شكل حمولة واحد إلى كل طرف مقابل بالصيغة التي يتوقعها بروتوكوله.
يقرأ Didit هذه المفاتيح من كل حمولة شخص عند بناء رسالة IVMS 101: first_name وlast_name (أو name واحدًا أو full_name)، وdate_of_birth، وplace_of_birth، وresidence_country (أو country)، وvendor_data (أو customer_identification)، وwallet_address.
قاعدتان عمليتان:
- الكتابة باللاتينية. يشترط IVMS 101 قيمًا بالحروف اللاتينية، لذا انقل الأسماء المكتوبة أصلًا بنظام كتابة آخر إلى الحروف اللاتينية. تقارن مطابقة الأسماء مجموعات الرموز بعد تطبيعها، فلا يُفشل اختلافُ ترتيب الكلمات أو حالة الأحرف التبادلَ، لكن اختلاف نظام الكتابة بين الطرفين يفعل ذلك.
- أرسل بيانات تحققت منها. عندما يكون الشخص الذي يخصّه
vendor_dataمستخدمًا لديه عملية تحقق KYC مقبولة في Didit، ينقل Didit الهوية الموثّقة لا القيم التي كتبتها: راجع البيانات الموثّقة ومصدرها. في أي حالة أخرى، ينقل Diditoriginator_dataكما أرسلتَه تمامًا، لذا ابنِه من الهوية التي تحققت منها عند التسجيل، لا من حقل نموذج غير موثّق. هذا ما يقتضيه نص FATF الذي يطلب «معلومات المُرسِل المطلوبة والدقيقة». على القناةINTERNAL، يتلقى تطبيقُ المستفيد أيضًاsubjectالذي أرسلتَه (الاسم وvendor_dataوتاريخ الميلاد والدولة والعنوان) بوصفه الطرفَ المقابل في معاملته الواردة.
البيانات الموثّقة ومصدرها
عندما يكون موضوع التحويل أحد مستخدميك، أي فردًا لديه عملية تحقق KYC مقبولة، يبني Didit ذلك الجانب من التبادل من البيانات الموثّقة:- المُرسِل في تحويل صادر تنشئه عبر
POST /v3/transactions/؛ - المستفيد في تحويل وارد تبلّغ عنه عبر
POST /v3/transactions/، أو عبرPOST /v3/travel-rule/inbound/عندما يسمّيholder_vendor_dataالخاص بالمحفظة المطابِقة ذلك المستخدم.
POST /v3/transactions/، ينطبق الأمران على معاملة transaction_category فيها هو travel_rule ما دامت Travel Rule مفعّلة.
تتقدّم الأسماء الموثّقة وتاريخ الميلاد ومكانه ورقم الوثيقة ونوعها والعنوان ودولة الإقامة (المستمدة من العنوان) على القيم التي أرسلتها. يحتفظ الحقل الذي لا يتضمنه التحقق بقيمتك، وكذلك العنوان الكامل الذي أرسلتَه عندما لا يكون العنوان الموثّق مكتملًا بما يكفي للإرسال.
تسجّل metadata الخاصة بالتحويل، على كائن travel_rule المضمّن في المعاملة، مصدر كل حقل والمواضع التي اختلفت فيها قيمك:
*_provenance كل حقل بـ verified أو caller، ويسرد *_verified_differences كل حقل أرسلتَ فيه قيمة تختلف عن القيمة الموثّقة، مع تجاهل حالة الأحرف والمسافات ورموز الدول المكوّنة من حرفين أو ثلاثة. وعندما تكون بيانات التحويل الوارد مخفية، تُحذف الفروق. يحسب Didit هذين المفتاحين بنفسه، لذا تُسقَط النسخ التي ترسلها من هذه المفاتيح أو من name_match ضمن travel_rule_details.metadata.
إرسال IVMS 101 مباشرة
إضافةً إلى كائنيoriginator_data وbeneficiary_data المسطّحين، يقبل travel_rule_details (في POST /v3/transactions/) وPOST /v3/travel-rule/inbound/ كتل IVMS 101 الحقيقية:
- كتلة
originatorوكتلةbeneficiary، يحمل كل منهماoriginatorPersonsأوbeneficiaryPersonsيضمnaturalPersonأوlegalPerson، وaccountNumber؛ - الكتلتين نفسيهما داخل كائن
ivms101(أوIVMS101)؛ - شخص IVMS 101 واحد، أو كتلة كاملة واحدة، بوصف أيٍّ منهما
originator_dataأوbeneficiary_data.
naturalPerson وإما legalPerson، والأخير يجعل الطرف شركة. وإذا كان originator_data أو beneficiary_data المسطّح غير فارغ للجانب نفسه، فإنه يتقدم على الكتلة.
400 يكون مفتاحها مسار الكتلة، على سبيل المثال {"travel_rule_details": {"originator.originatorPersons": ["must be a non-empty list of originator persons."]}}. وفي POST /v3/travel-rule/inbound/ تظهر المفاتيح نفسها في المستوى الأعلى، تحت ivms101. إذا استخدمت الغلاف.
البيانات المطلوبة حسب النظام
ما دامت Travel Rule مفعّلة، يجب أن يحمل التحويل الصادر الذي يبلغ حدّك أو يتجاوزه ما يشترطه نظامه، وإلا أعاد الخطأ400. يُفحَص الجانبان اللذان أرسلتهما فقط، ويُحسب ما وُثّق ضمن المُرسِل، وتظهر الأخطاء تحت travel_rule_details.originator_data أو travel_rule_details.beneficiary_data، مثل "EU TFR requires the originator name." أو "FATF Recommendation 16 requires the beneficiary wallet address.":
data_sharing_fields، لأنها كل ما ترسله القناة؛ ويُفحَص المستفيد كما أرسلتَه. وإذا لم يستوفِ التحويل النظام إلا بحقل لا تشاركه، فإن الخطأ 400 يذكر شرط النظام، ثم يذكر الحقول التي تشاركها إعداداتك.
رقم المستند الرسمي هو national_id (أو document_number)، ورقم تعريف العميل هو customer_identification (أو vendor_data)، وتاريخ الميلاد هو date_of_birth. يُملأ beneficiary_data.wallet_address الفارغ من counterparty.payment_method.account_id قبل الفحص، ويُعدّ سحب UTXO الذي استنتج Didit مخرج المستفيد فيه من travel_rule_details.utxo حاملًا للعنوان. ولا يُفحَص تحويل Lightning الذي لا تنطبق عليه Travel Rule.
عناوين Travel Address
Travel Address سلسلة مشفّرة ومضغوطة تشير إلى endpoint استعلاماتTRP الواردة الخاصة بـ VASP، ولا تكشف أبدًا عن أي رابط URL خام أو عنوان محفظة.
بعد تفعيل Travel Rule، ينشئ Didit لك travel_address الخاص بك (يُعاد للقراءة فقط في الإعدادات) لتتمكن من مشاركته مع VASPs الأخرى التي تصل إليك عبر قناة TRP.
كل محفظة من محافظ دفتر العناوين لديك تحصل أيضًا على receiving_travel_address خاص بها: الاستعلام المرسل إليها يحدد تلك المحفظة، فلا يضطر Didit إلى البحث عنها برقم الحساب أو اسم الحامل.
خادم TRP العام لدى Didit لا يستجيب لـ VASPs الأخرى بعد، لذلك لا يتوفر الاستقبال عبر TRP إلى أن يحدث ذلك: راجع شبكات Travel Rule.
عندما تعرف Travel Address الخاص بطرف مقابل، خزّنه في إدخال دفتر عناوين المحفظة الخاص بالوجهة، ليوجّه Didit تحويلات ذلك المستفيد مباشرة عبر TRP بدلًا من الرجوع إلى البريد الإلكتروني.
يتحقق Didit من Travel Address الذي تخزنه في إدخال دفتر عناوين المحفظة أو ترسله في originating_vasp: يجب أن يبدأ كل عنوان بـ ta وأن يُفكّ ترميزه إلى رابط استعلام يحمل t=i، فالقيمة النائبة مثل ta... تُرجع 400.
لا يخضع beneficiary_data.travel_address في المعاملة للتحقق: إذا تعذّر فك ترميزه يُتخطى مسار TRP.
يعتمد الترميز على Base58Check مع البادئة ta: مضيف endpoint ومساره (ويحمل دائمًا زوج الاستعلام t=i)، مع مجموع تحقق (checksum) من أربعة بايتات بخوارزمية SHA-256 المطبّقة مرتين، ثم يُرمَّز الناتج بـ base58. العنوان الحقيقي يبدو هكذا، ويبلغ طوله نحو 100 حرف:
400 مع "Invalid travel address.".
استخدم عنوانًا حقيقيًا من استجابة الإعدادات الخاصة بك، أو من الطرف المقابل نفسه.
الاختبار دون بيانات اعتماد الشبكة
لا تحتاج إلى عضوية في GTR أو TRUST أو VerifyVASP أو Sygna لاختبار المسارات التي لا تتطلب عضوية. ما زلت تحتاج إلى مفتاح API الخاص بـ Didit للتطبيق قيد الاختبار.- الصادر في البيئة التجريبية: أرسل معاملة
travelRuleمن تطبيق في البيئة التجريبية. بعد فحوصات الحد والمحفظة ذاتية الاستضافة، يردّ Didit Test VASP داخل Didit بالسيناريو الذي تختاره حسب محفظة المستفيد أو اسمه: القبول، أو الرفض، أو عدم تطابق الاسم، أو تعذر الوصول، أو انتهاء المهلة، أو ذاتية الاستضافة. لن يُرسل أي شيء إلى شبكة أو عبر البريد الإلكتروني، ولن يتلقى أي عميل آخر لدى Didit أي شيء. - الوارد في البيئة التجريبية: اطلب من Test VASP أن يرسل إليك تحويلًا عبر Trigger Test VASP Inbound Transfer، أو سجّل تحويلًا بنفسك عبر Register Inbound Travel Rule Transfer. اختبر محفظة غير معروفة، ومحفظة غير موثّقة، وعدم تطابق اسم المستفيد، ومحفظة موثّقة متطابقة. تختبر هذه الحالات قرارات حقيقية بشأن المعاملات الواردة دون الاتصال بأي شبكة.
- تطبيقان حيان خاضعان للتحكم: استخدم المسار
INTERNALبين تطبيقين حيين تملكهما، حيث يكون التطبيق المستقبِل قابلًا للاكتشاف ويثبت سيطرته على محفظة الوجهة برسالة موقّعة أو إيداع تجريبي. أو استخدم طرفًا مقابلًا خاضعًا للتحكم عبر TRP، لاختبار تبادل بين طرفين دون بيانات اعتماد تجارية للشبكة. قد تترتب على التطبيقات الحية رسوم وإشعارات عادية، لذا استخدم هويات وأطرافًا مقابلة تتحكم بها أنت.
إرسال تحويل
أدرِج التحويل فيPOST /v3/transactions/ بضبط transaction_category على travelRule (أو travel_rule) وإرفاق كائن travel_rule_details (الاسم البديل: travelRule).
كتلة travel_rule_details نفسها اختيارية، لكن بمجرد إرفاقها يصبح travel_rule_details.status مطلوباً: الكتلة التي لا تتضمنه تعيد 400.
عندما يكون Travel Rule مفعّلاً لتطبيقك، يتجاهل Didit قيمة status التي ترسلها ويدير تبادل المعلومات بنفسه:
- إذا كان المبلغ أقل من
threshold_amountالمضبوط لديك، يُحسم التحويل فوراً علىNOT_APPLICABLEولا يُتفاوض على شيء. - إذا أعلنت وسيلة دفع الطرف المقابل أن الوجهة محفظة ذاتية الاستضافة (
payment_method.method_type: "unhosted_wallet")، فلا تُجرَّب أي قناة، ويذهب التحويل مباشرة إلىUNCONFIRMED_OWNERSHIPويُنشأ أداة التحقق من ملكية المحفظة تلقائياً (انظر المحافظ الذاتية الاستضافة المُعلنة). - وإلا تُشغَّل القنوات بحسب ترتيب الأولوية، وأول قناة تصل إلى الطرف المقابل تحمل التبادل:
INTERNAL- يوجد عميل آخر واحد فقط لدى Didit يملك إثباتاً بتوقيع رسالة أو بإيداع تجريبي للعنوانbeneficiary_data.wallet_address، أُنجز بعد آخر تغيير في حائز المحفظة. لا يُحتسب تطبيقك أنت أبداً، وعندما يختار المستخدم النهائي عضواً في شبكة Didit ضمن أداة التحقق من ملكية المحفظة، يُحتسب إثبات ذلك العضو وحده. يعمل تطبيق ذلك العميل في بيئة الإنتاج (وليس البيئة التجريبية)، ومفعّل فيه Travel Rule مع ضبطis_discoverable، وهو الـ VASP الذي تسمّيه عند إرسالcounterparty_vasp_leiأوcounterparty_vasp_name. المحفظة المُعلنة ذاتياً فقط، أو المُثبَتة بلقطة شاشة، أو غير الموثّقة، أو المدرجة في دفتر العناوين فقط، لا تستلم شيئاً عبرINTERNALأبداً.TRP- للأصل المُحوَّل معرّف رمز رقمي وفق ISO 24165 (DTI) مطابق لشبكة التحويل (انظر معرّفات أصول TRP)، والحمولة تتضمنbeneficiary_data.travel_address، أو يخزّن إدخال دفتر العناوين الخاص بالوجهة عنواناً من هذا النوع، أو يُحلّ الطرف المقابل في الدليل (عبرcounterparty_vasp_leiأوcounterparty_vasp_name) ولديه نقطة نهاية TRP.GTR،TRUST،VERIFYVASP،SYGNA- ربطت عضويتك الخاصة في تلك الشبكة من Marketplace، ويُحلّ الطرف المقابل إلى معرّف شبكة (حقل صريح أو إدخال في الدليل، انظر حقول تحديد الطرف المقابل). ولا تملك Didit حالياً عضوية منصة في هذه الشبكات (وهي في طور الانضمام كمزوّد)، لذلك لا يغيّرdisabled_networksهذه الخطوة في الوقت الحالي.EMAIL- وُفِّرbeneficiary_data.counterparty_vasp_email، أو يُحلّcounterparty_vasp_nameإلى إدخال في الدليل يحتوي على عنوان بريد إلكتروني للامتثال.
- إذا لم تستطع أي قناة تحديد الطرف المقابل، تُشغّل Didit سلسلة اكتشاف الطرف المقابل. مع تفعيل
vasp_attribution_enabled، تتضمن هذه السلسلة إسناد تحليلات البلوكتشين: تُحدَّد المحفظة الوجهة إلى الـ VASP المالك لها، ويُكتب الاسم المُسنَد إلىbeneficiary_data.counterparty_vasp_name(إذا كان فارغاً) وإلى الدليل، وتُعاد تشغيل القنوات مرة واحدة. لذلك تُوجَّه المحفظة المُسنَدة إلى VASP يحمل إدخاله في الدليل نقطة نهاية TRP أو معرّف شبكة أو عنوان بريد إلكتروني للامتثال في هذه المرحلة الثانية. - إذا بقي الطرف المقابل غير محلول، ينتقل التحويل إلى
COUNTERPARTY_VASP_NOT_FOUND. في التحويلات الصادرة يُنشئ Didit تلقائياً أداة التحقق من ملكية المحفظة (عندما يكونauto_wallet_verificationمفعّلاً) حتى يمكن إتمام التحويل كتحويل إلى محفظة ذاتية الاستضافة، ويمكنك إعادة تشغيل التوجيه لاحقاً باستخدامaction: "resend"بعد توفر بيانات أفضل عن الطرف المقابل.
AWAITING_COUNTERPARTY مع protocol: null، ويصل كل نتيجة عبر webhook من نوع travel_rule.status.updated.
معرّفات أصول TRP
يسمّيTRP الأصل المُحوَّل بمعرّف الرمز الرقمي وفق ISO 24165 (DTI).
يحدد DTI رمزاً واحداً على بلوكتشين واحد، لذلك يملك الرمز متعدد السلاسل معرّف DTI لكل شبكة.
يرسل Didit التحويلات عبر TRP لهذه الأصول:
USDC هي التي ينشرها مُصدر الرمز في ورقته البيضاء وفق MiCA.
بالنسبة إلى الرمز متعدد السلاسل، يجب أن يحدد التحويل شبكته في transaction_details.crypto_params.crypto_chain (مثل CELO أو BASE أو OP أو ARB أو POL).
فبدون شبكة، أو على شبكة لا يوجد لها ربط، لا يُرسل التحويل عبر TRP، ويجرّب Didit القنوات المتبقية: شبكات التشغيل البيني أولاً، ثم البريد الإلكتروني.
USDT غير مربوط بعد.
أما الرمز الذي لا يملك ربط DTI، مثل العملة المستقرة الخاصة بشركة، فيحتاج مُصدره إلى تسجيل DTI لدى جهة التسجيل وفق ISO 24165، واحد لكل رمز على كل بلوكتشين.
وبعد وجود معرّفات DTI، تواصل مع دعم Didit وأرسل كل DTI وبلوكتشينه وعدد الخانات العشرية للرمز، فيضيف Didit الربط.
تُسجَّل طلبات الاستعلام الواردة عبر TRP التي تحمل DTI مرتبطاً بكل سلسلة بعملة الرمز، والمبلغ بالخانات العشرية للرمز، والشبكة.
اكتشاف الطرف المقابل
يصل التحويل الذي يحمل عنوان محفظة فقط إلى طرفه المقابل متى تمكّن أي أحد من تحديده. قبل إعلان تعذّر توجيه تحويل صادر، يسأل Didit بهذا الترتيب، ويتوقف عند أول خطوة تسمّي VASP له مسار:discovery في التحويل كل خطوة نُفِّذت مع outcome الخاص بها (match أو miss أو skipped)، وreason مقروءاً آلياً، وvasp وrail اللذين وجدتهما، والطابع الزمني.
يتقدّم VASP الذي اخترته أنت أو المستخدم النهائي على كل خطوة تلقائية، فلا يستبدله الاكتشاف أبداً بتخمينه الخاص.
عندما لا تسمّي أي خطوة طرفاً مقابلاً، ينتقل التحويل إلى COUNTERPARTY_VASP_NOT_FOUND، أو إلى COUNTERPARTY_VASP_NOT_REACHABLE إذا سُمّي VASP لكن لا يتوفر أيٌّ من مساراته لتطبيقك.
ثم يحمل التحويل reason (awaiting_end_user أو no_directory_match أو directory_match_no_route أو picked_member_without_proven_wallet عندما لم يثبت العضو الذي اختاره المستخدم النهائي ملكية المحفظة) وقائمة next_actions:
url في الإجراءات التي يؤديها المستخدم النهائي.
عندما يكون إجراء إثبات ملكية المحفظة معلّقاً، يحمل قسم action_required في المعاملة (من النوع wallet_ownership) الـreason نفسه وnext_actions نفسها، فيعرض العميل الذي يتتبع action_required أصلاً أداة الاختيار دون أي عمل إضافي.
حقول تحديد الطرف المقابل
تأتي كل المعلومات التي يعرفها Didit عن الطرف المقابل منtravel_rule_details.beneficiary_data.
تُشغّل هذه الحقول تحديد المسار، وجميعها اختيارية:
أشكال السلاسل
في سلسلة الحسابات (شبكات EVM وSolana وTron) ينقل التحويل الأموال من عنوان إلى آخر، ويقرأ Didit المحفظتين مباشرةً من المعاملة. تحتاج ثلاثة أشكال أخرى إلى مزيد من البيانات، وما يحدده Didit لها يظهر في التحويل بوصفهchain_details (فارغاً، أي {}، في سلسلة الحسابات).
يحمل كلٌّ من chain_details.originator وchain_details.beneficiary الحقول address وmemo وsource الذي أتى منه، ويبدأ التوجيه والمطابقة بقراءة المحفظتين منهما أولاً.
تحويلات UTXO
تُنفق معاملة Bitcoin أو Litecoin أو Bitcoin Cash أو Dogecoin مدخلاتٍ من أي عدد من العناوين، وتدفع أي عدد من المخرجات، وبعضها يعود إلى المرسل كباقي المبلغ. أرسل المدخلات والمخرجات فيtravel_rule_details.utxo (المبالغ بوحدة العملة نفسها، كسلاسل عشرية)، وسيستنتج Didit ما لا تصرّح به:
- المرسل:
originator_data.wallet_addressعندما ترسله، وإلا فعنوان المدخل الذي ساهم بأكبر قدر (source: largest_input). - المستفيد: المحفظة المعلنة، وإلا فالمخرج الوحيد الذي لا يُعيد دفع مبلغ إلى عنوان مدخل كباقٍ (
only_non_change_output)، وإلا فالمخرج الذي يدفع مبلغ التحويل (amount_match).
chain_details.warnings إلى ما لا يتطابق: declared_originator_not_an_input وdeclared_beneficiary_not_an_output وbeneficiary_output_ambiguous (لم يكن بالإمكان تمييز مخرَج واحد بعينه) وbeneficiary_output_amount_differs.
يُنتج إرسال utxo على أي سلسلة أخرى الخطأ 400، ولا تحتوي كل قائمة على أكثر من 1,000 عنصر.
يأخذ الإيداع المُبلَّغ عنه الكائن utxo نفسه (راجع تسجيل ما بعد الإيداع).
شبكات memo والوسم
في XRP وStellar (XLM) وEOS وCosmos (ATOM) وHedera (HBAR)، تستقبل المنصة كل عملائها عبر عنوان واحد مشترك، وتميّز بينهم بوسم الوجهة (destination tag) أو بالـ memo. لذلك يُعدّ الـ memo جزءًا من المحفظة: أرسله بوصفهpayment_method.memo على المشارك، أو بوصفه memo داخل originator_data / beneficiary_data (الاسم البديل destinationTag)، وتقارن Didit العنوان والـ memo معًا.
- يطابق إدخالُ دفتر العناوين الذي يحمل memo التحويلاتِ التي تذكر هذا الـ memo فقط. أما الإدخال الذي لا يحمل memo فيطابق العنوان كاملًا. ولا يصل أي تحويل لا يذكر memo إلى إدخال يحمل واحدًا: فعلى عنوان مشترك، الإيداع الذي لا وسم معه لا يخص أحدًا.
- عنوان X في XRP يحمل الوسم داخله: تفصله Didit إلى العنوان الكلاسيكي والوسم (
source: x_address)، فتطابق المحفظة المسجّلة بأي من الصيغتين تحويلًا بالصيغة الأخرى. - كل memo على عنوان مشترك يُعدّ وسيلة دفع مستقلة في المعاملة.
- ما تقبله كل شبكة: وسم الوجهة في XRP عدد صحيح من 0 إلى 4294967295 (يُقبل رقم JSON)؛ وmemo في Stellar نص يصل إلى 28 بايتًا، أو معرّف من 64 بت، أو تجزئة من 32 بايتًا بصيغة hex؛ وEOS يقبل حتى 256 بايتًا، وCosmos حتى 256 حرفًا، وHedera حتى 100 بايت. وأي memo على سلسلة لا تدعمه (Bitcoin وشبكات EVM وSolana وTron وLitecoin وBitcoin Cash وDogecoin وLightning) يُنتج الخطأ
400.
Lightning
لا تملك دفعة Lightning عنوان مستفيد على السلسلة: فالدافع يحمل طلب دفع. في تحويلات العملات المشفرة على bitcoin أو Lightning، يجوز أن يكونpayment_method.account_id هذا الطلب، فتحدد Didit المستفيد منه دون الاتصال بأي شبكة:
chain_details.beneficiary (source: invoice_payee)، على السلسلة LIGHTNING، ويُحفظ ما يذكره الطلب في chain_details.lightning.
عندما لا يذكر الطلب مستلمًا، تكون التحويلة الصادرة NOT_APPLICABLE ويُسجَّل السبب في chain_details.not_applicable_reason وفي timeline: LIGHTNING_NO_PAYEE_IDENTIFIER أو LIGHTNING_BOLT12_BLINDED_PAYEE أو LIGHTNING_LNURL_NAMES_NO_PAYEE.
لا تُقرأ القيمة على أنها Lightning إلا على bitcoin، أو على LIGHTNING، أو عندما لا تُسمَّى سلسلة (ولا يُعامَل عنوان Lightning إلا على bitcoin أو LIGHTNING): على أي سلسلة أخرى تكون هذه القيمة عنوان تلك السلسلة نفسه.
INTERNAL إلى عميل Didit آخر إلا عبر محفظة أثبت ذلك العميل سيطرته عليها برسالة موقّعة أو بإيداع تجريبي، وهذه الإثباتات متاحة اليوم لـ Bitcoin وشبكات EVM وSolana وTron فقط.
لذلك لا يصل التحويل أبدًا إلى عميل Didit آخر عبر INTERNAL على شبكات memo وLightning وLitecoin وBitcoin Cash وDogecoin.
أما مطابقة الـ memo والوسم وLightning فتظل سارية على دفتر عناوينك أنت: التحويلات الواردة، والإيداعات المُبلَّغ عنها، واستفسارات TRP الموجهة إلى محافظك.تحديث محفظة المُرسِل
غالبًا ما يُصرَّح بالسحب من محفظة العميل الرئيسية، ويُبثّ من العناوين التي كانت تحتفظ بالعملات فعلًا. سجّل المحفظة التي خرجت منها الأموال فعلًا باستخدامPATCH /v3/transactions/{transaction_id}/travel-rule/:
- التحويلات الصادرة فقط، ولا يجوز ذلك أبدًا لتحويل بالحالة
CANCELLEDأوEXPIRED(409). - عندما تكون مدخلات UTXO الخاصة بالتحويل معروفة، يجب أن تكون المحفظة إحدى هذه المدخلات (وإلا فالخطأ
400). أرسلmemoأيضًا على شبكة تستخدم الـ memo. - يغيّر ذلك السجل فقط: لا يُرسل أي شيء إلى الطرف المقابل مرة أخرى، وتبقى الحالة كما هي. يصبح
chain_details.originatorالمحفظة الجديدة معsource: updated، ويسجّلtimelineالمحفظتين السابقة والجديدة، ويُحدَّث الطرفINTERNALالمقابل للتحويل بالطريقة نفسها.
إسناد تحليلات البلوكتشين
عندما تمرّر عنوان محفظة وحده، تستطيع Didit مع ذلك أن تعرف من يقف على الطرف الآخر. إذا لم يستطع أي مسار توجيه التحويل، تطابق Didit محفظة الوجهة مع بيانات إسناد تحليلات البلوكتشين، وهي التحليلات نفسها التي تقف خلف فحص معاملات العملات المشفرة، فتعرف المحافظ التي تنتمي إلى كل منصة تداول وكل حارس أصول. يعمل الإسناد على التحويلات الصادرة التي تحمل محفظة وجهة وchain معًا (راجع حقول تحديد الطرف المقابل)؛ وبدون السلسلة يُتخطّى.
عندما تُسنَد المحفظة إلى جهة معروفة، تقوم Didit بما يلي:
- تسجّل الإسناد على التحويل: يُملأ
beneficiary_data.counterparty_vasp_nameإذا لم تكن قد زوّدتها أنت، وتحمل بيانات التحويل الوصفية كتلةvasp_attributionتضم اسم الجهة ونوعها وبلدها وsource: "blockchain_analytics". - تضيف الجهة إلى دليل VASP (أو تطابق الإدخال الموجود)، فيبقى الإسناد ثابتًا عبر التحويلات.
- تعيد تشغيل المسارات مرة واحدة باسم الجهة المُسنَد: فالإدخال الموجود في الدليل الذي يحمل بالفعل TRP endpoint يجعل التحويل يُوجَّه فورًا بدلًا من أن ينتهي إلى
COUNTERPARTY_VASP_NOT_FOUND.
custom_values.aml_screening_trigger للمعاملة على travel_rule_attribution حتى تعرف أن الفحص أُجري لأن توجيه Travel Rule استوجبه.
وإذا كانت مراقبة العملات المشفرة قد فحصت المحفظة نفسها في هذه المعاملة من قبل، أُعيد استخدام النتيجة المخزَّنة: فلا استدعاء ثانٍ للتحليلات ولا رسوم ثانية.
ولا يمنع الإسناد أبدًا إنشاء المعاملة: فإذا فشل بحث التحليلات أو لم يغطِّ رصيدك تكلفته، يمضي التحويل إلى COUNTERPARTY_VASP_NOT_FOUND دون أي رسوم على المحاولة.
وعند action: "resend"، يُعاد استخدام الإسناد المسجّل سابقًا بدلًا من إعادة الاستعلام عنه.
عطّل الخطوة كاملة باستخدام vasp_attribution_enabled: false في الإعدادات.
راجع الأسعار لمعرفة طريقة فوترة استدعاء الإسناد.
عندما تكون Travel Rule معطّلة، تُسجَّل travel_rule_details (بما فيها status الذي يحدده المستدعي، سواء كان القديم أو الحالي) على المعاملة، ولكن بشكل سلبي، دون إرسال أي مسار.
استخدم هذا لاستكمال بيانات الامتثال التي سبق أن تتبعتها بعمليتك الخاصة.
التحويل الصادر السلبي لا يبدأ أي تبادل، لذلك يُحتسب كـ Transaction Screening، وليس على بند Travel Rule.
عند تفعيل Travel Rule، يُوجَّه هذا الطلب عبر المسار EMAIL، لأنه يتضمن البريد الإلكتروني للامتثال لدى الطرف المقابل، ولا يستطيع أي مسار سابق الوصول إلى المستفيد:
originator_dataهي ما أرسلته، لأن المستخدم في المثال ليس لديه تحقق KYC معتمد؛ بالنسبة إلى مستخدم موثّق يبنيها Didit من البيانات الموثّقة (انظر البيانات الموثّقة ومصدرها). يعرض المسارEMAILللمستلم حقول المُرسِل التي تشاركها فيdata_sharing_fieldsفقط.beneficiary_data.chainهيETHلأن الطلب لم يحدد أي شبكة، فأخذها Didit من العملة.statusهيAPPROVEDلأن أي قاعدة لم تتخذ إجراءً على المعاملة؛ قد تحدد قواعدك الخاصة حالة أخرى.- تتلقى جهة اتصال الامتثال رابط الاستلام، ويكتمل التحويل، أو يفشل في مطابقة الاسم، أو يُرفض عند ردها، أو تنتهي صلاحيته إن لم تردّ (انظر استلام البريد الإلكتروني).
protocol ما زال null لأن المسار يُختار بعد الاستجابة (مثلاً EMAIL عندما تزوّد counterparty_vasp_email): يعرضه الحصول على المعاملة بعد اختياره، وكل webhook لاحق من نوع travel_rule.status.updated يحمله في rail.
إذا كانت محفظة المستفيد تنتمي إلى عميل آخر في Didit أثبت سيطرته عليها، يُسوّي مسار INTERNAL التبادل كله فور الاستجابة، وذلك الـ webhook يُبلغ بالفعل COMPLETED (أو COUNTERPARTY_MISMATCHED_DATA). عندما لا يتطابق الاسم، يصبح تحويلك COUNTERPARTY_MISMATCHED_DATA ولا يتلقى العميل الآخر أي شيء.
من دون counterparty_vasp_email، وعندما لا تستطيع المسارات ولا الإسناد الوصول إلى المستفيد، ينتهي التحويل لاحقاً بالحالة COUNTERPARTY_VASP_NOT_FOUND مع إجراء wallet_ownership على المعاملة.
travel_rule المضمّنة في المعاملة حقول التحويل نفسه (uuid وstatus وprotocol وrequired وobligations_count وoriginator_data وbeneficiary_data وmetadata)، وما حدّده Didit وقارنه (chain_details وname_match وdata_match وmismatched_fields، وحقلي الإخفاء data_masked وdata_unmasked)، والحقول التي توضح أين يقف التحويل: direction وrail وcounterparty_vasp وdelivery وdiscovery وreason وnext_actions (انظر لماذا يتوقف التحويل).
تحمل كل صف من GET /v3/transactions/ الكتلة نفسها، أو null للمعاملة التي لا تحتوي على تحويل.
تُرجع endpoints التحويل المخصصة أدناه تلك الحقول أيضاً، إضافة إلى ivms_version وownership_confirmed وdeadline_at وtimeline وcreated_at، لكنها لا تتضمن obligations_count أو metadata.COMPLETED بعد بثه على السلسلة:
COMPLETED، وإلا يُرجع API الرمز 409.
عند النجاح ينتقل كل من التحويل الصادر ونظيره على مسار INTERNAL إلى FINISHED، وتُضبط payment_txn_id للمعاملة على التجزئة التي أرسلتها. على مسار TRP، يرسل Didit التجزئة أيضاً إلى رابط استدعاء التأكيد لدى الطرف المقابل.
لإلغاء تحويل لم يصل بعد إلى حالة نهائية، أرسل {"action": "cancel"} إلى الـendpoint نفسه بدلاً من ذلك.
يُلغى أيضاً نظيره على مسار INTERNAL، وعلى مسار TRP، بعد أن يوافق الطرف المقابل على الاستعلام، يرسل Didit الإلغاء إلى رابط استدعاء التأكيد.
التحويل الذي بلغ FINISHED أو CANCELLED أو EXPIRED يُرجع 409.
الطلب الذي لا يتضمن payment_txn_id ولا action مدعوماً يُرجع 400.
على تحويل مرّ عبر GTR أو VERIFYVASP أو SYGNA، يُبلغ Didit الشبكة بالتجزئة على السلسلة (أو بالإلغاء) بعد الاستجابة، مع إعادات المحاولة نفسها؛ ويُظهر delivery في الاستجابة تلك الرسالة.
لإعادة محاولة تحويل صادر لم يصل إلى طرفه المقابل، أي تحويل في COUNTERPARTY_VASP_NOT_FOUND أو COUNTERPARTY_VASP_NOT_REACHABLE أو NOT_ENOUGH_COUNTERPARTY_DATA، حسّن أولاً بيانات الطرف المقابل، مثلاً بإضافة travel_address إلى إدخال المحفظة الوجهة، أو جهة اتصال لإدخال الطرف المقابل في الدليل، أو بإضافة VASP إلى الدليل.
ثم أرسل {"action": "resend"} إلى الـendpoint نفسه. يعيد Didit ضبط مهلة التأكيد، ويعيد وضع سلّم التوجيه في الطابور من البداية، مستخدماً البيانات المخزنة في التحويل: تُظهر الاستجابة التحويل عائداً إلى AWAITING_COUNTERPARTY (مع resend queued في timeline)، ويصل النتيجة عبر webhook من نوع travel_rule.status.updated.
طلب إعادة الإرسال الذي يُرسل أثناء تشغيل محاولة سابقة يُرسل بعد انتهائها.
أي حالة أخرى، أو تحويل وارد، يُرجع 409.
{"action": "beneficiary_vasp", "beneficiary_vasp": "<directory VASP id>"} (وهو id من GET /v3/travel-rule/vasps/).
يُوجَّه التحويل إلى ذلك VASP ويُعاد إرساله.
لا يقبل ذلك إلا تحويل صادر في COUNTERPARTY_VASP_NOT_FOUND أو COUNTERPARTY_VASP_NOT_REACHABLE أو NOT_ENOUGH_COUNTERPARTY_DATA؛ أي حالة أخرى تُرجع 409، والمعرّف غير المعروف يُرجع 404.
لماذا يتوقف التحويل
يوضح التحويل فيCOUNTERPARTY_VASP_NOT_FOUND أو COUNTERPARTY_VASP_NOT_REACHABLE سبب توقفه وما يدفعه إلى الأمام، وذلك في كل قراءة لمعاملته.
تحمل كتلة travel_rule في GET /v3/transactions/{transaction_id}/ وفي استجابة 201 من POST /v3/transactions/ وفي كل صف من GET /v3/transactions/ هذه المعلومات، وكذلك كائن التحويل الذي تُرجعه endpoint التحويل:
reasonهو السبب المقروء آليًا، و""لأي حالة أخرى.next_actionsيسرد الإجراءات التي تُحرّك التحويل إلى الأمام، و[]لأي حالة أخرى.deliveryهو أحدث رسالة يدين بها التحويل للطرف الآخر، ويتضمنkindوstatus(وهيQUEUEDأوSENDINGأوRETRYINGأوDELIVEREDأوFAILED) وrailوattemptsوlast_errorوnext_attempt_atوdelivered_at، أوnullعندما لا يكون مدينًا بأي رسالة.discoveryيبيّن أين بحثت Didit عن الطرف المقابل، بإدخال واحد لكل خطوة نفّذتها، ويتضمنstepوoutcome(وهيmatchأوmissأوskipped) وreasonوvaspوrailوat.
email_undeliverable وdelivery_failed إلا عند COUNTERPARTY_VASP_NOT_REACHABLE، وتنتجان عن تسليم فاشل.
يُحدِّد التسليم الفاشل قيمة reason أولًا، لذلك لا يُبلَّغ أبدًا عن طرف مقابل وجدته Didit على أنه طرف لم تتمكن من العثور عليه.
أما الأسباب الأربعة الأخرى فتأتي من سجل الاكتشاف، في أي من الحالتين.
reason.
يحمل إدخال الجدول الزمني لأي انتقال تسبب فيه تسليم فاشل القيمةَ نفسها ضمن reason.
إذا رفض مزوّد البريد بريد الامتثال الإلكتروني الخاص بـمثال التحويل أعلاه، فإن كتلة travel_rule في معاملته تقرأ، مختصرة:
delivery مع reason، لأن الإرسال نفسه قد رُفض.
أما الرفض في التذكير، أو الارتداد المُبلَّغ عنه لاحقًا، فيُبقي delivery.status على DELIVERED، لأن الرسالة الإلكترونية الأولى قد أُرسلت بالفعل.
اقرأ reason لا delivery لتعرف سبب تعطّل التحويل.
لإصلاح ذلك، أعطِ بريد الامتثال الإلكتروني للطرف المقابل مرة أخرى، ثم أعد الإرسال:
update_compliance_email بالعنوان في beneficiary_data.counterparty_vasp_email الخاص بهذا التحويل: إنه يخص هذا التحويل وحده، ولا يُضاف إلى أي إدخال في الدليل أو إلى أي قائمة جهات اتصال.
لا تتغير الحالة، ويضيف الجدول الزمني السطر compliance email updated، ويحتفظ التحويل بـreason: email_undeliverable حتى تُرسل إعادةُ الإرسال الطلبَ إلى العنوان الجديد.
لا يقبل هذا الإجراء إلا تحويل صادر في COUNTERPARTY_VASP_NOT_FOUND أو COUNTERPARTY_VASP_NOT_REACHABLE أو NOT_ENOUGH_COUNTERPARTY_DATA؛ وأي تحويل آخر يُجيب بالرمز 409، وتؤدي قيمة compliance_email المفقودة أو غير الصحيحة إلى استجابة بالرمز 400.
المحافظ ذاتية الاستضافة المُصرَّح بها
عندما يخبرك عميلك أن الوجهة هي محفظته الذاتية الاستضافة (غير المستضافة) الخاصة به، فصرّح بها في المعاملة بدلًا من ترك التوجيه يكتشف أنه لا توجد VASP خلفها: اضبط نوع طريقة الدفع للطرف المقابل علىunhosted_wallet.
- لا تُجرَّب أي قناة، إذ لا توجد VASP مقابلة للتبادل معها، ويُنشأ التحويل مباشرةً في
UNCONFIRMED_OWNERSHIP. - عند تفعيل
auto_wallet_verification(وهو الافتراضي)، تُنشئ Didit تلقائيًا جلسة أداة إثبات ملكية المحفظة مرتبطة بالمعاملة، وتعيدها في كتلةaction_requiredالخاصة بالمعاملة، وتشغّلها حزم SDK تلقائيًا عند الإرسال من جانب العميل. - بمجرد أن يُثبت العميل سيطرته على المحفظة، يُجري التحويل مطابقة اسم المستفيد ويتقدم إلى
COMPLETED، أو إلىCOUNTERPARTY_MISMATCHED_DATAعند فشل المطابقة.
UNCONFIRMED_OWNERSHIP ليست حالة قابلة لإعادة الإرسال؛ أكمل إثبات الملكية (أو ألغِ التحويل) بدلًا من استدعاء action: "resend".
الاستلام
تأكيد ملكية المحفظة
ينتظر التحويل في حالةUNCONFIRMED_OWNERSHIP في حالتين:
- يُحل تحويل وارد إلى إدخال في دفتر العناوين لم تُتحقق ملكيته بعد.
- أُعلن أن محفظة الطرف المقابل ذاتية الاستضافة (
counterparty.payment_method.method_type: "unhosted_wallet")، في سحب صادر أو إيداع وارد. راجع المحافظ ذاتية الاستضافة المُعلنة.
confirmed (مطلوب، قيمة منطقية) وwallet_address اختياري.
تقبل نقطة النهاية تحويلاً في أي من هذه الحالات، وتُعيد 409 مع {"detail": "Transfer is not awaiting ownership confirmation."} لأي حالة أخرى:
UNCONFIRMED_OWNERSHIP، في أي من الاتجاهين.ON_HOLDفي تحويل وارد.UNCONFIRMED_OWNERSHIPأوON_HOLDأوCOUNTERPARTY_MISMATCHED_DATAفي تحويلTRPوارد، حتى يتمكن مسؤول الامتثال من حل استفسار الطرف المقابل يدوياً.
INTERNAL، أو تسجيل Sunrise، أو محفظة ذاتية الاستضافة مُعلنة):
confirmed: trueيضع علامة التحقق من الملكية على إدخال عنوان المحفظة المطابق في دفتر العناوين، ثم يُجري مطابقة الاسم معholder_nameلذلك الإدخال:COMPLETEDإذا نجحت، وCOUNTERPARTY_MISMATCHED_DATAإذا لم تنجح. يحصل الطرف المقابل في مسارINTERNAL، إن وُجد، على النتيجة نفسها. بالنسبة إلى محفظة ذاتية الاستضافة مُعلنة، يسجلconfirmed: trueأن عميلك تحقق ممن يتحكم في محفظة الطرف المقابل، ويُكمل التحويل (COMPLETED)، صادراً كان أو وارداً. أما التحويل الوارد الذي سُوّي إيداعه بالفعل فينتقل مباشرة إلىFINISHED.confirmed: falseينقل طرفي التحويل كليهما إلىCOUNTERPARTY_VASP_GENERAL_DECLINE.- يُبحث عن الإدخال المطابق في دفتر عناوينك بعنوان المحفظة وحده، دون السلسلة.
حل تحويل وارد في حالة
ON_HOLDدون إدخال مطابق ينشئ الإدخال منbeneficiary_data.wallet_addressويعلّمه كمتحقق منه. المحفظة ذاتية الاستضافة المُعلنة هي الاستثناء: تُسجل المحفظة في دفتر عناوينك بوصفها متحققة الملكية، كما هو موضح أدناه. في كل الحالات الأخرى، يُعيد الطلب409عند عدم العثور على الإدخال مع{"detail": "wallet address entry no longer available"}، وهو الرد نفسه الذي يُعيده الإدخال المحذوف.
TRP وارد، يجيب الاستدعاء نفسه عن استفسار الطرف المقابل:
confirmed: trueيحتاج إلى عنوان دفع لإعادته، ويُؤخذ منwallet_addressفي الطلب، ثم من الإدخال المطابق، ثم منbeneficiary_data.wallet_address. إذا لم يتوفر أي من الثلاثة، يُعيد الطلب400مع{"wallet_address": ["wallet_address is required to approve."]}. ينشئ Didit الإدخال أو يتحقق منه، ويرسل استدعاء حل الاستفسار إلى الطرف المقابل، ويضبطownership_confirmed، وينقل التحويل إلىCOMPLETED.confirmed: falseيرسل ردّ رفض الاستفسار، وينقل التحويل إلىCOUNTERPARTY_VASP_GENERAL_DECLINE.- إذا تعذر الوصول إلى نقطة نهاية الطرف المقابل، يُعيد الطلب
502ويحتفظ التحويل بحالته الحالية. عند الموافقة، يُنشأ إدخال عنوان المحفظة أو يُعلَّم بأنه متحقق منه.
COUNTERPARTY_MISMATCHED_DATA.
إذا لم يذكر التحويل محفظة للطرف المقابل إطلاقاً، مرّر wallet_address في جسم الطلب.
إذا لم يوفّر التحويل ولا الطلب عنوان محفظة، يُرفض الطلب بالرمز 400.
- تحويل من شبكة أُجيب عليه بالفعل عبر شبكته (تحويل VerifyVASP، أو تحويل GTR أو Sygna Bridge قُرّر بالفعل) يُعيد
409(راجع التحويلات من شبكة).
INTERNAL جديد هنا أبداً، إذ لا يصل إلا إلى عميل أثبت سيطرته على المحفظة من قبل. أما تحويل INTERNAL أقدم ما زال ينتظر من قبل تطبيق تلك القاعدة، فيُبقي تفاصيل المُرسِل مخفية حتى تُثبت سيطرتك على المحفظة (بتوقيع رسالة أو إيداع تجريبي)، ويؤدي تأكيده إلى إرجاع 409 حتى تفعل ذلك. الرفض لا يزال يعمل.
عندما يجيب الطرف المقابل بأن المحفظة ذاتية الاستضافة (حالياً يقدّم Didit Test VASP هذه الإجابة في بيئة الاختبار)، يضيف التأكيد المحفظة إلى دفتر عناوينك باسم المستفيد، ويُجري مطابقة الاسم: المطابقة الناجحة تُكمل التحويل وتعلّم المحفظة كمتحققة الملكية.
التحويلات من شبكة
يمكن لـ Didit أيضاً استلام تحويلات Travel Rule يرسلها طرف مقابل عبرGTR أو VERIFYVASP أو SYGNA.
يفعّل Didit الاستلام لكل شبكة على حدة، ولا توجد شبكة مفعّلة حالياً: يتطلب ذلك أيضاً تسجيل كل شبكة لنقاط endpoint الخاصة بـ Didit، وليس لدى Didit عضوية في هذه الشبكات بعد.
لا يمكن استلام تحويلات TRUST.
بمجرد تفعيل شبكة، تُعالج الرسالة التي تسلّمها كما يلي:
- التوجيه. تصل الرسالة إلى تطبيقك عندما تذكر رمز المشارك لعضويتك المتصلة في تلك الشبكة. لا تصل الرسالة الموجهة إلى عضوية Didit نفسها إلا إذا كنت عميل Didit الوحيد في بيئة الإنتاج الذي أثبت السيطرة على محفظة المستفيد. أي رسالة أخرى تُرفض، فلا يتلقى عميل أبداً تحويلاً لعميل آخر.
- القرار. ينشئ Didit التحويل الوارد في تطبيقك ويقرره كما يُقرَّر استفسار TRP: مطابقة المحفظة، والملكية، ومطابقة الأسماء، و
inbound_auto_accept. - مرة واحدة. تعيد الشبكات إرسال الرسالة حتى تُجاب. تحصل المحاولة المكررة على الإجابة التي حصلت عليها النسخة الأولى، ولا يُنشأ شيء أو يُقرَّر مرتين.
- مراجعتك، عبر
GTRوSYGNA. التحويل الذي ينتظرك (UNCONFIRMED_OWNERSHIPأوON_HOLDأوCOUNTERPARTY_MISMATCHED_DATA) يُقرَّر عبرPOST .../travel-rule/ownership/: يوافقconfirmed: trueعليه (مرّرwallet_addressعندما لا يذكر التحويل محفظة)، ويرفضهconfirmed: false. يرد Didit على الشبكة بعد الاستجابة، مع إعادة المحاولة نفسها. التحويل الذي أُجيب عليه بالفعل يُعيد409. - لا إجابة لاحقة عبر
VERIFYVASP. يأخذ VerifyVASP إجابته لحظة سؤاله، لذلك يُرفض التحويل الذي كان سيحتاج إلى مراجعتك فوراً، ولا يمكن تقريره لاحقاً (409).
بيانات الطرف المقابل المقنّعة
إلى حين تأكيد ملكية المحفظة، تعود البيانات الشخصية للطرف المقابل في التحويل الوارد مقنّعةً لا محذوفةً. يُبقى الحرف الأول من كل كلمة، وتظل المحفظة والسلسلة وتوجيه VASP مقروءةً. قيمةdata_masked هي true، ويُقنَّع الطرف المقابل في تفاصيل المعاملة وقائمتها وتصدير CSV بالطريقة نفسها.
بعد تأكيد الملكية تظهر القيم الحقيقية ويسجّل data_unmasked قيد التدقيق:
reason إحدى القيم التالية: ownership_confirmed وwallet_proof وaddress_book_match وown_verified_wallet وinquiry_approved (أي وافقت على استفسار TRP أو على رسالة GTR أو Sygna)، وknown_payment_method (ردّت محفظة المستخدم النهائي المسجّلة في سجلاتك)، أو travel_address (حدّد Travel Address الخاص بكل محفظة تلك المحفظة).
يحمل التحويل الوارد من نوع INTERNAL السجل data_unmasked بالقيمة reason: wallet_proof منذ لحظة إنشائه، لأن المستلم الذي أثبت ملكية المحفظة وحده يتلقاه.
الحقل actor هو البريد الإلكتروني لمستخدم وحدة التحكم أو تسمية مفتاح API، ويبقى فارغاً في القرار التلقائي.
يُقنّع تقريرُ سجل تحويلات Travel Rule (CSV وPDF) أسماءَ التحويل الوارد بالطريقة نفسها إلى حين تأكيد الملكية. أما التقارير التي تقدّمها إلى جهة تنظيمية، مثل ملف goAML XML وتقرير PDF التنظيمي، فتحمل البيانات الحقيقية للطرف المقابل.
ملاحظات عدم التطابق
يذكر تحويلُCOUNTERPARTY_MISMATCHED_DATA الآن الحقولَ المختلفة.
يحتوي data_match على المقارنة، ويسرد mismatched_fields الحقول.
يتضمن الحقلان أسماء الحقول والنتائج فقط دون القيم، فيبقيان مقروءين أثناء إخفاء البيانات:
TRP بعد عدم التطابق الـVASP المُرسِل بالحقول المختلفة، مثلاً «بيانات المستفيد لا تطابق صاحب المحفظة: الاسم».
ما يعرفه الـVASP الآخر محدودٌ عن قصد: ما دام الاسم خاطئاً، يكون الاسم الحقلَ الوحيد المُبلَّغ عنه (mismatched_fields: ["name"]، وتُعدّ كل الحقول الأخرى not_compared)، وبعد أن يتطابق الاسم تُذكر كل الحقول المختلفة. ينطبق هذا على جانب المُرسِل في تبادل INTERNAL وعلى رفض TRP.
يظهر الاسم الغائب من الطرفين بالقيمة not_compared ولا يُعدّ عدم تطابق، وتتطابق الدولة بحسب الرمز الذي يحدّدها، ولذلك تتفق ES وESP.
درجة مطابقة الاسم
يحمل كل تحويل قورن اسمُه الكائنَname_match، ويحمل إدخالُ الجدول الزمني الذي حسم النتيجة الكائنَ نفسه بجانب detail الخاص به:
scoreهي درجة التشابه بين الاسمين الكاملين، من 0 إلى 100، وتكون0إذا كان أيٌّ منهما فارغاً.matchedهي النتيجة عندstrictness، لذا قد تختلف النتيجتان: إذ يحكمFUZZYبنسبة تشابهه الخاصة البالغة0.85.matched_namesهي كلمات اسم التحويل الموجودة في الاسم الآخر، أماmismatched_namesفهي الكلمات التي يملكها أحد الطرفين ويفتقر إليها الآخر.methodهي إحدى القيمnoneأوexact_tokenأوtoken_subsetأوtoken_containmentأوsequence_ratio، وتقابلNONEوSTRICTوDEFAULTوCONTAINSوFUZZY.compared_withهيwallet_holder(صاحب المحفظة المسجّل لها) أوcounterparty_vasp(الاسم الذي ردّ به الطرف المقابل في صفحة الرد عبر البريد الإلكتروني، أو ردّ Didit Test VASP).- تتبع نتيجة التحويل
data_matchالذي يقارن كل الحقول، لذا قد يكون التحويلCOUNTERPARTY_MISMATCHED_DATAبينما تكون قيمةname_match.matchedهيtrue. - يوجد
name_matchفي كائن التحويل الذي تعيده endpoints الخاصة بـTravel Rule، وفي الحقلtravel_ruleالمضمَّن في المعاملة، وتكون قيمتهnullإلى أن يُقارَن اسم. لا تتضمنه قائمة التحويلات ولا الـwebhooks. - تُخفى الأسماء ما دام الطرفان مقنّعَين، وفي نسخة العميل الآخر من تبادل
INTERNAL.
الرسائل وطلبات المستندات
يمكن لـVASPين حلّ تحويل متنازع عليه داخل Didit بدلاً من البريد الإلكتروني. يعرضGET وPOST /v3/transactions/{transaction_id}/travel-rule/messages/ الرسائل على التحويل ويرسلانها:
- قيمة
kindهيMESSAGE(الافتراضية) أوDOCUMENT_REQUEST. يحتاج طلب المستند إلى عنصر واحد على الأقل فيrequested_documents(بحدّ أقصى 10)، وتحتاج الرسالة إلىbody(بحدّ أقصى 5000 حرف) أو ملف. - يشير
reply_toإلى رسالة سابقة من التحويل نفسه. يُلبّي الردُّ من الطرف الآخر الذي يحمل مستنداً طلبَ المستند الذي يجيب عنه، ويُضبط عليهfulfilled_at. ولا يُلبّي ردُّك أنت طلبَك أبداً. - أرسل الطلب بصيغة multipart مع
filesلإرفاق حتى 5 ملفات بحجم 10 MB لكل منها، بصيغ PDF أو PNG أو JPEG أو WebP. يفحص Didit محتوى كل ملف لا نوعه المُعلَن فقط، ويردّ أي ملف آخر بالرمز400علىfilesمع الرسالة «أرفق ملفات PDF أو PNG أو JPEG أو WebP فقط.» - يتسع التحويل لما يصل إلى 100 رسالة، منها 20 رسالة كحدّ أقصى تحمل مرفقات (كل منها حتى 5 ملفات)، مع احتساب الطرفين معاً؛ وتجاوُز أيٍّ من الحدّين يردّ الطلب بالرمز
400. لا تُحتسب مرفقات الرسائل ضمن مستندات المعاملة نفسها. - تعمل الرسائل عبر مساري
INTERNALوEMAIL. فيINTERNALيرى العميل الآخر في Didit الرسالة على جانبه من هذا التبادل. وفيEMAILيُرسل بريد إلكتروني إلى الطرف المقابل، فيردّ عبر بوابة الاستلام. وإن تعذّر إرسال بريد الإشعار، تُحفظ الرسالة على أي حال ويردّ الطلب بالرمز201. - يردّ الطلب بالرمز
409مع سبب فيdetailفي الحالات التالية: تحويل عبرTRPأوGTRأوTRUSTأوVerifyVASPأوSygna(هذه البروتوكولات لا تحمل رسائل)، وتحويل ليس له طرف مقابل بعد، وتحويلINTERNALلم تطابق بياناته صاحب المحفظة (فلم يصل شيء إلى المستلم)، وتحويلINTERNALلم يثبت فيه المستلم سيطرته على المحفظة بعد (على الطرفين إلى أن يثبت ذلك)، وتحويلEMAILلم يعد للطرف المقابل فيه عنوان بريد إلكتروني للامتثال، وتحويلEMAILانتهت صلاحية رابط استلامه أو أُغلق التحويل. - لا يُطلق أي webhook عندما يكتب الطرف المقابل رداً. اقرأ endpoint الرسائل.
- يُرسل البريد الذي يُعلم الطرف المقابل برسالة جديدة باللغة الإنجليزية.
دفتر عناوين المحافظ
سجّل المحافظ التي تتحكم فيها كي تتمكن التحويلات الواردة (مسارINTERNAL واستفسارات TRP الواردة والتسجيلات بعد الإيداع) من المطابقة معها، باستخدام POST/GET /v3/travel-rule/wallet-addresses/ وPATCH/DELETE /v3/travel-rule/wallet-addresses/{entry_uuid}/.
PATCH الحقول holder_name وholder_vendor_data وentity_type وtravel_address، ويُرجع هذه الحقول الأربعة فقط.
يحذف DELETE السجل (204)، فيصبح عنوانه متاحاً لإعادة التسجيل، ويتوقف فوراً عن مطابقة التحويلات الجديدة.
memo على شبكات memo، وتُحصر بـchain.
عندما يحمل التحويل سلسلة، لا يُطابِق السجل إلا إذا كانت chain الخاصة به القيمة نفسها (دون حساسية لحالة الأحرف) أو كانت فارغة.
تُقارن أسماء السلاسل كنص ولا تُترجم، فالسجل المخزَّن باسم ethereum لا يطابق تحويلاً سلسلته ETH.
تأخذ التحويلات الصادرة سلسلتها من beneficiary_data.chain، ثم من transaction_details.crypto_params.crypto_chain، ثم من custom_properties.crypto_chain، ثم من العملة (مثل ETH، أو بالنسبة لـUSDT وUSDC السلسلة المستنتجة من payment_reference_id)، ولا تُعتمد إلا إذا كانت سلسلة يتعرف عليها Didit. أما التسجيلات بعد الإيداع فتأخذها من حقل chain بأحرف صغيرة.
لا تأخذ استعلامات TRP الواردة السلسلة في الحسبان: فهي تطابق بالعنوان، أو، إذا لم يذكر الاستعلام محفظة، باسم المستفيد مقارنةً بسجلاتك المحققة.
تطابق POST .../travel-rule/ownership/ بالعنوان وحده أيضاً.
عندما تطابق عدة سجلات، يفضّل مسار INTERNAL والتسجيلات بعد الإيداع السجلَّ المثبت الملكية، ثم الأقدم، بينما يأخذ البحث الصادر عبر TRP أقدم سجل يخزّن عنوان Travel Address.
تأخذ استعلامات TRP الواردة وendpoint الملكية أيّاً من السجلات المطابقة، لذا احتفظ بسجل واحد لكل عنوان حيثما أمكن.
لا يطابق مسار INTERNAL أي سجل إطلاقاً عندما توجد سجلات محققة للعنوان في أكثر من تطبيق آخر لديك.استيراد المحافظ دفعةً واحدة
استورد حتى 10,000 محفظة في استدعاء واحد عبرPOST /v3/travel-rule/wallet-addresses/import/، بصيغة JSON ({"entries": [...]} أو مصفوفة مباشرة)، أو كملف CSV بترميز UTF-8 لا يتجاوز 5 MB ويحتوي على صف رأس.
يُعرّف الصف المحفظةَ بعنوانها address، أو بـaddress_hash (هاش SHA-256 للعنوان بعد تحويله إلى أحرف صغيرة، ويكون 64 حرفاً سداسي عشري أو 44 حرفاً بترميز base64)، أو بهما معاً، مع أي من حقول السجل أعلاه. أرسل الهاش وحده لتسجيل محفظة دون الكشف عن عنوانها: فهي تطابق التحويلات إلى ذلك العنوان كأي سجل آخر، ويمكن إثبات ملكيتها عبر أداة إثبات ملكية المحفظة.
created (أُنشئ)، وexisting (المحفظة والسلسلة والـ memo نفسها موجودة في الدفتر وتُركت دون تغيير)، وduplicate (يكرر صفاً سابقاً، ويُذكر في duplicate_of_row)، أو failed مع errors الخاصة به.
لا يُفشل صفٌّ سيئ الصفوفَ الأخرى: تكون الاستجابة 200 إذا لم يفشل أي صف، و207 إذا فشل صف واحد على الأقل، مع الأعداد في total وcreated وexisting وduplicate وfailed.
تُعد الخلية الفارغة في CSV غير مُرسلة، وتُتجاهل الأعمدة التي لا يقرؤها الاستيراد، فيُستورد ملف مُصدَّر من أداة أخرى كما هو.
في GET /v3/travel-rule/transfers/، يحمل كل صف originator_memo وbeneficiary_memo إلى جانب المحفظتين.
استلام البريد الإلكتروني للأطراف المقابلة غير القابلة للوصول
عندما لا يستطيع أي مسار بروتوكول الوصول إلى الطرف المقابل، يلجأ Didit إلىEMAIL: يرسل رابط استلام لمرة واحدة إلى beneficiary_data.counterparty_vasp_email (أو إلى بريد إلكتروني يُستخرج من beneficiary_data.counterparty_vasp_name بالبحث في سجل دليل VASP).
لا يحتاج المستلم إلى حساب Didit أو مفتاح API، فرمز الرابط هو وسيلة المصادقة.
وإليك كيفية إرسال الطلب:
- إلى العناوين الصحيحة.
counterparty_vasp_emailالذي تمرّره هو المستلم الوحيد. وإلا يكتب Didit إلى ثلاثة عناوين كحد أقصى: جهات اتصال الطرف المقابل التي لم تتعرض رسائلها للارتداد (أولاً جهة اتصالك، ثم جهات اتصال المنصة، ثم جهات الاتصال التي سبق أن ردّت، بهذا الترتيب)، ثم بريد الامتثال في سجل الدليل إذا بقيت خانة. يتلقى كل عنوان بريداً خاصاً به. - باسمك. يُرسل البريد من عنوان Didit باسم “اسمك القانوني عبر Didit”، بلغة الولاية القضائية للطرف المقابل (الإنجليزية إذا لم تكن معروفة)، ويفتح الرابط صفحة الرد بتلك اللغة. لا تُرسل لك نسخة، فتابع الجدول الزمني للتحويل.
- مع التذكيرات. إلى أن يرد الطرف المقابل، يذكّره Didit قبل الموعد النهائي بـ72 و24 و6 ساعات، بعد 12 ساعة على الأقل من الطلب أو من آخر تذكير، ولا يذكّره بعد الموعد النهائي أبداً.
- مع معالجة الارتدادات. يُعلِّم الارتداد أو شكوى البريد المزعج جهةَ الاتصال التي وصلها بأنها غير صالحة مع سببها، ويسجل الجدول الزمني للتحويل ذلك. فجهة اتصال المنصة تصبح غير صالحة لكل عملاء Didit، أما جهة اتصال تطبيقك فتكون غير صالحة لتطبيقك وحده. وعندما يتوقف تسليم بريد Didit إلى عنوان ما نهائياً (بعد ارتداد دائم أو شكوى بريد مزعج أو إلغاء اشتراك)، تُعلَّم كل جهات الاتصال على ذلك العنوان بأنها غير صالحة (
suppressed)، وتعود صالحة حين يُستأنف الإرسال إلى العنوان. وعندما لا يبقى عنوان يُكتب إليه، تنتقل حالة التحويل إلىCOUNTERPARTY_VASP_NOT_REACHABLEمع سبب خادم البريد المستلم في جدوله الزمني. ويؤدي الارتداد الدائم أو شكوى البريد المزعج علىcounterparty_vasp_emailالذي مررته إلى ذلك فوراً، لأنه المستلم الوحيد. أما الارتداد المؤقت (soft_bounce) عليه، أو على بريد الامتثال في سجل الدليل، فيُبقي الطلب مفتوحاً للتذكيرات.
reference هو uuid الخاص بالتحويل. يحتوي originator_data وحقل ivms_request المبني منه وفق معيار IVMS 101 على حقول المُرسِل التي تشاركها فقط في data_sharing_fields، أما الاسم والمحفظة فيُدرجان دائمًا. يكون fiat_amount وfiat_currency فارغين إذا لم ترسل قيمة محوّلة، ويعيد contact القيمة الموجودة في المعامل ?contact= الخاص بالرابط، ويسرد decline_reasons القيم التي يقبلها decline_reason. تكون قيمة join_invitation.eligible مساوية لـ true طالما أن الطرف المقابل لديه دعوة نشطة للانضمام إلى Didit، أما رابط الدعوة نفسه فيُرسل عبر البريد الإلكتروني فقط.
يُعيد endpoint هذا المبلغ المخزّن كما هو، فيتضمن جميع المنازل العشرية البالغ عددها 18، بدلًا من الصيغة المختصرة التي يعرضها كائن تفاصيل المعاملة.
decision هي accept أو decline.
عند accept، يُقارَن beneficiary_name المُرسَل مع beneficiary_data.name الخاص بالتحويل وفق قاعدة الأسماء لديك: تكون النتيجة COMPLETED عند التطابق، وCOUNTERPARTY_MISMATCHED_DATA بخلاف ذلك. يمكن للطرف المقابل أن يرسل استجابة IVMS 101 كاملة في ivms_response بدلًا من ذلك، فيكون اسم المستفيد الوارد فيها هو المعتمد بدلًا من beneficiary_name.
عند decline، ينتقل التحويل إلى COUNTERPARTY_VASP_GENERAL_DECLINE. يمكن للطرف المقابل أن يذكر decline_reason من بين decline_reasons، مع decline_detail (بحد أقصى 512 حرفًا، ويكون مطلوبًا عند القيمة other)، ويسجّل الجدول الزمني للتحويل ذلك، مثلًا: counterparty declined: The beneficiary is not our customer.
تكون الاستجابة هي {"status": "..."} الناتجة، مع decline_reason بعد الرفض. وبعد أن يرد الطرف المقابل، تُعيد كلتا النقطتين الاستجابة 410. وتُعيد النقطتان الاستجابة 404 عند استخدام رمز غير معروف، والاستجابة 410 بعد إغلاق النافذة، أي عندما يغادر التحويل الحالة AWAITING_COUNTERPARTY، أو عندما تنتهي مهلة التأكيد (confirmation_timeout_hours بعد الإنشاء أو بعد آخر إعادة إرسال).
يقرأ الطرف المقابل ويجيب عبر رابط الاستلام باستخدام GET وPOST /v3/travel-rule/pickup/{token}/messages/. لا يحتاج إلى مفتاح API، ويقبل مرفقات بصيغة PDF أو PNG أو JPEG أو WebP فقط، ويُعيد الاستجابة 410 بعد انتهاء صلاحية الرابط أو إغلاق التحويل.
وتُظهر الرسالة التي أرسلتها اسم VASP الخاص بك كقيمة لـauthor وكقيمة لـuploaded_by في المرفق، ولا تُظهر أبدًا الشخص أو مفتاح API الذي يقف وراءها.
الإيداعات غير المطابقة
قد يصل إيداع وارد قبل وصول بيانات Travel Rule الخاصة بـ VASP المُرسِل. يبقى هذا الإيداع فيAWAITING_COUNTERPARTY أو NOT_ENOUGH_COUNTERPARTY_DATA إلى أن تصل البيانات.
اسرد الإيداعات التي ما زالت تنتظر باستخدام مفتاح API يملك صلاحية المعاملات:
GET /v3/travel-rule/transfers/، مقصورةً على الإيداعات الواردة التي تنتظر بيانات VASP المُرسِل.
عندما تصل البيانات في رسالة مستلمة منفصلة، فاقرن بين الاثنين بنفسك:
{transaction_uuid} هو الإيداع.
transaction_id هو المعاملة الخاصة برسالة Travel Rule المستلمة.
يعيد الاستدعاء { "deposit": ..., "message": ... }.
تُعيد الاستجابة 404 إذا لم تعد أي من المعاملتين موجودة، وتُعيد 409 مع travel_rule_match_conflict (“This deposit can no longer be matched.”) إذا كان أحد الطرفين قد انتقل إلى حالة أخرى.
التسجيل اللاحق للإيداع
عندما تُسوّى عملية إيداع للعملات الرقمية على السلسلة قبل أن تجري أي عملية تبادل لـ Travel Rule، سواء لأن VASP المُرسِل كان غير قابل للوصول، أو لأنك تُسجّل الإيداعات التاريخية بأثر رجعي، فسجّلها لاحقًا باستخدامPOST /v3/travel-rule/inbound/.
originating_vasp.travel_address عنوان Travel Address حقيقيًا وصحيح المجموع الاختباري، فإن كان عنصرًا نائبًا أعادت النقطة الاستجابة 400 مع "Invalid travel address.".
يجب أن يكون originating_vasp.lei فارغًا أو مكوّنًا من 20 حرفًا بصيغة LEI (18 حرفًا أو رقمًا، يليها رقمان)، وأي قيمة أخرى تُعيد الاستجابة 400 مع "Invalid LEI: expected 20 alphanumeric characters (ISO 17442).".
تتحقق Didit من هذه الصيغة فقط، لا من أرقام التحقق في ISO 17442.
travelRule واردة (أو يعيد استخدام إيداع وارد أرسلته سابقاً بالتجزئة نفسها والمحفظة نفسها، إذا لم يكن له تحويل Travel Rule بعد) ويعالج التحويل بالرجوع إلى دفتر عناوين محافظك، بحسب العنوان والملاحظة (memo) الخاصة به:
- لا يوجد إدخال مطابق ->
ON_HOLD. احلّه باستخدامPOST .../travel-rule/ownership/: تؤدي قيمةconfirmed: trueإلى إضافة المحفظة إلى دفتر عناوينك كعنوان متحقَّق منه، ويُجري مطابقة الاسم. عند التطابق ينتقل التحويل إلىCOMPLETED، ثم إلىFINISHED. - يوجد إدخال لكن ملكية العنوان غير مُثبتة ->
UNCONFIRMED_OWNERSHIP. - لا يطابق اسم المستفيد قيمة
holder_nameفي الإدخال ->COUNTERPARTY_MISMATCHED_DATA. - إدخال متحقَّق منه والاسم مطابق ->
COMPLETED، ثمFINISHEDفوراً (الإيداع مُسوّى بالفعل).
txn_id للمعاملة الجديدة هو tr-in-، وأول 80 حرفاً من txid، وتجزئة من 12 حرفاً لـtxid والمحفظة.
تكون قيمة created هي true (HTTP 201) عندما يسجّل هذا الاستدعاء الإيداع، وتكون false (HTTP 200) عندما يكون txid وwallet_address نفسهما مسجَّلين من قبل، وفي هذه الحالة يُعاد التحويل الموجود دون تغيير.
في مسار الإدخال المتحقَّق منه يحدث الانتقالان قبل أن ترد نقطة النهاية، لذا تُظهر الاستجابة المتزامنة الحالة النهائية FINISHED مباشرة، أما خطوة COMPLETED الوسيطة فما تزال مسجّلة في timeline الخاص بالتحويل.
يُطلق كل انتقال حدث webhook باسم travel_rule.status.updated (ويُطلق كلٌّ من COMPLETED وFINISHED حدثاً واحداً).
عندما ينشئ Didit معاملة جديدة، يرسل أولاً transaction.created ثم هذه الأحداث بعده.
لا تترتب على التسجيل أي رسوم لـ Travel Rule أو للمراقبة.
عند تفعيل مراقبة العملات المشفّرة، وكانت chain من السلاسل التي يستطيع Didit فحصها، يُفحص الإيداع الجديد المنشأ أيضاً ويُحتسب مرة واحدة بسعر Wallet Verification & Screening.
أداة ملكية المحفظة
لنقل تحويل منUNCONFIRMED_OWNERSHIP، أو للتحقق مسبقاً من محفظة قبل وصول أي تحويل، أنشئ أداة إثبات ملكية المحفظة المستضافة عبر POST /v3/travel-rule/widget-session/ ووجّه عميلك إلى url المُعاد.
تُثبت الأداة سيطرة العميل على المحفظة عبر توقيع الرسائل (بما فيها حسابات العقود الذكية في EVM من خلال ERC-1271 وERC-6492)، أو اختبار Satoshi، أو لقطة شاشة، أو الإقرار الذاتي، ثم تُعلّم إدخال دفتر العناوين المطابق بأنه متحقَّق منه حتى تتمكن البورصة من المتابعة.
لقطة الشاشة هي الاستثناء: فهي لا تُثبت ملكية المحفظة بمفردها أبداً، لذا يراجعها فريق الامتثال لديك ويؤكد التحويل في UNCONFIRMED_OWNERSHIP باستخدام POST .../travel-rule/ownership/.
راجع أداة ملكية المحفظة للاطلاع على التدفق الكامل وطرق الإثبات، والملاحظة المهمة بأن الأداة يجب أن تُفتح في متصفح حقيقي، وليس أبداً داخل عرض الويب المضمّن في SDK الجوال.
الدليل
GET /v3/travel-rule/vasps/ يبحث في دليل VASP، وهو يضم إدخالات الدليل المشتركة إلى جانب أعضاء شبكة Didit. يُظهر كل صف الطريقة التي يمكن لتطبيقك بها الوصول إلى ذلك VASP اليوم.
search جزءاً من اسم أو اسم مستعار، أو رقم LEI دقيقاً، أو نطاقاً (counterparty.example أو عنواناً URL ضمنه). لتضييق النتائج استخدم:
risk وresponsiveness إدخالات الدليل فقط. تُرتَّب النتائج حسب الاسم، ويُقسَّم الناتج إلى صفحات بالمعاملين limit (القيمة الافتراضية 50) وoffset. أي شبكة أو رمز دولة أو فئة مخاطر أو حالة استجابة غير معروفة يُعيد الرد 400 مع ذكر الحقل المعني. يعيد GET /v3/travel-rule/vasps/{vasp_id}/ صفاً واحداً بحسب id الخاص به. ولا يظهر ملف VASP الخاص بك أبداً ضمن نتائجك.
تحمل الحقول dd_status وdd_score وdd_score_breakdown وdd_assessed_at وdd_score_semantic وrisk_level وresponsiveness وessential_checks التقييم المشترك للعناية الواجبة لإدخال في الدليل. أما decision وentity_screening وquestionnaires فهي سجلات تطبيقك الخاصة عن الـVASP، ولا يطّلع عليها أي عميل آخر. راجع العناية الواجبة للطرف المقابل لمعرفة معنى كل منها، واعتمد عليها عبر حقول قاعدة الطرف المقابل بدلاً من التفرع عليها مباشرة في الكود. أعضاء شبكة Didit غير مُقيَّمين: إذ تكون قيمة dd_status هي COMPLETED، وdd_score هي null، وrisk_level فارغة، والتفصيل فارغ.
المسارات
يسردroutes الطرق التي يستطيع تطبيقك بها التسليم إلى الـVASP، وفق الترتيب الذي يجربه سلم المسارات، ويكون reachable بقيمة true عندما يكون مسار واحد على الأقل available:
TRPعندما يحمل الإدخال endpoint لـ TRP تحقق منه الدليل.GTRأوTRUSTأوVERIFYVASPأوSYGNAعندما تُدرج شبكة الـVASP تحت رمز. لا يكون المسارavailableإلا إذا كان بوسع تطبيقك الإرسال عبر تلك الشبكة اليوم (via: own_membershipلعضويتك المتصلة، وvia: didit_membershipلعضوية Didit). وإلا يكونunavailableمعreason: no_membershipأوreason: network_switched_off، ولا يُعرض أبداً كطريقة للوصول إلى الـVASP. عضويات Didit في شبكاتها غير مفعّلة بعد، لذا فإن مسار الشبكة لا يكون متاحاً اليوم إلا عبر عضوية ربطتها أنت في Marketplace.EMAILعندما توجد جهة اتصال للامتثال للإدخال.INTERNALلعضو في شبكة Didit.
TRISA ضمن networks ويخبرك بهوية الـVASP على TRISA، لكن Didit لا تملك قناة TRISA، لذا لا يكون أبداً مساراً.
لا يتاح أي مسار من الدليل لتطبيق في البيئة التجريبية، لأنه يرسل إلى Didit Test VASP فقط. لذلك يكون كل مسار unavailable مع reason: sandbox_application، ويكون reachable: false في كل صف، ويعيد reachable=true صفر صفوف.
العلامات التجارية وكياناتها
العلامة التجارية التي تعمل عبر عدة كيانات قانونية تُمثَّل بصف واحد، وتكونentities فيه هي الكيانات القانونية التي تستقبل تحويلاتها. ويشير الكيان إلى علامته التجارية في umbrella. يُرسَل التحويل الذي يسمّي العلامة التجارية إلى أحد كياناتها، ويُختار من بينها جميعاً: الكيان الموجود في الولاية القضائية المذكورة في beneficiary_data.counterparty_vasp_jurisdiction، وإلا فالكيان الذي ربطه إسناد تحليلات البلوكتشين بالمحفظة المستلمة، وإلا فالكيان الوحيد للعلامة التجارية عندما لا تُذكر أي ولاية قضائية. وعندئذ فقط يجب أن يكون ذلك الكيان قابلاً للوصول؛ فإذا لم يكن كذلك، لا يُرسَل التحويل أبداً إلى كيان آخر ولا إلى العلامة التجارية نفسها. وعندما لا يناسب أي كيان (إذا تساوى اثنان في الملاءمة، أو كانت الولاية القضائية المذكورة لا تنتمي إلى أي منها)، يُستخدم مسار العلامة التجارية نفسها، وإن لم يوجد لها مسار فيكون التحويل COUNTERPARTY_VASP_NOT_FOUND. ولا يُترك تحديد الكيان القانوني الذي يستقبل بيانات المُرسِل للتخمين أبداً، ولا يُحدَّد بحسب الكيان الذي يتوفر له جهة اتصال.
تُميَّز الإدخالات المتعددة التي تحمل الاسم نفسه، مثل شركات المجموعة التي تشترك في اسم تجاري واحد، بالقاعدة ذاتها: الإدخال الموجود في الولاية القضائية المذكورة، وإلا الإدخال الذي ربطه إسناد تحليلات البلوكتشين. وإذا لم تحدد أي من القاعدتين إدخالاً واحداً، فلا يُستخدم أي إدخال، وتُحسب التفاصيل كأنها لا تسمّي أي إدخال في الدليل.
جهات الاتصال
contacts هي العناوين التي يكتب إليها مسار EMAIL. يخدم scope: platform كل عملاء Didit: إذ تنتقيها Didit (source: staff)، أو يجيب عنوان على نطاق VASP الخاص بالجهة عبر رابط الرد الخاص به (source: learned). أما scope: application فهي خاصة بتطبيقك: وهي التي تضيفها أنت (source: application)، وعنوان خارج نطاقات الـVASP الخاصة أجاب على أحد طلباتك (source: learned). يرسل التحويل بريداً إلكترونياً أولاً إلى جهة الاتصال الخاصة بتطبيقك، ثم إلى جهة اتصال من المنصة سبق أن أجابت (مع تعيين verified_at)، ثم إلى الباقي.
أدِر جهات الاتصال الخاصة بتطبيقك عبر واجهة برمجة جهات اتصال الطرف المقابل:
- يسرد
GET /v3/travel-rule/counterparty-contacts/كل جهة اتصال يحق لتطبيقك الإرسال إليها، بما فيها جهات اتصال المنصة وتلك التي ارتدت، وترتيبها من الأحدث إلى الأقدم، ويُقسَّم الناتج إلى صفحات بالمعاملينlimitوoffset. صفِّ النتائج بـ?counterparty=ومعرّف إدخال الدليلid؛ والقيمة التي ليست UUID تُعيد400. - يضيف
POSTجهة اتصال خاصة (counterpartyوemailواختيارياًname) ويعيد201. وإضافة عنوان يملكه الإدخال بالفعل ضمن جهات اتصالك الخاصة تُعيد جهة الاتصال تلك. ويُعيد إدخال غير معروف الرد400("No directory entry with this id."). - يحذف
DELETE /v3/travel-rule/counterparty-contacts/{contact_uuid}/إحدى جهات اتصالك الخاصة (204). ولا يمكن حذف جهات اتصال المنصة، فتُعيد404.
is_invalid: true، مع invalid_reason (hard_bounce أو soft_bounce أو spam_complaint أو suppressed) وinvalid_detail وinvalid_at، ويتخطاها مسار EMAIL. يكون created_by فارغاً في جهات اتصال المنصة وفي جهات الاتصال التي أضافها مفتاح API.
كيف تدخل الإدخالات إلى الدليل
- سجل ESMA المؤقت لمقدّمي خدمات الأصول المشفّرة بموجب MiCA، ويُقرأ كل أسبوع: اسم كل مزوّد وLEI الخاص به والدولة العضو الأصلية لمقدّم الخدمة.
- دليل TRISA العام، ويُقرأ كل أسبوع: هوية كل عضو وبلده وإدراجه في TRISA. يُحذف الإدراج الذي يتوقف الدليل عن عرضه لمدة ثلاثة أسابيع.
- سجل LEI العالمي يملأ LEI والأسماء القانونية الأخرى وبلد الإدخال، ويعثر على LEI الخاص بالإدخال عبر اسمه القانوني الدقيق في بلده.
- إسناد تحليلات البلوكتشين يضيف VASP الذي يملك محفظة الوجهة أو يطابقه، ويحتفظ بالاسم المنسوب كاسم بديل.
- يتولى فريق Didit تنظيم الإدخالات: معرّفات التوجيه التي تحقّقوا منها وإدراجات الشبكات التي تحقّقوا منها، وجهات اتصال الامتثال، والأسماء البديلة، والعلامات التجارية وكياناتها، والإدخالات المكررة المدموجة، والإدخالات المخفية.
originating_vasp الوارد أو Travel Address، تبقى على معاملة ذلك العميل ولا تغيّر أي إدخال مشترك أبداً. تُطابَق الإدخالات أولاً بواسطة LEI، ثم بالاسم الدقيق أو الاسم البديل، وتُثرى ولا تُكرَّر.
شبكة Didit
ملفك الشخصي كـ VASP عضو في شبكة Didit، فيجده عملاء Didit الآخرون في الدليل وفي منتقي VASP الخاص بالمستخدم النهائي، فقط عندما تتحقق كل الشروط التالية:- تطبيقك في بيئة الإنتاج (وليس البيئة التجريبية) وقاعدة Travel Rule مفعّلة؛
is_discoverableقيمتهtrueوlegal_nameمضبوط؛compliance_emailمضبوط ومؤكَّد: أرسل التأكيد باستخدامPOST /v3/travel-rule/settings/compliance-email/confirmation/وافتح الرابط المرسل إلى بريدك. يتطلب تغيير العنوان تأكيداً جديداً؛- لم تُعلّم Didit التطبيق كتطبيق اختبار أو تجريبي أو داخلي.
INTERNAL نفسه على محفظة أثبتّ ملكيتها، كما هو موضح في الإرسال عبر التحويل: يحتاج إلى أن يكون تطبيقك في بيئة الإنتاج وأن تكون Travel Rule مفعّلة وis_discoverable، لا بقية القائمة.
يعيد GET /v3/travel-rule/settings/ الحقل didit_network الذي يتضمن listed وcompliance_email_confirmed، وفي missing كل شرط ما زال غير محقَّق (travel_rule_off، sandbox_application، not_discoverable، legal_name_missing، compliance_email_missing، compliance_email_unconfirmed، excluded_by_staff، internal_organization).
معرّفات التوجيه
يستعلم التوجيه الصادر إدخالاً في الدليل كلما سمّى تحويلك الطرف المقابل عبرcounterparty_vasp_lei أو counterparty_vasp_name (والاسم البديل يعمل أيضاً). يحمل الإدخال معرّفاً واحداً لكل شبكة يمكن الوصول إلى الإدخال عبرها، وendpoint محقَّقاً لـ TRP وجهات اتصال الامتثال الخاصة به، ويبيّن routes أيّاً منها يمكن لتطبيقك استخدامه. يمكنك دائماً تجاوز الدليل في تحويل واحد بتمرير الحقول الصريحة الموضحة في حقول تحديد الطرف المقابل.
العناية الواجبة تجاه الطرف المقابل
قبل أن تصل أموال عملائك إلى VASP آخر، عليك أن تعرف من هو هذا الـ VASP، وما إذا كنت تقبله. تتكون العناية الواجبة تجاه الطرف المقابل من طبقتين:- التقييم المشترك. يحمل كل إدخال في الدليل درجة العناية الواجبة ومستوى مخاطره وحالة استجابته وتقرير الفحوصات الأساسية. يحسب Didit هذه القيم من بيانات المنصة نفسها، فيرى كل عميل القيم ذاتها.
- سجلاتك الخاصة. قرارك بشأن الـ VASP، وفحص العقوبات والأخبار السلبية الذي تجريه عليه، واستبيانات العناية الواجبة التي تبادلتها معه تخص تطبيقك وحده. لا يراها عميل آخر، ولا تغيّر الإدخال المشترك أبداً.
id الذي يعرضه الدليل للإشارة إلى أيٍّ منهما.
الدرجة ومستوى المخاطر
يتراوحdd_score من 0 إلى 100، وكلما ارتفعت الدرجة، زاد الأمان. تذكر كل نتيجة ذلك في dd_score_semantic، وقيمته دائماً higher_is_safer. أما risk_level فهو النطاق الذي تقع فيه الدرجة:
dd_score_breakdown كل مكوّن بالشكل {points, max, reason, detail}: إذ تمثل max وزن المكوّن، وreason رمزاً قابلاً للقراءة الآلية يمكنك ترجمته، وdetail شرحاً إنجليزياً من سطر واحد.
history الطلبات نفسها التي يقرأها مدى الاستجابة، دون نافذة زمنية: تُعد عملية COMPLETED أو FINISHED نجاحاً، وتُعد EXPIRED أو COUNTERPARTY_MISMATCHED_DATA أو COUNTERPARTY_VASP_GENERAL_DECLINE فشلاً. ولا يُحتسب الإلغاء أبداً، لأنه فعل صادر عن المرسل نفسه.
يعيد Didit احتساب التقييم عندما تكتمل عملية تبادل مع الإدخال أو تنتهي أو تنتهي مهلتها أو يحدث فيها عدم تطابق في البيانات أو يتم رفضها، وعندما يحدّث الإدخال أو يتولى تنظيمه، وفي كل إدخال ضمن التحديث الأسبوعي للدليل. يبيّن dd_assessed_at موعد آخر تشغيل له. أما الإدخال الذي لم يُقيَّم بعد فلا يملك dd_assessed_at، ودرجته dd_score تساوي 0، ومستوى مخاطره risk_level فارغ، لذا اقرأ risk_level ولا تعتمد على الدرجة الخام. لا تُقيَّم أعضاء شبكة Didit: فدرجتهم dd_score قيمتها null، ومستوى مخاطرهم risk_level فارغ، وdd_score_breakdown فارغ.
الفحوصات الأساسية
essential_checks تقرير موجز عن أربعة فحوصات يجريها Didit على كل إدخال في الدليل، من المصادر التي تحققت منها المنصة فقط، ولا يُعتمد أبداً على بيانات أبلغ عنها العميل:
checks حالته status (pass أو fail أو unknown عندما لا يستطيع أي مصدر مسجل حسمه)، وreason رمزاً قابلاً للقراءة الآلية، وdetail شرحاً إنجليزياً من سطر واحد. تحسب passed وfailed وunknown وtotal عدد الفحوصات، وlevel قيمته essential، وchecked_at يبيّن وقت بناء التقرير. يحتسب مكوّن essentials نسبة الفحوصات المحسومة التي تجتاز، فلا يُحتسب الفحص unknown لا له ولا عليه. ويبقى التقرير فارغاً حتى يفحص أول تحديث أسبوعي للدليل الإدخال، ويبقى فارغاً دائماً لعضو في شبكة Didit.
مدى الاستجابة
responsiveness يبيّن ما إذا كان الـ VASP يردّ على طلبات Travel Rule:
total الطلبات التي بدأها عملاء Didit نحو الـ VASP خلال آخر 90 يوماً، والتي حُسمت نتيجتها، ويحسب answered الطلبات التي ردّ عليها:
- تُحتسب: الطلبات المرسلة عبر شبكة أو مسار بروتوكول، والطلبات المرسلة بالبريد الإلكتروني إلى عنوان مشترك، وهو عنوان الامتثال في الإدخال أو عنوان جهة اتصال للمنصة.
- لا تُحتسب أبداً: طلب أُرسل بالبريد الإلكتروني إلى عنوان كتبه عميل واحد، أو إلى عنوان جهة اتصال خاصة بالعميل. تبقى هذه العملية في سجل تحويلات العميل وحده، ولا تصنّف الـ VASP لدى الآخرين أبداً.
- تُعد ردّاً:
COMPLETEDأوFINISHEDأوCOUNTERPARTY_VASP_GENERAL_DECLINEأوCOUNTERPARTY_MISMATCHED_DATA. فالرفض أو عدم تطابق البيانات ردّ، لأن الـ VASP تفاعل مع الطلب. - بلا ردّ:
EXPIRED. - خارج الحساب: كل حالة أخرى. فالطلب
CANCELLEDفعل صادر عن المرسل نفسه، وCOUNTERPARTY_VASP_NOT_REACHABLEقد يكون إخفاقاً من جهة المرسل، والطلب الذي ما زال قيد التنفيذ لم يُحسم بعد.
UNKNOWN مع أعداد صفرية. تصرّف تجاه طرف مقابل غير مستجيب عبر سياسة عدم الاستجابة أو عبر الإعداد المسبق travel-rule-counterparty-unresponsive.
قرارك بشأن الطرف المقابل
سجّل قرار فريق الامتثال لديك بشأن طرف مقابل باستخدامPUT /v3/travel-rule/vasps/{vasp_id}/decision/:
- القرار خاص بتطبيقك: لا يراه عميل آخر، ولا يغيّر الإدخال المشترك أبداً.
- لا يُحدث القرار أي تغيير بمفرده، ولا يغيّر أبداً طريقة توجيه التحويل. يصل إلى معاملاتك عبر حقل القاعدة
counterparty_dd_status: ثبّت الإعدادات المسبقة، أو اكتب قاعدتك الخاصة التي تعتمد عليه. - ينطبق القرار على العلامة التجارية على كل كيان تابع لها ليس له قرار خاص به، ويُظهر الكيان العلامة التجارية في
inherited_from. ويتجاوز قرار الكيان قرار العلامة التجارية لذلك الكيان. - يعيد
GETالقرار المنطبق، أو{"decision": null}. ويمسحDELETEقرارك الخاص ويعيد ما ينطبق بعد ذلك: قرار العلامة التجارية للكيان، وإلاnull. ويعيد404عندما لا يكون لتطبيقك قرار خاص به على الطرف المقابل. reasonاختياري، ويصل إلى 255 حرفاً، وكلPUTيستبدله. وdecided_byهو مستخدم وحدة التحكم الذي اتخذ القرار أو عدّله آخر مرة، ويكون فارغاً عندما يتم ذلك عبر مفتاح API.
فحص الطرف المقابل
يتحقق فحص الطرف المقابل من VASP الطرف المقابل نفسه، بصفته شركة، مقابل العقوبات والأخبار السلبية. وهو اختياري وخاضع للفوترة: فعّله بالمعاملcounterparty_screening_enabled في الإعدادات. وما دام متوقفاً، لا يُفحص شيء ولا يُحتسب شيء.
- الفوترة. يكلّف كل فحص جديد مؤسستك وفق سعر فحص AML للشركات. يعيد
GET /v3/travel-rule/vasps/{vasp_id}/screening/هذاpriceوfresh_daysوما إذا كان الفحصenabled، دون أي رسوم. الفحص الذي عمره أقل من 30 يوماً يُعدّ حديثاً: يعيدهPOSTمرة أخرى بالحالة200وoutcome: reusedدون أن يُحتسب شيء، إلا إذا أرسلتrefresh: true. ويجيب الفحص الجديد بالحالة201معoutcome: screened. لا تخضع تطبيقات البيئة التجريبية للفحص أو الفوترة أبداً. - الفحص التلقائي. عند تفعيل الفحص، يفحص Didit أيضاً الطرف المقابل الذي يصل إليه التبادل الصادر عند بدئه، قبل أن تقيّم قواعدك المعاملة، بنفس إعادة الاستخدام والفوترة. ولا يؤدي الرفض أو الفشل هنا أبداً إلى فشل التحويل.
- النتيجة. تكون
statusبقيمةApprovedعندما لا يحتاج أي تطابق محتمل إلى اهتمام، وبقيمةIn ReviewأوDeclinedعند وجود تطابق محتمل مع العقوبات أو الأخبار السلبية. وتكونflaggedبقيمةtrueطالما كان الفحص الحديث بحالةIn ReviewأوDeclined. - الأثر. طالما كان آخر فحص لديك للطرف المقابل مُعلَّماً والفحص مفعّلاً، تكون
counterparty_risk_levelبقيمةHIGHفي قواعدك، فيحتجز الإعداد المسبقtravel-rule-counterparty-high-risk، بعد تثبيته، معاملاته. ولا يتغيرrisk_levelالمشترك في الدليل. - الخصوصية. الفحص خاص بتطبيقك: لا يراه عميل آخر، ولا يغيّر الإدخال المشترك أبداً. ويعرض الدليل آخر فحص لديك باسم
entity_screening. - الرفض. يعيد
409عندما يكون الفحص متوقفاً، أو عندما يكون التطبيق في البيئة التجريبية، أو أثناء تشغيل فحص آخر للطرف المقابل نفسه. ويعيد403معcode: insufficient_creditsعندما لا يغطي رصيدك السعر. ويعيد502عندما يتعذر إكمال الفحص. ولا تُفرض رسوم على أيٍّ منها.
استبيان العناية الواجبة
اطلب من الطرف المقابل إجاباته عن العناية الواجبة عبر استبيان Didit القياسي. يرسلPOST /v3/travel-rule/vasps/{vasp_id}/questionnaires/ الاستبيان بالبريد الإلكتروني إلى عنوان امتثال مسجّل للطرف المقابل، مع رابط لمرة واحدة إلى صفحة الإجابة:
- الوجهة. يجب أن تكون
sent_toعنوان امتثال مسجّلاً: بالنسبة إلى إدخال في الدليل، عنوان امتثاله أو جهة اتصال يراها تطبيقك فيcontacts؛ وبالنسبة إلى عضو في شبكة Didit، بريده الإلكتروني للامتثال المؤكد. لا تُدرجها لاستخدام العنوان الذي يُرسل إليه بريد Travel Rule إلى الطرف المقابل: جهة اتصالك أولاً، ثم جهة اتصال المنصة، ثم عنوان الامتثال للإدخال. ولا يرسل Didit الاستبيان إلى أي جهة أخرى أبداً، ويعيد400عندما لا يكون لدى الطرف المقابل عنوان امتثال مسجّل. - الإجابة. يفتح الرابط
/v3/travel-rule/questionnaires/{token}/، وهي صفحة موجهة لشخص لدى الطرف المقابل، لا يحتاج إلى حساب أو مفتاح API. يجيب عن كل سؤال، بما لا يتجاوز 2,000 حرف لكل منها (N/Aحيث لا ينطبق السؤال)، ويرسل مرة واحدة. ويمكن للطرف المقابل أيضاً أن يرسل{"answers": {"<question id>": "<answer>"}}إلى الرابط نفسه. ويعرض الرابط الذي استُخدم أو استُبدل أو تجاوز موعده صفحة انتهاء صلاحية بدلاً من ذلك. يُترجم البريد والصفحة، أما الأسئلة نفسها فتبقى دائماً بالإنجليزية، حتى لا يتغير معناها بين اللغات. - المواعيد. لدى الطرف المقابل 30 يوماً للرد (
expires_at). تبقى الإجابات سارية لمدة 365 يوماً (valid_until)، ويكونcurrentبقيمةtrueحتى ذلك الحين. ويصبح الاستبيان غير المُجاب عنه بعد موعده، وتصبح الإجابات بعد انتهاء صلاحيتها، بحالةEXPIRED. - استبيان مفتوح واحد لكل طرف مقابل. يحل الإرسال الجديد محل استبيانك السابق غير المُجاب عنه إلى الطرف المقابل نفسه: يصبح بحالة
EXPIREDويتوقف رابطه عن العمل. ولا يُرسل أكثر من بريد واحد في الدقيقة إلى العنوان نفسه، ويعيد الإرسال الأسرع429. - قراءة الإجابات. يسرد
GET /v3/travel-rule/vasps/{vasp_id}/questionnaires/استبياناتك مع الطرف المقابل، من الأحدث إلى الأقدم، معanswersمفهرسة بمعرّف السؤال بعد الإجابة. ويعرضها الدليل باسمquestionnaires. وهي خاصة بتطبيقك.
1 من النموذج هذه الأسئلة الاثني عشر:
المعاملات مع طرف مقابل واحد
يسردGET /v3/travel-rule/transfers/?counterparty={vasp_id} تحويلات Travel Rule الخاصة بتطبيقك مع طرف مقابل واحد، مرتبةً من الأحدث إلى الأقدم، وذلك بحسب id الذي يعرضه الدليل: تحويلاتك التي يكون طرفها المقابل إدخال الدليل ذاك، أو عمليات التبادل INTERNAL التي تمّت مع عضو شبكة Didit ذاك. القيمة التي ليست معرّفاً صالحاً تُعيد الاستجابة 400.
يحمل كل صف الحقل reason مباشرةً بعد status: وهو reason الخاص بالتحويل نفسه، بحيث توضّح القائمة أن بريد الامتثال ارتدّ بسبب عدم وجود مسار لدى الطرف المقابل، دون فتح كل تحويل على حدة.
سياسات Sunrise وعدم الاستجابة
عندما ينتهي تحويل (أي تجاوزconfirmation_timeout_hours وهو في حالة AWAITING_COUNTERPARTY)، يحدد timeout_outcome ما يحدث لمعاملته. يتيح لك إعدادان معاملة سببين شائعين لانتهاء المهلة بشكل مختلف:
HOLD أو REJECT أو PROCEED، بالمعنى نفسه المستخدم في timeout_outcome، أو سلسلة نصية فارغة لاتباع timeout_outcome. وكلاهما فارغ افتراضياً، لذا لا يتغير شيء حتى تضبط أحدهما:
sunrise_policy، إذا كان مضبوطاً ودولة الطرف المقابل مدرجة فيsunrise_jurisdictions.non_responsive_policy، إذا كان مضبوطاً والطرف المقابلUNRESPONSIVE.timeout_outcomeفي كل الحالات الأخرى، بما في ذلك التحويل إلى عضو شبكة Didit أو إلى طرف مقابل لا يملك إدخالاً في الدليل.
sunrise_jurisdictions للقراءة فقط، وقيمتها الحالية ["CO", "KH", "VN"]. تُحسب هذه الدول وحدها ضمن فترة Sunrise: أي دولة أخرى، وكذلك الولاية القضائية الفارغة أو غير المعروفة، تتبع timeout_outcome، حتى لا يحصل طرف مقابل على سياسة Sunrise الخاصة بك بالخطأ. يذكر الجدول الزمني للتحويل المنتهي النتيجة التي طُبّقت، مثل sunrise_policy=PROCEED (counterparty jurisdiction CO does not enforce the Travel Rule yet). تستمر قواعدك في العمل بعد انتهاء المهلة، لذا قد تنقل قاعدة مثل tr_eu_expired_review من حزمة EU TFR المعاملة إلى المراجعة تحت PROCEED.
حقول القواعد والإعدادات المسبقة
تحمل أربعة حقول من حقول القاعدة ما يعرفه تطبيقك عن الطرف المقابل الذي وصل إليه تبادل Travel Rule الخاص بالمعاملة: إدخاله في الدليل، أو، في حالة التبادلINTERNAL، عميل Didit الذي استلمه. يكون كل حقل فارغاً عندما لا يصل التبادل إلى أي طرف مقابل معروف.
travel_rule على هذه الحقول. ثبّتها من Transactions → Rules → Library في Business Console أو باستخدام واجهة API لمكتبة القواعد:
Webhooks
اربط وجهة webhook بـtravel_rule.status.updated لتتلقى إشعارًا عند كل تغيير في الحالة لكل تحويل في تطبيقك، الصادر والوارد، بما في ذلك أجزاء مسار INTERNAL.
يبدأ التوجيه بعد أن تُرجع عملية إنشاء المعاملة استجابتها، لذلك تصلك نتائج مثل COUNTERPARTY_VASP_NOT_FOUND وCOUNTERPARTY_VASP_NOT_REACHABLE، أو تبادل INTERNAL تتم تسويته فورًا، عبر هذا الحدث لا في استجابة الإنشاء.
rail فارغًا حتى يُختار مسار.
بالنسبة لمعاملة واحدة، يرسل Didit الحدث travel_rule.status.updated بعد transaction.created: عندما ينشئ Didit المعاملة (طلب POST /v3/transactions/ منك، أو تسجيل sunrise، أو استعلام TRP وارد، أو الجزء الوارد عبر مسار INTERNAL)، يرسل transaction.created أولًا.
يُسلَّم كل حدث ويُعاد إرساله بشكل مستقل، لذلك قد يصل حدث transaction.created الذي احتاج إلى إعادة محاولة بعد حدث الحالة: اجعل معالج الأحداث يعتمد على transaction_id، واقرأ المعاملة عندما يذكر حدث حالة معرّفًا لم تره من قبل.
لا يصل حدث COMPLETED للتحويل المرسل عبر TRUST: إذ لا يعيد TRUST أي شيء يستطيع Didit قراءته، لذلك ما لم يفشل الإرسال أو تلغيه، يكون آخر حدث له EXPIRED. أما عبر GTR وVERIFYVASP وSYGNA، فيتبع الحدث الرد الذي يقرؤه Didit من الشبكة.
تُرسل تطبيقات البيئة التجريبية هذا الحدث أيضًا، مع "environment": "sandbox"؛ والتحويل الذي ردّ عليه Didit Test VASP يحمل rail: "TEST_VASP" وtest_vasp: true وtest_vasp_scenario.
راجع دليل webhook للاطلاع على كتالوج الأحداث الكامل، والتحقق من التوقيع، وسياسة إعادة المحاولة.
الأسعار
رسم Travel Rule هو $0.02 لكل تبادل صادر يبدأ، يُحتسب مرة واحدة عند إنشاء المعاملة، بغض النظر عن عدد تغييرات الحالة التي يمر بها التبادل لاحقًا. ولا يُحتسبresend مرة أخرى أبدًا.
- ينطبق على معاملة
travelRuleصادرة في تطبيق فعّلت فيه Travel Rule، وكان مبلغها يساويthreshold_amountلديك أو يتجاوزه، فيبدأ Didit لها تبادلًا مُدارًا. ويُحتسب التحويل إلى محفظة ذاتية الاستضافة، أو إلى محفظة موثّقة تعود لمستخدمك، بالطريقة نفسها: إثبات الملكية هو التبادل الخاص بها. - تُحتسب كل معاملة صادرة أخرى تحمل بيانات Travel Rule كفحص معاملات قياسي، تشمله خطتك حتى حد المعاملات الشهري ويُسعَّر بـ $0.001 بعده: تحويل أقل من الحد لديك، ودفعة Lightning لا تنطبق عليها Travel Rule، وتحويل يُسجَّل بشكل سلبي والـ Travel Rule متوقف. وتُحتسب المعاملة على أحد البندين فقط، لا على كليهما أبدًا.
- لا تحمل معاملات
travelRuleالواردة أي رسوم Travel Rule أو مراقبة: الجزء الوارد الذي ينشئه Didit في تطبيق المستفيد عبر مسارINTERNAL، واستعلاماتTRPالواردة، وتسجيلات ما بعد الإيداع. - لا يدفع التطبيق الذي انضم إلى Travel Rule مجانًا أيًّا من الرسمين على معاملات
travelRuleالصادرة، إلى أن يفعّل الفوترة الصادرة عبرPOST /v3/travel-rule/workspace/billing/؛ أما الاستلام فيبقى مجانيًا. - لا تُحتسب تطبيقات البيئة التجريبية أبدًا.
- يُدرج رسم Travel Rule بسعر $0.02، ويُدرج Transaction Screening بسعر $0.001 بعد حدك. وتُحتسب كل معاملة بسعر البند الذي تُحتسب عليه.
- إذا أُجري فحص AML للعملات الرقمية أو فحص المحافظ على المعاملة نفسها أيضًا (مثلًا إيداع وارد بـ
currency_kind: "crypto"والمراقبة مفعّلة)، فيُحتسب ذلك بشكل منفصل وفق سعر Wallet Verification & Screening القياسي، ولا يُدمج في رسم Travel Rule. - فحص الطرف المقابل اختياري ويُحتسب بشكل منفصل، بسعر فحص AML للشركات عن كل فحص؛ ويُعاد استخدام الفحص الذي يقل عمره عن 30 يومًا دون رسوم. القرارات والاستبيانات وتقييم العناية الواجبة في الدليل لا تكلّف شيئًا.
تسعير الإسناد
يحتاج إسناد تحليلات البلوكتشين إلى استعلام واحد عن المحفظة لدى مزوّد التحليلات، وتحصل دائمًا على القيمة الكاملة لذلك الاستعلام:- كانت مراقبة العملات الرقمية قد فحصت المحفظة في هذه المعاملة بالفعل - تُعاد استخدام النتيجة المخزّنة. استدعاء واحد للتحليلات، ورسم واحد لـ Wallet Verification & Screening (الرسم نفسه الذي احتُسب بالفعل لمراقبة العملات الرقمية). ولا يضيف الإسناد شيئًا.
- لم تعمل مراقبة العملات الرقمية - يُجري Didit استدعاء التحليلات المطلوب مرة واحدة، ويمرّر قيمته الكاملة: تُحتسب المعاملة بسعر Wallet Verification & Screening القياسي ($0.15 مع التحليلات المُدارة من Didit)، وتحصل في المقابل على مخرجات فحص العملات الرقمية كاملة: درجة المخاطر، وفئة مخاطر المحفظة، ولقطة الشبكة، لا اسم VASP فقط. يُوسم بند الاستخدام بالمُشغِّل
travel_rule_attributionحتى يكون الرسم قابلًا للتفسير. - مفتاح تحليلات خاص بك - ينطبق هنا رسم المنصة نفسه الذي ينطبق على مراقبة العملات الرقمية بمفتاحك الخاص ($0.02 لكل استدعاء). ويُحاسبك مزوّدك على الاستعلام الأساسي مباشرة.
- لا تُحتسب الاستعلامات الفاشلة أبدًا، وتستخدم عمليات إعادة الإرسال الإسناد المسجّل دون استدعاء جديد أو رسم جديد، ولا تُحتسب تطبيقات البيئة التجريبية أبدًا.
حزمة قواعد EU TFR
تتضمن حزمةtravel_rule_eu_tfr أربع قواعد جاهزة، صُمّمت خصيصاً لالتزامات EU TFR.
مثل كل حزمة قواعد جاهزة أخرى، فعّلها من Business Console ضمن Transactions → Rules → Library.
لماذا يعمل هذا بشكل أفضل على منصة واحدة
الامتثال لـ Travel Rule مشكلة هوية في ثوب مدفوعات، وDidit يحتفظ بالهوية بالفعل:- بيانات موثّقة في متناول يدك. يُربط الطرف المعني بكل تحويل بملف KYC الذي تحقّقت منه Didit أثناء التسجيل (استخراج المستند بالتعرف الضوئي على الحروف، وكشف الحيوية، ومطابقة الوجه) عبر
vendor_data، لذا يمكنك بناءoriginator_dataمن حقول موثّقة على المنصة نفسها. بالنسبة إلى مستخدم لديه عملية تحقق KYC مقبولة، ترسل Didit الهوية الموثّقة بدلاً من القيم التي أدخلتها (راجع البيانات الموثّقة والمصدر). - قياسات بيومترية إضافية للتحويلات عالية المخاطر. يمكن لـقاعدة أن توقف أي تحويل عالي القيمة إلى أن يجتاز المستخدم فحصاً بيومترياً إضافياً مقارنةً بالوجه الذي التُقط أثناء التسجيل، وهو أقوى رد ممكن على الاستيلاء على الحساب لحظة السحب. راجع المثال التطبيقي.
- مخطط واحد للأجهزة. تحمل التحويلات المُرسلة عبر SDK معلومات الجهاز نفسها المتاحة في جلسات KYC، لذلك تصبح قاعدة “جهاز جديد + مبلغ مرتفع + مستفيد لأول مرة” قاعدة واحدة، لا عملية ربط في مستودع البيانات.
- الفحص والقواعد والحالات وتبادل البيانات في مكان واحد. فحص المحافظ الذي يحدد VASP للطرف المقابل هو الفحص نفسه الذي تراه قواعدك ومحللوك، فتدفع رسماً واحداً، وتعمل من لوحة واحدة، وتحتفظ بسجل تدقيق واحد.
- أسعار منشورة. $0.02 لكل تبادل صادر، دون مستويات حسب الحجم، ودون الحاجة إلى مكالمة مبيعات.
الأسئلة الشائعة
كيف يعمل التبادل
هل تُشغّل Didit تبادل VASP إلى VASP بنفسها، أم أنها تسجّل بيانات Travel Rule فقط بعد أن تُكمل شبكة أخرى المصافحة؟
هل تُشغّل Didit تبادل VASP إلى VASP بنفسها، أم أنها تسجّل بيانات Travel Rule فقط بعد أن تُكمل شبكة أخرى المصافحة؟
travelRule، تنفّذ Didit اكتشاف الطرف المقابل مباشرةً، وترسل بيانات المُرسِل والمستفيد إلى VASP المستلم عبر قنوات الشبكات التي تنقلها، ولا تُعلّم التحويل COMPLETED إلا بعد أن يؤكد الطرف المقابل ذلك. إنها مصافحة مُدارة وليست سجلاً يُكتب بعد التسوية.
تُعالَج إجابة الطرف المقابل على كل شبكة ما عدا TRUST، الذي يرسل فقط، لذلك تنتهي صلاحية التحويل عبره بدلاً من اكتماله. وعبر GTR وVERIFYVASP وSYGNA، تقرأ Didit الإجابة من استجابة الشبكة أو من خلال الاستعلام عنها كل 5 دقائق، ولم يكتمل أي تبادل عبرها مع شبكة عاملة حتى الآن. وعبر TRP، تحتاج الإجابة التي لا يقدمها الطرف المقابل فورًا إلى مضيف TRP العام التابع لـ Didit، وهو غير مفعّل بعد (انظر شبكات Travel Rule).
كما تحتفظ Didit بالسجل الكامل وحالة الامتثال لكل تبادل: دورة حياة من 13 حالة (دورة الحياة)، وwebhooks، وسجل التدقيق.
إذا كنت تفضّل تشغيل Travel Rule عبر عمليتك الخاصة، يمكن لـ Didit بدلاً من ذلك أن تسجّل البيانات بشكل سلبي بوصفها نظام السجل الوحيد لديك (انظر السؤال عن التسجيل السلبي أدناه).
كلا الوضعين متاح: يعمل التبادل النشط بعد أن تضبط is_enabled: true في الإعدادات (القيمة الافتراضية false)، وحتى ذلك الحين تسجّل Didit البيانات بشكل سلبي.هل التبادل أصلي في Didit، أم أنه يعتمد على مزود خارجي للتشغيل البيني (Notabene أو Sygna أو VerifyVASP أو TRP)؟
هل التبادل أصلي في Didit، أم أنه يعتمد على مزود خارجي للتشغيل البيني (Notabene أو Sygna أو VerifyVASP أو TRP)؟
trp.didit.me بمجرد تفعيل المضيف العام (وهو لا يستجيب بعد، لذا لا يمكن للأطراف المقابلة الوصول إليك عبر TRP اليوم)، وتطبّق بروتوكولات شبكات القطاع مباشرة: GTR (Global Travel Rule)، وTRUST، وVerifyVASP، وSygna Bridge (تُطبَّق أصلاً، بما في ذلك IVMS 101 المشفّر بـ ECIES لدى Sygna وطلبات الإذن الموقّعة). Didit ليست وسيطًا لطبقة تشغيل بيني واحدة لطرف ثالث. والوصول إلى طرف مقابل موجود على شبكة عضوية محددة يتم بالضرورة عبر تلك الشبكة، فليس هناك قناة عالمية واحدة في Travel Rule، لكن الاكتشاف والمصافحة والتحقق وحفظ السجلات تنفّذها Didit جميعًا.
اليوم، تحتاج كل شبكة إلى عضويتك الخاصة، وTRUST يرسل فقط: انظر نظرة عامة.ما شبكات وبروتوكولات Travel Rule التي تدعمها Didit؟
ما شبكات وبروتوكولات Travel Rule التي تدعمها Didit؟
INTERNAL (كلا VASPَين على Didit)، وTRP (بروتوكول OpenVASP Travel Rule)، وGTR، وTRUST، وVerifyVASP، وSygna Bridge، وقناة احتياطية EMAIL للأطراف المقابلة التي لا تملك أدوات مناسبة.
تحتاج قنوات الشبكات إلى عضويتك الخاصة اليوم، وTRUST يرسل فقط، ولم يُفعَّل الاستلام عبر أي شبكة بعد: انظر شبكات Travel Rule.
تظهر CODE وTRISA وNotabene وVeriscope في Marketplace لدى Didit كبطاقات طلب وصول، دون تكامل حتى الآن.
انظر كيف توجّه Didit التبادل للاطلاع على جدول التوجيه الكامل، وشبكات Travel Rule لمعرفة ما تصل إليه كل شبكة اليوم.أي شبكة يجب أن أستخدم، وهل أحتاج إلى عضويات خاصة بي؟
أي شبكة يجب أن أستخدم، وهل أحتاج إلى عضويات خاصة بي؟
TRP هو الخيار الأساسي الذي نوصي به: فهو لا يتطلب عضوية مشتركة، ومشاركة Travel Address الخاص بك تتيح لأي مزود يتحدث TRP الوصول إليك. وعندما لا يُعرف الطرف المقابل إلا بعنوان المحفظة، فإن إسناد تحليلات البلوكتشين يوجّهه عبر TRP فقط إذا كان إدخال الدليل المُسنَد يملك endpoint لـ TRP بالفعل.
عند الإرسال، ترسل Didit الأصول المدرجة ضمن معرّفات أصول TRP عبر TRP، والطرف المقابل الذي لا يجيب فورًا يحتاج إلى مضيف TRP العام التابع لـ Didit، وهو غير مفعّل بعد.
بالنسبة إلى الشبكات التي تتطلب عضوية، اختر وفق مكان عمل أطرافك المقابلة: تقدم GTR تغطية عالمية واسعة، وTRUST موجّهة إلى VASPs في الولايات المتحدة، وVerifyVASP (الذي يدير الآن Sygna Bridge أيضًا) تغطيته قوية في آسيا.
نعم، تحتاج اليوم إلى عضويتك الخاصة: لا تملك Didit عضوية منصة على هذه الشبكات بعد، وهي تخضع حاليًا لعملية التسجيل بصفتها مزودًا.
اربط عضويتك في Marketplace، فأنت لست مقيّدًا بشبكة واحدة.
تذكّر أن TRUST يرسل فقط، وأن التحويل عبره لا يكتمل، وأن أي تبادل عبر شبكة لم يكتمل مع شبكة عاملة حتى الآن.كيف تجد Didit الـ VASP المقابل عندما يكون لديّ عنوان المحفظة فقط؟
كيف تجد Didit الـ VASP المقابل عندما يكون لديّ عنوان المحفظة فقط؟
INTERNAL)، ثم دفتر عناوين المحافظ الخاص بك لعنوان Travel Address مخزّن، ثم مسارات البروتوكولات والشبكات، وأخيرًا إسناد تحليلات البلوكتشين.
إذا حمل التحويل محفظة وجهة واحدة فقط، تحلّها Didit مقابل بيانات تحليلات البلوكتشين لإسناد خدمة البورصة أو الحفظ المقابلة، وتسجّلها في دليل VASP.
لا يُوجَّه التحويل تلقائيًا إلا إذا كان إدخال الدليل يملك مسارًا بالفعل، مثل endpoint لـTRP. وإلا فإنه ينتهي بالحالة COUNTERPARTY_VASP_NOT_FOUND مع تسجيل اسم الطرف المقابل، ويمكنك إعادة الإرسال بمجرد وجود مسار، مثلاً بعد تخزين travel_address الخاص بالطرف المقابل في إدخال دفتر العناوين لتلك المحفظة (للأصول المدرجة ضمن معرّفات أصول TRP).الأسعار والترخيص
كم تكلفة Travel Rule، وهل تُدرج ضمن خطة KYC أو التحقق لديّ؟
كم تكلفة Travel Rule، وهل تُدرج ضمن خطة KYC أو التحقق لديّ؟
هل سأُحاسَب مرتين عندما يعمل Travel Rule والمراقبة للعملات المشفرة معًا؟
هل سأُحاسَب مرتين عندما يعمل Travel Rule والمراقبة للعملات المشفرة معًا؟
travelRule في سطر واحد: رسوم Travel Rule ($0.02) عندما تبدأ تبادلاً صادرًا، وفحص المعاملات المعتاد في غير ذلك. يُحتسب بحث تحليلات البلوكتشين مرة واحدة: إذا كانت مراقبة العملات المشفرة قد فحصت محفظة الوجهة للمعاملة، فإن الإسناد يعيد استخدام تلك النتيجة المخزّنة دون تكلفة إضافية.
لا تُحتسب أبدًا رسوم Travel Rule على الأرجل الواردة التي تُنشئها Didit على جانب المستفيد.هل تستطيع Didit تقديم توثيق أو بيان قدرات لملف ترخيصي أو ملفي لدى الجهة التنظيمية كـ VASP؟
هل تستطيع Didit تقديم توثيق أو بيان قدرات لملف ترخيصي أو ملفي لدى الجهة التنظيمية كـ VASP؟
العمليات والبيانات
ماذا يحدث للتحويلات التي تقل عن الحد المحدد لي؟
ماذا يحدث للتحويلات التي تقل عن الحد المحدد لي؟
NOT_APPLICABLE: لا يتم تجربة أي مسار، ولا تغادر أي بيانات هوية منصة Didit.
تُحتسب المعاملة كفحص معاملات قياسي (Transaction Screening)، لا كرسوم Travel Rule.
تقارن Didit الحد بالمبلغ بعد تحويله إلى العملة الافتراضية لتطبيقك.
إذا تعذّر على Didit تحويل المبلغ، لا يُطبَّق الحد ويُجرى التبادل.
لا يوجد حد أدنى في لائحة نقل الأموال الأوروبية (EU TFR)، لذا ينبغي لمزودي خدمات الأصول المشفّرة (CASPs) في الاتحاد الأوروبي الإبقاء على threshold_amount عند 0.00.ماذا لو لم يستجب الطرف المقابل أبداً؟
ماذا لو لم يستجب الطرف المقابل أبداً؟
AWAITING_COUNTERPARTY حتى تنقضي confirmation_timeout_hours، ثم تنقله مهمة تنظيف دورية إلى EXPIRED وتطبّق timeout_outcome على المعاملة الأساسية: HOLD للمراجعة، أو REJECT للرفض، أو PROCEED لتركها دون تغيير. كما تعرض القاعدة الجاهزة tr_eu_expired_review كل حالة انتهاء على محلليك. أما الطرف المقابل الذي لا يستلم الرسالة إطلاقاً فالأمر مختلف: تعيد Didit المحاولة، وبعد المحاولة السادسة الفاشلة ينتقل التحويل إلى COUNTERPARTY_VASP_NOT_REACHABLE (راجع التسليم وإعادة المحاولات).ليست لدى الطرف المقابل أي أدوات Travel Rule. هل يمكنني الامتثال؟
ليست لدى الطرف المقابل أي أدوات Travel Rule. هل يمكنني الامتثال؟
EMAIL. تتلقى جهة الامتثال لدى الطرف المقابل رابط استلام لمرة واحدة، ويمكنها تأكيد التبادل أو رفضه من صفحة مستضافة دون حساب في Didit ودون مفتاح API ودون أي تكامل. أما الإيداعات التي تمّت تسويتها قبل أن يصبح أي تبادل ممكناً، فاستخدم تسجيل Sunrise بعد الإيداع.كيف تعمل عمليات السحب إلى محفظة المستخدم الخاصة؟
كيف تعمل عمليات السحب إلى محفظة المستخدم الخاصة؟
payment_method.method_type: "unhosted_wallet"). تتجاوز Didit توجيه VASP، وبدلاً من ذلك تجمع دليلاً على أن عميلك يتحكم في المحفظة: توقيع الرسالة، أو اختبار Satoshi، أو لقطة شاشة، أو إقرار ذاتي، وذلك عبر أداة إثبات ملكية المحفظة. وهذا هو التزام المحافظ ذاتية الاستضافة في لائحة نقل الأموال الأوروبية (EU TFR) في صورة واجهة API.هل بيانات الطرف المقابل الشخصية محمية أثناء التبادل؟
هل بيانات الطرف المقابل الشخصية محمية أثناء التبادل؟
GTR، TRUST، VERIFYVASP، SYGNA) إضافةً إلى ذلك بتشفير payload الخاص بـ IVMS بالمفتاح العام لـ VASP المستقبِل.أنا أتتبع امتثال Travel Rule بعملياتي الخاصة. هل يمكنني تسجيله فقط؟
أنا أتتبع امتثال Travel Rule بعملياتي الخاصة. هل يمكنني تسجيله فقط؟
travel_rule_details ترسله، بما في ذلك status الذي تحدده بنفسك سواءً كان الحالي أو القديم، ولا يُستدعى أي مسار، ولا يُتفاوض على شيء. استخدم ذلك للتعبئة الرجعية أو لتشغيل عمليتك الخاصة مع الحفاظ على نظام سجل واحد.كيف أختبر هذا قبل الإطلاق؟
كيف أختبر هذا قبل الإطلاق؟
transaction.created وtransaction.status.updated، وتُوجَّه كل عملية تبادل صادرة إلى Didit Test VASP عبر مسار TEST_VASP، الذي يجيب داخل Didit بالسيناريو الذي تختاره: قبول، أو رفض، أو عدم تطابق الاسم، أو عدم الوصول، أو انتهاء المهلة، أو محفظة ذاتية الاستضافة. تتلقى webhook من نوع travel_rule.status.updated معلَّمة بـ environment: sandbox، ولا تصل أي رسالة إلى طرف مقابل حقيقي.
أما التحويلات الواردة، فاطلب من VASP الاختبار إرسال تحويل إليك عبر تشغيل تحويل وارد من Test VASP، أو سجّل واحداً باستخدام POST /v3/travel-rule/inbound/. يُبتّ في الحالتين استناداً إلى دفتر عناوين محافظك: ON_HOLD، أو UNCONFIRMED_OWNERSHIP، أو COUNTERPARTY_MISMATCHED_DATA، أو COMPLETED ثم FINISHED.
أما التبادل بين طرفين مع تطبيق حقيقي آخر، فاستخدم تطبيقين من تطبيقاتك في بيئة الإنتاج على مسار INTERNAL: يجب أن يكون التطبيق المستقبِل قابلاً للاكتشاف، وأن يثبت سيطرته على محفظة الوجهة بتوقيع رسالة أو بإيداع تجريبي.