الانتقال إلى المحتوى الرئيسي
تُجري Didit تبادل بيانات المُرسِل والمستفيد وفق لائحة FATF/الاتحاد الأوروبي بشأن تحويل الأموال (TFR) لتحويلات الأصول الافتراضية. فهي تبحث عن مسار يصل إلى الـ VASP المقابل، وتتفاوض على البيانات المطلوبة عبره، وتتابع التبادل عبر دورة حياة من 13 حالة، وتُخطرك عند اكتمال التبادل حتى تتمكن من تحرير الأموال على السلسلة.

نظرة عامة

تُلزم توصية 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 مقابل للتبادل معه، وينتقل مباشرةً إلى إثبات ملكية المستخدم النهائي للمحفظة.
يدير Didit أيضاً الجانب الموجَّه للطرف المقابل من التبادل: نقاط نهاية TRP العامة للهوية والاستعلام التي تستدعيها VASPs الأطراف المقابلة للوصول إليك، ونقاط نهاية الاستدعاء، وتدفق استلام البريد الإلكتروني. أنت لا تستدعي تلك النقاط بنفسك، فهي بنية تحتية مُدارة، لكن صفحة التوافق البيني لـ Travel Rule توثّق كل واحدة منها حتى تتمكن VASPs الأطراف المقابلة من الاتصال بك. لا تتعامل مع Travel Rule إلا عبر نقاط النهاية /v3/ الموضحة في هذه الصفحة.

ما الذي ينفَّذ عند إرسال التحويل

تنفَّذ الفحوصات داخل استدعاء واحد هو POST /v3/transactions/. عند استلام الاستجابة يكون الفحص قد اكتمل، وتكون بيانات التحويل مخزَّنة، وتكون القواعد قد قُيّمت. يبدأ التبادل نفسه بعد الاستجابة: تختار Didit المسار وتتواصل مع الطرف المقابل خارج طلبك، لذلك لا يمكن لأي شبكة أو طرف مقابل أن يبطئ إنشاء التحويل أو يُفشله. يصل كل تغيير في الحالة بعد ذلك في صورة حدث travel_rule.status.updated يذكر المسار، ويحمل كائن التحويل الذي تعيده نقاط نهاية تحويلات Travel Rule حقلَ delivery الذي يبيّن ما إذا وصلت الرسالة. لا يوجد خط معالجة يجب الاستعلام عنه بشكل دوري؛ الأجزاء غير المتزامنة هي التبادل، وردّ الطرف المقابل، وأي إجراء يقوم به المستخدم النهائي. خطوة بخطوة:
  1. التحقق وربط الجهة المعنية. يُتحقق من صحة الحمولة. يُرفض transaction_id سبق استخدامه في تطبيقك بالرمز 400، وإذا تزامن طلبان بالمعرّف نفسه، يتلقى أحدهما الرمز 409. تُربط subject بملفك الشخصي للمستخدم أو للشركة عبر vendor_data، فيظهر التحويل على ذلك الملف لأغراض المراقبة. تُرفض الجهة المعنية أو قيمة vendor_data للطرف المقابل إذا كانت مدرجة في قائمة الحظر بالرمز 400، ولا يُنشأ أي معاملة. تُفحص المحافظ وحسابات البنوك وعناوين IP والأجهزة المدرجة في قائمة الحظر في الخطوة 6، وترفض المعاملة هناك بعد تشغيل التوجيه.
  2. إثراء الجهاز والشبكة. تُضاف بيانات الموقع الجغرافي إلى عناوين IP للأطراف، ويُوحَّد أي سياق للجهاز (يُجمع تلقائياً عند إرسال SDK) داخل مخطط الأجهزة نفسه الذي تغذّيه جلسات KYC، فتعمل خاصية “مرتبطة بالجهاز” عبر التسجيل والمعاملات معاً.
  3. فحص العملات الرقمية. بالنسبة إلى تحويلات العملات الرقمية مع تفعيل المراقبة، تُفحص المحفظة المعنية (أو التجزئة على السلسلة) أمام تحليلات البلوكتشين قبل تبادل أي شيء، لكشف التعرض للعقوبات، والقرب من خدمات الخلط، والأموال المسروقة. راجع فحص العملات الرقمية والمحافظ.
  4. تحويل Travel Rule. يتحقق محرك التبادل من الحد، ويعتمد إقرار المحفظة ذاتية الاستضافة، ثم يخزّن التحويل: الحالة NOT_APPLICABLE إذا كان دون حدك، وUNCONFIRMED_OWNERSHIP لمحفظة ذاتية الاستضافة مُقرّ بها، وCOMPLETED عندما تكون المحفظة الأخرى محفظتك الموثّقة، وAWAITING_COUNTERPARTY في غير ذلك، مع بقاء مساره فارغاً. يضع كل تغيير في الحالة حدث travel_rule.status.updated في الطابور، ويرسله Didit بعد transaction.created (الخطوة 7).
  5. إصدار جلسة ملكية المحفظة. إذا كان التحويل يتطلب إثبات سيطرة من المستخدم النهائي، وكان auto_wallet_verification مفعلاً، تُنشأ جلسة أداة ملكية المحفظة وتُعاد ضمن action_required.
  6. تقييم القواعد. تعمل كل قاعدة نشطة. تتفاعل حزمة EU TFR مع حالة التحويل المحددة في الخطوة 4، وترى قواعدك المخصصة نتائج الفحص في الخطوة 3، ويمكن لقاعدة أن تحتجز المعاملة (IN_REVIEW)، أو ترفضها، أو تطلب تحققاً إضافياً (AWAITING_USER).
  7. الفوترة والإشعار. تُحتسب المعاملة الصادرة في بند واحد: بند Travel Rule عندما تبدأ تبادلاً، وفحص المعاملات في غير ذلك. لا يُحتسب على التحويل الوارد أي رسم، ولا يُحاسَب تطبيق البيئة التجريبية أبداً. تُعاد الاستجابة، ويُطلق transaction.created إلى وجهات webhook الخاصة بك، تليه أحداث travel_rule.status.updated من الخطوة 4.
  8. التوجيه، بعد الاستجابة. يمرّ 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 التي ترسلها بنفسك تتقدم دائماً على القيمة المشتقة).
لا تبثّ أبداً على السلسلة قبل الوصول إلى COMPLETED. إطلاق الأموال على السلسلة أثناء وجود التحويل في حالة AWAITING_COUNTERPARTY أو UNCONFIRMED_OWNERSHIP، أو في أي حالة عدم تطابق أو رفض، يُفرغ عملية التبادل من غايتها. اجعل خطوة البث مشروطة بـ status == "COMPLETED"، ثم استدعِ PATCH .../travel-rule/ مع payment_txn_id الناتج للانتقال إلى FINISHED.
الانتقالات بنظرة سريعة (يمكن الوصول إلى CANCELLED إضافةً من كل حالة غير نهائية باستخدام action: "cancel"):

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

يرسل Didit كل رسالة يتعيّن على التحويل إرسالها بصورة منفصلة عن الطلب الذي تسبّب فيها، ويعيد محاولة إرسالها حتى يستلمها الطرف الآخر. يعيد الكائن الذي تُرجعه إنهاء التحويل أو إلغاؤه أو إعادة إرساله، وتأكيد الملكية، وتسجيل sunrise آخرَ رسالة في delivery، ويحملها أيضاً كائن travel_rule المضمّن في المعاملة. وتكون قيمته null إذا لم يكن يتعيّن على التحويل إرسال أي رسالة قط. تُعاد محاولة الفشل الذي قد تتعافى منه الشبكة (عدم الرد في الوقت المحدد، أو فشل الاتصال، أو رمز HTTP 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 الهوية الموثّقة لا القيم التي كتبتها: راجع البيانات الموثّقة ومصدرها. في أي حالة أخرى، ينقل Didit originator_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 حرف:
عند تحقق Didit من Travel Address، فإن القيمة التي لا تكون عنوانًا سليم التكوين وصحيح مجموع التحقق تُرجع 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، لاختبار تبادل بين طرفين دون بيانات اعتماد تجارية للشبكة. قد تترتب على التطبيقات الحية رسوم وإشعارات عادية، لذا استخدم هويات وأطرافًا مقابلة تتحكم بها أنت.
راجع اختبار Travel Rule في البيئة التجريبية لكل السيناريوهات ونتيجة كل منها بدقة. لا تُثبت الاختبارات الناجحة في البيئة التجريبية أو اختبارات البروتوكول المحلي بيانات اعتماد شبكة تجارية أو عضويتها أو قابليتها للتشغيل البيني في بيئة الإنتاج. تتطلب هذه الأمور بيئة تجريبية لشبكة معتمدة، أو تبادلًا حيًا خاضعًا للتحكم مع بيانات اعتماد عضوية صالحة.

إرسال تحويل

أدرِج التحويل في POST /v3/transactions/ بضبط transaction_category على travelRule (أو travel_rule) وإرفاق كائن travel_rule_details (الاسم البديل: travelRule). كتلة travel_rule_details نفسها اختيارية، لكن بمجرد إرفاقها يصبح travel_rule_details.status مطلوباً: الكتلة التي لا تتضمنه تعيد 400. عندما يكون Travel Rule مفعّلاً لتطبيقك، يتجاهل Didit قيمة status التي ترسلها ويدير تبادل المعلومات بنفسه:
  1. إذا كان المبلغ أقل من threshold_amount المضبوط لديك، يُحسم التحويل فوراً على NOT_APPLICABLE ولا يُتفاوض على شيء.
  2. إذا أعلنت وسيلة دفع الطرف المقابل أن الوجهة محفظة ذاتية الاستضافة (payment_method.method_type: "unhosted_wallet")، فلا تُجرَّب أي قناة، ويذهب التحويل مباشرة إلى UNCONFIRMED_OWNERSHIP ويُنشأ أداة التحقق من ملكية المحفظة تلقائياً (انظر المحافظ الذاتية الاستضافة المُعلنة).
  3. وإلا تُشغَّل القنوات بحسب ترتيب الأولوية، وأول قناة تصل إلى الطرف المقابل تحمل التبادل:
    • 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 إلى إدخال في الدليل يحتوي على عنوان بريد إلكتروني للامتثال.
  4. إذا لم تستطع أي قناة تحديد الطرف المقابل، تُشغّل Didit سلسلة اكتشاف الطرف المقابل. مع تفعيل vasp_attribution_enabled، تتضمن هذه السلسلة إسناد تحليلات البلوكتشين: تُحدَّد المحفظة الوجهة إلى الـ VASP المالك لها، ويُكتب الاسم المُسنَد إلى beneficiary_data.counterparty_vasp_name (إذا كان فارغاً) وإلى الدليل، وتُعاد تشغيل القنوات مرة واحدة. لذلك تُوجَّه المحفظة المُسنَدة إلى VASP يحمل إدخاله في الدليل نقطة نهاية TRP أو معرّف شبكة أو عنوان بريد إلكتروني للامتثال في هذه المرحلة الثانية.
  5. إذا بقي الطرف المقابل غير محلول، ينتقل التحويل إلى COUNTERPARTY_VASP_NOT_FOUND. في التحويلات الصادرة يُنشئ Didit تلقائياً أداة التحقق من ملكية المحفظة (عندما يكون auto_wallet_verification مفعّلاً) حتى يمكن إتمام التحويل كتحويل إلى محفظة ذاتية الاستضافة، ويمكنك إعادة تشغيل التوجيه لاحقاً باستخدام action: "resend" بعد توفر بيانات أفضل عن الطرف المقابل.
تحدث الخطوتان 1 و2 ضمن الطلب نفسه. وتعمل الخطوات من 3 إلى 5 مباشرة بعد الاستجابة: يعود التحويل بالحالة 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 بما يلي:
  1. تسجّل الإسناد على التحويل: يُملأ beneficiary_data.counterparty_vasp_name إذا لم تكن قد زوّدتها أنت، وتحمل بيانات التحويل الوصفية كتلة vasp_attribution تضم اسم الجهة ونوعها وبلدها وsource: "blockchain_analytics".
  2. تضيف الجهة إلى دليل VASP (أو تطابق الإدخال الموجود)، فيبقى الإسناد ثابتًا عبر التحويلات.
  3. تعيد تشغيل المسارات مرة واحدة باسم الجهة المُسنَد: فالإدخال الموجود في الدليل الذي يحمل بالفعل 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 عند فشل المطابقة.
هذا هو الالتزام الخاص بالمحافظ ذاتية الاستضافة في لائحة نقل الأموال الأوروبية (EU TFR) في صورة واجهة برمجية: التحقق من ملكية المحفظة غير المستضافة، بدلًا من تبادل البيانات مع VASP مقابلة. يُقرأ التصريح من طريقة الدفع في جهة الوجهة للتحويل الصادر، وفي جهة المصدر للتحويل الوارد، وفي الحالتين يكون الطرف المقابل هو المشارك المعني. يسير التحويل المُصرَّح فيه بأنه غير مستضاف بالفعل على مساره النهائي الخالي من القنوات، لذلك فإن UNCONFIRMED_OWNERSHIP ليست حالة قابلة لإعادة الإرسال؛ أكمل إثبات الملكية (أو ألغِ التحويل) بدلًا من استدعاء action: "resend".

الاستلام

تأكيد ملكية المحفظة

ينتظر التحويل في حالة UNCONFIRMED_OWNERSHIP في حالتين: يؤكد تطبيقك التحويل أو يرفضه:
يقبل الجسم confirmed (مطلوب، قيمة منطقية) وwallet_address اختياري. تقبل نقطة النهاية تحويلاً في أي من هذه الحالات، وتُعيد 409 مع {"detail": "Transfer is not awaiting ownership confirmation."} لأي حالة أخرى:
  • UNCONFIRMED_OWNERSHIP، في أي من الاتجاهين.
  • ON_HOLD في تحويل وارد.
  • UNCONFIRMED_OWNERSHIP أو ON_HOLD أو COUNTERPARTY_MISMATCHED_DATA في تحويل TRP وارد، حتى يتمكن مسؤول الامتثال من حل استفسار الطرف المقابل يدوياً.
في تحويل أنشأه Didit بنفسه (مسار 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 ويحتفظ التحويل بحالته الحالية. عند الموافقة، يُنشأ إدخال عنوان المحفظة أو يُعلَّم بأنه متحقق منه.
بالنسبة إلى محفظة ذاتية الاستضافة مُعلنة، المحفظة التي تتحقق منها هي محفظة الطرف المقابل: وجهة السحب الصادر، أو مصدر الإيداع الوارد (محفظة المُرسِل). يضيف Didit المحفظة إلى دفتر عناوين المحفظة بوصفها متحققة الملكية باسم الطرف المقابل، فتُطابَق المحفظة مع اسم الطرف المقابل (المُرسِل في حالة الإيداع). إذا كان دفتر العناوين يحمل تلك المحفظة باسم حامل مختلف، ينتقل التحويل بدلاً من ذلك إلى 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) عليه، أو على بريد الامتثال في سجل الدليل، فيُبقي الطلب مفتوحاً للتذكيرات.
يفتح الرابط صفحة مستضافة تعرض التحويل المعلّق، وتستدعي endpointين العامين التاليين الخاصين بالمستلم. بالنسبة إلى تحويل المثال أعلاه، تكون تفاصيل الاستلام كما يلي:
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. يُنشئ Didit معاملة 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 ضمنه). لتضييق النتائج استخدم: أعضاء شبكة Didit غير مُقيَّمين، لذا تُعيد 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، وفحص العقوبات والأخبار السلبية الذي تجريه عليه، واستبيانات العناية الواجبة التي تبادلتها معه تخص تطبيقك وحده. لا يراها عميل آخر، ولا تغيّر الإدخال المشترك أبداً.
تعمل سجلاتك الخاصة مع الإدخال في الدليل ومع عضو في شبكة Didit على حد سواء: استخدم 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 قد يكون إخفاقاً من جهة المرسل، والطلب الذي ما زال قيد التنفيذ لم يُحسم بعد.
يردّ أعضاء شبكة Didit داخل Didit، ويظهرون دائماً بالحالة 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. وكلاهما فارغ افتراضياً، لذا لا يتغير شيء حتى تضبط أحدهما:
الإعداد الأول الذي ينطبق يحدد النتيجة:
  1. sunrise_policy، إذا كان مضبوطاً ودولة الطرف المقابل مدرجة في sunrise_jurisdictions.
  2. non_responsive_policy، إذا كان مضبوطاً والطرف المقابل UNRESPONSIVE.
  3. 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 لمكتبة القواعد: تنطبق الإعدادات الخمسة جميعها على معاملات Travel Rule فقط.

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 لكل استدعاء). ويُحاسبك مزوّدك على الاستعلام الأساسي مباشرة.
  • لا تُحتسب الاستعلامات الفاشلة أبدًا، وتستخدم عمليات إعادة الإرسال الإسناد المسجّل دون استدعاء جديد أو رسم جديد، ولا تُحتسب تطبيقات البيئة التجريبية أبدًا.
إذن، يكلّف التحويل الصادر الذي احتاج إلى إسناد $0.02 (Travel Rule) + رسم واحد لـ Wallet Verification & Screening، وعندما يكون المنتجان مفعّلين معًا، يُجرى استدعاء التحليلات وتُحتسب رسومه مرة واحدة فقط.

حزمة قواعد 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 التبادل بنفسها. عند إرسال تحويل 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 تطبيقها الخاص لبروتوكول Travel Rule (TRP)، ويُقدَّم عبر trp.didit.me بمجرد تفعيل المضيف العام (وهو لا يستجيب بعد، لذا لا يمكن للأطراف المقابلة الوصول إليك عبر TRP اليوم)، وتطبّق بروتوكولات شبكات القطاع مباشرة: GTR (Global Travel Rule)، وTRUST، وVerifyVASP، وSygna Bridge (تُطبَّق أصلاً، بما في ذلك IVMS 101 المشفّر بـ ECIES لدى Sygna وطلبات الإذن الموقّعة). Didit ليست وسيطًا لطبقة تشغيل بيني واحدة لطرف ثالث. والوصول إلى طرف مقابل موجود على شبكة عضوية محددة يتم بالضرورة عبر تلك الشبكة، فليس هناك قناة عالمية واحدة في Travel Rule، لكن الاكتشاف والمصافحة والتحقق وحفظ السجلات تنفّذها Didit جميعًا. اليوم، تحتاج كل شبكة إلى عضويتك الخاصة، وTRUST يرسل فقط: انظر نظرة عامة.
مطبّقة أصلاً، وتُجرَّب بترتيب أولوية صارم: 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 أثبت سيطرته على المحفظة (مسار INTERNAL)، ثم دفتر عناوين المحافظ الخاص بك لعنوان Travel Address مخزّن، ثم مسارات البروتوكولات والشبكات، وأخيرًا إسناد تحليلات البلوكتشين. إذا حمل التحويل محفظة وجهة واحدة فقط، تحلّها Didit مقابل بيانات تحليلات البلوكتشين لإسناد خدمة البورصة أو الحفظ المقابلة، وتسجّلها في دليل VASP. لا يُوجَّه التحويل تلقائيًا إلا إذا كان إدخال الدليل يملك مسارًا بالفعل، مثل endpoint لـTRP. وإلا فإنه ينتهي بالحالة COUNTERPARTY_VASP_NOT_FOUND مع تسجيل اسم الطرف المقابل، ويمكنك إعادة الإرسال بمجرد وجود مسار، مثلاً بعد تخزين travel_address الخاص بالطرف المقابل في إدخال دفتر العناوين لتلك المحفظة (للأصول المدرجة ضمن معرّفات أصول TRP).

الأسعار والترخيص

Travel Rule منتج منفصل قائم على الاستخدام. لا يُضمّ إلى التحقق من الهوية. يكلّف **$0.02 لكل تبادل صادر يبدأ** ([الأسعار المنشورة](#الأسعار)، دون شرائح حجم، ودون مكالمة مبيعات). عندما يحتاج التوجيه إلى بحث تحليلات البلوكتشين لتحديد الـ VASP المقابل، يُحتسب ذلك البحث مرة واحدة بسعر [الإسناد](#تسعير-الإسناد): بدءًا من $0.15 للاستعلام المُدار، أو $0.02 مع مفتاح مزود موجود لديك. لا يُحتسب مرة أخرى إذا كانت مراقبة العملات المشفرة قد فحصت المحفظة نفسها. يُفوتَر التحويل الذي يقل عن حدّك، أو الذي يُسجَّل والـ Travel Rule متوقف، كفحص المعاملات؛ أما التحويلات الواردة، مثل الأرجل التي تُنشئها Didit على جانب المستفيد، فلا تُحتسب عليها رسوم Travel Rule.
لا. تُفوتَر معاملة travelRule في سطر واحد: رسوم Travel Rule ($0.02) عندما تبدأ تبادلاً صادرًا، وفحص المعاملات المعتاد في غير ذلك. يُحتسب بحث تحليلات البلوكتشين مرة واحدة: إذا كانت مراقبة العملات المشفرة قد فحصت محفظة الوجهة للمعاملة، فإن الإسناد يعيد استخدام تلك النتيجة المخزّنة دون تكلفة إضافية. لا تُحتسب أبدًا رسوم Travel Rule على الأرجل الواردة التي تُنشئها Didit على جانب المستفيد.
نعم. قدرات KYT ومراقبة المعاملات، وفحص AML والعقوبات، وتقييم مخاطر المحافظ بتحليلات البلوكتشين، وقدرات Travel Rule موثّقة كلها علنًا على docs.didit.me، ويحق لك الاستشهاد بهذه الصفحات في أي ملف تنظيمي. أما بيان قدرات منتج رسمي مخصّص لتقديم محدد، مثل طلب ترخيص AIFC/AFSA أو MiCA أو أي ترخيص آخر لـ VASPs، فتواصل مع فريق حسابك في Didit بالصيغة التي تشترطها جهتك التنظيمية.

العمليات والبيانات

تنتقل فوراً إلى 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 (راجع التسليم وإعادة المحاولات).
نعم، وذلك عبر مسار EMAIL. تتلقى جهة الامتثال لدى الطرف المقابل رابط استلام لمرة واحدة، ويمكنها تأكيد التبادل أو رفضه من صفحة مستضافة دون حساب في Didit ودون مفتاح API ودون أي تكامل. أما الإيداعات التي تمّت تسويتها قبل أن يصبح أي تبادل ممكناً، فاستخدم تسجيل Sunrise بعد الإيداع.
صرّح بالوجهة على أنها ذاتية الاستضافة (payment_method.method_type: "unhosted_wallet"). تتجاوز Didit توجيه VASP، وبدلاً من ذلك تجمع دليلاً على أن عميلك يتحكم في المحفظة: توقيع الرسالة، أو اختبار Satoshi، أو لقطة شاشة، أو إقرار ذاتي، وذلك عبر أداة إثبات ملكية المحفظة. وهذا هو التزام المحافظ ذاتية الاستضافة في لائحة نقل الأموال الأوروبية (EU TFR) في صورة واجهة API.
نعم. إلى أن تتأكد ملكية المحفظة من جهتك، تُخفى البيانات الشخصية للطرف المقابل في التحويل الوارد، سواءً في كائن التحويل أو في جزء الطرف المقابل من المعاملة (راجع بيانات الطرف المقابل المخفية). لا تعرض صفحات الاستلام عبر البريد الإلكتروني إلا ملخص التحويل (اسم VASP المُرسِل، والمبلغ، ومحفظة الوجهة)، ولا تعرض أبداً payload الخاصة بالأشخاص. تعمل تبادلات المسارات عبر HTTPS، وتقوم شبكات المسارات (GTR، TRUST، VERIFYVASP، SYGNA) إضافةً إلى ذلك بتشفير payload الخاص بـ IVMS بالمفتاح العام لـ VASP المستقبِل.
نعم. مع تعطيل Travel Rule، يُسجَّل بشكل سلبي على المعاملة أي travel_rule_details ترسله، بما في ذلك status الذي تحدده بنفسك سواءً كان الحالي أو القديم، ولا يُستدعى أي مسار، ولا يُتفاوض على شيء. استخدم ذلك للتعبئة الرجعية أو لتشغيل عمليتك الخاصة مع الحفاظ على نظام سجل واحد.
استخدم تطبيقاً في البيئة التجريبية مع تفعيل Travel Rule. تتجاوز البيئة التجريبية القواعد والفوترة وwebhook الخاصة بـ 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: يجب أن يكون التطبيق المستقبِل قابلاً للاكتشاف، وأن يثبت سيطرته على محفظة الوجهة بتوقيع رسالة أو بإيداع تجريبي.

الخطوات التالية

أداة ملكية المحفظة

أثبت أن العميل يتحكم في محفظة عبر التوقيع، أو اختبار Satoshi، أو لقطة شاشة، أو إقرار ذاتي.

قابلية التشغيل البيني

endpoint الموجهة للطرف المقابل التي تستخدمها VASPs الأخرى للوصول إليك: TRP، وعمليات الاستدعاء، واستلام البريد الإلكتروني، وSunrise.

الشبكات

ما يصل إليه كل مسار وكل شبكة اليوم، وما ينتظر عضوية.

الاختبار في البيئة التجريبية

اختبر كل نتيجة مقابل Didit Test VASP قبل الإطلاق.

إرسال المعاملات

كيفية تضمين بيانات Travel Rule في payload الخاص بـ Create Transaction.

مكتبة القواعد

كتالوج القواعد الجاهزة كاملاً، بما في ذلك حزمة Travel Rule.

webhook

اشترك في travel_rule.status.updated وفي كل عائلة أحداث أخرى.

دليل وحدة التحكم

عرض حالة Travel Rule في عرض تفاصيل المعاملة.
آخر تعديل في ٧ نوفمبر ٢٠٢٦